【问题标题】:How to Delegate from Parent to Multiple Different Childs?如何从父母委托给多个不同的孩子?
【发布时间】:2022-02-20 22:05:18
【问题描述】:

我有这个方案:

  • 家庭控制器
    • 导航控制器
      • Step1(NavigationController 的子级)
      • Step2(NavigationController 的子级)

我的问题是我不知道如何将信息从父母传递给所有不同的孩子,因为当我将代表分配给孩子时,总是取最后一个。所以如果我打电话给委托,只有我添加的最后一个孩子会收到信息

一些基本代码:

class HomeController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        addSubview(showSteps)
    }
    @objc func showSteps() {
        let controller = NavigationController()
        self.present(controller, animated: true, completion: nil)
    }
}


protocol UserProtocol: AnyObject {
    func userHasChanged(_ user: User?)
}


class NavigationController: UIViewController {

    var user: User?
    weak var delegate: UserProtocol?
    
    override func viewDidLoad() {
        
        // all step childs are differents
        let step1 = Step1()
        addStep(step: step1)
        delegate = step1
        
        let step2 = Step2()
        addStep(step: step2)
        delegate = step2
        
        let step3 = Step3()
        addStep(step: step3)
        delegate = step3
        
        let step4 = Step4()
        addStep(step: step4)
        delegate = step4
        
    }
    
    func addStep(step: UIViewController){
        self.addChild(step)
        step.willMove(toParent: self)
        view.addSubview(step.view)
        step.didMove(toParent: self)
    }
    
    func userHasChanged(_ user: User?){
        // this observe new user data from firebase
        Service.shared.fetchUserData(uid: "XXX") { user in
            self.user = user
            // send new data to childs
            // ONLY SEND TO CHILD4, (THE LAST ONE OBVIOUSLY)
            self.delegate?.userHasChanged(user)
        }
    }

}

// that is a child example
class Step1: UIViewController, UserProtocol {
    
    var user: User?
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    func tripHasChanged(_ new_user: User?) {
        print("changed1")
        self.user = new_user
    }

}

class Step2: UIViewController, UserProtocol {
    
    var user: User?
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    func tripHasChanged(_ new_user: User?) {
        print("changed2")
        self.user = new_user
    }

}

class Step3: UIViewController, UserProtocol {
    
    var user: User?
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    func tripHasChanged(_ new_user: User?) {
        print("changed3")
        self.user = new_user
    }

}

class Step4: UIViewController, UserProtocol {
    
    var user: User?
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    func tripHasChanged(_ new_user: User?) {
        print("changed4")
        self.user = new_user
    }

}

*只打印“changed4”

我的问题:

如何使用一个委托(可能是数组?)向所有不同的孩子发送数据?有可能,属于好的做法吗?

谢谢!

【问题讨论】:

  • 这和你在stackoverflow.com/questions/71162586/…问的一样。你接受了那个答案。你怎么又问了?
  • 对于这种行为(多个对象可以看到数据更改),您应该通知中心:每次用户更改时都会发送通知。每个步骤都注册到此通知,因此可以在收到通知后进行纠正。此外,在您的情况下,您应该有一个 BasicStep 类女巫来处理这个,而类 stepx 将是它的子类。
  • 是的,很抱歉再次询问,但一些逻辑已经改变。我该如何处理 BasicStep 类和 stepx 之类的子类。我认为这比在每个 stepX 上放置一个通知观察者要好。
  • 如果我必须删除上一个问题,我没有问题,我是新手,无论您推荐什么,我都会做。谢谢
  • @nacho1111 - 您将 step1 分配给 .delegate ... 然后将 step2 分配给 .delegate ... 等等。这相当于label.text = "A" 后跟label.text = "B",然后想知道为什么标签文本不是“A”。作为旁注,您正在向后使用协议/委托模式......

标签: ios swift delegates protocols


【解决方案1】:

可以创建一个委托数组...但这确实不是委托/协议设计模式。

您也可以采用NotificationCenter addObserver / post 通知方法...但这更适合不一定“受当前类控制”的多个对象。

对于您的情况 - 多个子视图控制器都需要基于同一事件“做某事”,更好的方法可能是创建一个“基础”视图控制器并制作该基础的每个“步骤”子类.

