繁体   English   中英

将UIViewController视图属性设置为不带故事板/笔尖的自定义UIView类

[英]Set UIViewController view property to custom UIView class without storyboard / nib

我有一个名为LoginViewControllerUIViewController 我想在一个名为LoginView的自定义UIView类中以编程方式完全构建LoginViewController的视图,而不是在我的LoginViewController中构建所有元素。 这样,我就可以防止Controller类(MVC)中的“查看”代码。

在下面的代码中,我将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) } } } 

结果:

结果

看起来这里确实有两个问题。 第一,以编程方式设置ViewController的最佳方法是什么。 另外,如何以编程方式设置视图。

首先,让ViewController以编程方式使用其他UIView子类的最佳方法是在loadView方法中对其进行初始化和分配。 根据Apple的文档

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

看起来像这样:

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

这样,您就不必处理它的大小,因为View Controller本身应该照顾它(就像它自己的UIView一样)。

请记住,不要调用super.loadView()否则控制器会感到困惑。 另外,第一次尝试此操作时,我出现黑屏,因为我忘记在我的应用程序委托中调用window.makeKeyAndVisible() 在这种情况下,视图甚至从未添加到窗口层次结构中。 您始终可以使用Xcode中的view 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指南》。

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

而且永远不要调用super.loadView() 记录了loadView方法。

您应该在已经加载LoginViewController的布局约束时加载自定义视图,请尝试以下操作:

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM