I am having difficulty geting a very simple view to display. This view has a custom view controller that is manages by a switching view controller. The XIB has one UIViewController component on it with its Image property set. The view controller is as follows:
InstructionsViewController.h
#import <UIKit/UIKit.h>
#interface InstructionsViewController : UIViewController {
}
#end
InstructionsViewController.m
#import "InstructionsViewController.h"
#implementation InstructionsViewController
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
// Release anything that's not essential, such as cached data
}
- (void)dealloc {
[super dealloc];
}
#end
I have also set the class property for the XIB's File's Owner to InstructionsViewController, and Ctrl+Dragged File's Owner to the View icon and selected View from the popup menu.
The code to display the view is:
- (void) showInstructions
{
//Lazy load the instruction view.
if (self.instructionsViewController == nil)
{
InstructionsViewController *viewController = [[InstructionsViewController alloc]
initWithNibName:#"InstructionsView"
bundle:nil];
self.instructionsViewController = viewController;
[viewController release];
}
[self.view insertSubview:viewController.view atIndex:0];
}
My view controller that animates different views when they are switched, has no problems loading and displaying three other views. It just doesn't like this one.
I'm sure I've missed something simple, but for the life of me I can't get it to work for some reason. Does anyone have any pointers?
Thanks,
Steve
I just had the same thing. Ended up deleting the UIImageView, placing a new one then relinked and it worked. Seems like some corruption somewhere.
Not even really sure how this is compiling. You are inserting the subview outside of your nil check, where viewController is out of scope.
You probably want to do
[self.view insertSubview:self.instructionsViewController.view atIndex:0];
Have you verified that showInstructions is being called by setting a breakpoint there? One other thing to check is that you are inserting the Instructions view at the bottom of your z-order, so it will be behind all other items. Try using:
[self.view addSubview:self.instructionsViewController.view];
This will add it to the top of the z-order. If that doesn't work, also try:
[self.view setNeedsLayout];
and/or:
[self.instructionsViewController.view setNeedsLayout];
Ok, I've finally solved it!
The problem was something in XCode or Interface Builder was not agreeing with the PNG file I specified for the Image View control in IB. I tried re-naming, and re-specifying, etc. to no avail.
I finally had to delete the graphic and re-copy it from it's source location. Then I re-specified it in Image View, build and ran. Now it works.
Everything is good in the world again.
Thanks to those who took the time to read and offer help. I appreciate it very much!
Steve
Related
I load InfoViewController's view as a subview in MainViewController like this (declared in .h):
- (void)viewDidLoad
{
[super viewDidLoad];
infoViewController = [[InfoViewController alloc] initWithNibName:#"InfoViewController" bundle:nil];
mainInfoView = (MainInfoView *)[mainInfoViewController view];
}
(MainViewController is push-presented from a segue from a tableviewcontroller cell and has a navigation bar.)
This is in InfoViewController's viewDidLoad to set the size of view:
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.frame = CGRectMake(130, 0, 178, 299);
}
In the Size inspector for InfoViewController I've set the same size (with: 178, height: 299).
I disabled "Use Autolayout" both for InfoViewController in XIB and for the MainViewController in Storyboard.
I have some UILabels, UITextViews and UIImageViews in the InfoViewController. They are declared with properties in .h and synthesized in .m. and connected to the elements in the XIB. Their content is defined in viewWillAppear of MainViewController like this:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
infoViewController.kategoriLabel.text = [[objArr objectAtIndex:detailIndex] objectForKey:#"Type"];
}
When I run the app and this view is entered, some of the labels, images and textViews are suddenly placed in what appears to be random locations. This is only happening to some of the elements.
I have done this same thing in another app and it works fine on both simulator (iPhone 4 ver 6,0) and iPhone 5 device, but in this app it just gets messy on both.
I cannot seem to find anything different anywhere in the Utilities or anywhere else when I compare the apps.
If you have experienced something similar and solved it or if you might know what could be causing it, I will appreciate your answer. Thanks.
PS. I'm using Xcode 4.5.1
I had a different problem, but my brute-force resolution might work for you as well. In my case, I wanted to disable the nav bar on the final item in a series of view controllers. IB, though, won't let you remove the Nav bar so I had to do it manually in viewWillAppear. However, once the bar was hidden, IOS shifted up all my buttons and objects that had been defined in IB by the height of the (now hidden) nav bar.
I resorted to the following in the Controller's viewWillAppear. (objects tended to shift visibly when I tried this in viewWillAppear)
-(void) viewWillAppear:(BOOL)animated
{
//turn off nav bar
[[self navigationController]setNavigationBarHidden:YES animated:NO];
//correct positions of items that shift when the bar disappears
myThing.center = CGPointMake(desired_x_coord, desired_y_coord);
myOtherThing.center = CGPointMake(desired_x_coord2, desired_y_coord2);
...etc...
//don't forget to call the base class
[super viewWillAppear:animated];
}
Annoying, but worked. If you can't find the source of your objects moving, you could try forcing their positions using viewWillAppear.
I downloaded iAdSuite and looked into ADBannerNavigation.
Inside, I changed the RootViewController to subclass TextViewController in order to take advantage of the iAd banner resizing. I want to display ads on the RootView as well.
This is now RootViewController.h:
#import <UIKit/UIKit.h>
#import "TextViewController.h"
#interface RootViewController : TextViewController
#end
Everything else is the same. When I compile and run, no ads show up in RootView, and when I click into TextView, ads suddenly show up.
When I click to go back, there is now white space in RootView.
WHY?
How do you remove the white space?
Found the error in how I was removing the ADBannerView.
iAd Suite tells us to:
Note: If your application has multiple tabs or views displaying an iAd banner, be sure to share a single instance of ADBannerView across each view. Then, before your users navigate to a new view, set the shared instance’s delegate property to nil, remove it from the old view hierarchy, then add the same instance to the opening view and set its delegate to the appropriate view controller. The "AdBannerNavigation" sample shows this technique.
So, in my iADBannerView.m, I have:
- (void)viewWillDisappear:(BOOL)animated{
[self removeADBannerFromView];
[super viewWillDisappear:animated];
}
- (void)removeADBannerFromView{
NSLog(#"ad removed from view");
ADBannerView *adBanner = SharedAdBannerView;
adBanner.delegate = nil;
[adBanner removeFromSuperview];
}
- (void)dealloc{
// we are being called here when we navigate away from this view controller,
// so go ahead and reset our AdBannerView for the next time
//
ADBannerView *adBanner = SharedAdBannerView;
adBanner.delegate = nil;
[adBanner removeFromSuperview];
[contentView release]; contentView = nil;
[super dealloc];
}
By setting breakpoints, I saw that by exiting a view, viewWillDisappear was being called on view1, then viewWillAppear on view0 and then dealloc on view1.
The problem was that view1 already removed the ADBannerView from the view, so [adBanner removeFromSuperView] was removing the Ad from view0.
Problem solved by removing offending code from the dealloc method.
I've followed the tutorial below, it basically just tells you to create a button and a label. When you click the button the label should change to some text.
http://paulpeelen.com/2011/03/17/xcode-4-ios-4-3-hello-world/
It runs but it shows a blank screen. When you click the screen it goes blue like the entire page is a button. I think it's loading the mainwindow.xib file instead of the default ViewController.xib
Any ideas, I'm obviously missing something?
Thanks
EDIT
#import <UIKit/UIKit.h>
#interface fun_buttonViewController : UIViewController {
UILabel *textLabel;
}
#property (nonatomic, retain) IBOutlet UILabel *textLabel;
- (IBAction)changeTheTextOfTheLabel;
#end
The implementation:
#import "fun_buttonViewController.h"
#implementation fun_buttonViewController
#synthesize textLabel;
- (void)dealloc
{
[super dealloc];
[textLabel release];
}
- (IBAction)changeTheTextOfTheLabel
{
[textLabel setText:#"Hello, World!"];
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
/*
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
[super viewDidLoad];
}
*/
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#end
You probaly made a simple mistake. I would go through the example again and see if you did anything diffrently.
If you post your code or upload your files somewhere I could take a look at it for you.
Its kind of hard to guess what the problem is without seeing what you actually did^^
It was as I suspected. It's not a code problem, since the code is fine. The tutorial is also perfectly fine. I thought it had to do with the XIB, and when I looked into there this is what I saw:
In your MainWindow.xib, you had a button loading from the ViewController. Why? I don't know.
It might have been placed there by accident. Here's a screenshot of what I mean:
The left view is from your view controller. See the UIButton loading in the ViewController? It is blocking the rest of the view. Just delete the button that's loading OVER your view, and it works perfectly. The view hierarchy, was essentially blocked by that massive extraneous button.
It could be anything from your XIB (most likely) or from your app delegate.
Are you sure you connected the buttons properly?
Send us your code/zip of the project so far.
I have an application that, on load, displays a UITableView with the user's data in it.
However, when the user first loads the application (before they've created any data), I'd like to display, instead of an empty table, a background image (with an arrow pointing to the 'add a record' navbar button). Once the user has added their first record, the tableview is displayed instead. I've seen numerous apps do this - the only example I can think of at present is Cha-Ching, before you have any budgets/etc set up. I can't for the life of me work out how to do this, though.
I initially added a navigationcontroller to the main window's xib, the rootViewController of which was a custom viewcontroller/xib. This rootViewController contained the background image with a hidden tableview above it, and a custom tableviewcontroller that managed the tableview. This seemed to work just fine, and if there was data it would load and display in the table. However, if I was to scroll the data offscreen, the app would crash, with this error:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason:
'*** -[UITextEffectsWindow tableView:cellForRowAtIndexPath:]: unrecognized selector sent to instance 0xd2d130'
I have no clue what a UITextEffectsWindow is, or why it was trying to manage my tableview. I presume something may be hooked up incorrectly in my view hierarchy...
If there's a much simpler/more straightforward way of doing this, I'd be very grateful if someone could explain it. How would you do this?
Thanks in advance.
Here's one solution that I've been satisfied with so far.
First, I created a transparent view that was the same size as my TableView. I add this view as a sibling of the TableView whenever the TableView's data source has no data in it. I completely remove the view when data is available because transparency can affect the smoothness of the scrolling animation with TableViews.
I simply added a transparent label to this view that says something to the effect of "No Data Available". Adding a little shadowing to this label really helped to reinforce the concept that the label was 'floating' over top of the empty TableView.
I like your method of using an image though. Done properly, it might save you some work if you don't have to localize a string like I currently do.
To achieve this using a UITableViewController subclass as my only view (within a UINavigationController as per the Apple template) I used the following approach:
Create a UIView of the size of my tableView in the XIB that contains your UITableViewController and tableView.
Add an ImageView set with my placeholder image to the UIView.
Wire up the UIView as an IBOutlet (in the example code below, I called it emptyTableView)
When it is time to show the placeholder from within the UITableViewController subclass :
[self.tableView addSubView:emptyTableView];
[self.tableView setScrollEnabled:FALSE];
Disabling the scroll is necessary otherwise the user will be able to move the placeholder image up and down. Just remember to enable it once the user adds an item.
To remove the image view
[emptyTableView removeFromSuperview];
To do this, I use the following controller instead of UITableViewController. It will automatically place a view over the table when it is empty, and remove it when it is filled.
Just call [self reloadData] instead of [self.tableView reloadData] so that it can check if the table became empty.
In your subclass, implement a makeEmptyOverlayView function that will create the view to show over an empty table.
#interface MyTableViewController : UITableViewController
{
BOOL hasAppeared;
BOOL scrollWasEnabled;
UIView *emptyOverlay;
}
- (void) reloadData;
- (void) checkEmpty;
#end
#implementation MyTableViewController
- (void)viewWillAppear:(BOOL)animated
{
[self reloadData];
[super viewWillAppear: animated];
}
- (void)viewDidAppear:(BOOL)animated
{
hasAppeared = YES;
[super viewDidAppear: animated];
[self checkEmpty];
}
- (void)viewDidUnload
{
if (emptyOverlay)
{
self.tableView.scrollEnabled = scrollWasEnabled;
[emptyOverlay removeFromSuperview];
emptyOverlay = nil;
}
}
- (void) reloadData
{
[self.tableView reloadData];
if (hasAppeared &&
[self respondsToSelector: #selector(makeEmptyOverlayView)])
[self checkEmpty];
}
- (void) checkEmpty
{
BOOL isEmpty(YES);
id<UITableViewDataSource> src(self.tableView.dataSource);
NSInteger sections(1);
if ([src respondsToSelector: #selector(numberOfSectionsInTableView:)])
sections = [src numberOfSectionsInTableView: self.tableView];
for (int i(0); i<sections; ++i)
{
NSInteger rows([src tableView: self.tableView numberOfRowsInSection: i]);
if (rows)
isEmpty = NO;
}
if (!isEmpty != !emptyOverlay)
{
if (isEmpty)
{
scrollWasEnabled = self.tableView.scrollEnabled;
self.tableView.scrollEnabled = NO;
emptyOverlay = [self makeEmptyOverlayView];
[self.tableView addSubview: emptyOverlay];
[emptyOverlay release];
}
else
{
self.tableView.scrollEnabled = scrollWasEnabled;
[emptyOverlay removeFromSuperview];
emptyOverlay = nil;
}
}
else if (isEmpty)
{
// Make sure it is still above all siblings.
[emptyOverlay retain];
[emptyOverlay removeFromSuperview];
[self.tableView addSubview: emptyOverlay];
[emptyOverlay release];
}
}
#end
If you use Three20, you can easily set any image you want as a place holder prior to your table being populated.
So, to solve this I did as discussed in the comments above:
I created a normal UIViewController subclass, which contained a UIImageView and a UITableView. The viewController conforms to the UITableViewDelegate and UITableViewDatasource protocols, and looks after the tableView. The viewController class simply shows or hides the imageView depending on whether data is available.
I was going wrong before by trying to manage both these views with a UITableViewController. A UITableViewController has to have a tableView as its view, whereas, with this solution, a viewController can contain both the image and the tableView, and implement the necessary protocols to manage the tableView.
Thanks for all the help!
I have got my own custom UIViewController, which contains a UIScrollView with an UIImageView as it's subview. I would like to make the image to auto rotate when device orientation changes, but it doesn't seem to be working...
In the header file, I've got;
#interface MyViewController : UIViewController <UIScrollViewDelegate> {
IBOutlet UIScrollView *containerView;
UIImageView *imageView;
}
These components are initialised in the loadView function as below;
containerView = [[UIScrollView alloc] initWithFrame:frame];
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://..."]];
UIImage *image = [[UIImage alloc] initWithData:data];
imageView = [[UIImageView alloc] initWithImage:image];
[image release];
[containerView addSubview:imageView];
And I have added the following method, assuming that's all I need to make the view auto-rotate...
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
MyViewController loads fine with the image I've specified to grab from the URL, and the shouldAutorotate... function is being called, with the correct UIInterfaceOrientation, when I flip the device too.
However, didRotateFromInterfaceOrientation method do not get called, and the image doesn't seem to rotate itself...
Could someone please point out what I need to add, or what I have done wrong here?
Thanks in advance!
This may not be the right answer for you, because you don't specify the context that the UIViewController's in, but I just found an important gotcha in the Apple documentation that explains the similar problem I'm having.
Tab bar controllers support a portrait
orientation by default and do not
rotate to a landscape orientation
unless all of the root view
controllers support such an
orientation. When a device orientation
change occurs, the tab bar controller
queries its array of view controllers.
If any one of them does not support
the orientation, the tab bar
controller does not change its
orientation.
I've noticed that there are issues when rotating a UIView that's not the first or only view as a direct child of the main window.
So if your UIView is part of a Navigation Controller or a Tab View Controller, you'll also need to override shouldAutoRotateToInterfaceOrientation on the Navigation Controller or Tab View Controller.
Also: using [UIApplication setStatusBarOrientation] helps to work around things if/when you need to do it manually.
To make this kind of thing work in my application, I had to override
- (void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
[self layoutSubviews];
}
and also layoutSubviews
- (void)layoutSubviews
{
NSLog(#"layoutSubviews called");
...recalc rects etc based on the new self.view.bounds...
}
I'm not sure that this is absolutely required, but it worked for me.
Sometimes, if you add a subview to a view, it's your responsibility to make sure that the methods are passed to the subview; a couple of days ago I wrote a short post about this. For example, if you have a UIViewController and add a UINavigationController as subview, you must add this code to the UIViewController if you want viewWillAppear:animated: to be called when UINavigationController.view appears:
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[projectNavigationController viewWillAppear:animated];
}
It might be the case that the willRotateToInterfaceOrientation and didRotateFromInterfaceOrientation method also need to be called by the superview; I am not really sure about this, but give it a try.
This is discussed in Apple Technical Q&A QA1688.
Sometimes if you stack multiple views on top of each other for some reason, the anotherController might not receive rotation event.
[myWindow addSubview:primaryViewController.view];
[myWindow addSubview:anotherController.view];
A lazy way (not a good design) to fix this is only add one subview on window, but initialize multiple controller on the app delegate. Then when you need to switch window, remove the current view and add the view you want
[self.view removeFromSuperview];
AppDelegate *dg = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[[dg window] addSubview:[[dg viewController] view]];
I just came across this having a similar problem. I have a series of view controllers/complex views, that all rotate perfectly and couldn't figure out why the new one I just added on wasn't rotating. After a LOT of trial and error, the reason was that I wasn't calling the init method (it's the standard init method) when allocating the view controller;
e.g. I was doing
m_timerViewController = [TimerViewController alloc];
instead of
m_timerViewController = [[TimerViewController alloc] init];
To expand on jonoogle's post. I had a similar error. My view has a nib and my custom init:
- (id)initWithCategory:(Category *)category inManagedObjectContext:context{
didn't include the call to init the nib.
self = [super initWithNibName:nil bundle:nil];
Adding that line made my view rotate like it is supposed to.
I copied this from this link
And it works for me.... Reason why i have added this here is to make it easy for others to find. It took me many hours to find this fix:
Make a new set of class files of the UIViewController type, go into the .h file of this class and change this line
#implementation MyTabBarController: UIViewController {}
#end
to something like this
#implementation MyTabBarController: UITabBarController{
}
Now go into the nib file and click on the UITabBarController object and go to it's identity tab, and make it Class MyTabBarController.
now in MyTabBarController.m make sure this is in it.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)io {
return YES;
}
You can probably get rid of everything else in there if you want.
just do this if you what to rotate from landscape to portrait!
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}