Trouble Passing Array to Another VC

Discussion in 'iOS Programming' started by bkm, Apr 18, 2013.

  1. bkm macrumors newbie

    Joined:
    Apr 18, 2013
    #1
    I'm trying to pass an array of objects (IUButtons) from ViewController to SecondViewController. I can't figure out what piece I'm missing (edited for relevancy):

    ViewController.h
    Code:
    #import SecondViewController.h
    
    @interface ViewController : UIViewController{
        NSMutableArray *myButtons;
    }
    
    @property (nonatomic, retain) NSMutableArray *myButtons;
    ViewController.m
    Code:
    #import ViewController.h
    #import SecondViewController.h
    
    @synthesize myButtons;
    ...
    - (void)addMyButton
    {
        //'for' loop with buttons created here
    
           [myButtons addObject:button];
    
        NSLog(@"Array contents: %@", myButtons);
    }
    
    - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
    {
        if ([[segue identifier] isEqualToString:@"toResults"])
        {
            SecondViewController *obj = [[SecondViewController alloc]init];
            obj.myButtons = [[NSMutableArray alloc]initWithArray:self.myButtons];
        }
    }
    Note: At this point, the log shows myButtons has been properly loaded.

    SecondViewController.h
    Code:
    #import "ViewController.h"
    
    @interface SecondViewController : UIViewController{
            NSMutableArray *myButtons;
    }
    
    @property (nonatomic, retain) NSMutableArray *myButtons;
    SecondViewController.m
    Code:
    #import "SecondViewController.h"
    #import "ViewController.h"
    
    @synthesize myButtons;
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        NSLog(@"Array contents: %@", myButtons);
        ...
    }
    Note: Log shows this version of myButtons as (null).

    I've tried myButtons = [[NSMutableArray alloc]init]; in SecondViewController.m right before the log, which returned ( ). I've also tried
    Code:
    obj.myButtons = [[NSMutableArray alloc]init];
    obj.myButtons = myButtons;
    Thanks in advance!
     
  2. TheWatchfulOne macrumors 6502

    TheWatchfulOne

    Joined:
    Jun 19, 2009
    #2
    #1 Can we see the rest of your -(void)addMyButton method? I don't think that's where the problem is but one can never be too safe.

    #2 You've declared an instance variable:

    Code:
    NSMutableArray *myButtons;
    and a property:

    Code:
    @property (nonatomic, retain) NSMutableArray *myButtons;
    You populate the property like this:

    Code:
    obj.myButtons = [[NSMutableArray alloc]initWithArray:self.myButtons];
    But you read the instance variable directly like this:

    Code:
    NSLog(@"Array contents: %@", myButtons);
    What is your understanding of the connection between instance variables and properties.
    And what would you say are the best practices for accessing instance variables that are "related" to a property?
     
  3. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #3
    The SecondViewController that you instantiate here is not the same instance that the segue will use to present the new view.

    Also, why do you need to pass an array of buttons?
     
  4. bkm thread starter macrumors newbie

    Joined:
    Apr 18, 2013
    #4
    The segue is done via IB. Is it possible the problem stems from using IB and not performSegueWithIdentifier?

    The buttons are in array because they are created programmatically with a loop and need to be passed to the next view controller to read the selected state. It seemed like the most organized way to do it.
     
  5. bkm thread starter macrumors newbie

    Joined:
    Apr 18, 2013
    #5
    True that it's not the optimum way to return a value from the array and it gives me a weird return on the log (properties for the buttons), but all I really want to know is whether it holds anything or not.
     
  6. TheWatchfulOne, Apr 18, 2013
    Last edited: Apr 18, 2013

    TheWatchfulOne macrumors 6502

    TheWatchfulOne

    Joined:
    Jun 19, 2009
    #6
    I use this approach myself in certain situations. The same number of buttons is not always needed or even wanted. The frames can be calculated depending on how many buttons will be added. And your new view controller doesn't even have to care what the buttons are or what they do which makes for a good re-usable view controller. What a bonus!:cool:

    It's not that it's not the optimum way. It's that, in your code, the instance variable that you declared is not the same instance variable that's backing up your property

    You declare a property like this:
    Code:
    @property (nonatomic, retain) NSMutableArray *myButtons;
    You automatically get this instance variable:
    Code:
    NSMutableArray *_myButtons;
    Which is different from the instance variable you accessed in your NSLog statment.

    If you use your property like this:
    Code:
    self.myButtons
    Then you don't have to worry about accessing the incorrect variable.
     
  7. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #7
    Not really. The problem stems from instantiating a whole new SecondViewController (one that gets thrown away as soon as prepareForSegue: is done), rather than taking advantage of the destination view controller that's already part of the UIStoryboardSegue.

    I would suggest only passing the "states" of your buttons, rather than the whole UIButtons. But are these buttons related in some way to the "model" part of your app? Perhaps your model could be set based on the state of the buttons from one view controller, and then read from the model in your second view controller.
     
  8. Duncan C, Apr 18, 2013
    Last edited: Apr 18, 2013

    Duncan C macrumors 6502a

    Duncan C

    Joined:
    Jan 21, 2008
    Location:
    Northern Virginia
    #8
    Indeed. UI objects should not be used to pass information. You should treat UI objects as private to a view controller, and pass state information to the other view controller.

    If you're passing an array of buttons to the other view controller it's bad design. If all you need to pass is the selected state from the buttons, that's all you should pass.

    Create a mutable array that's the same size as your array of buttons. (using arrayWithCapacity). Then loop through your array of buttons and add a boolean NSNumber to the array for each button:

    Code:
    NSMutableArray *selectedStates = [NSMutableArray arrayWithCapacity: self.myButtons.count];
    
    for (UIButton *aButton in self.myButtons)
    {
       [selectedStates addObject: [NSNumber numberWithBool: aButton.isSelected]]
    }
    

    Finally, as dejo said, do not create a second view controller, fetch the destination VC of the segue:



    Code:
    - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
    {
      NSMutableArray *selectedStates = [NSMutableArray arrayWithCapacity: self.myButtons.count];
    
      for (UIButton *aButton in self.myButtons)
      {
         [selectedStates addObject: [NSNumber numberWithBool: aButton.isSelected]]
      }
      if ([[segue identifier] isEqualToString:@"toResults"])
        {
           SecondViewController *destination = (SecondViewController *)  segue.destinationViewController;
           destination.selectedStates = selectedStates;
        }
    
    
     
  9. bkm thread starter macrumors newbie

    Joined:
    Apr 18, 2013
    #9
    Thanks for the advice. I'm guessing I'll also need to add a setter sender.selected in my buttonPressed method, correct?
     

Share This Page