Where do you declare UITapGestureRecognizer() in relation to addGestureRecognizer() in Swift? - swift

Problem
centerLabelGesture is being defined outside of the scope of viewDidLoad(), so at the time that .addGestureRecognizer(centerLabelGesture) is called, centerLabelGesture is not defined yet.
import UIKit
import SnapKit
class ViewController: UIViewController {
var screen: UIView!
var centerLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
screen = UIView()
centerLabel = UILabel()
view.addSubview(screen)
screen.addSubview(centerLabel)
screen.backgroundColor = .white
screen.snp.makeConstraints { (make) in
make.top.equalTo(view)
make.right.equalTo(view)
make.left.equalTo(view)
make.bottom.equalTo(view)
}
centerLabel.text = "I hope I'm centered."
centerLabel.snp.makeConstraints { (make) in
make.center.equalTo(screen)
}
centerLabel.isUserInteractionEnabled = true
centerLabel.addGestureRecognizer(centerLabelGesture)
}
let centerLabelGesture = UITapGestureRecognizer(target: self, action: #selector(centerLabelTapped))
#objc func centerLabelTapped() {
centerLabel.text = "Ouch, you tapped me!"
}
}
Update 1/19/2019
matt pointed out that centerLabelGesture needs to be declared prior to centerLabel.addGestureRecognizer(centerLabelGesture), inside viewDidLoad()

This is a subtle mistake. The problem is the place you've put this line:
let centerLabelGesture = UITapGestureRecognizer(target: self, action: #selector(centerLabelTapped))
Move that line into the viewDidLoad code:
centerLabel.isUserInteractionEnabled = true
let centerLabelGesture = UITapGestureRecognizer(target: self, action: #selector(centerLabelTapped))
centerLabel.addGestureRecognizer(centerLabelGesture)
The reason is that where you've got that line, it's an instance property, and when you say self as the target in an instance property initializer, it doesn't mean what you think it does (it means the class, not the instance), so the message when you tap is misdirected and does nothing.
I have filed a bug on this issue; in my opinion the compiler should at least warn you that you're making a potential mistake.

Try adding this line in viewDidLoad
centerLabelGesture.numberOfTapsRequired = 1

Related

Why is superview returning nil? [duplicate]

This question already has answers here:
Displaying instances of view controllers within Xcode without disturbing the current hierarchy
(1 answer)
Adding a view controller as a subview in another view controller
(8 answers)
Closed 1 year ago.
At the very bottom is my code where MainController initiates a subview called setController. However, within setController's code I get back a nil when I try to the superview:
override func viewDidLoad() {
super.viewDidLoad()
print(self.view.superview ?? "no parent")
}
I am assigning the second view as a subview, but obviously I am missing something. Have I misunderstood how UIView hierarchy works?
class MainController: UIViewController {
private lazy var setController = SetController()
var invButton : MyButton!
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .black
invButton = makeButton(vControl: self, btype: ButtType.inv, action: #selector(self.buttonAction(sender:)))
invButton.frame.origin.x = self.view.frame.width * 0.1
invButton.frame.origin.y = self.view.frame.height * 0.1
invButton.setTitle("Settings", for: .normal)
}
override var prefersStatusBarHidden: Bool {
return false
}
#objc func buttonAction(sender: UIButton!) {
guard let theButton = sender as? MyButton else { return}
UIView.transition(with: self.view, duration: 0.5, options: .transitionCurlDown, animations: { [self] in
self.view.addSubview(setController.view)
}, completion: nil)
}
}
For anyone coming across this problem as well, I found the answer here:
https://guides.codepath.com/ios/Adding-and-Removing-Child-View-Controllers
The error was mine in that I didn't realize that adding a subview was a multiple line process.
So self.view.addSubview(setController.view) became:
addChild(setController);
view.addSubview(setController.view);
setController.didMove(toParent: self);

How to add a tap gesture to multiple UIViewControllers

I'd like to print a message when an user taps twice on the remote of the Apple TV. I got this to work inside a single UIViewController, but I would like to reuse my code so that this can work in multiple views.
The code 'works' because the app runs without any problems. But the message is never displayed in the console. I'm using Swift 3 with the latest Xcode 8.3.3. What could be the problem?
The code of a UIViewController:
override func viewDidLoad() {
super.viewDidLoad()
_ = TapHandler(controller: self)
}
The code of the TapHandler class
class TapHandler {
private var view : UIView?
required init(controller : UIViewController) {
self.view = controller.view
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.message))
tapGesture.numberOfTapsRequired = 2
self.view!.addGestureRecognizer(tapGesture)
self.view!.isUserInteractionEnabled = true
}
#objc func message() {
print("Hey there!")
}
}
Your TapHandler just getting released. Try This:
var tapHandler:TapHandler? = nil
override func viewDidLoad() {
super.viewDidLoad()
tapHandler = TapHandler(controller: self)
}
I have tested the code and is working.

