代码之家  ›  专栏  ›  技术社区  ›  Hapeki

将UIViewController视图属性设置为自定义UIView类,而不使用故事板/nib

  •  24
  • Hapeki  · 技术社区  · 9 年前

    我有一个 UIViewController 打电话 LoginViewController .我想构建一个这样的视图 LoginView控制器 完全程序化 按惯例 UIView 类调用 LoginView 而不是在我的 LoginView控制器 通过这种方式,我防止在控制器类(MVC)中“查看”代码。

    在下面的代码中,我设置了 LoginView控制器 我的 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)
            }
        }
    }
    

    结果:

    Result

    4 回复  |  直到 9 年前
        1
  •  27
  •   Jadar ohho    9 年前

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

    首先,让ViewController以编程方式使用不同的UIView子类的最佳方法是在 loadView 方法每个苹果的 docs :

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

    这看起来像这样:

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

    这样,您不必处理调整大小的问题,因为视图控制器本身应该处理它(就像它处理自己的UIView一样)。

    记住,不要打电话 super.loadView() 或者控制器将被混淆。而且,我第一次尝试时,因为忘记了打电话,所以屏幕变黑了 window.makeKeyAndVisible() 在我的应用程序代理中。在这种情况下,视图甚至从未添加到窗口层次结构中。您始终可以使用Xcode中的查看内省器按钮来查看发生了什么。

    第二,你需要打电话 self.addSubview(_:) 在UIView子类中,以使它们出现。将它们添加为子视图后,可以使用 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

        2
  •  3
  •   rmaddy    9 年前

    推翻 layoutSubviews 方法更新自定义视图中子视图的框架。

    永远不要打电话 super.loadView() loadView 方法

        3
  •  3
  •   DariusV    9 年前

    当LoginView控制器的布局约束已加载时,应加载自定义视图,请尝试以下操作:

      override func viewDidLoad() {
          super.viewDidLoad()
          let newView = LoginView(frame: view.bounds)
          view.addSubview(newView)
        }
    
        4
  •  2
  •   Ashish    8 年前

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

    推荐文章