【Swift】ScrollView内のボタンが押せなくなった時の対処法
ScrollViewのcontentView下部(スクロールしないと現れない部分)にボタンを設置した時に、ボタンがクリック出来ず試行錯誤したので、その時の対処法を書いていきます。
1. 問題の再現
まずは問題を再現していきましょう。
storyboardとauto layoutを使っていて、似たような設定であれば同じ問題の可能性が高いと思います。
1-1. Storyboardの設定
以下storyboardのview hierarchyです。
ScrollViewの中にUIViewを追加し、それぞれ違う位置にボタンを配置しています。
問題を再現するために、iPhone6のシミュレーターを使います。
うまく再現するには、画面が表示された際にファーストビューでは青いボタンのみが視認出来る状態にしてください。
ScrollViewとScrollView直下のUIViewのlayoutsconstraintは下記のようになっています。
1. scrollviewのlayouts constraint
四辺をSafe Areaに引っ付けています。
2. scrollviewcontentのlayouts constraint
四辺をScrollView(Superview)に引っ付けて、さらにAlign XとYをScrollView(Superview)に対して0にしています。
1-2. UIViewControllerの設定
次に、ボタンをタップしたらわかるようにUIViewControllerにIBActionを追加します。
さらに、UIViewControllerのSubView(scrollViewなど)がレイアウトされたタイミングでscrollViewのスクロール領域をscrollViewContentと同じ高さにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
import UIKit class ViewController: UIViewController { @IBOutlet weak var scrollView: UIScrollView! @IBOutlet weak var scrollViewContent: UIView! //scrollViewの高さはこのタイミングで変える必要がある override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() changeScrollViewContentSize() } func changeScrollViewContentSize(){ //scrollViewのスクロール領域をscrollViewContentの高さと合わせます。 scrollView.contentSize = CGSize(width: scrollView.bounds.width, height: 818) } func showAlert(_ message: String){ let alertController = UIAlertController(title: nil, message: message, preferredStyle: .alert) let action = UIAlertAction(title: "ok", style: .cancel, handler: nil) alertController.addAction(action) present(alertController, animated: true, completion: nil) } @IBAction func didTapBlueButton(_ sender: Any) { showAlert("青いボタンを押したよ!") } @IBAction func didTapRedButton(_ sender: Any) { showAlert("赤いボタンを押したよ!") } } |
それでは実際にビルドして試してみましょう。
青いボタンをタップしたらちゃんとアラートが出ますが、赤いボタンはタップしても何も起きません。
2.ボタンが押せない理由
結論から言いますと、画面をスクロールした際に、赤いボタンがscrollContentView内に収まらなくなってしまっているため、タップ出来ない状態となっています。
ボタンなどがタップできなくなった時に、個人的に私がまず確認することは以下の2つです。
- isUserInterationEnabledがfalseになっていないか
- ViewHierarchyを見てみて、別の要素が覆いかぶさっていないかなどのチェック
①isUserInterationEnabledがfalseになっていないか
何かの間違いでチェックを外していないか、コードで明示的に指定していないか確認します。
②View Hierarchyを見てみて、別の要素が覆いかぶさっていないかなどのチェック
XcodeにはView Debuggerというすごく便利な機能がついてます。
ビルド後にコンソールウィンドウの上にある、下記のアイコンを押すことで、絵画されているViewを階層的に見ることが出来ます。
View Debugger Icon:
実際のスクリーンショットが以下です。
別アングル:
上記のように、赤いボタンがscrollContentViewから外れてしまっているのがわかります。
それではこれを修正していきましょう。
3.対処法
色々調べたり、弄ったりしてたらalign contents to y axisが原因になっているようです。スクロールすると、autolayoutが勝手にリサイズしてy axisが0で固定されるのが原因なっているのではないかと思います(的外れなことを言ってたらすみません)。
色々試した結果、以下の手順でボタンをcontentView内に収めることができました。
- scrollViewContentのalign center Y to SuperviewのPriorityを1000(Required)から750(High)に下げる
- scrollViewContentのtranslatesAutoresizingMaskIntoConstraints = trueにする
- scrollViewContentのFrameを明示的に作り直す
align center Yの優先度を下げ、scrollViewContentのtranslatesAutoresizingMaskIntoConstraintsをtrueにします。以下、Swift Documentationからの抜粋です:
If this property’s value is true, the system creates a set of constraints that duplicate the behavior specified by the view’s autoresizing mask. This also lets you modify the view’s size and location using the view’s frame, bounds, or center properties, allowing you to create a static, frame-based layout within Auto Layout
これをtrueにすることによって、Viewのframeなどの修正を行うことが出来るようです。
ただし、その分layout constraintsを害さないように設計しないといけません。
それでは、以下の手順を行なっていきましょう:
- scrollViewContentのalign center Y to SuperviewのPriorityを1000(Required)から750(High)に下げる
- scrollViewContentのtranslatesAutoresizingMaskIntoConstraints = trueにする
- scrollViewContentのFrameを明示的に作り直す
ちゃんと赤いボタンもタップ出来るようになりました!
最終的にViewControllerは以下のようになっています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
class ViewController: UIViewController { @IBOutlet weak var scrollView: UIScrollView! @IBOutlet weak var scrollViewContent: UIView! //scrollViewの高さはこのタイミングで変える必要がある override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() changeScrollViewContentSize() } func changeScrollViewContentSize(){ //AutoLayoutを解除し明示的にscrollViewContentのframeを定義する scrollViewContent.translatesAutoresizingMaskIntoConstraints = true scrollViewContent.frame = CGRect(x: 0, y: 0, width: self.view.frame.width, height: 818) //scrollViewのスクロール領域をscrollViewContentの高さと合わせます。 scrollView.contentSize = CGSize(width: scrollView.bounds.width, height: 818) } func showAlert(_ message: String){ let alertController = UIAlertController(title: nil, message: message, preferredStyle: .alert) let action = UIAlertAction(title: "ok", style: .cancel, handler: nil) alertController.addAction(action) present(alertController, animated: true, completion: nil) } @IBAction func didTapBlueButton(_ sender: Any) { showAlert("青いボタンを押したよ!") } @IBAction func didTapRedButton(_ sender: Any) { showAlert("赤いボタンを押したよ!") } } |
以上、ScrollView内のボタンが押せなくなった時の対処法でした。
一つの参考例として読んでいただければ幸いです。