In my application I have 2 separate ways to get to the same UIViewController, on way is to go through 2 parent ViewControllers created using the storyboard and this way works perfectly just passing and id through each segue transition.
The other way (Which is what I need help with) goes directly from a button pressed on the RootViewController, it also just passes the id to a customly defined object called Event. The code for each transition is below:
From the segue:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSIndexPath *path = [self.tableView indexPathForSelectedRow];
Event *event = [nEventList objectAtIndex:path.row];
EventInfo *eventInfo = segue.destinationViewController; //Create new ViewController
eventInfo.eventID = event.id;
}
From the button:
- (void)FeaturedPressedAction:(id)sender
{
UIButton *button = (UIButton *)sender;
int tag = button.tag;
EventInfo *eventInfo = [[EventInfo alloc] init]; //Create new ViewController
eventInfo.eventID = #"1"; //There is an event with id '1'
[self.navigationController pushViewController:eventInfo animated:YES];
}
And the code to set up the ViewController:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
UIImage *image = [UIImage imageNamed:#"title.png"];
self.navigationItem.titleView = [[UIImageView alloc] initWithImage:image];
SingletonClass* myapp = [SingletonClass sharedInstance];
events = [myapp getEvents];
// Loop to filter out the events that Do not have this ID
Event *event;
BOOL found = false;
for (int i = 0; i < [events count]; i++) {
if(!found){
event = [events objectAtIndex:i];
if([event.id isEqualToString: eventID]){
found = true;
NSLog(#"Found the event with title %#", event.title);
}
}
}
self.eventTitle.text = event.title;
self.eventLocation.text = event.location;
self.eventDate.text = event.date;
self.eventTime.text = #"22:00";
self.eventDescription.text = event.details;
}
It finds the event fine as I can see with the NSLog's left in there, just none of the view loads when coming from the button press. Where am I going wrong?
Rather than alloc your EventInfo*, you should instantiate it from your storyboard instead.
So in your FeaturedPressedAction replace the initialisation line with:
EventInfo *eventInfo = [self.storyboard instantiateViewControllerWithIdentifier:#"..."];
And make sure you identify the ViewController in Interface Builder with the same identifier. Replace ... with something meaningful.
Related
I'm building my first basic tabbed, application with one of the views as a navigation controller that will display a view controller.
I'm running into an issue at the point the user selects a category from the first tableview as shown in the screenshot: http://www.cl.ly/7YOF
When another instance of the tableviewcontroller is loaded and pushed onto the stack of the navigationcontroller, the table is obstructed by the title bar:
http://www.cl.ly/7ZRz
The table view select logic is below:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
KHCategory *selectedItem = [categoryArray objectAtIndex:indexPath.row];
if (selectedItem.categories.count > 0) {
KHCategoryTableViewController *nextCategoryController = [[KHCategoryTableViewController alloc] init];
nextCategoryController.categoryArray = [[NSArray alloc] initWithArray:selectedItem.categories];
nextCategoryController.title = selectedItem.labelValue;
[self.navigationController pushViewController:nextCategoryController animated:YES];
[nextCategoryController release];
} else {
NSLog(#"show detail view");
}
}
EDIT:
I should be clear that an instance of KHCategoryTableViewController is the root of my NavigationController and the NavController is wired up to the first tab of a TabController.
Two interesting things: it measures 20 pixels down (size of status bar) and your line "nextCategoryController.title = ..." doesn't seem to do anything. So...
1) I assume you haven't used setStatusBarHidden?
2) Looks like navController stuff isn't working. Can you give the code from the appDelegate that creates the tabBar and NavController?
3) Add this code, and try calling [self dumpWindow: #"VDL"] from your Subcategory ViewDidLoad method. I find it invaluable whenever checking whether my view structure is correct.
- (void) dumpWindowFrom:(NSString *) fromText {
[self dumpViews: nil from:fromText];
}
void dumpViewsRecursive(UIView* view, NSString *text, NSString *indent) {
Class cl = [view class];
NSString *classDescription = [cl description];
if ([text compare:#""] == NSOrderedSame)
NSLog(#"%d: %# %# %#", (int)view, classDescription, NSStringFromCGRect(view.frame), view.hidden ? #"Inv" : #"Vis");
else
NSLog(#"%d: %# %# %# %#", (int)view, text, classDescription, NSStringFromCGRect(view.frame), view.hidden ? #"Inv" : #"Vis");
for (NSUInteger i = 0; i < [view.subviews count]; i++)
{
UIView *subView = [view.subviews objectAtIndex:i];
NSString *newIndent = [[NSString alloc] initWithFormat:#" %#", indent];
NSString *msg = [[NSString alloc] initWithFormat:#"%#%d:", newIndent, i];
dumpViewsRecursive (subView, msg, newIndent);
[msg release];
[newIndent release];
}
}
- (void) dumpViews: (UIView *) view {
dumpViewsRecursive (( (!view) ? [[UIApplication sharedApplication] keyWindow] : view), #"" ,#"");
}
- (void) dumpViews: (UIView *) view from:(NSString *) fromText{
dumpViewsRecursive ((!view) ? [[UIApplication sharedApplication] keyWindow] : view, fromText, #"");
}
4) You could always just cheat and add:
CGRect frame = [nextCategoryController.view frame];
frame.origin.y = frame.origin.y+20.0;
[nextCategoryController.view setFrame:frame];
Check the autoResizingMask of your KHCategoryTableViewController's view.
UINavigationController overview at iPhone Dev Center says:
Note: Because the amount of space
available for the custom view can vary
(depending on the size of the other
navigation views), your custom view’s
autoresizingMask property should be
set to have a flexible width and
height. Before displaying your view,
the navigation controller
automatically positions and sizes it
to fit the available space.
This issue became resolved when I built against iOS 4.3 and not iOS 5.
I am trying to implement AQGridView based upon the ImageDemo in the /examples folder. I have a view controller with the following declaration:
#interface ImageDemoViewController : UIViewController <AQGridViewDelegate, AQGridViewDataSource, ImageDemoCellChooserDelegate>
{
...
None of the datasource methods in my view controller such as
- (NSUInteger) numberOfItemsInGridView: (AQGridView *) aGridView
{
return ( [images count] );
}
are being called. Here is where I setup the gridview making my view controller the delegate for the gridview.
- (void)viewDidLoad
{
[super viewDidLoad];
self.gridView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
self.gridView.autoresizesSubviews = YES;
self.gridView.delegate = self;
self.gridView.dataSource = self;
images=[[NSMutableArray alloc]init];
[images addObject:#"http://t3.gstatic.com/images?q=tbn:ANd9GcTOXAzFMoK441mcn9V0OemVe_dtAuCpGjBkLrv4rffyOjYIo45BEw"];
[self.gridView reloadData];
}
If I set a breakpoint on
[self.gridView reloadData];
the line is executed but reloadData method in AQGridView is not called. The only difference from the ImageDemo is I do not have a .xib file for the view controller. Have I forgotten to hook up something, resulting in the datasource methods not being called?
If there's no XIB, then who's creating the gridView? If it's never created, then it would be NIL, and you'd have the behavior you describe. (If that's it, then just adding:
self.gridview = [AQGridView alloc] initWithFrame: ...]; should suffice.
Had the same problem. Solved by replacing the view with the AQGridView.
[self.view addSubview:self.gridView]
self.view = self.gridView;
Full method:
- (void) viewDidLoad
{
[super viewDidLoad];
self.gridView = [[AQGridView alloc] init];
self.gridView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
self.gridView.autoresizesSubviews = YES;
self.gridView.delegate = self;
self.gridView.dataSource = self;
self.view = self.gridView;
[self.gridView reloadData];
}
Maybe you could try implementing this:
- (void)LoadSearch
{
NSURL *test1 = [NSURL URLWithString:#"http://www.4ddraws.com/search_iphone.asp"];
NSURLRequest *test = [NSURLRequest requestWithURL:test1];
[web4D setScalesPageToFit:(YES)];
[web4D loadRequest:test];
}
I'm using UISplitViewController for app on iPad. The first task was to show master and detail view in portrait mode. I have done this like that:
// It is possible to keep the Master View in portrait mode
// also. Just pass YES to this method to enable this mode.
- (id) initWithMasterInPortraitMode:(BOOL) masterInPortrait {
self = [super init];
self.keepMasterInPortraitMode = masterInPortrait;
return self;
}
// Thanks to http://intensedebate.com/profiles/fgrios for this code snippet
-(void) viewWillAppear:(BOOL)animated {
NSLog(#"viewWillAppear");
if(keepMasterInPortraitMode == NO) {
return;
}
//check interface orientation at first view and adjust it
//if it is in portrait mode
if (self.interfaceOrientation == UIInterfaceOrientationPortrait || self.interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown) {
UIViewController* master = [self.viewControllers objectAtIndex:0];
UIViewController* detail = [self.viewControllers objectAtIndex:1];
[self setupPortraitMode:master detail:detail];
}
}
// Thanks to http://intensedebate.com/profiles/fgrios for this code snippet
- (void)setupPortraitMode:(UIViewController*)master detail:(UIViewController*)detail {
//adjust master view
CGRect f = master.view.frame;
f.size.width = 320;
f.size.height = 1024;
f.origin.x = 0;
f.origin.y = 0;
[master.view setFrame:f];
//adjust detail view
f = detail.view.frame;
f.size.width = 448;
f.size.height = 1024;
f.origin.x = 321;
f.origin.y = 0;
[detail.view setFrame:f];
}
I create splitView like this:
MySplitViewController *mySplitViewController = [[MySplitViewController alloc] initWithMasterInPortraitMode:YES];
Ok, that works ok and when I launch app in portrait mode it shows both master and detail view side by side.
Everything works for now. But I want to show a different view in detailView for each record (row) in master view.
My didSelectRowAtIndexPath method in masterView looks like that:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row == 0) {
FirstDetailViewController *firstDetailView = [[FirstDetailViewController alloc] initWithNibName:#"firstDetailView" bundle:nil];
UINavigationController *firstDetailNavigationController = [[UINavigationController alloc] initWithRootViewController:firstDetailView];
[firstDetailView release];
[self.splitViewController setViewControllers:[NSArray arrayWithObjects:self.navigationController, firstDetailNavigationController, nil]];
}
else {
SecondDetailViewController *secondDetailView = [[SecondDetailViewController alloc] initWithNibName:#"secondDetailView" bundle:nil];
UINavigationController *secondDetailNavigationController = [[UINavigationController alloc] initWithRootViewController:secondDetailView];
[secondDetailView release];
[self.splitViewController setViewControllers:[NSArray arrayWithObjects:self.navigationController, secondDetailNavigationController, nil]];
}
}
After a click (touch) on first or second row (in masterView) splitView shows only detailView (whole screen) with no masterView.
How can I force splitView to show on change both views, master and detail view ???
Thanks for your help!
I have found the solution. In masterView I created splitViewController instance and assign local instance to that instance:
settingsSplitViewController = (SettingsSplitViewController *)self.splitViewController;
In method didSelectRowAtIndexPath I did this like that:
[settingsSplitViewController setupPortraitMode:self.navigationController detail:detailNavigationController];
Hope it will help someone else to solve this kind of problem.
I have a small application for displaying several UIImageViews in a UIScroller in a similar fashion to the Photo app. I have a TableView which, when i select an item, parses an XML document of photos (added to an array) and adds a UIViewController which displays the images.
The problem is I have a tab bar controller which, upon clicking a tab bar item should go back to the TableView after remove the View that displays the images and dealloc all used memory. The problem is I can't work out how to achieve this. Whether it's a lack of understanding the memory rules I don't know.
Here's the process I'm having issues with.
Table View Controller
This is called after parserDidEndDocument:. backToMenu is called from the tab bar item.
#interface AlbumsViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> {
AlbumViewController *albumViewController;
}
#property (nonatomic, retain) AlbumViewController *albumViewController;
- (void)displayPhotos {
albumViewController = [[AlbumViewController alloc] initWithNibName:#"AlbumView" bundle:nil];
albumViewController.parentView = self;
albumViewController.arrPhotos = self.arrCurrentSetOfPhotos;
[self.view addSubview:albumViewController.view];
[albumViewController populateScroller];
}
- (void)backToMenu {
[albumViewController.view removeFromSuperview];
}
Album View Controller
This is a UIViewController containing a UIScroller and UIPageControl. It adds several ImageViewControllers.
- (void)populateScroller {
imagesScroller.pagingEnabled = YES;
imagesScroller.contentSize = CGSizeMake(imagesScroller.frame.size.width * [self.arrPhotos count], 380);
imagesScroller.showsHorizontalScrollIndicator = NO;
imagesScroller.showsVerticalScrollIndicator = NO;
imagesScroller.scrollsToTop = NO;
imagesScroller.delegate = self;
imagesScroller.backgroundColor = [UIColor blackColor];
pageControl.numberOfPages = [self.arrPhotos count];
pageControl.currentPage = 0;
pageControl.backgroundColor = [UIColor blackColor];
NSMutableArray *controllers = [[NSMutableArray alloc] init];
for (int i = 0; i < [self.arrPhotos count]; i++) {
CGRect frame = imagesScroller.frame;
frame.origin.x = frame.size.width * i;
frame.origin.y = 0;
NSString *strImagePath = [self.arrPhotos objectAtIndex:i];
ImageViewController *imageViewController = [ImageViewController alloc];
imageViewController.localImage = YES;
[imageViewController initWithPhotoName:strImagePath];
[controllers addObject:imageViewController];
imageViewController.view.frame = frame;
[imagesScroller addSubview:imageViewController.view];
[imageViewController release];
}
self.viewControllers = controllers;
[controllers release];
}
ImageViewController
This has one method. It adds an subclassed UIView called ImageView which handles adding a UIImageView.
- (void)loadImage {
NSString *strRootURL = #"http://www.marklatham.co.uk";
CGRect rect = CGRectMake(0.0, 0.0, 320.0, 480.0);
ImageView *imageView = [ImageView alloc];
imageView.strURL = [strRootURL stringByAppendingString:self.strThisPhoto];
imageView.strTmpPrefix = (self.localImage) ? #"_tmp_rockphotothumb_" : #"_tmp_rockphotolarge_";
[imageView initWithFrame:rect];
[self.view addSubview:imageView];
[imageView release];
}
The backToMenu function is supposed to remove/dealloc AlbumViewController and call delloc on ALL child subviews freeing up the used memory.
Where am going wrong? Any help would be appreciated.
Thanks.
Your AlbumViewController is still holding a reference to the view, so it won't get released until you release that reference. But, this is a good thing.
Don't worry about freeing memory every time the user changes screens. It'll just slow things down as they flip back and forth between different views. If the system gets low on memory, it'll call didReceiveMemoryWarning on all of your view controllers, which will cause them to release their views if they aren't being displayed.
You just want to be a bit more clever about how you allocate/release the albumViewController
- (void)displayPhotos {
////// Only create this view controller if it doesn't exist yet
if(albumViewController == nil) {
albumViewController = [[AlbumViewController alloc] initWithNibName:#"AlbumView" bundle:nil];
}
albumViewController.parentView = self;
albumViewController.arrPhotos = self.arrCurrentSetOfPhotos;
[self.view addSubview:albumViewController.view];
[albumViewController populateScroller];
}
- (void)backToMenu {
[albumViewController.view removeFromSuperview];
//// if you don't do this, you'll have a retain cycle between the two
//// view controllers and neither will ever get released
albumViewController.parentView = nil;
}
- (void)didReceiveMemoryWarning {
///// if the albunViewController isn't in use, go ahead and free its memory
if([self.albumViewController isViewLoaded] && self.albumViewController.view.superview == nil) {
self.albumViewController = nil;
}
[super didReceiveMemoryWarning];
}
Also, instead of calling populateScrollers from AlbumsViewController, call it in the setArrPhotos method of AlbumViewController, but only if arrPhotos has changed. For example:
- (void)setArrPhotos:(NSArray *)newArrPhotos {
if(newArrPhotos != arrPhotos)
{
[arrPhotos release];
arrPhotos = [newArrPhotos retain];
[self populateScrollers];
}
}
This way, if the user goes to view the same album over and over again, you won't recreate the same view hierarchy over and over.
After convincing my client I managed to get around this by going with the three20 framework.
I'm new to Objective-C and need help!
I'm following the example "PageControl" which can be found on the net. I added a button to the view in the NIB and hooked up an action which the implementation is found below.
Here is my definition for the view controller being displayed in the page control:
//ContactCardViewController.h
#interface ContactCardViewController : UIViewController
{
int pageNumber;
NSMutableString *s;
}
//ContactCardViewController.m implementation
- (id)initWithPageNumber:(int)page {
if (self = [super initWithNibName:#"ContactCardViewController" bundle:nil]) {
s = [[NSString alloc] initWithFormat:#"page: %d", page];
}
return self;
}
- (id)initWithString:(NSDictionary *)_s {
if (self = [super initWithNibName:#"ContactCardViewController" bundle:nil]) {
NSMutableString *t = [[NSMutableString alloc] initWithString:_s];
s = t;
}
return self;
}
-(IBAction)callOrAddToContacts:(id)sender
{
jobtitleLabel.text = s;
}
//AppDelegate.m
//in delegate that loads the scroll view with the view for the current page:
- (void)loadScrollViewWithPage:(int)page {
//init the view this way the page: and the correct page number is displayed
controller = [[ContactCardViewController alloc] initWithPageNumber:page ];
//init the view this way, the value of the first person is always displayed
controller = [[ContactCardViewController alloc] initWithString:[[self.allNames objectAtIndex:page] objectForKey:#"firstname"]];
}
Please help me to understand why when the view is created with initWithString and then accessed via the button action only value for the first person in the list is always returned. When i init the view using initWithPageNumber s has the correct value Page: X.
In the InitWithString code, you're passing in a Dictionary, not a string.
- (id)initWithString:(NSDictionary *)_s {
if (self = [super initWithNibName:#"ContactCardViewController" bundle:nil]) {
NSMutableString *t = [[NSMutableString alloc] initWithString:_s];
s = t;
}
return self;
}
You may want to change that to NSString, or change the NSMutableString... to an instance of NSDictionary. One or the other.
Found out the problem was in the plist file. The code is fine.