Using TapGestureRecognizer Proper Syntax - swift

Can anyone help me out with this error? Not sure what it's asking for...
TapGestureRecognizer Syntax
override func viewDidLoad() {
super.viewDidLoad()
let tapStart = UITapGestureRecognizer(target: self, action:#selector(tapped(gesture:)))
self.view.addGestureRecognizer(tapStart)
func tapped(gesture: UITapGestureRecognizer){
print("It actually worked")
}
}
my end goal seems like it should be fairly simple:
I want to segue to another view when the user taps anywhere on the screen. I'm creating a TapGestureRecognizer and for now am simply printing to the logs as the method so I can easily see if it works.
Thanks!

Try using #selector(tapped(gesture:)) if you're on Swift 3. If you're on Swift 2 the selector will probably be tapped(_:) or something instead.
The compiler will ensure the existence of the symbol you're referencing when you use #selector. If you use a string and let the compiler create a Selector from the string literal, all you get is a warning like this. Same disadvantage goes for using the Selector("funcname") constructor.

Try this code: Tested in Swift 3
Note: Syntax changed in Swift 3.
You can auto fix by holding function+control+option+command+f on your keyboard then Xcode will fix the issue for you.
Is nothing wrong with your code.Just, The way you using it.You,should placing your code like this...
override func viewDidLoad() {
super.viewDidLoad()
let tapStart = UITapGestureRecognizer(target: self, action:#selector(tapped(gesture:)))
self.view.addGestureRecognizer(tapStart)
}
func tapped(gesture: UITapGestureRecognizer){ // func tapped(_:) this will works to
print("Your in Right track mate")
}

Related

Weird behavior when I try to use a closure as the target of a UIButton

I know I need to replace the let keyword with lazy var for accessing the property otherwise I cannot access the 'self'.
But I found that the button.addTarget can build successfully as below,
Why? Normally if you try to access the property from a closure that needs to be a lazy variable, am I right?
For comparison, The testProperty shows red error message:
Cannot convert value of type (testController) -> () -> testController to specified type UITabBarController
import UIKit
class testController: UIViewController {
let actionButton: UIButton = {
let button = UIButton(type: .system)
button.addTarget(self, action: #selector(actionButtonTapped), for: .touchUpInside)
return button
}()
let testProperty: UIViewController = {
let obj: UIViewController = self
return obj
}()
#objc func actionButtonTapped() {
}
}
If you check the addTarget signature, you will see the following:
open func addTarget(_ target: Any?, action: Selector, for controlEvents: UIControl.Event)
First parameter target is Any?, so passing (Function) as target compiles fine. It even will work but can lead to weird issues, like opening keyboard will stop the button from calling the action.
You currently think that self refers to the instance.
But no, for an NSObject, it refers to partial application of this method. Actually running ViewController.self() before one has been instantiated, e.g. within the context of this closure, will crash your app, but self() used to still present itself as being available prior to Xcode 13.3.
As of now, Swift cannot cope with referring to this method without generating a warning. The warning tells you to use ViewController.self, which Swift can only interpret as a metatype, not a method. It doesn't understand what's going on, but at least it informs you that what you're doing is incorrect—the method is not actually the target.
Regardless of the warning, I don't know Objective-C well enough to tell you why a message sent to the method will trickle down to an instance of the related type. But don't do it.

Why my custom #objc function in swift have a strikethrough in the autofill?

I am currently learning swift 5.2 and I found a problem when I try to use #selector and #objc function.
I created a #objc function and I call it in the #selector, but the function has a strikethrough in the autofill. Just like some other functions that is deprecated. It doesn't really affect anything, the app work just fine. But I really want to know why the strikethrough appears.
Someone says that is because the function recommend a return value. And I tried to return something, like a Bool, in the #objc function. And the strikethrough is gone, but it still affect nothing, no matter what the function returns.
This really confused me. Is there any one who can tell me why? Thanks!
Here is my minimal reproducible example:
let refreshControl_A = UIRefreshControl()
let refreshControl_B = UIRefreshControl()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
refreshControl_A.addTarget(self, action: #selector(refresh_A(sender:)), for: .valueChanged)
refreshControl_B.addTarget(self, action: #selector(refresh_B(sender:)), for: .valueChanged)
}
#objc func refresh_A(sender: UIRefreshControl) {
print("Have strikethrough!")
}
#objc func refresh_B(sender: UIRefreshControl) -> Bool {
print("No strikethrough!")
return true
}
The refresh_A function has the strikethrough in the autofill.
But the refresh_B function doesn't have the strikethrough in the autofill.
The only difference between these two functions is the return type, one is Viod, and the other is Bool. Both functions work fine.

Setting and Saving UISwitch Value Error

My problem is that the UISwitch I have in my settings page keeps reverting to an off state once you leave the settings page meaning there is no way of using the switch properly. I did some searching around and found this question and answer:
How do i keep UISwitch state when changing ViewControllers?
I read this page and added the answer to my code:
#IBOutlet weak var NumSwitch: UISwitch!
override func viewDidLoad() {
super.viewDidLoad()
NumSwitch.isOn = UserDefaults.standard.bool(forKey: "switchState")
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func SaveSwitchPressed(_ sender: UISwitch) {
UserDefaults.standard.set(sender.isOn, forKey: "switchState")
}
However when I run this code I get:
Thread 1: EXC_BAD_INSTRUCTION(code=EXC_1386_INVOP,subcode=0x0)
(Highlighting the line: 'NumSwitch.isOn = UserDefaults.standard.bool(forKey: "switchState")')
[I am using Xcode 8.2.1]
I have connected two UIViewControllers to one ViewController Class so maybe that is causing the problem. I have tried many many other methods to make my switch work properly and this one seems to have worked for other people. Is there anything obvious that I am doing wrong?
[I would have liked to have added this as a comment on the aforementioned question but I do not have the reputation to do so]
If everything is hooked up to your outlets correctly then the following should work:
#IBOutlet weak var NumSwitch: UISwitch!
override func viewDidLoad() {
super.viewDidLoad()
let isOn = UserDefaults.standard.bool(forKey: "switchState")
// If the below statement prints `nil` this is what's causing the crash
print(NumSwitch)
NumSwitch.setOn(isOn, animated: false)
}
#IBAction func SaveSwitchPressed(_ sender: UISwitch) {
UserDefaults.standard.set(sender.isOn, forKey: "switchState")
}
The error Thread 1: EXC_BAD_INSTRUCTION(code=EXC_1386_INVOP,subcode=0x0) usually means that you're trying to force unwrap an optional value. The only thing that could be causing that in your code is if NumSwitch is nil.
I'd also suggest not naming your properties with capital letters, as this can be misleading. Capitalised names are usually used to indicate declared types/classes/structs etc.

Triggering function with parameters upon UIButton press

I'm trying to call a function with parameters on a button press, from what I understand #selector just checks that a function is there and then runs it. I've seen other answers where the button can be sent to the function but sadly I don't think that will solve my problem.
If I run this code (func is any function and a: 14 is just an example of a parameter being given):
myButton.addTarget(self, action: #selector(func(a: 14)), for: .touchUpInside)
I get an error saying 'Argument of #selector does not refer to an #objc method, property, or initializer
A workaround that I've been using:
myButton.addTarget(target: self, action: #selector(myFunc), for: .touchUpInside)
func myFunc() {
someOtherFunc(args)
}
The problem with this is that unless the argument that was going to be passed is global or class wide and known, you wont be able to use it.
Main Question:
Is there a way to have it run a function with parameters when a button is clicked without setting a class wide variable and assessing that instead of using a parameter?
My Solution:
Simplified, and buttons aren't setup and such...
var personName:String!
func scrollViewClicked(name:String) {
personName = name
myButton.addTarget(target: self, action: #selector(myFunc), for: .touchUpInside)
}
func myFunc() {
do something with personName
}
So pretty much I have a way of 'solving' the problem but it feels like a bit of a hack/improper way. Just trying to figure out if there is a 'real' way to do this or if it isn't meant to happen.
No, there's no way to have a UIButton run a function with arbitrary parameters.
But there is a way to have it run with some parameters, which may be useful to you.
The documentation for addTarget says that it takes a selector, which is essentially just a reference to a method. If you pass it a method with the right set of arguments, it will call it and pass whatever it's designed to pass. If you send a method with other arguments, you'll get an "unrecognized selector" error.
UIControl's addTarget understands three kinds of selectors:
func myFunc()
func myFunc(sender: UIButton)
func myFunc(sender: UIButton, forEvent event: UIEvent)
So you can set it to run a function with parameters, but the only parameters it knows how to send are the button that was pressed and the event it generated.
This is still potentially useful though, if you can use information about the button and/or the event to determine your action. For example you can set up your handler:
func myFunc(sender: UIButton, forEvent event: UIEvent) {
switch(sender) {
case myButton:
print("myButton was pressed")
default:
print("Something else was pressed.")
}
}
Depending on your use case, you could make use of the button's storyboard restoration ID, its title or other identifier, or you could even subclass UIButton and give it an instance variable to hold your parameter, like this:
class MyButtonClass: UIButton {
var argument:String = ""
}
Then when you're setting up your button you specify the argument:
myButton.argument = "Some argument"
And you can access it from your handler like this:
func myFunc(sender: UIButton, forEvent event: UIEvent) {
if let button = sender as? MyButtonClass {
print(button.argument)
}
}
It's still not as neat as just specifying your parameter in the selector, but as far as I know that's not possible.

How do you deal with Optional() in a string or any other value with Swift 2.0?

I've tried looking at other StackOverflow inquires about this, I can't seem to find the solution to this. I'm not sure if this is due to me looking at threads which are non-Swift 2.0.
I will need NSNotification to pass any kind of value to my Main View Controller. But I keep getting:
Optional(foobar)
Here's a function on View Controller B:
func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
let notificationName = "CoreLocationNSN"
let notification = NSNotification(name: notificationName, object: self, userInfo: ["doge":"foobar"])
NSNotificationCenter.defaultCenter().postNotification(notification)
}
Here's my main View Controller's initialization function:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
NSNotificationCenter.defaultCenter().addObserver(self, selector: "testNSNWithObject:", name: CoreLocationNSN, object: nil)
}
func testNSNWithObject(notification: NSNotification) {
print("Printing...")
print(String(notification.userInfo!["doge"]))
print(String(notification.userInfo?["doge"]))
print(notification.userInfo!["doge"])
print(notification.userInfo?["doge"])
}
But, here's my output:
Printing...
Optional(foobar)
Optional(foobar)
Optional(foobar)
Optional(foobar)
You just need to unwrap the String
func testNSNWithObject(notification: NSNotification) {
if let dodge = notification.userInfo?["dodge"] as? String {
print(dodge)
}
}
I suggest you to avoid the force unwrap operator ! since it's dangerous and there are several safer solutions available in Swift.
I've solved it, looks like it can be unwrapped by adding ! at the end, for some reason I thought my compiler was yelling at me for doing so:
func testNSNWithObject(notification: NSNotification) {
print("Printing...")
print(String(notification.userInfo!["doge"]))
print(String(notification.userInfo?["doge"]))
print(notification.userInfo!["doge"]!) // <<-- THIS ONE WORKED!
print(notification.userInfo?["doge"]!)
}
Just going to keep this here since this issue has been troublesome for me in the past and the syntax seems very strange.
Does anyone know what else I could have done instead that would've removed the "Optional()" from the string?