I migrated a project from using XIB's to Storyboard, according to these instructions: https://stackoverflow.com/a/9708723/2604030
It went good.
But I can't make the segues work programmatically, and I need to use them this way, because I have 2 buttons that link to the same ViewController, with different types, hope you understand why from this image.
There are 2 difficulty mode buttons. The code I use:
`- (IBAction)btnNormalAct:(id)sender {
LevelController *wc = [[LevelController alloc] initWithNibName:#"LevelController" type:0];
[self.navigationController pushViewController:wc animated:YES];
}
- (IBAction)btnTimedAct:(id)sender {
LevelController *wc = [[LevelController alloc] initWithNibName:#"LevelController" type:1];
[self.navigationController pushViewController:wc animated:YES];
}`
This worked when I used XIB's, and I am sure I linked everything correctly in the storyboard's VCs. The seagues works if I make them from the storyboard. But how can I manage this situation.
ALSO: are those lines good when changing from XIB's to Storyboard? Is that the right way to do this change (the way shown in the link above)?
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
}
You can use the PrepareForSegue method to set things on the incoming view controller before it is called:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Make sure your segue name in storyboard is the same as this line
if ([[segue identifier] isEqualToString:#"YOUR_SEGUE_NAME_HERE"])
{
// Get reference to the destination view controller
LevelController *vc = [segue destinationViewController];
// Pass any objects to the view controller here, like...
[vc setType:1];
}
}
Don't use the button actions. Connect the segues to the buttons and give the segues unique identifiers. Then implement prepareForSegue:sender: in your controller. When the method fires, check the seque identifier and set the appropriate type on the `destinationViewController'.
When using a storyboard you should instantiate your controllers from the storyboard rather than using initWithNibName:bundle:. This is done by giving each view controller a unique identifier and then calling instantiateViewControllerWithIdentifier: (or, for the initial view controller, just instantiateInitialViewController) on the storyboard which you can get from the current controller (or if required with storyboardWithName:bundle:).
Related
I have a function that returns a view to be displayed. When I use:
UIViewController* vcontroller= [storyboard instantiateViewControllerWithIdentifier:#"meterEnlarge"];
return vcontroller.view;
I see the view. However, when I do:
ViewController_Meter_Enlarge_iPad* controller = [[ViewController_Meter_Enlarge_iPad alloc]init];
return controller.view;
I don't see the view. Considering I need to modify properties on the VC the second option is critical to me (as I start calling [controller setxyz], etc). How can I return the view from the VC?
The view won't be set unless you create the view controller and initialize it from a NIB file:
- (id)initWithNibName:(NSString *)nibName bundle:(NSBundle *)nibBundle
(reference)
(or manually assign a view to the view controller).
I take it that you expect the first view controller (the one from the storyboard) to be of your custom view controller class? In that case, you can check the actual class of the view controller returned to you from the storyboard, and do your custom setup if it matches the class that you want:
UIViewController *storyboardVC =
[storyboard instantiateViewControllerWithIdentifier:#"meterEnlarge"];
if ([storyboardVC isKindOfClass:[ViewController_Meter_Enlarge_iPad class]]) {
ViewController_Meter_Enlarge_iPad *customVC =
(ViewController_Meter_Enlarge_iPad *)storyboardVC;
[customVC setXYZ:#"foo"];
}
I am trying to set a simple image on an UIImageView as such when a button is touched on the first view:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
ImageViewController *imageViewController = [segue destinationViewController];
imageViewController.title = #"test";
imageViewController.imvBig.image = [UIImage imageNamed:#"test.png"];
imageViewController.lblTest.text = #"test2";
}
The .title gets set and appears just fine, but for some reason I can't get the imvBig.image to show up, nor can I get the label text to appear. Yes, I have the property created with an Outlet, etc (as I just dragged it over from IB right into the code and it was autocreated).
Can someone tell me how to properly set a property from one regular view to the next in a Storyboard setup?
Probably the ImageViewController has not loaded its views from the storyboard yet when the system sends prepareForSegue:sender:, so those outlets (imvBig and lblTest) are nil. Messages sent to nil are silently ignored.
It's usually not appropriate for the source view controller to muck around with the destination view controller's views. The destination view controller should set those view's settings, usually in either viewDidLoad, viewWillAppear:, or viewDidAppear:.
Looks like I was able to get this to work by creating an NSString property and then setting the label to the string on viewDidLoad of the ImageViewController:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.lbltest.text = _myString;
}
I guess I can't set the property directly, until after the view is loaded.
You don't need to set the viewController just use the segue destinationViewController property. I always set a identifier in the storyboard first so I can use multiple segues:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"Show ImageViewController"]) {
[segue.destinationViewController setTitle:#Test"];
[segue.destinationViewController setFoo:#"Bar"];
//More stuff to set
}
}
also create an image in the ImageViewController as a property and synthesize it,then set that to the image view in the viewDidAppear:
self. imvBig.image = myNewImge;//this in the viewDidLoad
And Set that image in the segue:
[segue.destinationViewController setMyImage:[UIImage imageNamed#"test.png"]];
I am learning iOS and I have written a simple iPhone app using iOS 5. The app shows a UITableView populated with Speakers' names, when I select one of the names its supposed to go to a UIViewController and show details about that person (name, address, etc), so its really two ViewControllers a UITableViewController and UIViewController (both subclassed).
So, in MICSpeakersTableViewController : UITableViewController I have this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
MICSpeakerDetailViewController *detailViewController = [[MICSpeakerDetailViewController alloc] initWithNibName:#"Detail" bundle:nil];
[detailViewController setSpeaker:[[self getSpeakers] objectAtIndex:indexPath.row]];
[self.navigationController pushViewController:detailViewController animated:YES];
}
which gets called when I select it and populates the speaker (in that its not nil and description matches).
Then I have this in the same implementation:
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSIndexPath *indexPath = [ self.tableView indexPathForCell:sender];
if ([segue.identifier isEqualToString:#"Detail"])
[segue.destinationViewController setSpeaker:[[self getSpeakers] objectAtIndex:indexPath.row]];
}
Which also gets called and the segue.identifier is Detail and the destinationViewController's speaker is set correctly (is not nil, description matches). I am not quite sure why I have to set that again since I am setting it in didSelectRowAtIndexPath but I set it again and it seems harmless.
Finally, in the MICSpeakerDetailViewController, the initWithNibName method is called and the self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; returns an instance.
However, the segue never happens and viewDidLoad is never called.
Its probably something small but I can't figure it out... any advice?
Edit: Here is a screenshot of the storyboard showing the segue and the controllers:
You have to embed the view controllers in a navigation controller for push segues to work. I don't know why it lets you define them without this in place.
To do this, click on your table view controller, then choose Editor --> Embed In --> Navigation Controller. If you should also be within a tab bar controller, then the navigation controller is embedded in that in a similar fashion. You should see the following:
Your segue will now work.
I am currently trying to copy an array I have to a new view that I am creating programmatically. I actually have found how to do this with normal navigation controller syntax. My issue is I'm using the new storyboard and I don't know the syntax to do the same thing. Here is the code I have..
CustomerListViewController *second = [[CustomerListViewController alloc] initWithNibName:#"CustomerListViewController" bundle: nil];
[second setValue:customerList.list];
// [self.navigationController pushViewController:second animated:YES];
[self performSegueWithIdentifier:#"LoginSegue" sender:self];
as you can see, I am programmatically creating the second view controller and storing the local array customerList.List to the created view controller's array variable. The next step is to open the new created view. The line commented out is the syntax to open the view under a navigation controller. The line below is the storyboard way, but minus specifying the view I created. I need to know the syntax for the storyboard to do the same thing as the navigation controller.
It does not make sense to create your own instance of CustomerListViewController here if you're using segues. The segue itself will create the view controller from the storyboard and the instance you have created here will do nothing.
Instead, just call performSegueWithIdentifier:sender: here. Then implement the prepareForSegue:sender: method like this:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"LoginSegue"]) {
CustomerListViewController *destinationController = (CustomerListViewController *)segue.destinationViewController;
[destinationController setValue:customerList.list];
}
}
I have the relatively common setup of a TabBarController whose tabs contain NavigationControllers which have TableViewControllers as their roots. I'm trying to perform some logic on initialization of one of these TableViewControllers but can't seem to find what init function gets called.
My goal is to add a listener in the TableViewController (that I have subclassed) which can respond to events by updating the navigationController.tabBarItem.badgeVluew property.
I've tried putting code into initWithStyle: as well as init but neither of them end up getting called. I've also tried putting it in viewDidLoad, but that only gets called once the controller actually appears (I need to have it happen as soon as the controller is loaded / as soon as the tab bar item shows up).
Does anyone know where I would put this code for it to happen on initialization of the controller?
Also, this is all set up through interface builder / NIBs. I'm not adding the nav controller or tableviewcontroller manually, which is why it's not clear what init function I need to override.
If you select one of your UITabBarItems in IB, you will see 'View loaded from "YourView"'. Click into this "gray" View. In the Inspector window you will see in the Attributes Tab (the tab on the left) the title and the NIB name which will be loaded (lets call it "YourNibName").
Now select the right tab of the inspector (Identity) and change the Classname (Combo next to Class) to your "YourViewController" class, which you must create in xcode. Don't use the standard ViewController, which is already selected. The InterfaceBuilder loads your nib and attaches it to your ViewController.
Open YourNibName and change FilesOwner's Class (Inspector, right Tab) to "YourViewController", too.
Your TabBar's NIB contains a FilesOwner, too. Create a ViewController for this FilesOwner and set its Class to this Controller (i.e. TabBarController)
In "TabBarController" you can find out which Tab was selected by using this code:
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController{
if ([viewController.nibName isEqualToString:#"NIBName1"]){
// Do something here, if you like. (i.e. Save the state in a string or int)
}
if ([viewController.nibName isEqualToString:#"NIBNAme2"]){
// Do something here, if you like. (i.e. Save the state in a string or int)
}
...
}
Here you can do something "global" or preinitialize something. This is ONE thing you can do.
INIT OF YOUR VIEWS:
If you select a Tab and the view (which is handled by YourViewController) will be shown for the first time, "viewDidLoad" will be called in "YourViewController"
- (void)viewDidLoad {
// Here you can add views programatically
[self.view addSubview:myNavigationController.view];
[self.view bringSubviewToFront:myNavigationController.view];
// And if you like, do some INIT here
[super viewDidLoad];
}
I hope this is what your question was about.
Now something about the badge. It's a hack, but works fine for me.
Header file:
Add an outlet to your controller, which is representing your TabBarController:
#interface yourController : UIViewController <UITabBarControllerDelegate> {
UITabBarController *tabBarController;
}
#property (nonatomic, retain) IBOutlet UITabBarController *tabBarController;
#end
Connect this outlet in IB with your TabBar.
Implementation:
In your TabBarControllerClass you can overwrite 'initWithNibName':
#synthesize tabBarController;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
// Do some init here
// select your desired item (it will be loaded)
// then you can assign the badge
tabBarController.selectedIndex = 1;
tabBarController.selectedViewController.tabBarItem.badgeValue = #"222";
// and select the item you will start with
tabBarController.selectedIndex = 0;
// if you like you can add a notification, which you can activate from anywhere else
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(itemBadgeChanged:)
name:#"itemBadgeChangedNotification"
object:nil];
}
return self;
}
if you don't use nib, use '- (void)loadView { ... }' instead.
You are using a subclass of the TabBar controller, maybe you can use 'self.selectedIndex = 1;' instead of 'tabBarController.selectedIndex = 1;', and so on. Just try this out
Hope this helps!