这是一个简单的例子...

// this is our "Step" base view controller
//  creates the "user" property
//  defines the "userHasChanged()" method
class StepBaseViewController: UIViewController {

    var user: String?
    func userHasChanged(_ new_user: String) {
        user = new_user
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // we can do anything that may be
        //  "common" to all "Steps"
    }

}

您创建StepBaseViewController 子类的任何视图控制器现在都将具有user 属性和处理userHasChanged 的默认方法。此外,步骤中“常见”的任何内容(例如标签、按钮等 UI 元素)都可以在 viewDidLoad() 中设置。

现在你的 4“步骤”类变成了:

class Step1: StepBaseViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        // setup specific to this "Step"
    }
    override func userHasChanged(_ new_user: String) {
        super.userHasChanged(new_user)
        print("User changed to:", self.user, "in:", self)
        // do something specific to Step 1
    }
}
class Step2: StepBaseViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        // setup specific to this "Step"
    }
    override func userHasChanged(_ new_user: String) {
        super.userHasChanged(new_user)
        print("User changed to:", self.user, "in:", self)
        // do something specific to Step 2
    }
}
class Step3: StepBaseViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        // setup specific to this "Step"
    }
    override func userHasChanged(_ new_user: String) {
        super.userHasChanged(new_user)
        print("User changed to:", self.user, "in:", self)
        // do something specific to Step 3
    }
}
class Step4: StepBaseViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        // setup specific to this "Step"
    }
    override func userHasChanged(_ new_user: String) {
        super.userHasChanged(new_user)
        print("User changed to:", self.user, "in:", self)
        // do something specific to Step 4
    }
}

这里是您的NavigationController 的修改版本,展示了如何使用这种方法:

class NavigationController: UIViewController {

    // we'll simulate the user changing
    //  so on first tap the user will become "User 1"
    //  on next tap user will become "User 2"
    //  on next tap user will become "User 3"
    // and so on
    var n: Int = 0
    var user: String = ""
    
    override func viewDidLoad() {
        
        // all step childs are differents
        let step1 = Step1()
        addStep(step: step1)
        
        let step2 = Step2()
        addStep(step: step2)
        
        let step3 = Step3()
        addStep(step: step3)
        
        let step4 = Step4()
        addStep(step: step4)
        
    }
    
    func addStep(step: UIViewController){
        self.addChild(step)
        step.willMove(toParent: self)
        view.addSubview(step.view)
        step.didMove(toParent: self)
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        // simulate the "user changed" event
        n += 1
        self.user = "User \(n)"
        self.children.forEach { child in
            if let vc = child as? StepBaseViewController {
                vc.userHasChanged(self.user)
            }
        }
    }

}

加载 NavigationController 后,点击 3 次会导致此调试控制台输出:

User changed to: Optional("User 1") in: <MyProj.Step1: 0x7fbd7a21caf0>
User changed to: Optional("User 1") in: <MyProj.Step2: 0x7fbd7a21c480>
User changed to: Optional("User 1") in: <MyProj.Step3: 0x7fbd7a21d480>
User changed to: Optional("User 1") in: <MyProj.Step4: 0x7fbd7a21dca0>
User changed to: Optional("User 2") in: <MyProj.Step1: 0x7fbd7a21caf0>
User changed to: Optional("User 2") in: <MyProj.Step2: 0x7fbd7a21c480>
User changed to: Optional("User 2") in: <MyProj.Step3: 0x7fbd7a21d480>
User changed to: Optional("User 2") in: <MyProj.Step4: 0x7fbd7a21dca0>
User changed to: Optional("User 3") in: <MyProj.Step1: 0x7fbd7a21caf0>
User changed to: Optional("User 3") in: <MyProj.Step2: 0x7fbd7a21c480>
User changed to: Optional("User 3") in: <MyProj.Step3: 0x7fbd7a21d480>
User changed to: Optional("User 3") in: <MyProj.Step4: 0x7fbd7a21dca0>

【讨论】:

  • 这就是我所需要的。太棒了@Donmag ? 非常感谢你
猜你喜欢
  • 2013-07-10
  • 2020-04-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-07
相关资源
最近更新 更多