Add tap gesture recognizer from another file

Currently I have 2 classes: ViewController and class A. My objective is to add tap gesture recognizer to a UIView from class A. My ViewController:
class ViewController {
#IBOutlet var area: UIView!
func enterClassA() {
let classA = A(self.area)
//some processing
}
}
class A : UIGestureRecognizerDelegate {
private var currView: UIView!
init(newView: UIView) {
self.currView = newView
self.addTapGesture()
}
func addTapGesture() {
let tap = UITapGestureRecognizer(target: self.currView, action:#selector(tapDetected(_:)))
tap.delegate = self.currView as! UIGestureRecognizerdelegate? //***
self.currView.addGestureRecognizer(tap)
}
#objc func tapDetected(_ tapGesture: UIGesturerecognizer) {
print ("Tap detected!")
}
}
But now it's giving error: could not cast value of type 'UIView' to "UIGestureRecognizerDelegate". I tried modifying line *** to
tap.delegate = self
But it's still not working, showing error: unrecognized selector sent to instance. Deleting the line gives the same error. May I know if this is even doable or not (adding gesture recognizer from a different class)? If so then how should I approach it?
In this line
let tap = UITapGestureRecognizer(target: self.currView, action:#selector(tapDetected(_:)))
You say the gesture recognizer to search the tapDetected() function in the self.currView object. Try to change the target:
let tap = UITapGestureRecognizer(target: self, action:#selector(tapDetected(_:)))
And yes, it should be like that:
tap.delegate = self
Since your A class conforms to UIGestureRecognizerDelegate.

How to make a gestureRecogniser selector work in swift 3?

Below is the code I have been using to try and add a gesture recogniser to something. I am getting the yellow error: "No method declared with objective C selector dragging" and then the program is crashing when I go to pan on it. The code and way of using a selector seems to work in all tutorials but it's the problem here.
class GameViewController: UIViewController, UIGestureRecognizerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let p = UIPanGestureRecognizer(target: self, action: #selector("dragging"))
p.delegate = self
characterGridView!.addGestureRecognizer(p)
}
func dragging(p: UIPanGestureRecognizer) {
print("works")
}
Your selector is incorrect.
Change
let p = UIPanGestureRecognizer(target: self, action: #selector("dragging"))
to
let p = UIPanGestureRecognizer(target: self, action: #selector(dragging(p:)))

Unwrapping UILabel always returns nil

I've hooked a UILabel to my VC using the storyboard, does generating a weak var but connected to a storyboard reference. Anyway, whenever I try to unwrap it, all I get is a nil value. What confuses me the most is that I'm able to access it on another method. I've already safe unwrapped it, it avoids the compiler errors, but doesn't solve my problem.
The compiler message is: fatal error: unexpectedly found nil while unwrapping an Optional value
Here is some of the code:
#IBOutlet weak var linesLeftCountLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
skView = SKView(frame: sceneView.frame)
skView.multipleTouchEnabled = false
//Create and configure the scene.
scene = LogoRefactoryScene(size: skView.bounds.size)
scene.scaleMode = .AspectFill
skView.presentScene(scene)
tapGesture = UITapGestureRecognizer(target: self, action: "eraseLine:")
tapGesture.delegate = self
view.addGestureRecognizer(tapGesture)
view.addSubview(skView)
}
Here I'm able to access it:
#IBAction func colorPickerOfColor(sender: UIButton) {
var pickerName = ""
switch sender.tag {
case 0:
pickerName = "green"
case 1:
pickerName = "red"
case 2:
pickerName = "blue"
default:
pickerName = "orange"
}
pickerName += "ColorPicker"
colorPickerView.image = UIImage(named: pickerName)
currentColorName = ColorSelection.fromRaw(sender.tag + 1)!.colorName
let color = ColorSelection.fromRaw(sender.tag + 1)!.getSKColor()
scene.lineColor = color
linesLeftCountLabel.textColor = color
}
Here is where the compiler complains:
func lineHasBeenDrawn() {
linesLeftCountLabel.text = "Any String"
}
Sorry for that guys. lineHasDrawn is a delegate method, that's why the strange behaviour.
My bad.
Please check if you have connected the referencing outlet in the storyboard to the IBOutlet of your code