2

I have "screen A" and "Screen B", I have button on "screen A" using which I can navigate to "screen B",

On "screen B" I have two buttons "Button red" and "Button Green", I want to change color of "screen A" by pressing buttons on "screen B"

I want to do this using delegate

even solution in Objective-C also helpful

New iOS Dev
  • 1,937
  • 7
  • 33
  • 67

4 Answers4

3

I'm not going to spell out the code for you because the delegate pattern is something I think you really need to work out on your own so it makes sense, but I'll give you the requirements you'll need.

  1. Make a protocol in Screen B. This will contain your delegate method
  2. Add a delegate method for passing back the color to Screen A. Naming convention would be something like - (void)shouldUpdateToColor:(UIColor *)color
  3. Add a weak, nonatomic property of type id<YourProtocol> called delegate (or whatever you'd like to call it).
  4. In Screen A, declare that Screen A is going to conform to YourProtocol from Screen B
  5. Implement the delegate method in Screen A
  6. Set Screen A as the delegate of Screen B when you are transitioning to Screen B
  7. Call the delegate method in Screen B when the button is pressed. Since Screen A is the delegate, it will call the method in Screen A, which will update your button.

Here's a link about delegation in Swift.

Ben Kane
  • 9,331
  • 6
  • 36
  • 58
  • Plus it sounds like a homework assignment. Why would anyone in their right minds not just access the view object and change its color? – Shamas S Apr 02 '15 at 14:14
  • Thanks, really nice answer i'll try to do this own using your way and yes its like something to learn about delegates – New iOS Dev Apr 02 '15 at 14:15
  • I'm assuming it's two separate view controllers, not just two views. – Ben Kane Apr 02 '15 at 14:16
  • A hint for you: You'll want your delegate property to be weak and optional (`weak var delegate: MyProtocol?`). In Swift, you will only be able to make it weak if you declare your protocol as a class-only protocol (`protocol MyProtocol: class`). – Ben Kane Apr 02 '15 at 14:41
  • @iosDev82, it is a bad, bad idea for one view controller to muck around with another view controller's view hierarchy. (It violates the encapsulation between view controllers.) Better to add a property or method to the other view controller and call that. – Duncan C Apr 02 '15 at 15:26
  • Yes @iosDev82 you don't want to be importing Screen A into Screen B. Then they're both referencing each other which will cause a retain cycle if you're doing it wrong, plus as Duncan C says, that'd be violating the encapsulation. Delegation is the standard way to handle passing info back to the previous view controller. – Ben Kane Apr 02 '15 at 15:33
  • @DuncanC Sorry, I clearly didn't understand the question right. I thought op wanted to change viewB's color from within viewB. – Shamas S Apr 02 '15 at 19:44
3

You can do it in the following way :

protocol ViewControllerBDelegate: class {
   func changeColor(color : UIColor)
}

class ViewControllerB: UIViewController {

   weak var delegate : ViewControllerBDelegate?   

   @IBAction func changeColorInViewController(sender: UIButton) {
       // send the message to change the color in A regarding the color
       sender.tag == 0 ? delegate?.changeColor(UIColor.redColor()) : 
                         delegate?.changeColor(UIColor.greenColor())
   }
}

The above ViewController is the ViewControllerB in which you want to change the color for the ViewControllerA.

Then you can implement the ViewControllerA in the following way :

class ViewControllerA: UIViewController , ViewControllerBDelegate {

    var viewControllerB : ViewControllerB! 

    // In this method you receive the notification when the button in B is tapped
    func changeColor(color: UIColor) {
       self.view.backgroundColor = color
    } 

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {        
       var dest = segue.destinationViewController as! ViewControllerB  

       self.viewControllerB = dest  // instantiate the reference
       self.viewControllerB.delegate = self  // set the delegate for B
    }
}

Two important things :

  1. I set the reference for the ViewControllerB in the prepareForSegue because you have a button to open the ViewControllerB, but in you present it manually you can change it as do you want.
  2. I only implemented an action for the two buttons in the ViewControllerB, and I assigned a tag for each (you can do it in Interface Builder or in code) to recognize it and send the color regarding the button pressed, but you can do it separately if you want.

I hope this help you.

Ben Kane
  • 9,331
  • 6
  • 36
  • 58
Victor Sigler
  • 23,243
  • 14
  • 88
  • 105
  • You'd want the delegate property to be weak to avoid retain cycles. I'll suggest an edit for this. A lot of people aren't doing it in Swift for some reason, but still should be. – Ben Kane Apr 02 '15 at 15:35
  • really nice answer , but can u tell me that , can we set delegate in viewDidLoad() , or other than prepareForSegue ()? – New iOS Dev Apr 06 '15 at 06:08
  • Yes, but you have to set the reference to the same `ViewController` you present, it's very important. It's why I put it in the `prepareForSegue` in the above case – Victor Sigler Apr 06 '15 at 15:58
1

Objective c:

@protocol ScreenBDelegate <NSObject>

- (void)screenBChangedColor:(UIColor *)color;

@end

@interface ScreenB : UIViewController

@property (weak, nonatomic) id<ScreenBDelegate>delegate;

@end

@implementation ScreenB

- (IBAction)buttonRedTapped
{
   if([self.delegate respondsToSelector:@(screenBChangedColor:)]){
      [self.delegate screenBChangedColor:[UIColor redColor]];
   }
}

@end

@interface ScreenA () < ScreenBDelegate>

@end

@implementation ScreenA

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.screenB.delegate = self; //find the best place to do it
}

- (void)screenBChangedColor:(UIColor *)color
{
   self.view.backGroudColor = color;
}

@end
Ben Kane
  • 9,331
  • 6
  • 36
  • 58
Klevison
  • 3,342
  • 2
  • 19
  • 32
0

The easiest way i can think of

Say that Screen A is FirstViewController and it has Storyboard name "Screen A"

 var storyboard = UIStoryboard(name: "Main", bundle: nil)
 var controller: UIViewController = storyboard.instantiateViewControllerWithIdentifier("Screen A") as FirstViewController
controller.view.backroundColor = UIColor.redColor()