0

Very basic: I need to pass information from one view to another. They are in the same ViewController. What is the most elegant way to approach this?

NotificationCenter is not an option

It would be ideal, if I could access certain properties of a view from another view directly

Marmelador
  • 947
  • 7
  • 27
  • please check the updated answer with KVO example. – inokey Mar 16 '18 at 14:34
  • The most elegant way would have the logic in a seperate class (non-view class). A view shouldn't be directly affected by an other view. The delegate's like inokey would work, but do not use the delegates in the view but in your viewcontroller. – J. Doe Mar 16 '18 at 14:35

2 Answers2

3

Let's say you have two views view1 and view2. For example some data changed in view2 and you need to pass this data to view1. I'd do this with delegation pattern. So here's the setup:

protocol View2Delegate {
    func didChangeSomeData(data: String) 
}

Now in view2

class View2: UIView {

   var delegate: View2Delegate?

   var text: String = String() {
        didSet {
           self.delegate.didChangeSomeData(data: text)
        }
   }
}

and in view1

class View1: UIView, View2Delegate { 

     var textToChange: String = String()

     func didChangeSomeData(data: String) {
          // Do whatever you want with that changed data from view2
          textToChange = data
     }
}

and in your viewController

class MyViewController: UIViewController {
     var: view1 = View1()
     var: view2 = View2()

  func viewDidLoad() {
      super.viewDidLoad()
      view2.delegate = view1
  }

Now, if you don't really want to couple view1and view2, you could listen via the same pattern changes of view2 in ViewController and then pass it directly to the view1 via property accessor.

class MyViewController: UIViewController, View2Delegate {
     var: view1 = View1()
     var: view2 = View2()

  func viewDidLoad() {
      super.viewDidLoad()
      view2.delegate = self
  }

  func didChangeSomeData(data: String) {
        view1.textToChange = data
  }

More on protocols in the Apple's Documentation

Update

You could also go with Key Value Observing pattern. You could setup it like this:

class MyViewController: UIViewController {
     var view1 = View1()
     var view2 = View2()

     override func viewDidLoad() {
         super.viewDidLoad()

         let observation = view2.observe(\.text) { (observed, change) in
           if let newValue = change.newValue {
                view1.textToChange = newValue
           }
         }

     }

 }
inokey
  • 5,434
  • 4
  • 21
  • 33
  • I think the viewcontroller should be responsible for the delegates, not the views itself. – J. Doe Mar 16 '18 at 14:36
  • @J.Doe ideally yes. That's why I included decoupled logic in the answer. But I guess there're some cases, when let's say you don't work with concrete values like Strings, and you may want a call on animation complete? In my understanding ViewController should not be concerned about completion of animation in some of it's subviews but other views might be. – inokey Mar 16 '18 at 14:42
0

A way to communicate between 2 objects (may it be a UIView or not) is as suggested in a previous answer by using delegates or KVO.

You can also use a completion handler, which is quite simple by using closures.

Below is an example

class Object1 {
    func foo(completed: () -> Void) {
        print("do some work")
        completed()
    }
}

class Object2 {
    func callFoo() {
        let obj1 = Object1()
        obj1.foo {
            print("foo() from object 1 completed")
        }

    }
}


let obj2 = Object2()
obj2.callFoo()

// should print:
// do some work
// foo() from object 1 completed
Chakir
  • 488
  • 4
  • 10