I am trying to release the label text each time the person click on a book title from the table view, it should change the detailViewController label (titleLabel) however it keeps showing the same book title.
Wondering If i have done something wrong - well I know I have but wondering how I fix it...
//
// BookDetailViewController.m
#import "BookDetailViewController.h"
#import "Book.h"
#implementation BookDetailViewController
#synthesize aBook, titleLabel;
// Implement viewDidLoad to do additional setup after loading the view.
- (void)viewDidLoad {
[super viewDidLoad];
[self bookDes];
self.title = #"Book Detail";
}
-(void)bookDes {
[self.titleLabel setText:nil];
[self.titleLabel setText:aBook.title];
}
- (void)dealloc {
[aBook release];
[titleLabel release];
[super dealloc];
}
#end
You are calling [self bookDes] from viewDidLoad... This method is called after a view controller has loaded its associated views into memory. How are you creating the BookDetailViewController? If you only create it once and then reuse the controller each time a user presses a book title, the viewDidLoad method will also only be called once.
If you already have the book title in your parent controller, why don't you just set the property from there when you push the child onto the navigation controller?
bookDetailsController.titleLabel.text = selectedBook.title;
EDIT FROM COMMENT:
Yes, the BookDetailsViewController is created once, then saved... so the viewDidLoad is only called once.
One thing you could try is setting the label in the parent's didSelectRowAtIndexPath method:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Navigation logic -- create and push a new view controller
if(bdvController == nil)
bdvController = [[BookDetailViewController alloc] initWithNibName:#"BookDetailView" bundle:[NSBundle mainBundle]];
Book *aBook = [appDelegate.books objectAtIndex:indexPath.row];
bdvController.aBook = aBook;
bdvController.titleLabel.text = aBook.title;
[self.navigationController pushViewController:bdvController animated:YES];
}
there are better ways to do this like overriding the setter on the details controller and setting the label... but you should keep it simple and get it working first.
Hope this helps
Related
I have a view controller inside a tab bar controller. When I'm "inside" the initialized view I want to be able to press the tab bar item again and redraw the view.
My tabbarcontroller is created in the AppDelegate
#AppDelegate.m
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {
NSString *titleV = viewController.title;
if (titleV == #"Random") {
DetailViewController *detailViewController = [[DetailViewController alloc] init];
[detailViewController reloadView];
}
}
#ViewController.m
-(void)reloadView{
[self.view setNeedsDisplay];
NSLog(#"view updated");
}
//code
- (void)viewDidLoad
{
[super viewDidLoad];
[self checkContent];
NSLog(#"viewDidLoad");
}
//code
-(void)checkContent{
if (theContent==NULL) {
contentText.numberOfLines=0;
contentText.text = randomContent;
NSLog(#"%#", contentText.text);
} else {
contentText.text = theContent;
}
}
From the log I can see that contentText.text gets updated though the visible label does not until I move to another view and then back again. I'm not sure why this isn't working. Any ideas on how to solve this are greatly appreciated.
If you need more code I'd be happy to provide it.
Cheers,
Dubbelsnurr
Instead of putting - tabBarController:didSelectViewController in appDelegate, I would sub-class my tabBarController and conform to UITabBarDelegate, and call - tabBarController:didSelectViewController from within that.
Here is a tutorial that implements a similar concept:
http://iosdevelopertips.com/user-interface/detect-taps-on-uitabbarcontroller-and-determining-class-type.html
The other day I asked about using a UINavigationController as a child of a UIViewController. I got that working via the answer. Now what I'm trying to do is push a controller onto the nav stack. When a table cell is touched, I do the following:
- (void) showSetup {
NSLog(#"Showing Setup");
SetupViewController *controller = [[SetupViewController alloc]initWithNibName:#"SetupViewController" bundle:nil];
self.setupViewController = controller;
self.setupViewController.title = #"Setup";
[self.navigationController pushViewController:self.setupViewController animated:YES];
[controller release];
}
I can see the log statement in my console, but the view never changes. Am I missing something?
Hmmm, well it's a bit tricky without knowing the details of your implementation -- I assumed that you implemented your navigation controller as in the linked article. Also although you give no details it sounds like you've added a table view controller somewhere along the line, so I made the UIViewController conform to the UITableView protocols to handle everything in one place:
#interface SOViewController : UIViewController<UITableViewDelegate,UITableViewDataSource > {
UINavigationController* navController;
}
- (IBAction) pushMe:(id)sender;
#end
I dropped a button on the SOViewController's view in IB and wired the pushMe: action to it. I also created another UIViewController-based class called JunkController and dropped a "Junk" label on it's view in IB -- that's all I did in IB. In the SOViewController's viewDidLoad:
navController = [[[UINavigationController alloc] init] retain];
navController.navigationBar.barStyle = UIBarStyleBlackOpaque;
navController.toolbarHidden = YES;
UITableViewController* tvController = [[UITableViewController alloc] init];
UITableView* tv = [[UITableView alloc] init];
tvController.tableView = tv;
tv.delegate = self;
tv.dataSource = self;
[navController setViewControllers:[NSArray arrayWithObject:tvController]];
In the pushMe: action implementation:
[self presentModalViewController:navController animated:YES];
Implemented the tableView delegate and datasource methods; for selection:
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"row selected");
JunkController* junk = [[JunkController alloc] initWithNibName:#"junkcontroller" bundle:nil];
[navController pushViewController:junk animated:YES];
[junk release];
}
This should yield an app that surfaces a screen with a "Push me" button. When that button is pressed you should get an animated modal navigation-based table view -- mine had one row in it that contained a label "select me". Touching this row should animate the junk controller into view.
There is no need to make setupViewController a declared property in this view controller. Also, I could be mistaken but I thought "controller" was a reserved name in Cocoa, I'd change that name. So make sure you have registered with the UITableViewDelegate and use - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath to hook into and push your new view controller as follows:
SetupViewController *detailViewController = [[SetupViewController alloc] initWithNibName:#"SetupViewController" bundle:nil];
detailViewController.title = #"Setup";
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release];
Goodluck!
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'm trying to work out the "best" way to use a UISegmentedControl for an iPhone application. I've read a few posts here on stackoverflow and seen a few people's ideas, but I can't quite sort out the best way to do this. The posts I'm referring to are:
Changing Views from UISegmentedControl
and
How do I use a UISegmentedControl to switch views?
It would seem that the options are:
Add each of the views in IB and lay them out on top of each other then show/hide them
Create each of the subviews separately in IB, then create a container in the main view to populate with the subview that you need
Set up one really tall or really wide UIView and animate it left/right or up/down depending on the selected segment
Use a UITabBarController to swap out the subviews - seems silly
For tables, reload the table and in cellForRowAtIndex and populate the table from different data sources or sections based on the segment option selected (not the case for my app)
So which approach is best for subview/non-table approaches? Which is the easiest to implement? Could you share some sample code to the approach?
Thanks!
I've come across this requirement as well in an iPad application.
The solution I came to was to create specialized view controllers for
each style of view to handle business logic relating to those views
(ie. relating to each segment), and programatically add/remove them as
subviews to a 'managing' controller in response to selected segment
index changes.
To do this, one has to create an additional UIViewController subclass that manages
UISegmentedControl changes, and adds/removes the subviews.
The code below does all this, also taking care of a few caveats/extras:
viewWillAppear/viewWillDisappear/etc, aren't called on the subviews
automatically, and need to be told via the 'managing' controller
viewWillAppear/viewWillDisappear/etc, aren't called on 'managing'
controller when it's within a navigation controller, hence the
navigation controller delegate
If you'd like to push onto a navigation stack from within a
segment's subview, you need to call back on to the 'managing' view
to do it, since the subview has been created outside of the
navigation hierarchy, and won't have a reference to the navigation
controller.
If used within a navigation controller scenario, the back button is
automatically set to the name of the segment.
Interface:
#interface SegmentManagingViewController : UIViewController <UINavigationControllerDelegate> {
UISegmentedControl * segmentedControl;
UIViewController * activeViewController;
NSArray * segmentedViewControllers;
}
#property (nonatomic, retain) IBOutlet UISegmentedControl * segmentedControl;
#property (nonatomic, retain) UIViewController * activeViewController;
#property (nonatomic, retain) NSArray * segmentedViewControllers;
#end
Implementation:
#interface SegmentManagingViewController ()
- (void)didChangeSegmentControl:(UISegmentedControl *)control;
#end
#implementation SegmentManagingViewController
#synthesize segmentedControl, activeViewController, segmentedViewControllers;
- (void)viewDidLoad {
[super viewDidLoad];
UIViewController * controller1 = [[MyViewController1 alloc] initWithParentViewController:self];
UIViewController * controller2 = [[MyViewController2 alloc] initWithParentViewController:self];
UIViewController * controller3 = [[MyViewController3 alloc] initWithParentViewController:self];
self.segmentedViewControllers = [NSArray arrayWithObjects:controller1, controller2, controller3, nil];
[controller1 release];
[controller2 release];
[controller3 release];
self.navigationItem.titleView = self.segmentedControl =
[[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObjects:#"Seg 1", #"Seg 2", #"Seg 3", nil]];
self.segmentedControl.selectedSegmentIndex = 0;
self.segmentedControl.segmentedControlStyle = UISegmentedControlStyleBar;
[self.segmentedControl addTarget:self action:#selector(didChangeSegmentControl:) forControlEvents:UIControlEventValueChanged];
[self didChangeSegmentControl:self.segmentedControl]; // kick everything off
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.activeViewController viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self.activeViewController viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self.activeViewController viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
[self.activeViewController viewDidDisappear:animated];
}
#pragma mark -
#pragma mark UINavigationControllerDelegate control
// Required to ensure we call viewDidAppear/viewWillAppear on ourselves (and the active view controller)
// inside of a navigation stack, since viewDidAppear/willAppear insn't invoked automatically. Without this
// selected table views don't know when to de-highlight the selected row.
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
[viewController viewDidAppear:animated];
}
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
[viewController viewWillAppear:animated];
}
#pragma mark -
#pragma mark Segment control
- (void)didChangeSegmentControl:(UISegmentedControl *)control {
if (self.activeViewController) {
[self.activeViewController viewWillDisappear:NO];
[self.activeViewController.view removeFromSuperview];
[self.activeViewController viewDidDisappear:NO];
}
self.activeViewController = [self.segmentedViewControllers objectAtIndex:control.selectedSegmentIndex];
[self.activeViewController viewWillAppear:NO];
[self.view addSubview:self.activeViewController.view];
[self.activeViewController viewDidAppear:NO];
NSString * segmentTitle = [control titleForSegmentAtIndex:control.selectedSegmentIndex];
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:segmentTitle style:UIBarButtonItemStylePlain target:nil action:nil];
}
#pragma mark -
#pragma mark Memory management
- (void)dealloc {
self.segmentedControl = nil;
self.segmentedViewControllers = nil;
self.activeViewController = nil;
[super dealloc];
}
#end
Hope this helps.
I'd go with the second option you mention, creating the subviews in IB and swapping them in and out of a main view. This would be a good opportunity to use UIViewController, unsubclassed: in your initial setup, create a controller using -initWithNibName:bundle: (where the first parameter is the name of the NIB containing the individual subview, and the second parameter is nil) and add its view as a subview of your main view as necessary. This will help keep your memory footprint low: the default behavior of a UIViewController when receiving a memory warning is to release its view if it has no superview. As long as you remove hidden views from the view hierarchy, you can keep the controllers in memory and not worry about releasing anything.
(edited in response to comment:)
You don't need to subclass UIViewController, but you do need separate XIBs for each view. You also don't need to add anything to the containing view in IB.
Instance variables, in the interface of whatever class is handling all this:
UIViewController *controllerOne;
UIViewController *controllerTwo;
UIViewController *currentController;
IBOutlet UIView *theContainerView;
In your setup (-applicationDidFinishLaunching: or whatever)
controllerOne = [[UIViewController alloc] initWithNibName:#"MyFirstView" bundle:nil];
controllerTwo = [[UIViewController alloc] initWithNibName:#"MySecondView" bundle:nil];
To switch to a controller:
- (void)switchToController:(UIViewController *)newCtl
{
if(newCtl == currentController)
return;
if([currentController isViewLoaded])
[currentController.view removeFromSuperview];
if(newCtl != nil)
[theContainerView addSubview:newCtl.view];
currentController = newCtl;
}
Then just call that with, e.g.,
[self switchToController:controllerOne];
Here's a great tutorial that explains this concept further: http://redartisan.com/2010/5/26/uisegmented-control-view-switching
and the github location to it: https://github.com/crafterm/SegmentedControlExample.git
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];