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) } } }
結果:
ここには本当に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ガイド を参照してください。
LoginViewControllerのレイアウト制約がすでにロードされている場合は、カスタムビューをロードする必要があります。これを試してください。
override func viewDidLoad() {
super.viewDidLoad()
let newView = LoginView(frame: view.bounds)
view.addSubview(newView)
}
layoutSubviews
メソッドをオーバーライドして、カスタムビュー内のサブビューのフレームを更新します。
また、super.loadView()
を呼び出さないでください。これは、loadView
メソッドについて説明されています。
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にフレームがあり、フレームを提供することにより、コードを介してすべてのビューをセットアップできます。