Could not execute a function on UITapGestureRecognizer - iphone

Whenever i touch on the screen nothing happens. looks like handleDismiss() function is never fired. Does anybody knows what is the real problem here.
import UIKit
class SettingLauncher : NSObject{
let blackView = UIView();
func showSettingMenu(){
if let window = UIApplication.shared.keyWindow{
blackView.backgroundColor = UIColor(white: 0, alpha: 0.5);
blackView.isUserInteractionEnabled = true;
blackView.addGestureRecognizer(UITapGestureRecognizer(target: nil, action: #selector(self.handleDismiss)));
window.addSubview(blackView);
blackView.frame = window.frame;
blackView.alpha = 0.1;
UIView.animate(withDuration: 0.5) {
self.blackView.alpha = 0.5;
}
}
}
func handleDismiss(){
print("Touch recognised");
}
}

You have to tell the UITapGestureRecognizer which class will be handling the response - not just which function, but which class
Change
blackView.addGestureRecognizer(UITapGestureRecognizer(target: nil, action: #selector(self.handleDismiss)));
To
blackView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.handleDismiss)))
Here's an example which I have working. I am initiating the view from a button - I don't know how yours is started, but that shouldn't matter
class ViewController: UIViewController
{
let blackView = UIView()
override func viewDidLoad()
{
super.viewDidLoad()
}
func handleDismiss(){
print("Touch recognised");
}
#IBAction func cmdDoOtherStuff(_ sender: Any)
{
if let window = UIApplication.shared.keyWindow{
blackView.backgroundColor = UIColor(red: 1.0, green: 0, blue: 0, alpha: 0.5)//(r: 0, alpha: 0.5)
blackView.isUserInteractionEnabled = true
blackView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.handleDismiss)))
window.addSubview(blackView)
blackView.frame = window.frame
blackView.alpha = 0.1
UIView.animate(withDuration: 0.5) {
self.blackView.alpha = 0.5;
}
}
}
}

I got the above error because I called the function showSettingMenu() through another class function as below:-
func handleMoreSetting(){
let settingLauncher = SettingLauncher();
settingLauncher.showSettingMenu(); // Call SettingLauncher class
}
The problem went away when I declared settingLauncher as global instance like below.
let settingLauncher = SettingLauncher();
func handleMoreSetting(){
settingLauncher.showSettingMenu(); // Call SettingLauncher class
}
Do anybody knows why the problem occured? In both cases, I am calling the same function but with a different result.

Related

NSSlider custom subclass - how to maintain the link between the knob position and user interaction?

Trying to create a custom NSSlider. Overriding the drawKnob() method of the NSSliderCell changes the knob's appearance but doing this somehow disconnects the link between the knob's position and user interactions with the slider.
In the objective C example that is often referenced (https://github.com/lucasderraugh/LADSlider) it looks like when you override drawKnob() you then need to explicitly deal with the startTracking method, but I haven't found a solution that works for me - at the moment I am just setting the cell's startTracking 'at' property to the current value of the slider, but not sure what the right approach is.
Quite a few examples include an NSCoder argument in the cell's custom initialiser - I don't understand why, but maybe this has something to do with ensuring a connection between the display of the knob and the actual slider value?
import Cocoa
class ViewController: NSViewController {
var seekSlider = NSSlider()
var seekSliderCell = SeekSliderCell()
override func viewDidLoad() {
super.viewDidLoad()
seekSlider = NSSlider(target: self, action: #selector(self.seek(_:)))
seekSlider.cell = seekSliderCell
seekSlider.cell?.target = self
seekSlider.cell?.action = #selector(self.seek(_:))
seekSlider.isEnabled = true
seekSlider.isContinuous = true
view.addSubview(seekSlider)
}
#objc func seek(_ sender: NSObject) {
let val = seekSlider.cell?.floatValue
let point = NSPoint(x: Double(val!), y: 0.0)
seekSlider.cell?.startTracking(at: point, in: self.seekSlider)
}
}
class SeekSliderCell: NSSliderCell {
// required init(coder aDecoder: NSCoder) {
// super.init(coder: aDecoder)
// }
override func drawKnob() {
let frame = NSRect(x: 0.0, y: 6.0, width: 20.0, height: 10.0)
let c = NSColor(red: 0.9, green: 0.0, blue: 0.6, alpha: 1.0)
c.setFill()
NSBezierPath.init(roundedRect: frame, xRadius: 3, yRadius: 3).fill()
}
override func startTracking(at startPoint: NSPoint, in controlView: NSView) -> Bool {
return true
}
}
The documentation of drawKnob() states:
Special Considerations
If you create a subclass of NSSliderCell, don’t override this method. Override drawKnob(_:) instead.
Instead of
func drawKnob()
override
func drawKnob(_ knobRect: NSRect)
Example:
class ViewController: NSViewController {
var seekSlider = NSSlider()
var seekSliderCell = SeekSliderCell()
override func viewDidLoad() {
super.viewDidLoad()
seekSlider = NSSlider(target: self, action: #selector(self.seek(_:)))
seekSlider.cell = seekSliderCell
seekSlider.cell?.target = self
seekSlider.cell?.action = #selector(self.seek(_:))
seekSlider.isEnabled = true
seekSlider.isContinuous = true
view.addSubview(seekSlider)
}
#objc func seek(_ sender: NSObject) {
let val = seekSlider.cell?.floatValue
print("\(String(describing: val))")
}
}
class SeekSliderCell: NSSliderCell {
override func drawKnob(_ knobRect: NSRect) {
var frame = NSRect(x: 0.0, y: 6.0, width: 20.0, height: 10.0)
frame.origin.x = knobRect.origin.x + (knobRect.size.width - frame.size.width) / 2
frame.origin.y = knobRect.origin.y + (knobRect.size.height - frame.size.height) / 2
let c = NSColor(red: 0.9, green: 0.0, blue: 0.6, alpha: 1.0)
c.setFill()
NSBezierPath.init(roundedRect: frame, xRadius: 3, yRadius: 3).fill()
}
}

NSCollectionViewItem button action No effect

Clicking the button has no effect
I hope someone with good intentions can help me see why
Below is my code
class cell_word_list_1: NSCollectionViewItem {
static let item = NSUserInterfaceItemIdentifier(rawValue: "cell_list_id_1")
override func viewDidLoad() {
super.viewDidLoad()
}
override func loadView() {
let rootView = NSView()
rootView.wantsLayer = true
rootView.layer?.backgroundColor = #colorLiteral(red: 0.5725490451, green: 0, blue: 0.2313725501, alpha: 1)
let button = NSButton()
button.frame = NSRect(x: 0, y: 0, width: 100, height: 100)
button.action = #selector(click)
rootView.addSubview(button)
self.view = rootView
}
#objc func click() {
print("------> button click")
}
}
Without a target the action is sent to the first responder and up the responder chain. This doesn't work because the item is not in the responder chain. Solution: set the target of the button:
button.target = self

Swift Xib UiView BottomSheet being called multiple times

so i make this bottomsheet view with xib and theres nothing wrong with my code, its just i only want to show it once, i mean like everytime i click the button its get triggered. which is fine but if i rapidly click the button it will also load bunch of time according on how many times i click. i only want to show once i mean no matter how much you rapidly click it only gonna show the xib view once, until i dismiss the button on the xib and it will do the same thing.
here's some video to make it more clearly
https://drive.google.com/file/d/12pwGdTiP_1QZlYc8tV-BlIIQQ5yYrfto/view?usp=sharing
i put a gif on that gdrive link
for the code
Xib Controller :
OrderActionSheetView: UIViewController {
#IBOutlet weak var Text: UILabel!
#IBOutlet weak var vieww: UIView!
#IBOutlet weak var botView: UIView!
#IBAction func cobaLagiBTn(_ sender: Any) {
let closeView = screenSize.height
UIView.animate(withDuration: 0.3, animations: {
self.view.alpha = 0.0
self.view.transform = CGAffineTransform(scaleX: 1.0, y: 1.0)
let frame = self.view.frame
self.view.frame = CGRect(x: 0, y: closeView, width: frame.width, height: frame.height)
})
}
let fullView: CGFloat = 0
let screenSize: CGRect = UIScreen.main.bounds
override func viewDidLoad() {
super.viewDidLoad()
vieww.layer.cornerRadius = 20
vieww.layer.borderWidth = 0.5
vieww.layer.borderColor = UIColor(red:222/255, green:225/255, blue:227/255, alpha: 1).cgColor
vieww.clipsToBounds = true
botView.layer.masksToBounds = false
botView.layer.shadowColor = UIColor.black.cgColor
botView.layer.shadowOpacity = 0.14
botView.layer.shadowOffset = CGSize(width: 0, height: 0)
botView.layer.shadowRadius = 2.7
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
UIView.animate(withDuration: 0.3) { [weak self] in
let frame = self?.view.frame
let yComponent = self?.fullView
self?.view.frame = CGRect(x: 0, y: yComponent!, width: frame!.width, height: frame!.height)
}
}
func prepareBackgroundView(){
let blurEffect = UIBlurEffect.init(style: .light)
let visualEffect = UIVisualEffectView.init(effect: blurEffect)
let bluredView = UIVisualEffectView.init(effect: blurEffect)
bluredView.contentView.addSubview(visualEffect)
visualEffect.frame = UIScreen.main.bounds
bluredView.frame = UIScreen.main.bounds
view.insertSubview(bluredView, at: 0)
}
func Show(){
self.view.transform = CGAffineTransform(scaleX: 1.3, y: 1.3)
self.view.alpha = 0.0
UIView.animate(withDuration: 0.25, animations: {
self.view.alpha = 1.0
self.view.transform = CGAffineTransform(scaleX: 1.0, y: 1.0)
})
}
}
View Controller :
class BottomSheetViewController: UIViewController {
#IBAction func Button(_ sender: Any) {
let text = "Connection Failed"
addBottomSheetView(text: text)
}
override func viewDidLoad() {
super.viewDidLoad()
}
func addBottomSheetView(text : String) {
// 1- Init bottomSheetVC
let bottomSheetVC = OrderActionSheetView()
// 2- Add bottomSheetVC as a child view
self.addChild(bottomSheetVC)
self.view.addSubview(bottomSheetVC.view)
bottomSheetVC.didMove(toParent: self)
// 3- Adjust bottomSheet frame and initial position.
let height = view.frame.height
let width = view.frame.width
bottomSheetVC.view.frame = CGRect(x: 0, y: self.view.frame.maxY, width: width, height: height)
bottomSheetVC.Text.text = text
}
}
i just need to know how to stop popping up twice, cause its kinda really some big bugs.....
like i said earlier i just want the xib view to shown only once no matter how many times you rapidly click the button.
Thanks guys :)
As here you add a new instance every click
func addBottomSheetView(text : String) {
// 1- Init bottomSheetVC
let bottomSheetVC = OrderActionSheetView()
// 2- Add bottomSheetVC as a child view
self.addChild(bottomSheetVC)
self.view.addSubview(bottomSheetVC.view)
bottomSheetVC.didMove(toParent: self)
So either
1-You need to add a bool variable like
var isShown = false
and in beginning of method add this code
guard !isShown else { return }
isShown = true
and when you remove the view make
isShown = false
Or
2- create an instance variable like
var bottomSheetVC:OrderActionSheetView?
and in beginning of method
guard bottomSheetVC == nil else { return }
bottomSheetVC = OrderActionSheetView()
and when you remove it do
bottomSheetVC.view.removeFromSuperview()
bottomSheetVC = nil

Add Gesture Recogniser

For some reason the gesture recogniser does not work, it doesn't make the background normal again. How do I fix it so the selector (self.dissmissMenu) works?
In View controller:
let menu = Menu()
#IBAction func menuButton(sender: AnyObject) {
menu.runMenu()
}
In Menu:
import UIKit
class Menu: NSObject {
let dimming = UIView()
public func runMenu(){
if let window = UIApplication.sharedApplication().keyWindow{
dimming.frame = window.frame
dimming.backgroundColor = UIColor(white: 0, alpha: 0.5)
dimming.addGestureRecognizer(UITapGestureRecognizer(target: self, action: Selector(self.dissmissMenu())))
window.addSubview(dimming)
UIView.animateWithDuration(0.5, animations: {
self.dimming.alpha = 1
})
}
}
public func dissmissMenu(){
UIView.animateWithDuration(0.5, animations: {
self.dimming.alpha = 0
})
}
}
Try to rewrite the call to addGestureRecognizer like this:
dimming.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(dissmissMenu())))

