Make self.view a UIScrollView? - iphone

I'm experimenting with a TabBarController and the default project creates the UITabBarController and also gives you two view controllers.
I want the view of one of these view controllers to be a UIScrollView, i.e. when calling self.view on FirstViewController I want to get back a UIScrollView * and not just a UIView *.
The view controller gets initialised with initWithNibName: but I can't see anything assigning the view property in there.
If this all sounds a bit weird, maybe I'm doing this wrong? I realise I can drop a UIScrollView onto the view that's already created with me, but it just seemed a bit pointless to have a parent view in this case.
Thanks in advance.

Ok, just realised how to do this.
I can do a cast in my code to make UIView a UIScrollView. Like so...
UIScrollView *tempScrollView = (UIScrollView *)self.view;
tempScrollView.contentSize = self.view.bounds.size;
Then, in Interface Builder, you can use the inspector to set a custom class for your UIView. I set the class as UIScrollView in here and all seems to work!

If you want to use interface builder. Just load up your nib, delete the view on the left panel, and drag a UIScrollView into the area.
Next link from Files Owner to the new UIScrollView as the view property.
The only downside to doing it this way is in your code, whenever you want specific UIScrollView functions you will have to typecast the view property (using (UIScrollView *)self.view ), or put it in a variable like so
UIScrollView *sview = self.view;
//Then use sview for your changes
The best way would be to do it in code however.

Related

IPhone : View property on a Xib, is it special?

I can't believe I am stuck on this but here goes.
I have a viewController which I am trying to add to another viewController via the addsubview property (basic as) however I am not doing viewController.view but viewController.myView1.
I basically have 2 root views on the xib (both with outlets) however I don't want to wire up the view property of the xib because I want to choose which view to show.
I can't get it to appear! Is the view property some sort of special hook?
I can't see why addsubview:mysubview should not work?
Some examples seem to use 2 different xib's but that seems a overkill
Firstly, Apple say a viewcontroller should be used for a whole screen full of views - (but I guess iPad container views are different).
I guess you're wanting to only use one of the viewcontrollers at once. In which case you shouldn't be using subview - just create the viewcontroller programmatically, create the view, assign the view to the viewcontroller's view property and then add the viewcontroller to the window or navigation bar.
and, in your viewcontroller:
self = [super initWithNibName:nibOne bundle:nil];
might be what you want - it allows you to use multiple nibs with a single viewcontroller.
Can you NSLog your viewController.myView1 before add it as a subview? Is it nil or not?
If it's nil, try to add this line before your addSubview: method:
[viewController view];
Note: The viewController I am talking about is the same viewController which have two root views view and myView1.
Your code should be good as this is fairly simple and I do it all the time.
Just make sure your outlets are wired properly and that you are synthesizing them and that they are public in your header.

Adding UIView subview to single table view controller in navigation stack

