- 投稿日:2019-10-21T21:55:10+09:00
Swift勉強の始まり
- 投稿日:2019-10-21T21:32:53+09:00
UITableViewをUIStackView + UIStackViewに置き換える
UITableViewにCustomCellゴテゴテで実装された画面を、UIScrollViewに置き換える機会がありましたので共有します。
主にUIStackViewへの追加で制約が入らなくてつまりました。※UITableViewCellをそのまま使う前提で書いてます。
普通にViewを置く場合はもっと簡単にできると思います。前説
まずUITableViewCellってコードから生成できるの…?
というところから始まりました。
途中、
制約全然効いてくれない!!
セルの高さが全部同じになっちゃう!!
横幅がおかしくなる!!!
とか色々やりながら、結果生まれたのが以下のコードです。その結果生まれたのが以下のコードです。
コードととストーリーボードの例
ViewController.swiftimport UIKit class ViewController: UIViewController { @IBOutlet weak var stack: UIStackView! override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. let row = 20 for i in 0...row { let cell = Cell.init(style: .default, reuseIdentifier: "Cell", widht: self.stack.frame.width) cell.heightAnchor.constraint(equalToConstant: cell.frame.height).isActive = true cell.hogeLabel.text = "\(i)" print("add to stack:\(cell)") self.stack.addArrangedSubview(cell) } } }Cell.swiftimport UIKit class Cell:UITableViewCell { // セルにはラベル1つだけおいてある @IBOutlet weak var hogeLabel: UILabel! // ストーリーボードで配置した時の初期化処理※ストーリーボードからの初期化は想定してません required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) self.nibInit(width: 0.0) } // 横幅を受け取って初期化する(制約がうまく効かなかったので) init(style: UITableViewCell.CellStyle, reuseIdentifier: String?, widht:CGFloat) { super.init(style: style, reuseIdentifier: reuseIdentifier) self.nibInit(width: widht) } // viewの初期化 fileprivate func nibInit(width: CGFloat) { guard let view = UINib(nibName: "Cell", bundle: nil) .instantiate(withOwner: self, options: nil) .first as? UIView else { return } //セルの高さはセル側でランダムに決める※使うときはちゃんとConstとか作って高さ設定してね let height = Bool.random() ? 50:100 view.frame = CGRect(x: 0, y: 0, width: width, height: CGFloat(height)) self.frame = CGRect(x: 0, y: 0, width: width, height: CGFloat(height)) self.addSubview(view) } }ストーリーボードは特に特別な設定はしておりません。
普通にScrollViewをおいて中にStackViewを配置するだけです。
もっとこんな方法あるよ!!や、
ここちょっとやばいよ!!などございましたらお教えいただけますと幸いです。ありがとうございました。
- 投稿日:2019-10-21T21:32:53+09:00
UITableViewをUIScrollView + UIStackViewに置き換える
UITableViewにCustomCellゴテゴテで実装された画面を、UIScrollViewに置き換える機会がありましたので共有します。
主にUIStackViewへの追加で制約が入らなくてつまりました。※UITableViewCellをそのまま使う前提で書いてます。
普通にViewを置く場合はもっと簡単にできると思います。前説
まずUITableViewCellってコードから生成できるの…?
というところから始まりました。
途中、
制約全然効いてくれない!!
セルの高さが全部同じになっちゃう!!
横幅がおかしくなる!!!
とか色々やりながら、結果生まれたのが以下のコードです。その結果生まれたのが以下のコードです。
コードとストーリーボードの例
ViewController.swiftimport UIKit class ViewController: UIViewController { @IBOutlet weak var stack: UIStackView! override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. let row = 20 for i in 0...row { let cell = Cell.init(style: .default, reuseIdentifier: "Cell", widht: self.stack.frame.width) cell.heightAnchor.constraint(equalToConstant: cell.frame.height).isActive = true cell.hogeLabel.text = "\(i)" print("add to stack:\(cell)") self.stack.addArrangedSubview(cell) } } }Cell.swiftimport UIKit class Cell:UITableViewCell { // セルにはラベル1つだけおいてある @IBOutlet weak var hogeLabel: UILabel! // ストーリーボードで配置した時の初期化処理※ストーリーボードからの初期化は想定してません required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) self.nibInit(width: 0.0) } // 横幅を受け取って初期化する(制約がうまく効かなかったので) init(style: UITableViewCell.CellStyle, reuseIdentifier: String?, widht:CGFloat) { super.init(style: style, reuseIdentifier: reuseIdentifier) self.nibInit(width: widht) } // viewの初期化 fileprivate func nibInit(width: CGFloat) { guard let view = UINib(nibName: "Cell", bundle: nil) .instantiate(withOwner: self, options: nil) .first as? UIView else { return } //セルの高さはセル側でランダムに決める※使うときはちゃんとConstとか作って高さ設定してね let height = Bool.random() ? 50:100 view.frame = CGRect(x: 0, y: 0, width: width, height: CGFloat(height)) self.frame = CGRect(x: 0, y: 0, width: width, height: CGFloat(height)) self.addSubview(view) } }ストーリーボードは特に特別な設定はしておりません。
普通にScrollViewをおいて中にStackViewを配置するだけです。
もっとこんな方法あるよ!!や、
ここちょっとやばいよ!!などございましたらお教えいただけますと幸いです。ありがとうございました。
- 投稿日:2019-10-21T17:14:29+09:00
無音カメラ on Pythonista
無音カメラ作った
Environment
- iPhone 11 Pro
- iOS 13.1.3
- Pythonista 3.3
機能
- iPhone 11 Proのタピオカメラに対応した
- タップでピント調整できるようにした
- ピンチイン/アウトでズームできるようにした
- 横向きでとったらちゃんと回転するようにした
- 4Kでちょっと画質がいい
バグ・まだなこと
- 解像度がちょっとおかしい
- 連写したら落ちる
- フラッシュ
- iPad
- Objc_utilとiOSのカメラの仕組み
- ☠️?スパゲティコード?☠️
- 11 Pro以外で動くかはちょっとわかんない
スクリーンショット
ソースコード
こちらからどうぞ?♂️
↓
?Github?cloneしてフォルダ構造はなるべくそのままで使ってください
感想
iOSのアプリ開発って難しいデス
感謝
宣伝
- 投稿日:2019-10-21T17:14:29+09:00
[iOS] 無音カメラ on Pythonista
無音カメラ作った
Environment
- iPhone 11 Pro
- iOS 13.1.3
- Pythonista 3.3
機能
- iPhone 11 Proのタピオカメラに対応した
- タップでピント調整できるようにした
- ピンチイン/アウトでズームできるようにした
- 横向きでとったらちゃんと回転するようにした
- 4Kでちょっと画質がいい
- 他のスクリプトから呼び出すことも考えた
バグ・まだなこと
- 解像度がちょっとおかしい
- 連写したら落ちる
- フラッシュ
- iPad
- Objc_utilとiOSのカメラの仕組み
- ☠️?スパゲティコード?☠️
- 11 Pro以外で動くかはちょっとわかんない
スクリーンショット
ソースコード
こちらからどうぞ?♂️
↓
?Github?cloneしてフォルダ構造はなるべくそのままで使ってください
感想
iOSのアプリ開発って難しいデス
感謝
宣伝
- 投稿日:2019-10-21T15:58:05+09:00
UIScrollVIewの上でも、キーボードが現れたときにいい感じにスクロールする
備忘録兼紹介です。
UIScrollView × 複数のUITextFieldで、キーボードが出たときにスクロールさせる処理を紹介します。動き
コード
import UIKit class ViewController: UIViewController { @IBOutlet weak var scroll: UIScrollView! // UITextFieldとかを配置するView @IBOutlet weak var stack: UIStackView! // 表示サンプル用 // ScrollViewのBottomの制約 @IBOutlet weak var scrollViewBottomConstraints: NSLayoutConstraint! private var nowEditingField:UITextField! // 編集中のUITextField private var offsetAdded:CGFloat? // スクロール戻す時用 override func viewDidLoad() { super.viewDidLoad() // 表示サンプルとして、UILabelを20個配置 let lines = 20 for count in 1...lines { let field = UITextField() field.placeholder = "\(count)" field.borderStyle = .roundedRect field.textAlignment = .center field.delegate = self // ← UITextFieldのdelegateを拾いたいので、delegateを設定する self.stack.addArrangedSubview(field) field.widthAnchor.constraint(equalToConstant: 250.0).isActive = true } } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) // キーボードが表示されるときと消えるときに動作させるメソッドを登録 // キーボードの高さがほしかった NotificationCenter.default.addObserver( self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil ) } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) NotificationCenter.default.removeObserver(UIResponder.keyboardWillShowNotification) } } extension ViewController: UITextFieldDelegate { // テキストフィールドにフォーカスがあたった時に呼ばれる func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool { self.nowEditingField = textField return true } // テキストフィールドからフォーカスが外れる時に呼ばれる func textFieldShouldEndEditing(_ textField: UITextField) -> Bool { self.nowEditingField = nil let offsetAdded = self.offsetAdded self.offsetAdded = nil var offset:CGPoint = CGPoint(x: 0, y: 0) if self.scroll.contentOffset.y - (offsetAdded ?? 0) < 0 { // 上端 offset = CGPoint(x: self.scroll.contentOffset.x, y: 0) }else if self.scroll.contentOffset.y > (self.scroll.contentSize.height - self.scroll.frame.size.height - self.scrollViewBottomConstraints.constant) { // 下端 offset = CGPoint(x: self.scroll.contentOffset.x, y: self.scroll.contentOffset.y) }else { // その他 offset = CGPoint(x: self.scroll.contentOffset.x, y: self.scroll.contentOffset.y - (offsetAdded ?? 0)) } self.scrollViewBottomConstraints.constant = 0 self.scroll.setContentOffset(offset, animated: true) return true } // キーボードが表示される直前に呼ばれる ※textFieldShouldBeginEditingより後に呼ばれる @objc func keyboardWillShow(_ notification: Notification) { guard let keyboardFrame: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else { return } guard let field = self.nowEditingField else { return } let keyboardHeight = keyboardFrame.cgRectValue.height let keyboardTopLine = self.view.frame.height - keyboardHeight let bottomLine = field.frame.origin.y + field.frame.height let displayBottom = bottomLine - self.scroll.contentOffset.y if displayBottom > keyboardTopLine { let sub = displayBottom - keyboardTopLine self.offsetAdded = sub let offset = CGPoint(x: self.scroll.contentOffset.x, y: self.scroll.contentOffset.y + sub) self.scroll.setContentOffset(offset, animated: true) self.scrollViewBottomConstraints.constant = keyboardHeight } } // キーボードのリターンでキーボードを閉じる func textFieldShouldReturn(_ textField: UITextField) -> Bool { self.view.endEditing(true) return true } }
ありがとうございました。
よかったら使ってください。(もっといい実装があれば教えて下さい。)
- 投稿日:2019-10-21T15:42:36+09:00
UILabelが省略されているか判定する
意外と見つからなくて困ったので、自分なりの実装方法を共有します。
コード
extension UILabel { var isOverflowing:Bool { return (realLineCount() > self.numberOfLines) } private func realLineCount()-> Int { guard let font = self.font else { return 0 } guard let text = self.text, text != "" else { return 0 } let sizeForWidthCheck = CGSize(width: Int.max, height: Int(ceil(font.pointSize))) let oneLineWidth = text.boundingRect(with: sizeForWidthCheck, options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: font], context: nil).width let boundingWidth = text.boundingRect(with: self.bounds.size, options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: font], context: nil).width return Int(ceil(oneLineWidth / boundingWidth)) } }解説
UILabel.textは省略されている場合でも全文を返してくれるので、それを使用します。
realLineCount
メソッドで、省略せずに全文を描画したときの行数を計算します。let oneLineWidth = text.boundingRect(with: sizeForWidthCheck, options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: font], context: nil).widthboundingRectメソッドはStringを描画した場合のRectを返してくれます。
これを使って、1行で全部描画したときの横幅を計算します。1return Int(ceil(oneLineWidth / boundingWidth))それを
boundingWidth
(Labelに描画したときの横幅)で割って、行数を計算します。2
小数点以下はいらないので、繰り上げます。var isOverflowing:Bool { return (realLineCount() > self.numberOfLines) }計算した行数を自身の行数(ここは実際に表示される行数)と比較し、
大きければ省略されているのでtrue
を返します。純正で用意して欲しいな……
ありがとうございました。
- 投稿日:2019-10-21T15:36:31+09:00
UIScreenとUIWindowの違い
前回iOSアプリの基本構造という記事を書いたのですが、
長くなりすぎて、UIScreenとUIResponderについても入れたかったのですが、割愛しました。
今回はUIScreenについて書きます。画面のサイズUIScreenからとるか?viewからとるか?
端末の画面サイズがコードの中で欲しいとき、ありますよね。
ViewControllerの中なら、view.boundsでとれますが、viewプロパティ持ってないクラスだと、この手はつかえません。
あと参照しているViewが画面全体を指定してないと、思ってたのと違うことになります。
色々やりようはあると思いますが、僕はUIScreen.main.boundsでとっていました。
あるいは、view.window?.boundsと指定する方法もありますね。
これだとUIViewが所属するUIWindowの領域がとれます。コード的には、どれでもいいっちゃどれでもいいのですが、自分の中でもやもやしていたところなので、調べてみました。
UIScreen/UIWindow/UIView
ところで、appDelegateの中に、windowプロパティがあって、UIWindowの参照を持ってますね?
これでも画面サイズは取得することができます。UIApplication.shared.keyWindow?.boundsつまり端末の画面サイズをとる選択肢としては、3つあるわけです。
- UIScreen
- UIWindow
- UIView
3番目のUIViewからとるケースは、UIViewが端末サイズとイコールで定義されている場合限定なので、使える状況は限られます。
なので、UIScreenとUIWindow、どっちを使うのがええんや? というのが問題なわけです。UIScreenとUIWindowの違い
端末サイズをとりたい、という目的だけで見ると、UIScreenとUIWindowは似たようなもんに見えるのですが、
ちゃんとドキュメント読んでみたら全然違うクラスでした。UIScreenとは
UIScreenは、「ハードウェアの画面を抽象化したクラス」です。
iPhoneで言えばiPhoneの画面を表現していますし、AppleTVで言えばテレビ画面を表現しています。
iOSアプリで、最低1つ持つ必要があります。
つまりUIScreenは、iOSアプリが表示したいもの(What)を表しているのではなくて、表示する場所(Where)を表現しています。通常のアプリであれば、UIScreen = 今アプリが稼働している端末の画面という理解でいいのですが、問題はミラーリングするようなアプリの場合です。
その場合、UIScreenはメインとサブが存在することになります。UIWindowとは
一方、UIWindowは、iOSアプリが表示したいもの(What)を表します。
ただUIWindowそのものはユーザーの目に見えるコンテンツを直接配置する場所ではなく、
具体的なコンテンツはUIViewに乗せて、そのUIViewを乗せる場所がUIWindowです。
いわば台紙ですね。UIWindowの取得方法ですが、「UIApplication.shared.keyWindow?」や「view.window」のwindowが実際に入るのが、
ViewDidAppear後らしいので、ViewControllerのタイミングによっては、とれないみたいです。UIViewControllerのview.windowがロードされるタイミング
となると、appDelegate経由になるんで、下記でとらなきゃですね。
UIApplication.shared.delegate?.window余談ですが、これでbounds取得しようとすると、
let window = UIApplication.shared.delegate?.window //UIWindow??型が返る print(window!?.bounds) //Optional((0.0, 0.0, 375.0, 812.0))となって、ちょっとビックリしました。
ベストプラクティスは?
というわけで、マルチスクリーンでないのであれば、UIScreenでとろうが、UIWindowでとろうが、返ってくる値としては一緒なのですが、
iPadに対応したアプリケーションを作る場合、マルチタスク機能が存在し、ScreenサイズとWindowサイズが等しくない場合があるため、アプリの表示領域のサイズを知りたい場合はWindowサイズを取得すると良い。
アプリの表示領域のサイズを取得するという注意点もあるみたいです。
まとめると、画面サイズを取得する際にどのクラス使うかの選択基準は、こんな感じですかね。
- UIScreenを使う→マルチスクリーンやマルチタスクを考えなくてよい
- UIWindow(not Application Delegate)を使う→ViewControllerのライフサイクルを考えなくてよい
- UIWindow(Application Delegate)を使う→特に考慮点が思いつかない……あれ? もしかして万能?→いやごめんなさい。UIWindow??型なので扱いがめんどくさいですねこいつ
「今アプリが稼働している端末の画面サイズが正確に欲しい」という意味合いでは、原理主義的に考えたら、AppDelegateでとるのが筋なのかな〜と思うものの、
UIScreenでとるのは楽ですし、ググったらすぐ出てくるし、ほとんどのケースで正しい値がとれるので、
マルチスクリーン対応するようなことが想定されるアプリ(動画系とか)でなければ、UIScreenでやっちゃっていい気がします。それぞれのクラスの違いを認識して、ケースバイケースで使っていくしかなさげです。
(おまけ)Application Delegateのwindowが2重オプショナル値返す件
この件、Swift書いていてもあんまり遭遇しないケースだったので、調べてみたら、StackOverflowで質問投げてる人がいました。
Why is main window of type double optional?
UIKitフレームワークの中のイマイチなトコなので、ユーザー的にはどうしようもないですが、
let window = app.delegate?.window??.`self`()とすることで、UIWindow?型にできます。
これはちょっとあまりエレガントではないので、let window = app.delegate?.window ?? nil // UIWindow?こっちにした方がいいんじゃね? という指摘が飛んでいます。
いずれにせよちょっと扱いづらいですね。(おまけ2)UIDevice
これを書いてしばらく経って急にそういえばUIDeviceってあったなと思い出しました。
https://developer.apple.com/documentation/uikit/uidevice
ただこのクラスは端末サイズはプロパティとして持ってませんでした。残念。
- 投稿日:2019-10-21T15:12:09+09:00
Flutterウィークリー #80
Flutterウィークリーとは?
FlutterファンによるFlutterファンのためのニュースレター
https://flutterweekly.net/この記事は#80の日本語訳です
https://mailchi.mp/flutterweekly/flutter-weekly-80※Google翻訳を使って自動翻訳を行っています。翻訳に問題がある箇所を発見しましたら編集リクエストを送っていただければ幸いです。
読み物&チュートリアル
フレアとFlutter使用したソロノードのアニメーション化
https://medium.com/@mandylowry/animating-solo-nodes-with-flare-and-flutter-20e92c54cacd
She Who Codesによるこのチュートリアルで、単一のFlareアニメーションでさまざまなアクセサリーを切り替える方法を学びます。WordPressバックエンドでFlutterアプリを構築する-パート1
Ritesh Sharmaが、WordpressをFlutterアプリのバックエンドとして使用する方法を説明します。Gitpod + Flutter =モバイルアプリ開発者向けの外出先での生産性
GitpodとオンラインIDEを使用してFlutterプロジェクトを作成する方法。FlutterプロジェクトをREST APIとしてホストする
https://medium.com/@rody.davis.jr/host-your-flutter-project-as-a-rest-api-751b8301f71f
Rody Davisは、クライアントFlutterアプリとDart APIサーバーでモデルとビジネスロジックを再利用する方法について説明しています。FlutterネイティブのサードパーティSDKを統合する
https://medium.com/@jessanirahim/integrating-native-third-party-sdk-in-flutter-8aab03afa9da
FlutterアプリでネイティブのサードパーティSDKを使用する必要がありますか? Rahim Khalidが記事を書いてくれました。最高のFlutterレスポンシブUIパターン
https://medium.com/flutter-community/the-best-flutter-responsive-ui-pattern-ba52875d70cd
Dane Mackierは、アプリをレスポンシブにするために必要なすべての情報を取得して使用する方法について説明します。Flutterアプリのアニメーション化
https://www.smashingmagazine.com/2019/10/animation-apps-flutter/
Shubhamは、 Flutterアニメーションのこのすばらしい詳細な紹介を書きました。ビデオ&メディア
Dart & Flutterタイプを使用した設計-合計タイプチュートリアル
https://www.youtube.com/watch?v=Pjx_Q5styqs
Dart Sum Typesを使用して、コードのエラーを起こしやすくする方法を学ぶFlutter + TensorFlow Lite |オブジェクト検出| YoloV2 | SSDチュートリアル
https://www.youtube.com/watch?v=0pYh7Js4GM8
TFLiteをTiny Yolov2およびSSDモデルで使用して、デバイス上のオブジェクト検出を実行する方法Flutter Provider:はじめに
https://www.youtube.com/watch?v=O71rYKcxUgA
Flutter Providerパッケージを使用する方法を学びます。フラッターのテキスト読み上げ(TTS)
https://www.youtube.com/watch?v=WnJZOi57oTY
Text To Speechについて、このビデオを使ってアプリに話しかけますライブラリ&コード
アントニオット15 / fgf
https://github.com/antoniott15/fgf
flutterでのGoogleフォントのインストール用CLI
bratan / flutter_translate
https://github.com/bratan/flutter_translate
Flutterの国際化(i18n)ライブラリ。
feilfeilundfeil / flutter_in_app_update
https://github.com/feilfeilundfeil/flutter_in_app_update
公式のAndroid APIを使用して、Androidのアプリ内アップデートを有効にします。
snowballdigital / flutter-unity-view-widget
https://github.com/snowballdigital/flutter-unity-view-widget
Flutter埋め込み可能な単一ゲームエンジンビュー
Vardiak /アニメーション
https://github.com/Vardiak/Animated
Flutter用の非常にシンプルな(まだ強力な)暗黙的なアニメーションライブラリ。
- 投稿日:2019-10-21T00:29:14+09:00
Xcode11のIBに追加されたContentLayoutGuideとFrameLayoutGuideについて
要約
- Xcode11からIBで
UIScrollView
を使うときはContent Layout Guide
とFrame Layout Guide
を利用するといいかも- iOS10以下をサポートしたい時は今までとちょっとやり方が変わったから気をつけよう
IB + UIScrollViewは大変
例として、夏目漱石の「我輩は猫である」を表示するだけの画面を作ってみます。
ここではXcode10.2を使用しています。この画面は、
UIScrollView
とContentView
(UIView
)、2つのUILabel
で構成されています。
UIScrollView
をAutolayoutを使って組む場合、以下の制約が必要です。
UIScrollView
自身の位置UIScrollView
の中身のサイズUIScrollView
の中身の固定された横/縦幅この制約を満たすために、以下の手順でConstraintを設定しています。
UIScrollView
の上下左右をUIViewController
のView
に合わせるContentView
の上下左右をUIScrollView
に合わせるContentView
の横幅をUIViewController
のView
に合わせる(Equal Widths
)ContentView
にオブジェクトを追加して高さを確定させる慣れれば大したことはないのかもしれませんが、
UIScrollView
が持つフレーム領域とコンテンツ領域が混ざって扱われているので、後からStoryboardをみた時どこがどうなってるんだっけ??と混乱すると思います。ContentLayoutGuideとFrameLayoutGuideの登場
Xcode11から、ScrollViewの中にContent Layout GuideとFrame Layout Guideが追加されました。
名前の通り、Content Layout Guide
はUIScrollView
の中身、Frame Layout Guide
はUIScrollView
のフレーム領域を示します。
以下の画面を見ると、中身とフレーム領域が明確に分けられていることがわかります。
これらはiOS11から追加された機能でしたが、IB上では扱えず、コード上でUIを組み立てる時しか恩恵を得られませんでした。
Xcode11ではScrollViewの中に追加されており、IBでUIScrollView
を扱う人でも使えるようになりました。さて、先ほど作った画面を
Content Layout Guide
とFrame Layout Guide
を使うように直すとこうなります。
UIScrollView
の上下左右をUIViewController
のView
に合わせるContentView
の上下左右をContent Layout Guide
に合わせるContentView
の横幅をFrame Layout Guide
に合わせる(Equal Widths
)ContentView
にオブジェクトを追加して高さを確定させる手順としては大きく変わっていないのですが、今まで
ContentView
をUIScrollView
やUIViewController
のView
に合わせていたところを、それぞれのLayout Guideに合わせることで役割が明確になりました。iOS10以下もサポートしたいんだけど。。。
残念ながら、
contentLayoutGuide
とframeLayoutGuide
はiOS11からの機能なので、iOS10以下で実行するとクラッシュします。
その場合は従来と同じ方法で実装する流れになりますが、Xcode11では上記の方法の3 .
ContentView
の横幅をUIViewController
のView
に合わせる(Equal Widths
)これをやろうとすると、そもそも
Equal Widths
の項目がなくなっています(バグ?)一時的な解決策として、Viewに対してではなくViewのSafeAreaに向けると項目が出てきますので、そっちを使っています。
参考