Reputation: 2754
I'm new to iOS programming so I want to make this thing from scratch to be able understand how the whole thing works. So instead of using the master detail template I did this from the ground up.
I'm having a huge roadblock in terms of passing data between the master view and the detail view. At the moment, whenever I tap on the item of the master view it would look for the first view on the detail view controller, let's call it mainswitchviewcontroller
. It's the first ViewController
connected via segue to the UINavigationController
for the detail controller part (Please refer to image below).
This happens after login where it shows that viewcontroller
, I want the user to pick an option before accessing the whole app. which will lead them to the last ViewController
of the flow (refer to the image below - red circle).
Now what I'm trying to do is this:
@interface MasterListViewController : UITableViewController{
}
@property (nonatomic) int matNum;
@property (strong, nonatomic) DetailStaffMainContentViewController *detailViewController;
@end
:
:
@implementation MasterListViewController
@synthesize matNum;
:
:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
for(UIViewController *vc in self.splitViewController.viewControllers){
NSLog(@"%@)", vc.title);
}
self.detailViewController.teststring = @"gronk";
}
This is my detailViewController (the one I want to access)
@implementation DetailStaffMainContentViewController
@synthesize tableView;
@synthesize teststring;
@synthesize lblOutput;
Now on select of the row, I assign the teststring
which I want to use to populate that label on the controller. That's what I did.
My questions are:
I'm getting an error. Wondering what I was doing wrong?
[MainSwitchBoardViewController setTeststring:]: unrecognized selector
sent to instance 0x7572190
Is this a recommended way of passing data? Or should I just stick with notificationcenters?
Thoughts?
EDIT Just to give you on the flow of the app:
User logs in (if username does not exist, the floating viewcontroller will pop up and force the user to log in.
Once the user logs in, the pop up goes away and then asks the user what type of role is to be used in the current session (eg. staff or fighter).
If the user picks fighter, it pushes the user to the top VC If the user picks staff, it pushes the user to the bottom VC (collections controller)
FOR Staff: At the moment I'm pushing to the last VC with this [self perfermSegueWithIdentifier:sender:self]
Once the user gets shown the last VC (DetailsStaffView), I want this to be the main Details View where the user does it's transaction. A few more pop ups? (Or maybe another push to another VC for specific transaction needs to be added). But ultimately I don't want the user to access the first Viewcontroller (MasterSwitchBoard)
EDIT 2 This is the main switch board class that controls the first VC after login.
@interface MainSwitchBoardViewController : UIViewController{
}
@property (strong, nonatomic) IBOutlet UILabel *txtLabel;
- (IBAction)btnStaff:(id)sender;
- (IBAction)btnAdmin:(id)sender;
@end
This is the collections VC which atm automatcially pushes to the last VC. In the beginning I was using a notification on this but it seems like it's not necessary so I took it out.
@interface MatListCollectionViewController ()
@end
@implementation MatListCollectionViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
[self performSegueWithIdentifier:@"SeguePushToStaffDetailFromMats" sender:self];
}
And then the details staff
@interface DetailStaffMainContentViewController : UIViewController{
}
@property (strong, nonatomic) IBOutlet UITableView *tableView;
@property (nonatomic, retain) IBOutlet UILabel *lblOutput;
@property (nonatomic, retain) NSString *teststring;
@end
@implementation DetailStaffMainContentViewController
@synthesize tableView;
@synthesize teststring;
@synthesize lblOutput;
Upvotes: 0
Views: 1502
Reputation: 438202
I'll tackle those in reverse order:
Is this a recommended way of passing data? Or should I just stick with notificationcenters?
You theoretically could use notification center, but I wouldn't in this case. Personally, I only use NSNotificationCenter
if:
I'm doing asynchronous process which has no reasonable way of knowing the state of the view controllers (e.g. I have some background task that's doing some time consuming update of data from a server, and the user could have navigated to just about anywhere in the app by the time I'm ready to notify the view controllers that the update is done); and/or
I potentially have multiple objects that I want to notify of some event.
Neither of those conditions apply here. Certainly you could use NSNotificationCenter
, but that should be unnecessary. Explicitly setting properties is generally preferable.
I'm getting an error. Wondering what I was doing wrong?
Unfortunately, it's hard to say on the basis of the evidence you've presented thus far. But there are a few things that I'm having trouble reconciling on the basis of your code and comments:
You've declared detailViewController
to be a DetailStaffMainContentViewController
(and that's the class for which you've synthesized the teststring
accessors);
But your error message suggests that detailViewController
is clearly a MainSwitchBoardViewController
object (which doesn't understand the setTeststring
accessor method); and
You haven't shown us how you're setting detailViewController
, so it's hard to say where you've gone wrong here.
Personally, in cases like this, I'm always reticent to define properties to maintain pointers to view controllers. I'd generally be inclined to ask the UISplitViewController
what's in the detail
split. So, rather than having a class property/ivar for that, in my master view controller, I have a method like:
- (UIViewController *)detailViewController
{
return [[self.splitViewController.viewControllers lastObject] topViewController];
}
That's basically saying "get the last object from my split view controller's array of viewControllers
(the detail split) and because I (personally) always use a navigation controller in that detail split, let's use the topViewController
to get a pointer to my subclassed UIViewController
that's in that detail split (embedded within the navigation controller).
Then I have my didSelectRowAtIndexPath
check to see if that view controller is the one I expected. If not, I segue to it (and have prepareSegue
pass the data I want to that controller).
So, for example, consider this storyboard:
Here, "A" is my default detail controller. "B" might be some random scene that I may have replaced "A" with. And "C" is my real detail view controller where I can see the details for the item I selected from the master view controller's table view. Thus, my didSelectRowAtIndexPath
for the master view controller might look like:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UIViewController *controller = [self detailViewController];
if (![controller isKindOfClass:[DetailCViewController class]])
{
// if I'm not already showing "C" in my detail split, then let's segue to it
[self performSegueWithIdentifier:@"GoToC" sender:self];
}
else
{
// if I'm already showing "C" in my detail split, let's just set the item of data I want to pass to it
DetailCViewController *cController = (DetailCViewController *)controller;
cController.detailItem = self.objects[indexPath.row];
}
// I have a method I call to make sure that the popover menu for the master view
// controller disappears (in case it popped up since I was in portrait mode) and
// adds the master view controller button to the navigation bar if I need it.
[self removePopoverAndAddNavigationButton];
}
In case I needed to segue to "C", I also have my `prepareSegue pass the detailItem as needed:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:@"GoToC"])
{
DetailCViewController *cController = (DetailCViewController *)[segue.destinationViewController topViewController];
cController.detailItem = self.objects[[[self.tableView indexPathForSelectedRow] row]];
}
}
As I said, trying to figure out why your detailViewController
has the wrong type of object is hard to say, but this is how I might tackle something like this.
Upvotes: 3
Reputation: 2270
Maybe you should first take a look at some nice movies from the last WWDC where they describe Storyboard Segues.
In the code you provided there is nothing written about segues. Be sure to use segues either via IB or programmatically via [self performSegueWithIdentifier:]
. See Apple Docs
Then you can provide additional operations in yout view controller:
[view shouldPerformSegueWithIdentifier:sender:]
[view prepareForSegue:sender:]
The first one decides whether to perform the segue or not. The second one is executed just before segue is performed. Within the secon operation you can provide information to the destinationViewController.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:@"showIssuesByProject"]){
if (_searchActive) {
((IssueListViewController *)segue.destinationViewController).project = [_filteredProjects projectAtIndex:[self.tableView indexPathForSelectedRow].row];
} else {
((IssueListViewController *)segue.destinationViewController).project = [_projects projectAtIndex:[self.tableView indexPathForSelectedRow].row];
}
}
if ([segue.identifier isEqualToString:@"showIssuesByFilter"]) {
((IssueListViewController *)segue.destinationViewController).filter = [_filters filterAtIndex:[self.tableView indexPathForSelectedRow].row];
}
}
Be careful: UISplitViewControll.viewControllers are only two controllers: the master and detail ones.
Upvotes: 2