I've got a custom subclass of UIStackView
that's got some UIImageViews
inside it. I want the view to accept a pan gesture to track a slow swipe across its surface. When I configure a UIPanGestureRecognizer
in Interface Builder it works, but not in code. Here's an abbreviated version of the class:
@IBDesignable
class CustomView: UIStackView {
private let panRecognizer = UIPanGestureRecognizer(target: self, action: #selector(CustomView.singleFingerSwipe))
override init(frame: CGRect) {
super.init(frame: frame)
initializeProperties()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initializeProperties()
}
@IBAction
private func singleFingerSwipe(_ sender: UIPanGestureRecognizer) {
print("Panning...")
}
private func initializeProperties() {
addGestureRecognizer(panRecognizer)
for _ in 1...3 {
let imageView = UIImageView(image: UIImage(named: "MyImage"))
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.isUserInteractionEnabled = true
addArrangedSubview(imageView)
imageView.widthAnchor.constraint(equalTo: imageView.heightAnchor).isActive = true
imageView.heightAnchor.constraint(equalTo: self.heightAnchor).isActive = true
}
}
}
As I swipe around inside the star view, no output prints. But if I drop a Pan Gesture Recognizer onto the view in IB and hook it up to the same selector, it works fine. What am I missing?
make your panRecognizer lazy var so that its initialization is delayed till its first use and till the time the frame of stack view got set. like
private lazy var panRecognizer = UIPanGestureRecognizer(target: self, action: #selector(CustomView.singleFingerSwipe))
or move the declaration of panRecognizer to initializeProperties() method right before addGesture call. like
let panRecognizer = UIPanGestureRecognizer(target: self, action: #selector(CustomView.singleFingerSwipe))
addGestureRecognizer(panRecognizer)
EDIT
The reason is the target is setting null and hence it unable to fire the method, when you are initializing with "private let ..". Since the init gets called later and object isn't initialized for CustomView. Here is the console log when declared with let(constant):
Printing description of self.panRecognizer: ; target= <(action=singleFingerSwipe:, target=<(null) 0x0>)>>
But when its lazy var or declared within the method after init is called, the instance for CustomeView is generated till the time of use of panRecognizer. the console print is:
Printing description of self.panRecognizer.storage.some: ; target= <(action=singleFingerSwipe:, target=)>>
here you can see the target is set.
P.S. you can check the same with debugger