UIViewController's view can contain another UIView object? - iphone

There are 4 items on MyViewController.xib window:
File's Owner (of type MyViewController)
First Responder (of type UIResponder)
View (of type UIScrollView)
FloatingView (of type UIView)
Both View and FloatingView contain some controls (such as labels and text fields...), and are designed using IB. The FloatingView must NOT occupy the full screen; and my app functionality requires the FloatingView to move to a newly tapped location when user taps on the screen.
An instance variable UIView *myFloatingView; is defined as a property/IBOutlet and synthesized, which is connected to the FloatingView view on IB.
On viewDidLoad method, I am adding myFloatingView to my view controller's view as its subview as:
-(void)viewDidLoad {
[super viewDidLoad];
CGRect myFrame = CGRectMake(50, 50, 150, 150); //initial location
self.myFloatingView.frame = myFrame;
[self.view addSubview:self.myFloatingView];
}
Apple documentation says each UIViews should have a corresponding view controller. However, I am using only one view controller here. I am treating the floating UIView object as a regular control, or say as a panel. I might in future allow user to hide/unhide this floating view.
Is this a good practice, or is there a better way (any example) to do what I am trying to do?
Thank you very much for your help.

What you are doing is fine. In fact, a viewController can often control a whole hierarchy. Also, you said your floatingView was in the same nib as your root view? If so, you can probably make it a subview there, instead of adding as a subview in viewdidLoad.

Related

Make self.view a UIScrollView?

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.

In an iOS app, even when no main custom UIView is added, I can still add other view objects as subview?

It is quite strange that in some other iOS apps, I created a Single View app, and then added MainView.h and MainView.m that is the subclass of UIView, but then in another program, I didn't add such class, but could immediately do:
ShapeView *sv;
sv = [[ShapeView alloc] initWithFrame:CGRectMake(10, 170, 140, 140)];
[self.view addSubview:sv];
inside of ViewController.m. Of course, ShapeView.m has to implement drawRect to properly draw the shape. But how come we don't need to add a MainView.h and MainView.m first this time and still draw on some main view?
You don't have to add MainView.h and .m for any app. That is necessary only when you want your own customized UIView named "MainView". If you don't need to draw the view or part of the view yourself, view controller + XIB/NIB work well.
The main UIViewController in your app (which is created from Single View App template as you mentioned) creates a default UIView which is loaded from the default NIB/XIB. It is available as self.view in your UIViewController code after the view is loaded.
In fact MainView is nothing special compared with ShapeView. They are both customized UIView subclasses.

How do I reduce the height of a TableView when it is constructed in IB?

I wanted to add a view to the bottom of my screen. The controller is a UITableViewController, how do I shrink the tableView and add a extra view at the bottom of the tableview?
I've tried setting the frame of self.tableView in different places (viewDidLoad, viewWillAppear etc) but nothing happens. The tableView is created by IB and not programtically.
I've tried added a footer to my table view but that's not what I want, because the footer actually scrolls up, I want a static non moving View at the bottom of the screen.
I'm not saying you can't do it otherwise, but you may not want a UITableViewController for this situation. You can still have your view controller implement UITableViewDelegate and UITableViewDataSource, but place a vanilla UIView in your nib, into which you place a UITableView. Then just make sure to set the view outlet to the UIView containing your table. This has the effect of allowing you to create your additional view within IB. I just tried this and it appeared to work.
I'm guessing you're using a UINavigationController. When you push a controller onto your navigation stack, UINavigationController resizes its view to full screen, ignoring the geometry and autoresizing behavior you've defined in IB.
This resizing seems to happen after viewWillAppear:. In the past I've had some success resizing a table view and adding a sibling view in viewDidAppear:, after calling [super viewDidAppear:]. This is a bit risky though, since Apple could break it by changing how UINavigationController works behind the scenes.
A safer option is to push a view controller onto your navigation stack that controls a wrapper view. Then add your UITableView and its sibling as subviews of that wrapper view. The annoying thing about this option is that you'll probably want to use a nested UITableViewController to manage your non-full screen table view, but the documentation for UIViewController says it's designed to manage full screen views only. If you decide to ignore this admonition and nest your view controllers anyway, you'll find that viewWill/DidAppear/Disappear don't get called on the nested controller, so you'll have to manually delegate those methods from your wrapper view controller. This lack of support for nested controllers is one of my biggest pet peeves about UIKit, and I've gone to great lengths to engineer around it.
If you want to toe the line and use view controllers only for full screen views, you can push a normal view controller that controls your full screen wrapper view, manually implement all the UITableViewDataSource and UITableViewDelegate methods in your view controller, and set it as the delegate for your table view.
you want to change the -loadView method. Not viewDidLoad or viewWillAppear. This will allow you to make additional configurations with your tableview even if it is created in IB.
- (void)loadView {
[super loadView];
CGRect titleRect = CGRectMake(0, 0, 300, 40);
UILabel *tableTitle = [[UILabel alloc] initWithFrame:titleRect];
tableTitle.textColor = [UIColor blueColor];
tableTitle.backgroundColor = [self.tableView backgroundColor];
tableTitle.opaque = YES;
tableTitle.font = [UIFont boldSystemFontOfSize:18];
tableTitle.text = [curTrail objectForKey:#"Name"];
self.tableView.tableHeaderView = tableTitle;
[self.tableView reloadData];
[tableTitle release];
}
I don't know how to do it in IB but the way to do it in code is with this:
- (void) loadView
{
UITableView *tv = [[UITableView alloc] initWithFrame: rect
style: UITableViewStyleGrouped];
// finishg configuring table view
self.view = tv;
[tv release];
}
Trying to do it in two stages -- style first and then frame or frame first and then style -- neither of them works.

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