環境
Swift 4
Xcode 10
問題
UIPageViewControllerにUIButtonを置くと横スクロールできない
UIPageViewControllerの横スクロールのイベントがUIButtonに取られちゃって、ボタンの上からスクロールを始めると横スクロールできない。結果、画面に大きめのボタンがあるとすごくスクロールしにくい。
func touchesShouldCancel(in view: UIView) -> Boolでtrueを返せばいいのでは?
func touchesShouldCancel(in view: UIView) -> Bool でtrueを返すと、UIScrollView上にUIButtonをおいてもいい感じになる。(Tapできるし、スクロールしたらTapがキャンセルされてそのままスクロールできる)
touchesShouldCancel(in:) - UIScrollView | Apple Developer Documentation
https://developer.apple.com/documentation/uikit/uiscrollview/1619387-touchesshouldcancel
UIPageViewControllerではできない😂
func touchesShouldCancel(in view: UIView) -> Boolでtrueを返すためにはoverrideしないといけないのだが、UIPageViewControllerの中にあるUIScrollViewってどうやったらoverrideできるの😂
※UIScrollView, UITableView, UICollectionViewはoverrideしたサブクラスを作成することでいい感じにできます👍
解決法
UIButtonをUIPageViewController上で使わない!
func touchesShouldCancel(in view: UIView) -> BoolのDocumentに書いてあるんですが、UIControlを継承しているクラスだとfalseを返して、それ以外だったらtrueを返すらしいんですよ。
なので、UIButtonじゃなくてUIViewにTapイベントとか設定すればいい感じに動く👍
まとめ
Document読もう!
touchesShouldCancel(in:) - UIScrollView | Apple Developer Documentation
https://developer.apple.com/documentation/uikit/uiscrollview/1619387-touchesshouldcancel
おまけ
以前の記事でUIButtonのサブクラスで、Highlight時に白っぽくなるボタンを作ったのですが、それのUIView版のコードを張っておきます。以下のUIViewを使用すれば横スクロールもいい感じ🌟
// タップイベントを処理したい場合は以下のdelegateを使用してください
protocol HighlightViewDelegate: class {
    func didTapHighlightView()
}
/// 以下参照
/// https://developer.apple.com/documentation/uikit/uiscrollview/1619387-touchesshouldcancel
class HighlightView: UIView {
    
    // MARK: Parameter
    
    weak var delegate: HighlightViewDelegate?
    
    private let highlightedAlpha: CGFloat = 0.4
    private lazy var normalBgColor: UIColor = .clear
    private lazy var highlightedBgColor: UIColor = {
        return UIColor.white.withAlphaComponent(CGFloat(1) - self.highlightedAlpha)
    }()
    
    private var isShowAnimation: Bool = false
    private var isHideAnimation: Bool = false
  
    private var isHighlighted: Bool = false {
        willSet {
            self.doAnimation(isShow: newValue) {
                self.backgroundColor = newValue ? self.highlightedBgColor : self.normalBgColor
            }
        }
    }
    
    // MARK: Init
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        self.commonInit()
    }
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.commonInit()
    }
    func commonInit() {
        let tap = UITapGestureRecognizer(target: self, action: #selector(self.didTapSelf))
        self.addGestureRecognizer(tap)
    }
    
    // MARK: Func
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        self.isHighlighted = true
    }
    
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        self.isHighlighted = false
    }
    
    override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
        self.isHighlighted = false
    }
    
    @objc
    private func didTapSelf() {
        self.delegate?.didTapHighlightView()
    }
}
extension HighlightView {
    
    private func doAnimation(isShow: Bool, animation: @escaping () -> Void) {
        if isShow {
            if !self.isShowAnimation {
                self.isShowAnimation = true
                UIView.animate(withDuration: 0.1, animations: {
                    animation()
                }, completion: { _ in
                    self.isShowAnimation = false
                })
            }
        } else {
            if !self.isHideAnimation {
                self.isHideAnimation = true
                UIView.animate(withDuration: 0.3, animations: {
                    animation()
                }, completion: { _ in
                    self.isHideAnimation = false
                })
            }
        }
    }
}