23
22

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

コードでSwiftのautolayoutを書く

Last updated at Posted at 2020-09-12

Storyboardで画面のレイアウトやUI部品をグラフィカルに配置できますが、チームで協同作業する時や細かいレイアウトの調整が発生する場合はStoryboardだとどうしても支障が出ます。

autolayoutの標準APIについて、AppleがNSLayoutConstraintNSLayoutAnchor(ios9~)、Visual Format Language(VFL)を提供しています。NSLayoutAnchorNSLayoutConstraintよりコード量が少なくなり、制約・声明もはっきりするようになっています。VFLはアスキーアートのように制約を定義することができる記法ですが、学習コストが高くかつリテラルな箇所が多いため、実行時にエラーが起こる恐れがあります(本文ではVFLパターンを紹介しません)。

##1、NSLayoutConstraint

NSLayoutConstraintを使用するには、各々初期化する必要があります。
制約する属性ごとにNSLayoutConstraintを作る必要があるため、コード量が増えます。
初期化メソッド:NSLayoutConstraint.init()

//UIImageViewを作成し、viewに追加する
let layoutIcon = "layoutIcon"
let layoutImg = UIImage(named: layoutIcon)
let layoutView = UIImageView(image: layoutImg)
layoutView.backgroundColor = .green
layoutView.layer.cornerRadius = 10
//AutoresizingMaskをAutoLayoutの制約に置き換えるかどうか指定する値(必ずfalse)
layoutView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(layoutView)
        
//制約を追加 top:50 
//パラメーター説明 item:制約オブジェクト attribute:制約する属性 relatedBy:制約タイプ toItem:制約の相手 attribute:制約相手に使用する属性 multiplier:乗数値 constant:定数値
let topConstraint = NSLayoutConstraint.init(item: layoutView, attribute: .top, relatedBy: .equal, toItem: view, attribute: .top, multiplier: 1.0, constant: 50.0)
//制約は定義した後、activateする必要あり
topConstraint.isActive = true
        
//制約を追加 left:50
let leftConstraint = NSLayoutConstraint.init(item: layoutView, attribute: .left, relatedBy: .equal, toItem: view, attribute: .left, multiplier: 1.0, constant: 50.0)
leftConstraint.isActive = true
        
//制約を追加 width:70
let widthConstraint = NSLayoutConstraint.init(item: layoutView, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 70.0)
widthConstraint.isActive = true
        
//制約を追加 height:70
let heightConstraint = NSLayoutConstraint.init(item: layoutView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 70.0)
heightConstraint.isActive = true

##2、NSLayoutAnchor

iOS9からはNSLayoutAnchorを使いNSLayoutConstraintオブジェクトを作ることができました。ただ直接にNSLayoutConstraintオブジェクトを作ることではなく、UIViewあるいはその子クラスやUILayoutGuideのあるanchorプロパティー値を利用します。これらのプロパティーはautolayout中の主要NSLayoutAttributeに相当します。
そのため、NSLayoutAnchorの子クラスでNSLayoutAttributeを設定してもいいです。
制約メソッド:xxxView.xxxAnchor.constraint( )

//紫色のUIImageViewを作成し、viewに追加する
let purLayoutIcon = "layoutIcon"
let purLayoutImg = UIImage(named: purLayoutIcon)
let purLayoutView = UIImageView(image: purLayoutImg)
purLayoutView.backgroundColor = .purple
purLayoutView.layer.cornerRadius = 10
//AutoresizingMaskをAutoLayoutの制約に置き換えるかどうか指定する値(必ずfalse)
purLayoutView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(purLayoutView)
        
//制約を追加 top:150 left:50 width:70 height:70
purLayoutView.topAnchor.constraint(equalTo: view.topAnchor, constant: 150.0).isActive = true
purLayoutView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 50.0).isActive = true
purLayoutView.widthAnchor.constraint(equalToConstant: 70.0).isActive = true
purLayoutView.heightAnchor.constraint(equalToConstant: 70.0).isActive = true

NSLayoutConstraintよりNSLayoutAnchorのほうは簡潔です。

##3、ScrollView中のautolayout

ScrollViewはセルフのcontentSizeがあるため、autolayout使用時に留意したほうがいいです。
ScrollViewの子viewのサイズは即ちcontentSizeです。
制約はScrollViewの完全なレイアウトを決める必要があり、上下左右ともに必要です。また、ScrollViewの子viewの最大サイズも必ず制約条件を通じて確定値を得られます。

//scrollViewを作成し、viewに追加する
let scrollView = UIScrollView()
scrollView.translatesAutoresizingMaskIntoConstraints = false
scrollView.backgroundColor = .cyan
//ページ単位のスクロールを可能する
scrollView.isPagingEnabled = true
view.addSubview(scrollView)
scrollView.topAnchor.constraint(equalTo: purLayoutView.bottomAnchor, constant: 30.0).isActive = true
scrollView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 0).isActive = true
//safeAreaのボトム距離を使う
scrollView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: 0).isActive = true
scrollView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 0).isActive = true
        
let bgColor = [UIColor.red,UIColor.green,UIColor.blue]
        
var leftView:UIView? = nil
    for i in 0..<3{
          let view = UIView()
          view.translatesAutoresizingMaskIntoConstraints = false
          view.backgroundColor = bgColor[i]
          scrollView.addSubview(view)
          if let left = leftView{
            view.leftAnchor.constraint(equalTo: left.rightAnchor, constant: 0).isActive = true
          }else{
            view.leftAnchor.constraint(equalTo: scrollView.leftAnchor, constant: 0).isActive = true
          }
          view.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 0).isActive = true
          view.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: 0).isActive = true
          view.widthAnchor.constraint(equalTo: scrollView.widthAnchor, multiplier: 1.0).isActive = true
          view.heightAnchor.constraint(equalTo: scrollView.heightAnchor, multiplier: 1.0).isActive = true
          leftView = view
        }
        //全ての子viewは上、下、左がscrollViewを相手として制約していたが、右のほうがまだ
        //右方向の制約を追加
        if let left = leftView{
          left.rightAnchor.constraint(equalTo: scrollView.rightAnchor, constant: 0).isActive = true
        }
23
22
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23
22

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?