How to update a detailView in a splitViewController? - objective-c

I'm trying to update a detailView in a splitViewController.
This is what I try:
//Get the application delegate
AppDelegate *delegate = [[UIApplication sharedApplication] delegate];
//Get the masterViewController
MasterViewController *master = [delegate.splitViewController.viewControllers objectAtIndex:0];
This seems to work fine so far.
Then I try to update a label in the detailViewController by doing this:
master.detailViewController.myLabel.text = #"someText";
But this doesn't work.
So how is the right way to access the outlets of in the detailView?
Thanks for help.

Define the DetailViewController; in your example the detailViewController is:
DetailViewController *detailViewController = [delegate.splitViewController.viewcontrollers objectAtIndex: 1];

You shouldn't be reaching inside another view controller and changing things. Instead, simply send a message to the DetailViewController, e.g. [detailViewController setCurrentEntry:selectedEntry]. Better yet, use notifications or KVO to let the DetailViewController observe the selection in the master list. This way, the two view controllers need not refer to each other at all; this is handy when you want to rearrange your app's layout, or use one view controller in isolation, say, in a modal view. Plan for the future.

Related

Confusion with UINavigationControllers in SplitViewController

I am setting up an iPad app that uses a SplitViewController. In my app delegate I have the following in didFinishLaunchingWithOptions:
UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;
UINavigationController *leftNavController = [splitViewController.viewControllers objectAtIndex:0];
LeftViewController *leftViewController = (LeftViewController*)[leftNavController topViewController];
DetailViewController *detailViewController = [splitViewController.viewControllers objectAtIndex:1];
NSLog(#"Detail View Ctrl >> %#", [detailViewController class]);
When I run the app, the NSLog statement returns "UINavigationController" when DetailViewController is actually a subclass of UIViewController. However, in XCode, code completion shows all the methods that are implemented in the DetailViewController subclass. Any ideas? Thanks!
I think your DetailViewController is actually embedded inside a UINavigationController, and your fourth line is in error. Take a look instead at the topViewController for the second view controller inside your split view controller, much like you do for the LeftViewController.
The reason Xcode is continuing to suggest completion for DetailViewController methods is because you've given it that type. Code completion doesn't rely on runtime behavior (how could it?) – instead, it relies on static analysis of the code that you type. If you tell Xcode that something is a DetailViewController, it'll believe you and autocomplete based on that information.

I lose access to my IBOutlets when making view controller new detail view controller

I have an iPad master-detail where the main detail controller is a navigation controller and depending on the table row (in the master view controller) selected, I may or may not replace the view controller managed by the detail navigation controller.
This seems to work fine, except that I lose access to the new detail view controller's IBOutlets when this move is made.
Here's me switching the navigation controller's view controller:
CustomerDetailViewController *subview = [[CustomerDetailViewController alloc] initWithNibName:#"CustomerDetailViewController" bundle:nil];
[subview setTitle:#"Testing"];
AppDelegate *app = (AppDelegate *)[[UIApplication sharedApplication] delegate];
app.splitViewController.delegate = subview;
[app.detailNavigationController setViewControllers:[NSArray arrayWithObjects:subview, nil]];
[subview setData:[custData objectForKey:#"name"]];
custData is an NSDictionary containing my view information. Here is the setData command:
- (void)setData:(NSDictionary *)cust {
NSLog(#"%#\n", [cust valueForKey:#"name"]);
self.nameLabel.text = [cust valueForKey:#"name"];
NSLog(#"%#\n", self.nameLabel.text);
}
So what happens is, subview becomes the new view controller but the label does not get changed - however, those two log commands are executed. The label is synthesized and wired up using IB and works if I push subview as a new view controller instead of replace it.
I'd say, the view is not yet initialized. Outlets are first connected in the viewDidLoad method. Try putting a log statement in the viewDidLoad to find out, which one gets called first.
If the viewDidLoad is called after your setData method, you can only set a local variable of the CustomerDetailViewController which is then read by viewDidLoad which sets the label accordingly.

Switch views - UIViewController to UITabBarController

I have made a very simple Navigation based app (UIViewController). The view has a single button on the Main RootViewController.
Next, I made 2 classes: TabOneViewController, TabTwoViewController. All good. I then created a new Class TabBarViewController. I opened up the NIB file and dropped on a ``UITabBarController onto it. The two tabs it creates in it by default were assigned (respectively) to my TabOne and TabTwo view controllers.
strong text
Then in my TabBarViewController, I made an IBOutlet for a UITabBarController, synthesized it etc etc. I linked it up in Interface builder via the "files owner".
In the RootViewController, I linked the button to my "pushView" method, and in this pushView method, I have the following code:
- (IBAction) pushView {
TabBarViewController *controller = [[TabBarViewController alloc] init];
[self.navigationController pushViewController:controller animated:YES];
[controller release];
}
The end result is it DOES push a view, but I cannot see the tab bar at the bottom, let alone any of the pages I've added to the controller.
What am I doing wrong? Why can't I link it in IB?
I am not 100% sure if that's allowed.. because you already have one tabBarController as rootViewController, and you dropped one more tabBarController as first tab controller, tabs ll overlap, considering amount of real estate you have on your iPhone, it make sense to not allow a tabViewController inside another
First, you need to allocate your view controller with your nib:
TabBarViewController *controller = [[TabBarViewController alloc] initWithNibName:#"YourNibName" bundle:nil];
Secondly, in IB, click the UITabBarController and go to the identity inspector and make sure you select your custom class. That said, unless you are overriding or adding some functionality you probably don't need the custom class at all, simply use a UITabBarController directly:
UITabBarController *controller = [[UITabBarController alloc] initWithNibName:#"YourNibName" bundle:nil];

AppDelegate can't add subView

Since appDelegate does not have a view, just window, its hard to figure out how to load a view from it. My problem has for long been that when didReceiveLocalNotification fires i cant load a new view with that event. I have been working around it til the point that i must do something about it. When i tries to addSubview, xcode gives me the error:
Receiver tupe 'UIWindow' for instance messages does not declare a method with selector 'addSubView'
for this: (at [self.window addSubView:view];)
screwLightBulbViewController *view = [screwLightBulbViewController newMyView];
[self.window addSubView:view];
I understand that the appDelegate file does'nt have a addSubview but i want to switch to a particular view when it fires.
I have tried many other ways, like calling a function in screwLightBulbViewController and make a view from that. My function in the viewController now looks like this:
+(id)newMyView
{
UINib *nib = [UINib nibWithNibName:#"MyView" bundle:nil];
NSArray *nibArray = [nib instantiateWithOwner:self options:nil];
screwLightBulbViewController *me = [nibArray objectAtIndex: 0];
return me;
}
any help in any way would be realy appreciated and thanks for you time. :)
It's addSubview not addSubView:. UIWindow is a subclass of UIView.
Adding a view directly as a subview to the window is not usually recommended, so instead you should try and add the view as a subview to the top controller view. If you can spare some time you should look over the view programming guide and view controller programming guide, it will be useful in the future.

How to present a view controller from another view controller

I am trying to open a ViewController from within another ViewController if certain conditions are met. The code seems to run without error but the view is never shown. I am new to xcode 4 /ios 5 so I must be missing something.
Here is the code responsible for opening the second viewcontroller:
CreateUserViewController *createUserController = [[CreateUserViewController alloc] initWithNibName:#"CreateUserView" bundle:[NSBundle mainBundle] keyWrapper:keyChainWrapper];
[self presentViewController:createUserController animated:YES completion:nil];
In my project I have a xib called, "CreateUserView". I have added a view controller to this xib and assigned it to, "CreateUserViewController".
Also I noticed in the apple documentation that is shows setting the delegate of the viewcontroller to be presented. But it seems that no property called, "delegate" is on the viewcontroller object. Is this documentation old? This is the document I am trying to use (section 9-1):
View Controller Programming
Can someone give me a hint? Thanks..
edit Adding Custom Constructor
-(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil keyWrapper:(KeychainItemWrapper *)keyWrapper
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if(self){
[self setKeyChainWrapper:keyWrapper];
}
return self;
}
Regarding CreateUserView.xib: you don't want to put a CreateUserViewController object in the nib. You want to set the custom class of the File's Owner placeholder to CreateUserViewController. Then you need to connect the view outlet of File's Owner to the top-level view in the nib.
Regarding the delegate property: The UIViewController class doesn't have its own delegate property. The idea is that you add a delegate property to your subclass of UIViewController. The delegate provides a way for your presented view controller to pass custom information back to the presenting view controller.
Why would you want to do that? Let's consider the code you posted. I'll assume you have a UserListViewController that shows a list of User objects, and has a "Create new user" button. When the user touches the "Create new user" button, you create a CreateUserViewController and present it.
The user interacts with the CreateUserViewController to set the attributes of the new User object - name, rank, hairstyle, etc. Then he touches a "Done" button. Your CreateUserViewController creates the new User object and puts it in the database. Then it needs to dismiss itself, so the UserListViewController's list of User objects will appear again.
But you want the User list to include the newly created User object and you want to scroll the list so that the new User is on the screen. So you need a way to have your CreateUserViewController tell the UserListViewController about the newly created User object. This is where the delegate comes in.
You define a protocol like this:
#protocol CreateUserViewControllerDelegate
- (void)didCreateUser:(User *)user;
#end
and you give your CreateUserViewController a delegate property:
#interface CreateUserViewController
#property (weak, nonatomic) id<CreateUserViewControllerDelegate> delegate;
// ...
When your CreateUserViewController's "Done" button is touched, you notify your delegate of the new User:
- (IBAction)doneButtonWasTouched:(id)sender {
User *user = [self createUser];
[self.delegate didCreateUser:user];
[self dismissViewControllerAnimated:YES completion:nil];
}
In your UserListViewController, you adopt and implement the protocol:
#interface UserListViewController <CreateUserViewControllerDelegate, UITableViewDelegate, UITableViewDataSource>
// ...
#end
#implementation UserListViewController
- (void)didCreateUser:(User *)user {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[self.users count] inSection:0];
[self.users addObject:user];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation: UITableViewRowAnimationAutomatic];
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition: UITableViewScrollPositionNone animated:YES];
}
and when you need to present a CreateUserViewController, you set the new controller's delegate to the UserListViewController:
- (IBAction)createUserButtonWasTouched:(id)sender {
CreateUserViewController *createUserController = [[CreateUserViewController alloc] initWithNibName:#"CreateUserView" bundle:[NSBundle mainBundle] keyWrapper:keyChainWrapper];
createUserController.delegate = self;
[self presentViewController:createUserController animated:YES completion:nil];
}
In iOS5 the method for pushing new view controllers was really changed around quite a bit from iOS4 and Xcode 3. In summary, storyboards are now used to create your application view controller flow. Even though you may use standalone .xib files to build an application it is much less common in iOS5.
Anyway, the main method for pushing new view controllers onto the screen is done using segues. Check out this tutorial for an introduction: http://www.raywenderlich.com/5138/beginning-storyboards-in-ios-5-part-1
It does a good job on explaining how to create a storyboard and use segues. You can still present view controllers in code "the old way" but it is much much less common now with the introduction of these new technologies. There are also some absolutely awesome tutorials on iTunes U - search for CS193P. It's the Stanford Introductory class to Objective-C and programming for iOS. This should get you started and maybe help you think of a way to push your createUserController in a way more up to speed with iOS5.
UPDATE
I just wanted to add. If you configure your program to use storyboards and segues you can use the method performSegueWithIdentifier:sender: to perform the segue to your createUserController view if the proper conditions are met. See the Apple API for UIViewController for information on how to use this method.