I'm new to storyboards and I'm having a problem with custom initialization.
Before using storyboards I used to do this to push a view controller from a table which has a custom initialization:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
//Creating Dummy Hotel
HotelItem *hotel = [[HotelItem alloc] init];
HotelDetailViewController *controller = [HotelDetailViewController controllerWithHotel:hotel];
controller.hidesBottomBarWhenPushed = YES;
[self.navigationController pushViewController:controller animated:YES];
[hotel release];
}
Now with storyboards I'm using prepareForSegue instead of didSelectRowAtIndexPath which turned into this:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
//Creating Dummy Hotel
HotelItem *hotel = [[HotelItem alloc] init];
hotel.name = #"Hotelucho";
// Make sure we're referring to the correct segue
if ([[segue identifier] isEqualToString:#"ShowSelectedHotel"]) {
HotelDetailViewController *controller = [segue destinationViewController];
}
}
I know that I can change my private variable "hotel" and set the property right after the [segue destinationViewController] but I find more useful to call my custom init method.
Is there a way to achieve this?
IF you've got a big chunk of code you need to do after an init, you might want to create a separate method for it, and then call that method in initWithCoder: and initWithFrame:.
Check this answer for more info:
Objective C - Custom view and implementing init method?
Related
How to pass values from one controller to another??? I use a StoryBoard.
I would like this to appear on the highlighted text view of the first view.
Call the next view of the code, I think something like this should look like:
UIStoryboard *finish = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
UIViewController *viewController = [finish instantiateViewControllerWithIdentifier:#"FinishController"];
viewController.modalPresentationStyle = UIModalPresentationPageSheet;
[self presentModalViewController:viewController animated:YES];
finishcontroller:
- (void)viewDidLoad
{
self.lblFinishTitle.text=self.FinishTitle;
self.lblFinishDesc.text = self.FinishDesc;
self.lblFinishPoint.text=self.FinishPoint;
[super viewDidLoad];
// Do any additional setup after loading the view.
}
first view:
-(void) prepareForSegue:(UIStoryboardPopoverSegue *)segue sender:(id)sender
{
if ([segue.identifier hasPrefix:#"FinishController"]) {
FinishController *asker = (FinishController *) segue.destinationViewController;
asker.FinishDesc = #"What do you want your label to say?";
asker.FinishTitle = #"Label text";
asker.FinishPoint = #"asdas";
}
}
I want to pass a value causing the transmission of the code
The issue is that you aren't actually using that segue, you are using presentModalController instead.
Note that usually, you can just ask self for it's storyboard. However, even that is unnecessary when you have a segues connected:
[self preformSegueWithIdentifier:#"FinishController" sender:self];
Then prepareForSegue will be called. Also note that you can (should) use something more authoritative than the segue identifier to determine if you should load the data... you can ask the segue's destination controller if it is the right class:
-(void) prepareForSegue:(UIStoryboardPopoverSegue *)segue sender:(id)sender
{
if ([segue.destinationViewController isKindOfClass:[FinishController class]]) {
FinishController *asker = (FinishController *) segue.destinationViewController;
asker.FinishDesc = #"What do you want your label to say?";
asker.FinishTitle = #"Label text";
asker.FinishPoint = #"asdas";
}
}
You are probably already aware (since you used the identifier in your code), but for the benefit of future discoverers of this post; segues are given identifiers in the inspector panel of Xcode when you are in the storyboard.
I have a tableview and a detailview, connected with segues.
I want the detailview's title to be the title of the selected cell.
If I use this code in GradeDetail.m, it works, but is static, of course:
self.navigationController.title = #"Title goes here";
If I use this on the other hand, it doesn't work, but should be dynamic:
self.navigationItem.title = grade.fag;
I have the class Grade imported where necessary and "fag" (means "school subject" in Danish, not gay) property defined in the class.
Here is an excerpt of the code from the tableview:
...
- (id)initWithCoder:(NSCoder *)aDecoder
{
if ((self = [super initWithCoder:aDecoder]))
{
grades = [[NSMutableArray alloc] init];
Grade* grade = [[Grade alloc] init];
grade.fag = #"Dansk";
grade.mundtlig = #"7";
[grades addObject:grade];
...
}
return self;
}
And the code pushing the detail-view:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
GradeDetail *detailViewController = [[GradeDetail alloc] init];
Grade* grade = [self.grades objectAtIndex:indexPath.row];
detailViewController.grade = grade;
//[self.navigationController pushViewController:detailViewController animated:YES];
}
Notice from the above code, what I commented out:
[self.navigationController pushViewController:detailViewController animated:YES];
If I remove the segue in the storyboard, and use this line of code instead, the detail-view shows the correct title, but the view isn't loading correctly. (Read: I don't want to use this line of code, I want it to work in storyboard.)
Please tell me if you want to see more of the code or have some of the above clarified.
You'll need to override -prepareForSegue:sender: to get a hook into the incoming view controller and set any properties required. You'll also need to specify segue identifiers in the storyboard.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"ShowGradeDetails"])
{
GradeDetail *detailViewController = (GradeDetail *)segue.destinationViewController;
NSIndexPath *selectedIndexPath = [self.tableView indexPathForSelectedRow];
Grade *selectedGrade = [self.grades objectAtIndex:selectedIndexPath.row];
detailViewController.grade = selectedGrade;
}
}
Maybe try setting the viewController's title: self.title = grade.fag;
Basically when I implement a split view like the one presented in the Apple example 'MultipleDetailsViews', everything works fine, it allocates a new detailed view each time a row is selected. Here is the relevant code from the example:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UIViewController <SubstitutableDetailViewController> *detailViewController = nil;
if (row == 0) {
FirstDetailViewController *newDetailViewController = [[FirstDetailViewController alloc] initWithNibName:#"FirstDetailView" bundle:nil];
detailViewController = newDetailViewController;
}
// ...
NSArray *viewControllers = [[NSArray alloc] initWithObjects:self.navigationController, detailViewController, nil];
splitViewController.viewControllers = viewControllers;
// ...
}
But what I'm looking for is reusing detailed view controllers, that is lazily allocating a view controller when it is selected, and keeping a reference on it in my object. That way when another row is selected the view controller is not deallocated and when it is selected again it would be reused instead of allocating a new one. Here is the relevant code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UIViewController <SubstitutableDetailViewController> *detailViewController = nil;
if (row == 0) {
if (self.firstDetailViewController == nil) {
FirstDetailViewController *newDetailViewController = [[FirstDetailViewController alloc] initWithNibName:#"FirstDetailView" bundle:nil];
self.firstDetailViewController = newDetailViewController;
[newDetailViewController release];
}
detailViewController = self.firstDetailViewController;
}
// ...
NSArray *viewControllers = [[NSArray alloc] initWithObjects:self.navigationController, detailViewController, nil];
splitViewController.viewControllers = viewControllers;
// ...
}
self.firstDetailViewController is instanciated the first time the first row is selected then it is reused.
When I'm doing that it works well in landscape mode but in portrait mode after a few clicks in the popover menu it raises an exception: *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Popovers cannot be presented from a view which does not have a window.'
So why I care? and why I don't want to reallocate view controllers? because in some of them I want to perform tasks that wouldn't be interrupted (killed) if the user navigate in a new detailed view while the task isn't completed yet.
Is there someone with an idea of what happens or with a working implementation of what I'm trying to achieve?
The view controllers are designed to be created and thrown away, if you need something to run in the background for longer then best move it into the master view controller or a separate object.
If you do however want to experiment with reusing a view controller this can be achieved by setting the viewControllers property on the new navigation controller with the previous detail controller saved in the viewDidLoad:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"showDetail"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
NSManagedObject *object = [[self fetchedResultsController] objectAtIndexPath:indexPath];
UINavigationController* navigationController = (UINavigationController*)[segue destinationViewController];
// reuse existing controller
navigationController.viewControllers = #[self.detailViewController];
// update the detail controller as normal.
[controller setDetailItem:object];
controller.navigationItem.leftBarButtonItem = self.splitViewController.displayModeButtonItem;
controller.navigationItem.leftItemsSupplementBackButton = YES;
}
}
I am doing a book kinda application using a viewBased application. The mainView acts as the backgroundView for loading pages. Each page is a different view and have its own nib. When i swipe on a page(actually on the mainView which have loaded the current view/page), that page will be removed and the next/previous page will be added.
[currentPage.view removeFromSuperview];
[self.view insertSubview:nextPage.view atIndex:0];
I have added a popoverController with a barbutton in this mainView. Its intialized with a tableviewController class named popClass. PopClass is another UITableViewController class which will act as the parent for the popViewController.
self.myPopController = [[PopController alloc] initWithStyle:UITableViewStylePlain];
self.myPopOverController = [[UIPopoverController alloc] initWithContentViewController:myPopController];
When I select certain row in the popover, I want the backgroundview(mainView) to load the page corresponding to the number of the row.
For that I have written a function in bookView(mainView) Controller class. And is calling it from the popOver's parent class when i select certain row.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSInteger toPageNumber = indexPath.row + 1;
[viewController changeToPage:toPageNumber];
}
Function and the lines of code are executing but nothing is happening. Even the log lines are printed but no action takes place. The code is the same for removing a page and adding another page which is working successfully when i swipe the view/page.
What is the problem? OR is there anyother way to make the view change in mainView by using the popover?
You need to have refernce of the background view controller in the popover controller.
In the PopController controller you should have a delagate like:
#interface PopController: UITablViewController{
//// other varibales;
id delegate;
}
#property (nonatomic, retain) id delgate;
Now from the class in which you are showing the popovercontroller
you should add this line:
[myPopController setDelegate:self];
Then you can easily acces any method of the main view controller class. By using
[delegate callTheRequiredMethod];
Hope this helps.
Thanks,
Madhup
It's hard to tell what's going wrong without looking at the code. That said, here's what I'd do:
(1) When a row is selected in mainView, present the popoverController modally:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
PopoverViewController *vc = [[PopoverViewController alloc] init];
[self.navigationController presentModalViewController:vc animated:YES];
[vc release];
}
(2) When something changes in the popoverController, for example a row is selected, set a value in the parentViewController (which should be the MainView):
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
self.parentViewController.value = someValue;
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
(3) Dismiss the popoverController where-ever you choose by calling:
[self dismissModalViewControllerAnimated:YES]`
i got a tableview controller.
if a cell is selected i perform the following:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
ablogSingleCatTableViewController *singleCatTableViewController = [[ablogSingleCatTableViewController alloc] initWithStyle:UITableViewStylePlain];
// Push the detail view controller.
[[self navigationController] pushViewController:singleCatTableViewController animated:YES];
[singleCatTableViewController release];
}
i want to commit an object to the next view controller that slides in after this row is selected.
do i have to do something like this?
ablogSingleCatTableViewController *singleCatTableViewController = [[ablogSingleCatTableViewController alloc] initWithStyle:UITableViewStylePlain];
[singleTableViewController setMyObject:superDuperObject];
or is there an easier way to do something like that?
i need this object directly after this tableviewcontroller is initialized, to fill it with specific data that belongs to this object.
please give me some advices.
thanks
You can also pass the object to the next view controller when you init the view controller.
To do so, you need to implement your own initializer for the view controller.
For example:
- (id)initWithStyle:(UITableViewStyle)style superDuper:(SuperDuper*)superDuperObject {
if (self = [super initWithStyle:style]) {
superDuper = superDuperObject;
}
return self;
}
Then,
ablogSingleCatTableViewController *singleCatTableViewController =
[[ablogSingleCatTableViewController alloc] initWithStyle:UITableViewStylePlain superDuper:superDuperObject];