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

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

ただいまの
回答率

88.10%

ScrollViewにAutoLayoutが付けられません…

解決済

回答 1

投稿 編集

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

score 16

以下のコードのScrollViewにAutoLayoutをつけて多画面に対応させたいのですが、通常のViewと同様に制約をつけても上手くいきません。Webで色々調べたのですが、自分の書いたコードのケースに当てはまるものが無く困っています。ご教授頂けると嬉しいです!

StoryBoardは元々のViewの上にScrollViewとButtonが載っている状態でLabelはコードで生成しています。
View as iPhoneXR以外でbuildすると大きさが合いません…

**TutorialViewController.Swift**

import UIkit
import Lottie

class TutorialViewController: UIViewController {

    var onboardStringArray = ["a","i","u","e","o","あ","い","う"]


    @IBOutlet var scrollView: UIScrollView!

    var animationArray = ["onboard1","onboard2","onboard3","onboard4","onboard5","onboard6","onboard7","onboard8"]



    override func viewDidLoad() {
        super.viewDidLoad()

        scrollView.isPagingEnabled = true

        setUpScroll()

        for i in 0...7{

            let animationView = AnimationView()
            let animation = Animation.named(animationArray[i])
            animationView.frame = CGRect(x: CGFloat(i) * self.view.frame.size.width, y: 0, width: self.view.frame.size.width, height: self.view.frame.size.width)

            animationView.animation = animation
            animationView.contentMode = .scaleAspectFit
            animationView.loopMode = .loop
            animationView.play()
            scrollView.addSubview(animationView)

        }

    }

    func setUpScroll(){

        scrollView.contentSize = CGSize(width: view.frame.size.width * 8, height: view.frame.size.height)

        for i in 0...7{

            let onboardLabel = UILabel(frame: CGRect(x: CGFloat(i) * self.view.frame.size.width, y: self.view.frame.size.height/3, width: scrollView.frame.size.width, height:scrollView.frame.size.height))

            onboardLabel.font = UIFont.boldSystemFont(ofSize: 15.0)
            onboardLabel.textAlignment = .center
            onboardLabel.text = onboardStringArray[i]
            scrollView.addSubview(onboardLabel)


        }

    }

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

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

  • OBN

    2019/07/01 10:27

    すみません、何度も更新しているのですが反映されません…。
    previewでは反映されているのですが…次質問や回答する機会があれば気を付けます。

    キャンセル

  • fuzzball

    2019/07/01 10:34 編集

    質問投稿画面の右下にある「更新する」を押しても更新されないということでしょうか?

    キャンセル

  • OBN

    2019/07/01 10:41

    あ、できました!更新内容が空欄のままだったので編集できていなかったようです。
    すみません、プログラミングどころかPC初心者なので…今後気を付けます。