How to add Animation Button like this?

How can I create an animated and shadowed button like in this video
(Once you click the button, it shines and swings)
Here is my code:
(This code implement swing and shadow but needs to be organized and arranged so that it is capable of running the light and the swing with one click and the button doesn't move from its place unnecessarily)
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var aBtn: ButtonWithShadow!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func btnClick(_ sender: UIButton) {
if sender.isSelected {
sender.isSelected = false
sender.layer.shadowColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.50).cgColor
sender.layer.shadowOffset = CGSize(width: 0, height: 3)
sender.layer.shadowOpacity = 1.0
sender.layer.shadowRadius = 10.0
sender.layer.masksToBounds = false
} else {
sender.isSelected = true
sender.layer.shadowColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.0).cgColor
}
aBtn.layer.anchorPoint = CGPoint(x: 0.5, y: 0)
aBtn.layer.transform = CATransform3DMakeRotation(-.pi / 15, 0, 0, 1)
let needleAnim = CABasicAnimation(keyPath: "transform.rotation.z")
needleAnim.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
needleAnim.duration = 1.0 as? CFTimeInterval ?? CFTimeInterval()
needleAnim.repeatCount = 5
needleAnim.autoreverses = true
// Setting fillMode means that, once the animation finishes, the needle stays in its end position.
needleAnim.fillMode = kCAFillModeForwards
needleAnim.isRemovedOnCompletion = true
needleAnim.toValue = Double.pi / 15
aBtn.layer.add(needleAnim, forKey: nil)
}
}
and
import UIKit
class ButtonWithShadow: UIButton {
override func draw(_ rect: CGRect) {
//updateLayerProperties()
}
}
Thanks for your time
Your code works as written and provides a swinging button when clicked. Make sure you control-click dragged from the button to the button definition.
You'll need to add two images to your button, one for the off state and one for the on state.
and on
Here is the intermediate result of adding setting the images.
and the way to change the images is to add them to the assets catalog and then change them with :
if sender.isSelected {
sender.isSelected = false
sender.setImage(#imageLiteral(resourceName: "off"), for: .normal)
sender.layer.shadowColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.50).cgColor
sender.layer.shadowOffset = CGSize(width: 0, height: 3)
sender.layer.shadowOpacity = 1.0
sender.layer.shadowRadius = 10.0
sender.layer.masksToBounds = false
} else {
sender.isSelected = true
sender.setImage(#imageLiteral(resourceName: "onbtn"), for: .normal)
sender.layer.shadowColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.0).cgColor
}