如何从UIView访问UI元素

时间:2017-07-12 12:26:07

标签: ios swift

我刚开始从Interface Builder迁移到100%代码。

我已成功创建了自动布局单屏应用程序作为概念验证。问题是,我觉得好像有一种更有效的方法。

从UIView类中获取对象并在UIViewController中访问对象的正确方法是什么?

这是我整个项目的代码

AppDelegate.swift

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?


    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        window = UIWindow(frame: UIScreen.main.bounds)
        window?.rootViewController = ViewController()
        window?.makeKeyAndVisible()
        return true
    }
}

ViewControllerView.swift

import UIKit

public enum ViewControllerTextFields: String {
    case username = "usernameField"
    case password = "passwordField"
}

public enum ViewControllerButtons: String {
    case login = "loginButton"
}

class ViewControllerView: UIView {

    private var views = [String: UIView]()

    override init(frame: CGRect) {
        super.init(frame: frame)
        backgroundColor = .lightGray
        createScreenComponents()
        createConstraints()
    }

    func makeTextField(withPlaceholder text: String, textColor color: UIColor) -> UITextField {
        let textField = UITextField()
        textField.attributedPlaceholder =  NSAttributedString(string: text, attributes: [.foregroundColor : color.withAlphaComponent(0.5)])
        textField.textColor = color
        textField.translatesAutoresizingMaskIntoConstraints = false
        return textField
    }

    func makeButton(withTitle title: String) -> UIButton {
        let button = UIButton()
        button.translatesAutoresizingMaskIntoConstraints = false
        button.setTitle(title, for: .normal)
        return button
    }

    func createScreenComponents() {
        let usernameField = makeTextField(withPlaceholder: "Username", textColor: .white)
        let passwordField = makeTextField(withPlaceholder: "Password", textColor: .white)

        let loginButton = makeButton(withTitle: "Login")

        views["usernameField"] = usernameField
        views["passwordField"] = passwordField
        views["loginButton"] = loginButton
    }

    func createConstraints() {
        for (key, val) in views {
            addSubview(val)
            addConstraints(NSLayoutConstraint.constraints(
                withVisualFormat: "H:|[\(key)]|",
                options: [],
                metrics: nil,
                views: views)
            )
        }

        addConstraints(NSLayoutConstraint.constraints(
            withVisualFormat: "V:|[usernameField]",
            options: [],
            metrics: nil,
            views: views)
        )

        addConstraints(NSLayoutConstraint.constraints(
            withVisualFormat: "V:[usernameField][passwordField(==usernameField)]",
            options: [],
            metrics: nil,
            views: views)
        )

        addConstraints(NSLayoutConstraint.constraints(
            withVisualFormat: "V:[passwordField][loginButton(==passwordField)]|",
            options: [],
            metrics: nil,
            views: views)
        )

    }

    public func getTextFieldWithId(_ identifier: ViewControllerTextFields) -> UITextField {
        return views["\(identifier.rawValue)"] as! UITextField
    }

    public func getButtonWithID(_ identifier: ViewControllerButtons) -> UIButton {
        return views["\(identifier.rawValue)"] as! UIButton
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

ViewController.swift

import UIKit

class ViewController: UIViewController {

    var username: UITextField!
    var password: UITextField!

    override func loadView() {
        let viewObject = ViewControllerView()
        view = viewObject

        username = viewObject.getTextFieldWithId(.username)
        password = viewObject.getTextFieldWithId(.password)

        viewObject.getButtonWithID(.login).addTarget(self, action: #selector(test), for: .touchUpInside)
    }

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    @objc func test() {
        print("Hello")
    }
}

有没有办法访问ViewControllerView对象,如UITextFields和UIButtons,而不必使用viewObject变量并使用函数返回视图?

2 个答案:

答案 0 :(得分:1)

我的建议是在ViewController视图中保存文本字段和按钮的私有变量,但提供可在控制器中访问的只读变量,

在ViewControllerView.swift中:

private var _username: UITextField!
private var _password: UITextField!
private var _login: UIButton!

var username: UITextField! { get { return _username } }
var password: UITextField! { get { return _password } }
var login: UIButton! { get { return _login } }

然后在ViewController.swift中,您可以替换以下行:

username = viewObject.getTextFieldWithId(.username)
password = viewObject.getTextFieldWithId(.password)

viewObject.getButtonWithID(.login).addTarget(self, action: #selector(test), for: .touchUpInside)

使用:

username = viewObject.username
password = viewObject.password

viewObject.login.addTarget(self, action: #selector(test), for: .touchUpInside)

如果愿意,您甚至可以将这些变量作为实例变量存储在ViewController类中。

答案 1 :(得分:0)

我认为这是一种更合乎逻辑且更容易实现的方式:

// Typical AppDelegate.swift for running WITHOUT storyboard

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

        window = UIWindow(frame: UIScreen.main.bounds)
        window?.rootViewController = ViewController()
        window?.makeKeyAndVisible()

        return true
    }

}
// our start-up View Controller

import UIKit

class ViewController: UIViewController {

    var theVCView = ViewControllerView()

    override func viewDidLoad() {
        super.viewDidLoad()

        // replace "default" view with our custom view
        self.view = theVCView

        // example of adding a "Call Back" closure to "hear from" and get access to the custom view elements
        theVCView.buttonCallBackAction = {
            _ in
            var sUsername = ""
            var sPassword = ""
            if let t = self.theVCView.usernameField.text {
                sUsername = t
            }
            if let t = self.theVCView.passwordField.text {
                sPassword = t
            }
            print("Username:", sUsername)
            print("Password:", sPassword)
            // do whatever else you want here...
        }

    }

}
// and... our custom UIView

import UIKit

class ViewControllerView: UIView {

    lazy var usernameField: UITextField = {
        let textField = UITextField()
        textField.translatesAutoresizingMaskIntoConstraints = false
        textField.attributedPlaceholder =  NSAttributedString(string: "Username", attributes: [NSForegroundColorAttributeName : (UIColor.white).withAlphaComponent(0.5)])
        textField.textColor = .white
        return textField
    }()

    lazy var passwordField: UITextField = {
        let textField = UITextField()
        textField.translatesAutoresizingMaskIntoConstraints = false
        textField.attributedPlaceholder =  NSAttributedString(string: "Password", attributes: [NSForegroundColorAttributeName : (UIColor.white).withAlphaComponent(0.5)])
        textField.textColor = .white
        return textField
    }()

    lazy var loginButton: UIButton = {
        let button = UIButton()
        button.translatesAutoresizingMaskIntoConstraints = false
        button.setTitle("Login", for: .normal)
        button.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside)
        return button
    }()

    // optional "Call Back" closure
    var buttonCallBackAction : (()->())?

    func buttonTapped(_ sender: Any?) -> Void {
        buttonCallBackAction?()
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.commonInit()
    }

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

    func commonInit() {
        self.backgroundColor = .lightGray
        createConstraints()
    }

    func createConstraints() {
        let views = [
            "usernameField": usernameField,
            "passwordField": passwordField,
            "loginButton": loginButton
        ] as [String : UIView]

        for (key, val) in views {
            addSubview(val)
            addConstraints(NSLayoutConstraint.constraints(
                withVisualFormat: "H:|[\(key)]|",
                options: [],
                metrics: nil,
                views: views)
            )
        }

        addConstraints(NSLayoutConstraint.constraints(
            withVisualFormat: "V:|-80-[usernameField][passwordField(==usernameField)][loginButton(==passwordField)]",
            options: [],
            metrics: nil,
            views: views)
        )

    }

}

现在,您拥有自定义视图的属性,您可以在" normal"方式。