71002


<https://developer.apple.com/library/archive/referencelibrary/GettingStarted/DevelopiOSAppsSwift/ImplementingACustomControl.html>  
上記のチュートリアルを実施していて、`Add Buttons to the View`という場所をやっていたところである疑問が出ました。  
ViewControllerからStackViewで生成されるButtonに対して制約をかけたい場合はどうすればいいのでしょうか？

【状況】  
チュートリアルではStackBiew(RatingControl.swift)でその内部のbuttonに制約をかけていますが、これではViewControllerで扱っているViewをanchorとして参照できません。そこで私はdelegateでやればいいのかな？と思い以下のように実装しました。

・RatingControl.swift

[code]

    import UIKit
    
    protocol RatingControlDelegate: class {
        func setConstraint(of target: UIView)
    }
    
    class RatingControl: UIStackView {
        //MARK: Parameters
        var ratingControlDelegate: RatingControlDelegate?
        
        //MARK: Initialization
        override init(frame: CGRect) {
            super.init(frame: frame)
            setupButtons()
        }
        
        required init(coder: NSCoder) {
            super.init(coder: coder)
            setupButtons()
        }
        
        //MARK: Private Methods
        private func setupButtons() {
            // Create the button
            let button = UIButton()
            button.backgroundColor = UIColor.red
            print("In RatingControl -- Button Address = \(ObjectIdentifier(button).hashValue)")
            
            // Add the button to the stack
            addArrangedSubview(button)
            
            // Add constraints
            ratingControlDelegate?.setConstraint(of: button)
        }
    }
    
[/code]

・ViewController.swift

[code]

    import UIKit
    
    class ViewController: UIViewController, UITextFieldDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate, RatingControlDelegate {
        
        // MARK: Properties
    ...
        @IBOutlet weak var rating: RatingControl!
        
        override func viewDidLoad() {
            super.viewDidLoad()
            ...
            // Handle the rating button constraint for auto layout.
            rating.ratingControlDelegate = self
        }
        
    ...
        
        //MARK: RatingControlDelegate
        func setConstraint(of target: UIView) {
            print("In ViewController -- Button Address = \(ObjectIdentifier(target).hashValue)")
            target.translatesAutoresizingMaskIntoConstraints = false
            target.heightAnchor.constraint(equalToConstant: 200.0).isActive = true
            target.widthAnchor.constraint(equalToConstant: 200.0).isActive = true
            target.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 10).isActive = true
        }
    }
    
[/code]

この状態で実行したらconstraintは適用されずMain.storyboardで配置してある場所に表示されました。またprint文の結果も以下のようになっておりViewController.swiftのsetConstraint(of:)が呼ばれていない気がします。

[code]

    In RatingControl -- Button Address = -6568234602690583333
    
[/code]

【聞きたいこと】

  1. delegateしたはずのViewController.swiftのsetConstraintが呼ばれていないのは何故でしょうか？
  2. そもそもこのようなパターンはどう実装するのが正しいのでしょうか？Classは参照型だったと思いますので、この方法だと実現できないのかと思っています。

以上よろしくお願いいたします。


CC BY-SA 4.0
2020-10-07T14:58:44.013
70999
2020-10-07T23:56:12.560
2020-10-07T23:56:12.560
19110
24870
question
swift uikit
ViewControllerのパーツのStackViewの内部のbuttonに制約をかけたい


> delegateしたはずのViewController.swiftのsetConstraintが呼ばれていないのは何故でしょうか？

`RatingControl`がインスタンス化される時(つまり`init(frame:)`または`init(coder:)`が呼ばれる時)には、まだ`ratingControlDelegate`は設定されていませんので、`nil`です。したがって、その中で`ratingControlDelegate?.〜`という呼び出しをしても静かに無視されるだけです。

> そもそもこのようなパターンはどう実装するのが正しいのでしょうか？

そもそもこのようなパターンは作らないのが正しい実装です。`UIStackView`と言うのは内部に配置されたviewの間に自動的に制約を設定することにより、縦並び・横並びを簡単に行わせることを目的としたもので、`UIStackView`内部のview(ボタンなども含めて)と`UIStackView`が置かれている外側のviewとの間に制約を付けることはしませんし、無理やり制約をつけても、`UIStackView`が自動的に付ける制約との間に矛盾が生じて、正しいレイアウトにならない場合が多いです。

`UIStackView`内部のレイアウトは`UIStackView`に任せて、`UIStackView`そのものと外部のviewの間に制約を設定してください。


CC BY-SA 4.0
2020-10-07T21:10:11.303
71002
2020-10-07T21:10:11.303
13972
70999
answer