    キャンセル

回答 1

checkベストアンサー

0

ScrollViewのcontentSizeはサイズの計算等が難しくて使いづらいので、
ScrollViewの上にStackViewを乗せてcontentSizeをStackViewに計算してもらうようにしてみました。
(参考: https://qiita.com/Masataka-n/items/c19456f172627359d0d8 )

Storyboardは以下のようにしました。
ScrollViewを画面いっぱいになるようにAutolayoutの制約をつけて、
さらにその上にStackView(Horizontal Stack View)を乗せてScrollViewに対して縦横全てのスペースが0になるように制約をつけます。
横方向にスクロールするようにしたいのでScrollViewに対して同じ高さになるような制約もつけてください。
追加したStackViewのAlignmentはFill、DistributionはEqual Spacing、Spacingは0にします。
これだけではエラーが出るのでStackViewを水平方向の中心に来るようにPriority750で制約を追加します。

Storyboard

ViewControllerは以下のようになります。
StackViewにUIViewを一枚追加してその上にLabelやLottieのViewを乗せるようにします。
普段あまりコードでAutolayoutの制約をつけたりしないので、かなり雑で申し訳ありません。
またLottieは入れてないのでコメントアウトしてあります。

薄く白色になっている部分がLottieのViewになります。

class ViewController: UIViewController {

    var onboardStringArray = ["a","i","u","e","o","あ","い","う"]
    var onboardColors: [UIColor] = [.blue, .red, .yellow, .orange, .green, .purple, .brown, .magenta]

    @IBOutlet var scrollView: UIScrollView!
    @IBOutlet weak var stackView: UIStackView!

    var animationArray = ["onboard1","onboard2","onboard3","onboard4","onboard5","onboard6","onboard7","onboard8"]

    override func viewDidLoad() {
        super.viewDidLoad()
        setUpScroll()
    }

    func setUpScroll(){
        scrollView.isPagingEnabled = true
        scrollView.showsHorizontalScrollIndicator = false

        for i in 0...7{

            // OnboardView
            // StackView に追加する UIView 高さは StackView の Fill により伸びるので
            // 幅だけ Autolayout で指定してあげる
            let onboardView = UIView(frame: .zero)
            onboardView.backgroundColor = onboardColors[i]

            // Lottie Animation View
            // OnboardView の上に乗せる Lottie の AnimationView
            let animationView = UIView(frame: CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: self.view.frame.size.width))
            animationView.backgroundColor = .white
            //animationView.animation = animation
            //animationView.contentMode = .scaleAspectFit
            //animationView.loopMode = .loop
            //animationView.play()
            onboardView.addSubview(animationView)

            // Label
            // OnboardView の上に乗せる Label
            let onboardLabel = UILabel()
            onboardLabel.translatesAutoresizingMaskIntoConstraints = false
            onboardLabel.font = UIFont.boldSystemFont(ofSize: 15.0)
            onboardLabel.textAlignment = .center
            onboardLabel.text = onboardStringArray[i]
            onboardView.addSubview(onboardLabel)

            // StackView
            // OnboardView を StackView の arrangedSubview に追加
            stackView.addArrangedSubview(onboardView)

            // Autolayout
            NSLayoutConstraint.activate([
                // Onboard
                onboardView.widthAnchor.constraint(equalTo: self.view.widthAnchor, constant: 0),

                // Lottie Animation View
                animationView.topAnchor.constraint(equalTo: onboardView.topAnchor, constant: 0),
                animationView.leadingAnchor.constraint(equalTo: onboardView.leadingAnchor, constant: 0),
                animationView.trailingAnchor.constraint(equalTo: onboardView.trailingAnchor, constant: 0),
                animationView.heightAnchor.constraint(equalToConstant: self.view.frame.width),

                // Label
                onboardLabel.topAnchor.constraint(equalTo: animationView.bottomAnchor, constant: 24.0),
                onboardLabel.leadingAnchor.constraint(equalTo: onboardView.leadingAnchor, constant: 0),
                onboardLabel.trailingAnchor.constraint(equalTo: onboardView.trailingAnchor, constant: 0),
            ])
        }
    }
}

2019/07/03 追記  

これだけではエラーが出るのでStackViewを水平方向の中心に来るようにPriority750で制約を追加します。  

この部分ですがScrollViewには以下のようなエラーが出ていると思います。  

Need constraints for: X position or width

エラーの通りX座標方向の制約もしくは幅の制約をつけることで解決することができるので、
以下のようにしてX座標方向(水平方向)の中心に来るように制約を追加します。

まずAlignから水平方向の中心に来るような制約を付けます。

Alignの追加

このままではコードで追加しているAutolayoutの制約とぶつかってしまい、
エラーが出てしまうので、ぶつかった際にブレイクするように以下のようにして優先度を下げます。
追加した制約のEditからPriorityHighにます。

優先度を下げる

2019/07/04 追記  

この画面から外面遷移するためのButtonを置きたいです。

こちらについてですが、
ScrollViewと同じ階層にボタンを置いてしまえば表示されると思います。

ボタンの配置

あとはその状態でAutolayoutの制約を追加してしまえば大丈夫です。

ボタンの制約

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/07/03 18:17 編集

    ご丁寧にありがとうございます!Priorityが理解できていませんでした…。
    優先度を下げるとコードでの制約とケンカしなくなるんですね!

    あと、一つだけよろしいでしょうか?この画面から外面遷移するためのButtonを置きたいです。
    ご教授頂いた方法でAutolayoutは思うようになったのですが、StackViewにViewを一枚置いて、そのViewの中にButtonを置くという方法では、Buttonが表示されません…。

    Viewを置くところまではエラーはないのですが、Viewの中にButtonを置くと、
    Main.storyboard: warning: Auto Layout Localization: Views without any layout constraints may clip their content or overlap other views.
    とエラーが出てしまいます…。このままBuildするとButtonは表示されません。

    何度も申し訳ありませんが、ご教授頂けると助かります。

    キャンセル

  • 2019/07/04 09:39

    追記しました!
    コードでレイアウトを作るのかStoryboardで作るのかもう少し整理した方が見やすかったかもしれません...
    申し訳ありません。

    キャンセル

  • 2019/07/04 09:58

    ありがとうございます!StackViewにViewを一枚置いていたので階層がおかしくなっていたみたいです…
    StoryBoardとコードでのUI生成合わせると混乱しちゃいます。これを機にコードのみでのUI構築も勉強してみようと思います。…SwiftUIもコードの方が恩恵大きそうですし。

    大変お世話になりました!聞くばかりでなく答えられるように励みます!…また聞いたときはお願いします。

    キャンセル

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

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

関連した質問

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