I'm working on an app that has three table view controllers in a navigation stack. The root view controller and the second VC have toolbars, but I want to add a subview to the second view controller like this. (The color is just there for visualization.)
I want to add the view programmatically, since I haven't been able to do it with IB without major headaches. Right now, I've been able to kind of get what I want by drawing a UIView in the second view controller like this:
- (void)viewDidLoad {
[super viewDidLoad]
UIView *detailView = [[UIView alloc] initWithFrame:CGRectMake(0, 392, 320, 44)];
detailView = [UIColor redColor];
[self.navigationController.view addSubview:detailView];
[detailView release];
}
The problem with this approach is that once the UIView is loaded in the second view controller, it stays loaded and is drawn in the third and root view controllers. I've tried a variety of methods of removing the UIView, including setting the detailView to nil in viewDidUnload, calling removeFromSuperview in didSelectRowAtIndexPath (which removed the view from the whole stack).
I've also tried adding the subview to self.view, but that pushes it below the visible area of the table view, so I have to scroll up to see it, and it snaps back down when I let go.
Clearly, adding this subview to the navigation controller is not the best way to do what I want, but I'm at a loss as to where to go from here.
As you've already discovered, you definitely should not be reaching up into the navigation controller's view.
You want your SecondViewController to be an UIViewController that implements the UITableViewDelegate and UITableViewDataSource and whose view lays out the UITableView and the UIView you wish to use for your stationary 'footer' in it's own main UIView.
It helps to keep in mind that UITableViewController is ultimately is just a convenience for creating a view controller whose view consists entirely of a UITableView.
Anyway, rather than attempt to put a pile of that code inline in this answer, you can browse it (or svn co) from this read-only svn repo.
EDITED (now that it's not midnight, putting some code/explanation directly in answer):
For the controller to be pushed onto the nav stack that needs the footer create a new UIViewController-based class (do NOT check the 'UITableViewController subclass' box in the template selection dialog).
Add instance variables for the UITableView and the UIView that is to be the extra bottom view.
#interface SecondViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> {
UITableView* tableView;
UIView* customFooterView;
}
#property (nonatomic,retain) IBOutlet UITableView* tableView;
#property (nonatomic,retain) IBOutlet UIView* customFooterView;
#end
In IB add a UITableView and UIView to the existing root view for the controller and lay them out as desired (probably worth altering the auto-resize parameters too if your app can be used in both landscape and portrait). Hook up the two views to the outlets defined for them in the "File's Owner" and also ensure you hook up the UITableView's delegate and dataSource properties to point at the "File's Owner."
Then just implement the UITableViewDelegate and UITableViewDataSource protocols as appropriate for your application.
If you want to lay out the entire 'footer' view in IB then go right ahead. Otherwise you can easily add items programmatically in viewDidLoad (and remember to tear it down in viewDidUnload).
I don't like the approach. You should put your table view inside another view, and put your detail view together in that view.
Despite of that, I think you can remove your view in viewWillDisappear method of your view controller. I also notice that you did not keep your detailView as a private variable, which you should do because you need to reference it when removing it later (I still wonder how you have done it.)
Note that viewDidUnload is called in case of view unloading (i.e. releasing from its controller), so it is not related to navigation.
Not sure which behavior you're looking for but try one of these:
Assign the detailView to the tableFooterView property of the tableview on the second VC.
Reduce the height of the table view and add the detailView to self.view.

How can I modify an existing UIViewController to allow scrolling?

I have already built a UIViewController subclass with a bunch of controls in it, and just realized that if I rotate the iPhone, half of the controls become invisible. So, I would like to somehow make the UIViewController's UIView scrollable so that when the device (or the Simulator) rotates, the user can scroll the view to see all the controls.
I was hoping to do this all in Interface Builder. I tried to change the class of the view from UIView to UIScrollView in the Class Identity editor, but nothing scrolls. The base class of my view controller is a simple UIViewController <UIScrollViewDelegate>.
Is there an easy way to make the main view in my view controller scrollable without having to recreate the whole thing in IB?
For people who are using storyboard, this is quite easy to do
in Document Outline select the topmost View of the UIViewController
then in Identity Inspecter, under Custom Class, for class enter UIScrollView
That's it.
Add a UIScrollView and make all your controls and widgets and labels subviews of the scroll view by dragging them from wherever they are "into" or "inside" the scroll view. This makes them subviews of the scroll view, which is what you want.
You can do this as follows:
Open your nib file and Create a UIScrollView object under your UIView Object.
Move all your controls onto the UIScrollView
Open your header file (.h) and add a new property for the scroll view:
#property (nonatomic, retain) IBOutlet UIScrollView *scrollView;
Open your implementation file (.m) and insert the following:
#synthesize scrollView;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// Enable scrolling for portrait
scrollView.contentSize = CGSizeMake(scrollView.frame.size.width, 600);
}
Go back to your nib file and wire up the scrollView on the file's owner to your UIScrollView object.
The real trick here is setting the scrollView.contentSize. Once this is set, the scrolling should occur.
If you wanted to enable scrolling for when the device is in portrait orientation, you could use this:
// Enable scrolling for landscape orientation
scrollView.contentSize = CGSizeMake(scrollView.frame.size.width, scrollView.frame.size.height);
This should give you some frame of reference.
Flea
It would be helpful to understand what your interface is displaying, but I'd suggest one of the following:
Don't allow rotation by returning NO from shouldAutorotateToInterfaceOrientation
Use Interface Builder to adjust your springs and struts so that all of your interface elements fit in landscape view
Add a new UIScrollView in Interface Builder and drag your UIView into it, then re-assign the view property of your File's Owner to the scroll view.
I was able to do this by adding a scroll view to the view in IB, making the view controller a UIScrollViewDelegate, hooking up the scrollview to a UIScrollView object in IB, hooking the delegate up to file owner, and adding this line in the viewDidLoad:
- (void) viewDidLoad {
scrollView.contentSize = CGSizeMake(1280, 960);
}
Scrolling behavior is only invoked if the contentSize of a UIScrollView is larger than its bounds.
Add a Scrollbar into Xib file and then drag & drop controls in to the Scrollbar. Inside load view use
scrollView.contentSize = CGSizeMake(CONTENT_WIDTH, CONTENT_HEIGHT);

