实现可重用和可配置的 ViewController

Posted

技术标签:

【中文标题】实现可重用和可配置的 ViewController【英文标题】:Implement Reusable & Configurable ViewController 【发布时间】:2017-12-02 10:15:49 【问题描述】:

在我的 ios 应用程序中,我需要一个跨多个父级 ViewControllers 的通用模式 ViewController(我们称之为 GetData)。 GetData 需要可配置(如隐藏/显示一些 Views)。 GetData 应该能够将数据返回给调用 ViewController

根据我对 SO 和网络上其他资源的阅读,这是我尝试实现它的方式的简化:

// ViewController.swift

import UIKit

class ViewController: UIViewController, GetDataDelegate 

    // This shows the data received from GetData
    @IBOutlet weak var textviewResult: UITextView!

    // Call GetData with only mandatory and without optional views
    @IBAction func onClickGetData(_ sender: Any) 
        let getData = GetData(caller: self)
        getData.display(delegate: self)
    

    // Call GetData with both mandatory and optional data views
    @IBAction func onClickGetDataWithOptional(_ sender: Any) 
        let getData = GetData(caller: self, hasOptional: true)
        getData.display(delegate: self)
    

    // Delegate function to received data sent by GetData
    func gotData(data1: String, data2: String!) 
        textviewResult.text = "Mandatory: \(data1)"
                + (data2 != nil ? "\nOptional: \(data2!)" : "")
    


// GetData.swift

import UIKit

protocol GetDataDelegate 
    func gotData(data1: String, data2: String!)


class GetData: UIViewController 

    @IBOutlet weak var textfieldMandatory: UITextField!
    @IBOutlet weak var textfieldOptional: UITextField!
    // View with the optional views
    @IBOutlet weak var stackViewOptional: UIStackView!

    var delegate:GetDataDelegate?
    var caller:UIViewController?
    var hasOptional:Bool?

    init(caller: UIViewController, hasOptional: Bool? = false) 
        super.init(nibName: "GetData", bundle: nil)

        self.caller = caller
        self.hasOptional = hasOptional
    

    func display(delegate:GetDataDelegate) 
        self.delegate = delegate

        let viewController = UIStoryboard(name: "GetData", bundle: nil).instantiateViewController(withIdentifier: "GetData")
        viewController.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext
        viewController.modalTransitionStyle = UIModalTransitionStyle.crossDissolve

        caller?.present(viewController, animated: true, completion: nil)
    

    required init?(coder aDecoder: NSCoder) 
        super.init(coder: aDecoder)
    

    @IBAction func onSubmit(_ sender: Any) 
        self.dismiss(animated: true, completion: nil)
        delegate?.gotData(data1: textfieldMandatory.text!, data2: hasOptional! ? textfieldOptional.text! : nil)
    

    override func viewWillAppear(_ animated: Bool) 
        super.viewWillAppear(animated)

        // Configure GetData (hide/show views)
        // But hasOptional is nil!
        //stackViewOptional?.isHidden = !hasOptional!
    


我面临以下问题:

    delegate func 未在 GetData 中调用。 我无法更改GetData views(在func viewWillAppear)。

以上两者似乎都是因为membersdelegate(在funconSubmit)和hasOptional(在funcviewWillAppear)即使在初始化之后也是nil

需要进行哪些更正或者我需要采用不同的方法?

注意:以上为简化。在我的实际用例中,有多个视图控制器将调用GetData,有或没有可选的Views

【问题讨论】:

创建一个类GetData的实例(init(caller:)然后再创建同一个类的另一个实例(instantiateViewController)的目的是什么? 【参考方案1】:

我认为用容器来实现会更好——你可以在一个屏幕上拥有多个视图控制器。 Dave DeLong 就这种方法写了great articles。

【讨论】:

【参考方案2】:

我终于按照我的要求让它工作了:

// ViewController.swift

import UIKit

class ViewController: UIViewController, GetDataDelegate 

    // This shows the data received from GetData
    @IBOutlet weak var textviewResult: UITextView!

    // Call GetData with only mandatory and without optional views
    @IBAction func onClickGetData(_ sender: Any) 
        GetData.generate(caller: self)
    

    // Call GetData with both mandatory and optional data views
    @IBAction func onClickGetDataWithOptional(_ sender: Any) 
        GetData.generate(caller: self, hasOptional: true)
    

    // Delegate function to received data sent by GetData
    func gotData(data1: String, data2: String!) 
        textviewResult.text = "Mandatory: \(data1)"
                + (data2 != nil ? "\nOptional: \(data2!)" : "")
    


// GetData.swift

import UIKit

protocol GetDataDelegate 
    func gotData(data1: String, data2: String!)


class GetData: UIViewController 

    @IBOutlet weak var textfieldMandatory: UITextField!
    @IBOutlet weak var textfieldOptional: UITextField!
    // View with the optional views
    @IBOutlet weak var stackViewOptional: UIStackView!  

    var delegate:GetDataDelegate?
    var hasOptional:Bool?

    static func generate(caller: UIViewController, hasOptional: Bool? = false) 
        let getData = UIStoryboard(name: "GetData", bundle: nil).instantiateViewController(withIdentifier: "GetData") as! GetData
        getData.delegate = caller as? GetDataDelegate
        getData.hasOptional = hasOptional
        caller.present(getData, animated: true, completion: nil)
    

    @IBAction func onSubmit(_ sender: Any) 
        self.dismiss(animated: true, completion: nil)
        delegate?.gotData(data1: textfieldMandatory.text!, data2: hasOptional! ? textfieldOptional.text! : nil)
    

    override func viewWillAppear(_ animated: Bool) 
        super.viewWillAppear(animated)

        // Configure GetData (hide/show views)
        stackViewOptional?.isHidden = !hasOptional!
    

希望这是一个正确的方法...

【讨论】:

以上是关于实现可重用和可配置的 ViewController的主要内容,如果未能解决你的问题,请参考以下文章

我怎样才能使这个对象映射在 Go 中更加干燥和可重用?

在 ViewController 中使用带有外部 DataSource 和 UITableView 的自定义 UITableViewCell

初始Mybatis

工作流中容器化的依赖注入!Activiti集成CDI实现工作流的可配置型和可扩展型

如何创建可扩展和可维护的前端架构

如何创建可重用的 Twig 组件