Closed7

『よくわかるAuto Layout』をXcode 13.3で動くようにする

nabeyangnabeyang

https://www.amazon.co.jp/dp/B01HG97S7K/

Xcode 13.3用の修正手順

$ mkdir auto_layout
$ cd auto_layout
$ mv /path/to/1032_src.zip .
$ unzip 1032_src.zip
$ curl -Lb /tmp/cookie "https://drive.google.com/uc?export=download&confirm=t&id=18wNfS34mfs1W0l2aKcMlARYQyDVu27zI" -o "xcode13.3.patch"
$ patch -p1 < xcode13.3.patch

Swiftのバージョンを指定する。

以下、上記手順に至るまでのメモなので、読む必要はないです。

nabeyangnabeyang

XCodeは13.3を使用。

とりあえずSwiftのバージョン指定がないので直す。

Value for SWIFT_VERSION cannot be empty.

Build SettingsSwift Compiler - LanguageSwift Language VersionSwift 5に変更。
Debug用のコードを修正。

/AutoLayoutBookSampler/Timer.swift b/AutoLayoutBookSampler/Timer.swift
@@ -11,9 +11,9 @@ import UIKit
 class Timer: NSObject {
     static let sharedInstance = Timer()
     
-    private var startTime: NSDate?
+    private var startTime: Date?
     func start() {
-        startTime = NSDate()
+        startTime = Date()
     }
     
     func elapsedTime() {
@@ -22,7 +22,7 @@ class Timer: NSObject {
             return
         }
         
-        let elapsedTime = NSDate().timeIntervalSinceDate(time) as Double
+        let elapsedTime = Date().timeIntervalSince(time)
         print("経過時間:\(elapsedTime)")
     }
nabeyangnabeyang

Chapter 4の修正

/AutoLayoutBookSampler/Ch4/XibAndStoryboard.swift
@@ -24,15 +24,15 @@ class XibAndStoryboard: UIView {
     }
     
     func commonInit() {
-        NSBundle.mainBundle().loadNibNamed("XibAndStoryboard", owner: self, options: nil)
+        Bundle.main.loadNibNamed("XibAndStoryboard", owner: self)
         self.contentView.translatesAutoresizingMaskIntoConstraints = false
         self.addSubview(self.contentView)
         
         // 親ビューの四隅に制約をはる
-        self.contentView.leadingAnchor.constraintEqualToAnchor(self.leadingAnchor).active = true
-        self.contentView.trailingAnchor.constraintEqualToAnchor(self.trailingAnchor).active = true
-        self.contentView.topAnchor.constraintEqualToAnchor(self.topAnchor).active = true
-        self.contentView.bottomAnchor.constraintEqualToAnchor(self.bottomAnchor).active = true
+        self.contentView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
+        self.contentView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
+        self.contentView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
+        self.contentView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
         self.layoutIfNeeded()
     }
AutoLayoutBookSampler/Ch4/XibAndStoryboardViewController.swift
@@ -17,8 +17,8 @@ class XibAndStoryboardViewController: UIViewController {
         subView.translatesAutoresizingMaskIntoConstraints = false
         self.view.addSubview(subView)
         
-        subView.leadingAnchor.constraintEqualToAnchor(view.leadingAnchor).active = true
-        subView.trailingAnchor.constraintEqualToAnchor(view.trailingAnchor).active = true
-        subView.centerYAnchor.constraintEqualToAnchor(view.centerYAnchor).active = true
+        subView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
+        subView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
+        subView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
     }
 }
nabeyangnabeyang

5章の修正

/AutoLayoutBookSampler/Ch5/LayoutGuideViewController.swift
@@ -22,19 +22,19 @@ class LayoutGuideViewController: UIViewController {
         view.addLayoutGuide(space)
         
         // レイアウトガイドの幅をボタンと揃える
-        space.widthAnchor.constraintEqualToAnchor(saveButton.widthAnchor).active = true
+        space.widthAnchor.constraint(equalTo: saveButton.widthAnchor).isActive = true
         
         // レイアウトガイドを中央揃え
-        space.centerXAnchor.constraintEqualToAnchor(view.centerXAnchor).active = true
+        space.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
         
         // 各ボタンとレイアウトガイドの水平方向の制約生成
-        saveButton.trailingAnchor.constraintEqualToAnchor(space.leadingAnchor).active = true
-        cancelButton.leadingAnchor.constraintEqualToAnchor(space.trailingAnchor).active = true
+        saveButton.trailingAnchor.constraint(equalTo: space.leadingAnchor).isActive = true
+        cancelButton.leadingAnchor.constraint(equalTo: space.trailingAnchor).isActive = true
         view.layoutIfNeeded()
     
     }
 
