web-dev-qa-db-ja.com

UIViewControllerビュープロパティを、ストーリーボード/ nibなしのカスタムUIViewクラスに設定します。

UIViewControllerというLoginViewControllerがあります。 LoginViewController内のすべての要素を構築するのではなく、UIViewというカスタムLoginViewクラスでLoginViewController完全にプログラムでのビューを構築したい。このようにして、コントローラークラス(MVC)の "View"コードを防ぎます。

以下のコードでは、_LoginViewControllerのビューをLoginViewに設定しています。単純化のため、2のみが含まれていますUILabels

class LoginViewController: UIViewController {

override func loadView() {
    super.loadView()

    self.view = LoginView(frame: CGRect.zero)
}

LoginViewクラスは両方のラベルを初期化し、いくつかの制約を設定する必要があります。

class LoginView: UIView {

var usernameLabel: UILabel!
var passwordLabel: UILabel!


override init (frame : CGRect) {
    super.init(frame : frame)

    setupLabels()
}

convenience init () {
    self.init(frame:CGRect.zero)
}

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
}

private func setupLabels(){
    //Init labels and set a simple text
    self.usernameLabel = UILabel()
    self.usernameLabel.text = "Username"
    self.passwordLabel = UILabel()
    self.passwordLabel.text = "Password"

    //Set constraints which aren't possible since there is no contentView, perhaps using the frame?
    }
}

ビューの境界が0であるため、これは機能しません。しかし、これが可能かどうかを洞察するリソースを見つけることができなかったので、機能しない私のアプローチを試しました。

プログラムで作成されたカスタムUIViewにUIViewControllerのビューをどのように設定しますか?または、上記のスニペットはお勧めですか?

これはJadarの回答に基づく実用的なソリューションです:

class LoginViewController: UIViewController {

    override func loadView() {
        view = LoginView()
    }

    override func viewDidLoad() {
        super.viewDidLoad()        
    //  Do any additional setup after loading the view.

    }
}

class LoginView: UIView {

var usernameLabel: UILabel!
var passwordLabel: UILabel!

override init(frame: CGRect) {
    super.init(frame: frame)

    self.usernameLabel = UILabel()
    self.usernameLabel.text = "Username"
    self.passwordLabel = UILabel()
    self.passwordLabel.text = "Password"

    addSubview(usernameLabel)
    addSubview(passwordLabel)

    if let superview = usernameLabel.superview{
        //Setting AutoLayout using SnapKit framework
        usernameLabel.snp.makeConstraints { (make) in
            make.center.equalTo(superview)
        }
    }
}

結果:

Result

17
Hapeki

ここには本当に2つの質問があるようです。 1つは、ViewControllerをプログラムで設定する最良の方法は何ですか。もう1つは、プログラムでビューを設定する方法です。

まず、ViewControllerにプログラムで別のUIViewサブクラスを使用させる最良の方法は、loadViewメソッドで初期化して割り当てることです。アップル社の docs

ビューを手動で作成するために、このメソッドをオーバーライドできます。選択する場合は、ビュー階層のルートビューをビュープロパティに割り当てます。作成するビューは一意のインスタンスである必要があり、他のビューコントローラーオブジェクトと共有しないでください。このメソッドのカスタム実装では、superを呼び出さないでください。

これは次のようになります。

_class LoginViewController: UIViewController {    
    override func loadView() {
        // Do not call super!
        view = LoginView()
    }
}
_

この方法では、ビューコントローラー自体が(独自のUIViewの場合と同様に)対応する必要があるため、サイズ変更に対処する必要はありません。

super.loadView()を呼び出さないでください。コントローラが混乱します。また、初めてこれを試したとき、アプリデリゲートでwindow.makeKeyAndVisible()を呼び出すのを忘れたため、黒い画面が表示されました。この場合、ビューはウィンドウ階層に追加されることすらありませんでした。 Xcodeでイントロスペクターの表示ボタンを使用すると、何が起こっているかをいつでも確認できます。

次に、UIViewサブクラスでself.addSubview(_:)を呼び出して、それらを表示させる必要があります。それらをサブビューとして追加したら、NSLayoutConstraintを使用して制約を追加できます。

_private func setupLabels(){
    // Initialize labels and set their text
    usernameLabel = UILabel()
    usernameLabel.text = "Username"
    usernameLabel.translatesAutoresizingMaskIntoConstraints = false // Necessary because this view wasn't instantiated by IB
    addSubview(usernameLabel)

    passwordLabel = UILabel()
    passwordLabel.text = "Password"
    passwordLabel.translatesAutoresizingMaskIntoConstraints = false // Necessary because this view wasn't instantiated by IB
    addSubview(passwordLabel)

    NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "V:|-10-[view]", options: [], metrics: nil, views: ["view":usernameLabel]))
    NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "V:|-20-[view]", options: [], metrics: nil, views: ["view":passwordLabel]))

    NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "H:|-10-[view]", options: [], metrics: nil, views: ["view":usernameLabel]))
    NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "H:|-20-[view]", options: [], metrics: nil, views: ["view":passwordLabel]))
}
_

制約の作成に使用されるビジュアルフォーマット言語の詳細については、 VFLガイド を参照してください。

17
Jadar

LoginViewControllerのレイアウト制約がすでにロードされている場合は、カスタムビューをロードする必要があります。これを試してください。

  override func viewDidLoad() {
      super.viewDidLoad()
      let newView = LoginView(frame: view.bounds)
      view.addSubview(newView)
    }
3
DariusV

layoutSubviewsメソッドをオーバーライドして、カスタムビュー内のサブビューのフレームを更新します。

また、super.loadView()を呼び出さないでください。これは、loadViewメソッドについて説明されています。

2
rmaddy

ViewcontrollerのloadViewメソッドでこれを行います:

class LoginViewController: UIViewController {
    override func loadView() {
        super.view = LoginView()
    }
}

UIViewのカスタムクラスで次のようにします。

class LoginView: UIView {
    convenience init() {
        self.init(frame: UIScreen.main.bounds)
        setupLabels()
    }
}

これで、UIViewにフレームがあり、フレームを提供することにより、コードを介してすべてのビューをセットアップできます。

0
Ashish