質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.07%

XcodeでScroll View自身とScrollVIew内の部品にAuto Layoutを適用させたい。

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,674

Tommy716

score 14

XcodeでScroll View自身とScrollVIew内の部品にAuto Layoutを適用させたいです。
いつも通り、Scroll View内の部品に制約をつけるとビルドした時に真っ白になってしまします。何か方法はありますか?

具体的にしたいこととしては、text Fieldを編集するときにtext FIeldが隠れないようにしたいです。
コードは参考サイトと全く同じように書きました。

参考サイト

import UIKit

class ViewController: UIViewController {
    @IBOutlet weak var scrollview: UIScrollView!
    @IBOutlet weak var textView: UITextField!
    @IBOutlet weak var label: UILabel!
    @IBOutlet weak var orange: UIView!

    var screenHeight:CGFloat!
    // Screenの幅
    var screenWidth:CGFloat!

    override func viewDidLoad() {
        super.viewDidLoad()

        scrollview.delegate = (self as? UIScrollViewDelegate)
        textView.delegate = self as? UITextFieldDelegate
        // Do any additional setup after loading the view, typically from a nib.

        let screenSize: CGRect = UIScreen.main.bounds
        screenWidth = screenSize.width
        screenHeight = screenSize.height
        scrollview.frame.size =
            CGSize(width: screenWidth, height: screenHeight)
        scrollview.addSubview(orange)
        scrollview.addSubview(label)
        scrollview.addSubview(textView)
        scrollview.contentSize = CGSize(width: screenWidth, height: screenHeight*2)
        scrollview.bounces = false
        print("screenWidth:\(screenWidth)")
        print("screenHeight:\(screenHeight)")

    }
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        self.view.endEditing(true)
        return true
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        NotificationCenter.default.addObserver(self,
                                               selector: #selector(ViewController.keyboardWillShow(_:)),
                                               name: NSNotification.Name.UIKeyboardWillShow,
                                               object: nil)
        NotificationCenter.default.addObserver(self,
                                               selector: #selector(ViewController.keyboardWillHide(_:)) ,
                                               name: NSNotification.Name.UIKeyboardWillHide,
                                               object: nil)
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)

        NotificationCenter.default.removeObserver(self,
                                                  name: .UIKeyboardWillShow,
                                                  object: self.view.window)
        NotificationCenter.default.removeObserver(self,
                                                  name: .UIKeyboardDidHide,
                                                  object: self.view.window)
    }

    @objc func keyboardWillShow(_ notification: Notification) {

        let info = notification.userInfo!

        let keyboardFrame = (info[UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue

        // bottom of textField
        let bottomTextField = textView.frame.origin.y + textView.frame.height
        // top of keyboard
        let topKeyboard = screenHeight - keyboardFrame.size.height
        // 重なり
        let distance = bottomTextField - topKeyboard

        if distance >= 0 {
            // scrollViewのコンテツを上へオフセット + 20.0(追加のオフセット)
            scrollview.contentOffset.y = distance + 20.0
        }
    }

    @objc func keyboardWillHide(_ notification: Notification) {
        scrollview.contentOffset.y = 0
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • Tommy716

    2018/08/14 19:15

    xAxis様 いえいえ全然大丈夫です。

    キャンセル

  • Tommy716

    2018/08/14 19:17

    t_obara様 制約はstoryboard上で行いました。制約を外すと、期待通り動作するのですが、iPadやiPhoneXなどでは変な余白が出てしまいそれを治したいと思っています。

    キャンセル

  • t_obara

    2018/08/14 19:53

    であれば、どの様な制約をつけたのかもご提示されると回答が得られやすいのではないでしょうか。https://virusee.net/hidden-ios-keyboard/ ちなみに、こちらで同様のことをされていますが、うまく動作するのかはわかりません。

    キャンセル

回答 1

checkベストアンサー

+1

まずDelegateが必要ならばAuto Layout以前にクラス名のとなりにUIScrollViewDelegateUITextFieldDelegateを書きましょう。

class ViewController: UIViewController, UIScrollViewDelegate, UITextFieldDelegate {

んで、viewDidLoad()内の

scrollview.delegate = (self as? UIScrollViewDelegate)
textView.delegate = self as? UITextFieldDelegate

scrollview.delegate = self
textView.delegate = self

としましょう。

 UIScrollViewとその他パーツをAuto Layoutで設定するには

ここからが本題です。
サイトではUIScrollView等UIをコードで生成、設定している様ですがAuto Layoutだけで作ることも可能です。よくやるのがUIScrollViewの子ViewにUIViewを設定してそいつをContent Viewとするやり方です。UITableViewCellとその中のContent Viewと同じ様な関係ですね。今回は上下にスクロールするやり方です。

  1. UIScrollViewと子ViewであるUIView(Content View)共にconstraintsを上下左右0に設定。
  2. ContentViewであるUIViewのwidthをViewController.viewとequalに設定。
  3. ContentViewの高さはお好みで。ただしUIScrollViewのheight以上の高さを設定する必要がある。
  4. 後はContentViewに必要なUILabelやUISlider、UITextField、UIButton等をのせて各々に制約をつけていく。
  5. 完成

UIScrollViewは子Viewが自身の領域外にある場合、スクロールできる様になっています。逆にUIScrollViewと子Viewが全く同一の制約を持った場合スクロールしません。なのでContentViewのheightは必ずUIScrollViewのheight以上にしましょう。

参考に画像を載せておきます。

この画像ではUIScrollViewにUIView(Content View)をのせ、Content ViewにUILabel、UISlider、UITextField、UIButtonを載せています(UIButtonはContentViewのbottom+16に設定しているので見えません)。サイトの構成に似せるためにContentViewのheightはUIScrollViewのheightの倍になっています。

親子関係

ViewController

さて、サイトに乗っているコードの何が気になるかというとIBOutlet接続されたscrollViewにIBOutlet接続されたUI群をaddSubViewしているところです。本来Storyboardに配置された時点でほぼaddSubViewしている様なもののはずが再度addSubViewするのはおかしいのではないかと思い上記の様なコメントをしました。ただしこの辺りは自分も解釈があやふやな部分もあるためおかしな所がありましたらご教授いただけると幸いです。

参考程度のコードを一応貼っておきます。細かいところまでは大して読んでいないので本当に参考程度ですが。

import UIKit

class ViewController: UIViewController {

    @IBOutlet var scrollView: UIScrollView!
    @IBOutlet var textField: UITextField!
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        NotificationCenter.default.addObserver(self,
                                               selector: #selector(ViewController.keyboardWillShow(_:)),
                                               name: NSNotification.Name.UIKeyboardWillShow,
                                               object: nil)
        NotificationCenter.default.addObserver(self,
                                               selector: #selector(ViewController.keyboardWillHide(_:)) ,
                                               name: NSNotification.Name.UIKeyboardWillHide,
                                               object: nil)
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        
        NotificationCenter.default.removeObserver(self,
                                                  name: .UIKeyboardWillShow,
                                                  object: self.view.window)
        NotificationCenter.default.removeObserver(self,
                                                  name: .UIKeyboardDidHide,
                                                  object: self.view.window)
    }
    @objc func keyboardWillShow(_ notification: Notification) {
        
        let info = notification.userInfo!
        
        let keyboardFrame = (info[UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
        
        // bottom of textField
        let bottomTextField = textField.frame.origin.y + textField.frame.height
        // top of keyboard
        let topKeyboard = UIScreen.main.bounds.height - keyboardFrame.size.height
        // 重なり
        let distance = bottomTextField - topKeyboard
        
        if distance >= 0 {
            // scrollViewのコンテツを上へオフセット + 20.0(追加のオフセット)
            scrollView.contentOffset.y = distance + 20.0
        }
    }
    
    @objc func keyboardWillHide(_ notification: Notification) {
        scrollView.contentOffset.y = 0
    }

}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/08/16 14:12

    できました!ご丁寧にありがとうございました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.07%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る