-    override func viewDidAppear(animated: Bool) {
+    override func viewDidAppear(_ animated: Bool) {
         super.viewDidAppear(animated)
         
     }
nabeyangnabeyang

6章の修正

/AutoLayoutBookSampler/Ch6/TorutsumeViewController.swift
@@ -34,27 +34,27 @@ class TorutsumeViewController: UIViewController {
     }
     
     @IBAction func PressButtonButton(sender: AnyObject) {
-        if button.hidden {
+        if button.isHidden {
             buttonHeightConstraint.constant = 30
             buttonTopConstraint.constant = 8
-            button.hidden = false
+            button.isHidden = false
         }
         else {            buttonHeightConstraint.constant = 0
             buttonTopConstraint.constant = 0
-            button.hidden = true
+            button.isHidden = true
         }
     }
     
     @IBAction func PressSwichButton(sender: AnyObject) {
-        if swichButton.hidden {
+        if swichButton.isHidden {
             swichTopConstraint.constant = 8
             swichHeightConstraint.constant = 31
-            swichButton.hidden = false
+            swichButton.isHidden = false
         }
         else {
             swichTopConstraint.constant = 0
             swichHeightConstraint.constant = 0
-            swichButton.hidden = true
+            swichButton.isHidden = true
         }
     }
 }
nabeyangnabeyang

7章の修正

/AutoLayoutBookSampler/Ch7/DeviceRotationViewController.swift
@@ -33,29 +33,29 @@ class DeviceRotationViewController: UIViewController {
         // Dispose of any resources that can be recreated.
     }
     
-    override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
+    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
         
         // 回転時のレイアウト変更を以下のメソッド内で定義することで、回転時のアニメーションと同期して動きます。
-        super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)
+        super.viewWillTransition(to: size, with: coordinator)
         
-        coordinator.animateAlongsideTransition(
-            {
+        _ = coordinator.animate(
+            alongsideTransition: {
                 context in
                 // 縦画面レイアウト
                 if size.width <= size.height {
                     // 横画面用制約を無効化
-                    NSLayoutConstraint.deactivateConstraints(self.landscapeConstraints ?? [])
+                    NSLayoutConstraint.deactivate(self.landscapeConstraints ?? [])
                     
                     // 縦画面用制約を有効化
-                    NSLayoutConstraint.activateConstraints(self.portraitConstraints ?? [])
+                    NSLayoutConstraint.activate(self.portraitConstraints ?? [])
                 }
                 // 横画面レイアウト
                 else {
                     // 縦画面用制約を無効化
-                    NSLayoutConstraint.deactivateConstraints(self.portraitConstraints ?? [])
+                    NSLayoutConstraint.deactivate(self.portraitConstraints ?? [])
                     
                     // 横画面用制約を有効化
-                    NSLayoutConstraint.activateConstraints(self.landscapeConstraints ?? [])
+                    NSLayoutConstraint.activate(self.landscapeConstraints ?? [])
                 }
             }, completion: nil)
     }
/AutoLayoutBookSampler/Ch7/OffscreenRenderingCellTableViewCell.swift
@@ -17,8 +17,7 @@ class OffscreenRenderingCellTableViewCell: UITableViewCell {
         super.awakeFromNib()
         // Initialization code
     }
-    
-    override func setSelected(selected: Bool, animated: Bool) {
+    override func setSelected(_ selected: Bool, animated: Bool) {
         super.setSelected(selected, animated: animated)
         
     }
/AutoLayoutBookSampler/Ch7/OffscreenRenderingCellViewController.swift
@@ -24,13 +24,13 @@ class OffscreenRenderingCellViewController: UIViewController, UITableViewDelegat
     
     
     // MARK: UITableViewDataSource
-    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
         return people.count
     }
     
-    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
-        let cell = tableView.dequeueReusableCellWithIdentifier("OffscreenRenderingCell") as? OffscreenRenderingCellTableViewCell
-        cell!.layoutWithData(people[indexPath.row])
+    internal func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+        let cell = tableView.dequeueReusableCell(withIdentifier: "OffscreenRenderingCell") as? OffscreenRenderingCellTableViewCell
+        cell!.layoutWithData(data: people[indexPath.row])
         return cell!
     }
     
@@ -39,11 +39,11 @@ class OffscreenRenderingCellViewController: UIViewController, UITableViewDelegat
     }
     
     // MARK: UITableViewDelegate
