Jonathan Daley Jonathan Daley -4 years ago 122
Swift Question

View is drawn but gesture recognizer are not detected

I have a

UIScrollView
which has a
UIView
as a subview, which in return holds all my
FooView
s, which is a
UIView
as well. Each of those
FooView
s has a gesture recognizer. The gestures are being detected just fine for the first
FooView
in my scroll view (meaning, the selector is being triggered correctly), but all subsequent
FooView
s do not trigger that gesture.

This question's answer Subview Gesture Recognizer not being called I assume explains the problem that I am facing. Quoting: "[...] the issue was that the subview with gesture recognizer was outside the frame of the superview. This means even though the view was being drawn, the gestures were not detected".

I understand the problem at hand, but am not sure how to solve it.

Btw: It works perfectly, if I add the
FooView
s directly to the scroll view, instead of adding them to
contentView
. However, for my use case that is not an option, because I am using auto-layout and the constraints would be messed up otherwise.

Attached you'll find my MWE where you can see that tapping on the first view will print out "Tapped", but there will be no output for the other views. The code is just a playground example, so can be copied there to run.

import UIKit
import PlaygroundSupport

class AwesomeView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)

backgroundColor = UIColor.getRandomColor()

let tapGesture = UITapGestureRecognizer(target: self, action: #selector(tapHandler))
self.addGestureRecognizer(tapGesture)
}

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}

override func layoutSubviews() {
guard let superview = superview else {
return
}

self.frame = superview.frame
}

func tapHandler(_ sender: AnyObject) {
print("Tapped")
}
}

extension UIColor {
static func getRandomColor(_ hash: UInt32 = arc4random()) -> UIColor{

let randomRed:CGFloat = CGFloat(hash) / CGFloat(UInt32.max)
let randomGreen:CGFloat = CGFloat(hash << 4) / CGFloat(UInt32.max)
let randomBlue:CGFloat = CGFloat(hash << 2) / CGFloat(UInt32.max)

return UIColor(red: randomRed, green: randomGreen, blue: randomBlue, alpha: 1.0)
}
}

class DummyVC: UIViewController, UIScrollViewDelegate {

let scrollView = UIScrollView(frame: CGRect(x:0, y:0, width:320,height: 300))
var contentView: UIView!
var frame: CGRect = CGRect(x:0, y:0, width:0, height:0)

override func viewDidLoad() {
super.viewDidLoad()

contentView = UIView(frame: scrollView.frame)
scrollView.addSubview(contentView)

self.view.addSubview(scrollView)

for index in 0..<4 {

frame.origin.x = self.scrollView.frame.size.width * CGFloat(index)
frame.size = self.scrollView.frame.size
self.scrollView.isPagingEnabled = true

let subView = AwesomeView(frame: frame)
self.contentView.addSubview(subView)
}

self.scrollView.contentSize = CGSize(width:self.scrollView.frame.size.width * 4, height: self.scrollView.frame.size.height)
}
}

PlaygroundPage.current.liveView = DummyVC().view
PlaygroundPage.current.needsIndefiniteExecution = true

Answer Source

I think you need to correctly set the contentView's size. Right now you set the contentView's frame to the size of the scrollView's frame, which is 1/4 the total size. Notice the last line in viewDidLoad where we set the contentView's frame to be the scrollView's content size.

    class DummyVC: UIViewController, UIScrollViewDelegate {

        let scrollView = UIScrollView(frame: CGRect(x:0, y:0, width:320,height: 300))
        var contentView: UIView!
        var frame: CGRect = CGRect(x:0, y:0, width:0, height:0)

        override func viewDidLoad() {
            super.viewDidLoad()

            var contentFrame = scrollView.frame
            contentFrame.size.width *= 4
            contentView = UIView(frame: .zero)
            scrollView.addSubview(contentView)

            self.view.addSubview(scrollView)

            for index in 0..<4 {

                frame.origin.x = self.scrollView.frame.size.width * CGFloat(index)
                frame.size = self.scrollView.frame.size
                self.scrollView.isPagingEnabled = true

                let subView = AwesomeView(frame: frame)
                self.contentView.addSubview(subView)
            }

            self.scrollView.contentSize = CGSize(width:self.scrollView.frame.size.width * 4, height: self.scrollView.frame.size.height)
            //new line to set contentView's frame
            contentView.frame = CGRect(origin: .zero, size: scrollView.contentSize)
        }
    }
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download