将UIViewController视图属性设置为不带有storyboard / nib的自定义UIView类

时间:2016-12-31 00:40:53

标签: ios swift uiview uiviewcontroller

我有UIViewController名为LoginViewController。我想在名为LoginViewController的自定义UIView类中构建LoginView 完全以编程方式的视图,而不是构建LoginViewController中的所有元素。这样我就阻止了Controller类(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.但是我找不到任何能够深入了解这是否可行的资源,所以我尝试了我的方法,但是没有用。

如何将UIViewController的视图设置为以编程方式创建的自定义UIView?或者上面的片段是推荐的吗?

  

这是基于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)
        }
    }
}

结果:

{{3}}

4 个答案:

答案 0 :(得分:12)

看来这里真的有两个问题。一,以编程方式设置ViewController的最佳方法是什么。另一种,如何以编程方式设置View。

首先,以编程方式使用不同UIView子类的ViewController的最佳方法是在loadView方法中初始化并分配它。根据Apple的docs

  

您可以覆盖此方法以手动创建视图。   如果选择这样做,请将视图层次结构的根视图指定给   视图属性。您创建的视图应该是唯一的实例   不应与任何其他视图控制器对象共享。您的   这种方法的自定义实现不应该调用super。

这看起来像这样:

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

这样你就不必处理它的大小调整,因为View Controller本身应该处理它(就像它使用它自己的UIView一样)。

请记住,不要拨打super.loadView()或控制器会混淆。此外,我第一次尝试这个时,我得到了一个黑屏,因为我忘了在我的App Delegate中拨打window.makeKeyAndVisible()。在这种情况下,视图从未添加到窗口层次结构中。您始终可以使用Xcode中的视图introspecter按钮查看正在进行的操作。

其次,您需要在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 Guide

答案 1 :(得分:2)

重写layoutSubviews方法以更新自定义视图中的子视图的框架。

永远不要致电super.loadView()。这是针对loadView方法记录的。

答案 2 :(得分:2)

你应该在加载了LoginViewController的布局约束时加载自定义视图,试试这个:

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

答案 3 :(得分:0)

在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有一个框架,您可以通过提供框架来设置所有视图。