-    func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
+    internal func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
         
         // セル生成時の計算コストを減らすため一度だけインスタンス化
         if cellForCalculatingHeight == nil {
-            guard let reusableCell = tableView.dequeueReusableCellWithIdentifier("OffscreenRenderingCell") as? OffscreenRenderingCellTableViewCell else {
+            guard let reusableCell = tableView.dequeueReusableCell(withIdentifier: "OffscreenRenderingCell") as? OffscreenRenderingCellTableViewCell else {
                 fatalError("Your cellIdentifier is invalid")
             }
             cellForCalculatingHeight = reusableCell
@@ -56,10 +56,10 @@ class OffscreenRenderingCellViewController: UIViewController, UITableViewDelegat
         
 
         // レイアウト用データを代入
-        cell.layoutWithData(self.personForRow(indexPath.row))
+        cell.layoutWithData(data: self.personForRow(index: indexPath.row))
         
         // レイアウトに必要なテーブルビューの幅を与える
-        cell.bounds = CGRectMake(0, 0, CGRectGetWidth(tableView.bounds), CGRectGetHeight(cell.bounds))
+        cell.bounds = CGRect(x: 0, y: 0, width: tableView.bounds.width, height: cell.bounds.height)
         
         // セルの高さを計算する
         return cell.calculateHeight()
@@ -75,9 +75,9 @@ extension UITableViewCell {
     func calculateHeight() -> CGFloat {
         self.layoutIfNeeded()
 
-        let cellHeight = contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
+        let cellHeight = contentView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).height
         
 //         セパレータの高さ1.0を足す
         return cellHeight + 1.0
     }
-}
\ No newline at end of file
+}
/AutoLayoutBookSampler/Ch7/People.swift
@@ -26,18 +26,17 @@ class People: NSObject {
         class func generateCellData() -> [Person] {
             var people = [Person]()
             
-            var plistArray: NSArray?
-            if let path = NSBundle.mainBundle().pathForResource("people", ofType: "plist") {
-                plistArray = NSArray(contentsOfFile: path)
+            var plist: NSArray?
+            if let path = Bundle.main.path(forResource: "people", ofType: "plist") {
+                plist = NSArray(contentsOfFile: path)
             }
             
             for _ in 0..<numberOfData {
-                let index = Int(arc4random_uniform(UInt32(plistArray!.count)))
-                let personDic = plistArray![index]
-                
-                let name = personDic["name"] as? String
-                let detail = personDic["detail"] as? String
+                let index = Int(arc4random_uniform(UInt32(plist!.count)))
+                let personDic = plist![index] as? Dictionary<String, String>
                 
+                let name = personDic?["name"]
+                let detail = personDic?["detail"]
                 let person = Person(name: name!, detail: detail!)
                 people.append(person)
             }
/AutoLayoutBookSampler/Ch7/SelfSizingCellViewController.swift
@@ -18,35 +18,35 @@ class SelfSizingCellViewController: UIViewController, UITableViewDelegate, UITab
         
         // Set up auto resizing cell
         self.tableView.estimatedRowHeight = 100.0
-        tableView.rowHeight = UITableViewAutomaticDimension
+        tableView.rowHeight = UITableView.automaticDimension
         
         people = People.generateCellData()
         
         // Dynamic Typeのための通知受け取り
-        NSNotificationCenter.defaultCenter().addObserver(self, selector:#selector(self.didChangePreferredContentSize(_:)), name: UIContentSizeCategoryDidChangeNotification, object: nil)
+        NotificationCenter.default.addObserver(self, selector:#selector(self.didChangePreferredContentSize), name: UIContentSizeCategory.didChangeNotification, object: nil)
     }
     
     deinit {
-        NSNotificationCenter.defaultCenter().removeObserver(self)
+        NotificationCenter.default.removeObserver(self)
     }
     
-    func didChangePreferredContentSize(notification: NSNotification) {
+    @objc func didChangePreferredContentSize(notification: NSNotification) {
         tableView.reloadData()
     }
     
     // MARK: UITableViewDataSource
-    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
+    internal func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
         // セルの取得
-        guard let cell = tableView.dequeueReusableCellWithIdentifier("SelfSizingCellTableViewCell", forIndexPath: indexPath) as? SelfSizingCellTableViewCell else {
+        guard let cell = tableView.dequeueReusableCell(withIdentifier: "SelfSizingCellTableViewCell", for: indexPath) as? SelfSizingCellTableViewCell else {
             fatalError("Your cellIdentifier is invalid")
         }
         
         // データを渡しレイアウト実行
-        cell.layoutWithData(self.personForRow(indexPath.row))
+        cell.layoutWithData(data: self.personForRow(index: indexPath.row))
         return cell
     }
 
-    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
         return people.count
     }
/AutoLayoutBookSampler/Ch7/keyboardAppearanceViewController.swift
@@ -13,46 +13,46 @@ class keyboardAppearanceViewController: UIViewController {
         
     override func viewDidLoad() {
         super.viewDidLoad()
-        NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(keyboardAppearanceViewController.keyboardWillShow(_:)), name: UIKeyboardWillShowNotification, object: nil)
-        NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(keyboardAppearanceViewController.keyboardWillHide(_:)), name: UIKeyboardWillHideNotification, object: nil)
+        NotificationCenter.default.addObserver(self, selector: #selector(keyboardAppearanceViewController.keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
+        NotificationCenter.default.addObserver(self, selector: #selector(keyboardAppearanceViewController.keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
     }
 
-    func keyboardWillShow(notification: NSNotification) {
+    @objc func keyboardWillShow(_ notification: Notification) {
         guard let info = notification.userInfo else {
             fatalError("Unexpected notification")
         }
 
         // キーボードの高さを取得
-        guard let keyboardHeight = info[UIKeyboardFrameEndUserInfoKey]?.CGRectValue.size.height else {
+        guard let keyboardHeight = (info[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.height else {
             fatalError("No keyboard height found")
         }
         
         // キーボード表示アニメーションの時間を取得
-        guard let animationDuration = info[UIKeyboardAnimationDurationUserInfoKey]?.doubleValue else {
+        guard let animationDuration = (info[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double) else {
             fatalError("No keyboard height found")
         }
         
         // 取得した情報を元にスクロールビューのレイアウトを変更する
         buttomConstraint.constant = keyboardHeight
-        UIView.animateWithDuration(animationDuration, animations: { () -> Void in
+        UIView.animate(withDuration: animationDuration, animations: { () -> Void in
             self.view.layoutIfNeeded()
         })
     }
     
-    func keyboardWillHide(notification: NSNotification) {
+    @objc func keyboardWillHide(_ notification: Notification) {
         
         guard let info = notification.userInfo else {
             fatalError("Unexpected notification")
         }
         
         // キーボード表示アニメーションの時間を取得
-        guard let animationDuration = info[UIKeyboardAnimationDurationUserInfoKey]?.doubleValue else {
+        guard let animationDuration = (info[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double) else {
             fatalError("No keyboard height found")
         }
         
         // 取得した情報を元にスクロールビューのレイアウトを変更する
         buttomConstraint.constant = 0
-        UIView.animateWithDuration(animationDuration, animations: { () -> Void in
+        UIView.animate(withDuration: animationDuration, animations: { () -> Void in
             self.view.layoutIfNeeded()
         })
     }
nabeyangnabeyang

8章の修正

/AutoLayoutBookSampler/Ch8/AmbiguousLayoutViewController.swift
@@ -10,7 +10,7 @@ import UIKit
 
 class AmbiguousLayoutViewController: UIViewController {
 
-    override func viewDidAppear(animated: Bool) {
+    override func viewDidAppear(_ animated: Bool) {
         super.viewDidAppear(animated)
         
         // 曖昧なレイアウトを持つラベルを定義
@@ -20,12 +20,12 @@ class AmbiguousLayoutViewController: UIViewController {
         view.addSubview(ambiguousLabel)
         
         // 制約を垂直方向のみに与える
-        ambiguousLabel.centerYAnchor.constraintEqualToAnchor(view.centerYAnchor).active = true
+        ambiguousLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
         
         // 曖昧なレイアウトか確認
         print(view.hasAmbiguity())
         
         // 8.2.2 曖昧なレイアウトを視覚で確認する
-        view.exerciseAmbiguityInLayoutRepeatedly(true)
+        view.exerciseAmbiguityInLayoutRepeatedly(recursive: true)
     }
 }
/AutoLayoutBookSampler/Ch8/AmbiguousLayoutWithPrivateMethodsViewController.swift
@@ -15,25 +15,25 @@ class AmbiguousLayoutWithPrivateMethodsViewController: UIViewController {
     // ビューの階層構造を出力
     @IBAction func recursiveDescriptionAction(sender: AnyObject) {
         textView.text = view.recursiveDescription()
-        textView.textColor = UIColor.whiteColor()
+        textView.textColor = UIColor.white
     }
 
     // ビューの階層構造とAuto Layoutの情報を出力
     @IBAction func autolayoutTraceAction(sender: AnyObject) {
         textView.text = view._autolayoutTrace()
-        textView.textColor = UIColor.whiteColor()
+        textView.textColor = UIColor.white
     }
     
     // ビューコントローラの階層を確認する
     @IBAction func printHierarchyAction(sender: AnyObject) {
         textView.text = self._printHierarchy()
-        textView.textColor = UIColor.whiteColor()
+        textView.textColor = UIColor.white
     }
     
     // 全てのインスタンスを確認する
     @IBAction func ivarDescriptionAction(sender: AnyObject) {
         textView.text = self._ivarDescription()
-        textView.textColor = UIColor.whiteColor()
+        textView.textColor = UIColor.white
     }
     
 }
/AutoLayoutBookSampler/Ch8/ConflictedConstraintsViewController.swift
@@ -14,14 +14,14 @@ class ConflictedConstraintsViewController: UIViewController {
  
     @IBAction func constraintsAffectingLayoutForAxisAction(sender: AnyObject) {
         // 軸ごとに制約を分解する
-        textView.text = textView.constraintsAffectingLayoutForAxis(.Horizontal).description
-        textView.textColor = UIColor.whiteColor()
+        textView.text = textView.constraintsAffectingLayout(for: .horizontal).description
+        textView.textColor = UIColor.white
     }
     
     @IBAction func constraintsReferringViewAction(sender: AnyObject) {
         // ビューごとに制約を分解する
-        textView.text = view.constraintsReferringView(textView).description
-        textView.textColor = UIColor.whiteColor()
+        textView.text = view.constraintsReferringView(view: textView).description
+        textView.textColor = UIColor.white
     }
 }
/AutoLayoutBookSampler/Ch8/DebugHelper.swift
@@ -14,7 +14,7 @@ extension UIView {
         var hasAmbiguity = false
         
         #if DEBUG
-            if self.hasAmbiguousLayout() {
+        if self.hasAmbiguousLayout {
                 print("\(self.description) : AMBIGUOUS")
                 hasAmbiguity = true;
             }
@@ -32,13 +32,13 @@ extension UIView {
     
     func exerciseAmbiguityInLayoutRepeatedly(recursive: Bool) {
         
-        if self.hasAmbiguousLayout() {
-            NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: #selector(UIView.exerciseAmbiguityInLayout), userInfo: nil, repeats: true)
+        if self.hasAmbiguousLayout {            
+            UIKit.Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(UIView.exerciseAmbiguityInLayout), userInfo: nil, repeats: true)
         }
         
         if recursive {
             for view in subviews {
-                view .exerciseAmbiguityInLayoutRepeatedly(recursive)
+                view .exerciseAmbiguityInLayoutRepeatedly(recursive: recursive)
             }
         }
     }
@@ -57,7 +57,7 @@ extension UIView {
         // 共通の祖先ビューを探す
         var currentTarget: UIView? = target
         while let candidate = currentTarget {
-            if let _ = hierarchy.indexOf(candidate) {
+            if hierarchy.contains(candidate) {
                 return candidate
             }
             currentTarget = candidate.superview
このスクラップは2022/04/10にクローズされました