superview and parentviewcontroller nil after adding a subview

I think I'm missing something fundamental and so I want to ask the community for some help. I'm building an app based around a basic iPhone Utility Application. My MainView and FlipsideView share some elements so I have created separate ViewControllers and nib files for those pieces. In order to do this I have done the following:
1. Created a viewcontroller called searchDateViewController which is the file's owner of searchDateView.xib
2. searchDateView.xib is basically a UIView with a UILabel inside, the view is wired up correctly
3. Inside both MainViewController.m and FlipsideViewController.m I add a subview as folllows:
- (void)loadView{
[super loadView];
searchDateViewController = [[SearchDateViewController alloc] initWithNibName:#"SearchDateView" bundle:nil];
[[searchDateViewController view] setFrame:[searchDateView frame]];
[[self view] addSubview:[searchDateViewController view]];
...
}
Everything displays and works just fine. Basically depending on actions that happen in each of the main and flipside views the UILabel of the nib is changed. However, I wanted to do something slightly different if the searchDateViewController is loaded from the MainView or the FlipsideView. However, I can't seem to figure out which ViewController is adding the searchDateViewController subview.
In searchDateViewController I tried:
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"superview %#", self.view.superview);
NSLog(#"parentviewcontroller %#", self.parentViewController);
}
In both cases I get nil.
So my question is - can I find out which ViewController is adding searchDateViewController a a subview? If so how? Or if my logic here is completely messed up, how should I be doing this?
Thanks!
viewDidLoad is invoked when the view controller has loaded its view. In your case, that happends in this line:
[[searchDateViewController view] setFrame:[searchDateView frame]];
At that moment, you haven't yet called addSubview: so it is no wonder the view's superview is nil.
To solve your problem, you should define a property inside SearchDateViewController to distinguish between the different cases. This property would then be set accordingly by the parent controller that creates the SearchDateViewController instance.
Generally, I do not think it is a good idea to use a UIViewController subclass as a controller for a view that is used as a subview of one or several fullscreen views rather than be used as a fullscreen view itself. Much of UIViewController's logic works on the assumption that it is used to manage a fullscreen view. For instance, with your design, I think it's possible that SearchDateViewController will modify the view's frame when the device orientation changes etc. Since you don't need all this functionality for a non-fullscreen subview, I suggest you subclass your SearchDateViewController directly from NSObject.
ViewController and views are completely separate.
In most cases, when you add a subview to a parent view you don't add its controller to the parent's viewController. The exception to this rule is the navigation controller which adds the controller instead of the view to maintain a hierarchy of view controllers.
Your SearchDate viewController can't find a parent controller because you never assigned one and the system does not do it automatically. You can just assign a parent controller when you evoke the view from another controller.
searchDateViewController.parentController=self;

Multiple Views in one Window

I'm writing a view based app, but I'm a bit confused about loading my views. I'd like to have four different views loaded at the same time in the same window. I can't seem to figure out how to do this. I'd prefer to do everything programatically rather than with the interface builder if possible.
My 4 views are: a UIView, a UIWebView, a UITableView and another UIView with buttons.
Thanks in advance for the help.
Views in an iPhone app are arranged hierarchically - that is, each view has a "parent" view (excepting the root view). The interesting bit here is that UIWindow is itself a subclass of UIView, so you can add all four views to your window directly. (This may not be the best approach, but it's perhaps the simplest.)
All you really have to do is initialize each of your four views programmatically with the location and dimensions you want them to have in the UIWindow. You do this by giving each view a frame parameter, either in the init method or afterwards (depending on the type of view). So, for example, in your app delegate you could add this code:
CGRect frame = CGRectMake(0.0, 0.0, 100.0, 100.0);
UIView *view = [[[UIView alloc] initWithFrame:frame] autorelease];
[window addSubview:view];
This will create a 100x100-pixel view and add it to the upper left corner of the window. You can do similar things for each of the other three views.
Note that developers usually don't initialize views directly in the app delegate - a better approach might be to have a fifth view take the place as the root view for the other four, then add that root view to the window. You can use a view controller for the fifth view to make this task easier - move the view initialization code into that view controller's implementation, then from the app delegate you can just instantiate the view controller and let it take over from there.
You can use [self parentViewController] to get access to the parent UIView