I want to create a 'detail view' in my navigation-based app similar to the address book app.
I want to create a UIView that has an UIImageView and a UILabel that I can pass to a UITableVIew's tableHeaderView property when pushed by a navigation controller. The label text and image must take information from the managed object context when it loads.
I started trying to do this using IB, but go fed up when the label text wouldn't update, regardless of where I put the myView.UILabel.text = #"some new text". It just kept presenting the text I entered in the IB inspector window.
So without using IB, how would I go about creating a UIView that has a UILabel and a UIImageView in it?
Should I be creating a new class that is a sub-class of UIViewController or just UIView?
How do I go about creating the sub-views for the label and image?
Where should the text and image URL be set in code? (viewDidLoad or loadView or viewWillLoad)(UIView, UIViewController, detailViewController)?
If you started using IB and the UILabel text wouldn't update, sounds like the IBOutlet of your view controller isn't correctly connected to the label in IB.
Hopefully that will fix your problem with using IB. And in the viewController code, you should update that text in viewDidLoad.
But if you want to do this programmatically, I would make a subclass of UIView (not UIViewController).
And I would override initWithFrame and do all the setup there.
Something like the following:
myView.m
#implementation myView
#synthesize imageView, myLabel;
- (id) initWithFrame:(CGRect) frame
{
if (self = [super initWithFrame:frame]) {
// Setup your image view and add it to the view.
self.imageView = [[[UIImageView alloc] initWithFrame:initWithFrame:frame] autorelease];
self.imageView.image = ...
[self addSubview:self.imageView];
// Setup your label
self.myLabel = [[[UILabel alloc] initWithFrame:frame] autorelease];
self.myLabel.text = #"whatever you like";
[self addSubview:self.myLabel];
}
return self;
}
Make sure to clean up memory properly in the dealloc method of course depending on whether you like make class properties vs class variables.
Related
I want to show my apps logo image in top of every view. But I want to create it once in a view that show it in every view like master page in ASP.net. please some one help me.
Create a subclass of UIViewController, and in viewDidLoad method, get and place the image where your want :
.h
#interface MyLogoViewController : UIViewController
// your interface
#end
.m
#implementation MyLogoViewController
- (void) viewDidLoad {
UIImage* image = [UIImage imageNamed:#"image.png"];
UIImageView* imageView = [[UIImageView alloc] initWithImage:image];
imageView.frame.origin.x = // whatever you want
imageView.frame.origin.y = // whatever you want
[self.view addSubview:imageView];
[self.view bringSubviewToFront:imageView];
[imageView release];
}
#end
Then for any of your view controller that controls a view in wich you want that image to be displayed should inherit that subclassed View controller instead of UIViewController.
#interface MyNewViewController : MyLogoViewController {
// your interface
}
#end
That way, image displays automatically without having to do anything special.
If you plan to add subviews programmatically in your view controllers, then keep a reference for imageView into MyLogoViewController class as a member var (instead of just creating / releasing it on the fly), so you will be able to put your new views behind the existing logo if needed.
you can place that image in main.window. so you can see that image in every view which views are having subviews to main window. But you need fix all view sizes according to your images size fixed in main window.Thats enough.
You can also add the image to window and have fix the size of the view then u can see in sub view
YOu can add that view in window and add other view below that window image. so you can see that in every page.
Create a modal class for the image and call this from viewDidLoad of every controller.
I am initializing a new UIViewCOntroller object.
then attempting to set its view's position of stage but I am having some trouble.
here is the code I am using
Note: this code is placed in the application main UIViewController's viewDidLoad method
UIViewController * cont = [[UIViewController alloc] init];
cont.view.backgroundColor = [UIColor redColor];
CGRect rect = CGRectMake(100, 0, 320, 480);
cont.view.frame = rect;
this code is still positioning the subview at (0,0) instead of (100,0)
However, if I introduce a decimal, such as using 320.01 (for the width value) or 480.01 (for the height value). The view would be positioned correctly.
It seems that if I use a size with an exact width:320.0 height: 480.0,
the origin will always be set to (0,0) !!!
This is a bit strange. I was hoping that someone could explain why this is happening, and possibly how it may be resolved.
Cheers ....
NSLog the value of cont.view and I think you will find it to be nil, which explains why nothing's happening. This is not the normal way to create a UIViewController -- it's not wrong to create one programmatically, but 99.99% of the time UIViewController subclasses are created with the main UIView in a .xib file. A freshly created UIViewController object has a nil "view" member, so you've got to initialize it somehow, either by loading a .xib:
MyViewController *vc = [[[MyViewController alloc] initWithNibName#"MyViewController" bundle:nil] autorelease];
or manually creating the view:
MyViewController *vc = [[[MyViewController alloc] init] autorelease];
UIView *theView = [[[UIView alloc] initWithFrame:viewframe] autorelease];
vc.view = theView;
Then you can move the view's frame to your heart's content, but moving the base view of a view controller is usually not what you want to do, you want to create sub-views and move those around.
[[UIViewController alloc]init] is wrong. The designated initializer for UIViewController is initWithNibName:(NSString *)nibName bundle:(NSBundle *)nibBundle. Even that does not necessarily initialize the view outlet to an actual UIView immediately. You need to subclass UIViewController and perform your customisations in the viewDidLoad method of that subclass.
In the interim is likely that view is nil so you can try setting whatever properties of it you like without anything ever happening.
I think you should be able to use
-(void) loadView {
[super loadView];
//create your views programmatically here
}
in order to create your viewController programmatically and avoid the IB. Normally the IB calls this method for you when your 'view' property is nil, however if you're avoid the IB make sure to include the above method so your view property is not nil.
I have an image I want to display inside a UIView. In Interface Builder, the UIView is the root and a UIImageView is its child.
The view is connected to view controller's view outlet, and the image view is connected to the image view outlet:
#property (nonatomic, retain) IBOutlet UIImageView *imageView;
If I try to set the image property of UIImageView before it's visible, the image doesn't show up.
TestView *testView = [[TestView alloc] initWithNibName:#"TestView" bundle:nil];
testview.imageView.image = [logos objectAtIndex:indexPath.row];
[self.navigationController pushViewController:testView animated:YES];
If, however, I pass the image to the controller and set the image property in viewDidLoad, the image becomes visible.
TestView *testView = [[TestView alloc] initWithNibName:#"TestView" bundle:nil];
testview.image = [logos objectAtIndex:indexPath.row];
[self.navigationController pushViewController:testView animated:YES];
- (void)viewDidLoad
{
[super viewDidLoad];
imageView.image = image;
}
What is causing the image to not show up in the first scenario?
The iOS documentation for a UIViewController's initWithNibName:bundle: method states that:
…
The nib file you specify is not loaded right away. It is loaded the first time the view controller’s view is accessed. If you want to perform additional initialization after the nib file is loaded, override the viewDidLoad method and perform your tasks there. …
A consequence of the nib/xib file not being loaded immediately (i.e. it uses so called lazy loading) means that the imageView IBOutlet property in your TestView controller is still nil when you try to set it in your first example.
In your second example you are setting it in the viewDidLoad which is called after the nib/xib file is loaded, and therefore it works as you expect.
I am using the iPhone toolchain on Linux and so I have no Interface Builder. So how could I layout my view in my ViewController subclass? For example, I want a UITextView in the middle of the screen? Should I do this in the loadView or viewDidLoad. Do I also have to set the view for the ViewController subclass to itself?
It is not an easy job to layout all the view using code. Here are some code:
UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake (100, 100, 100, 100)];
[self.view addSubview:textView];
The frame is the place (the first and second argument is x and y coordinator) and the size (the third and fourth argument is width and height of the text view).
Using this way, you can add any view into your class. Some of the view is built in and you don't have to draw yourself, some of them is not, and you need to subclass UIView and override drawRect.
You should do this in viewDidLoad when your main view controller is finished loading
I've written an open source project that does exactly this:
https://github.com/charlesmchen/WeViews
Here's another project that you might find useful:
http://code.google.com/p/layoutmanagers/
I usually build the entire view hierarchy in the loadView method and perform additional setup in the viewDidLoad, for example to set up the subviews content to reflect the data associated to the view controller. The important thing is to set the view controller view outlet in the loadView method.
#synthesize label; // #property(nonatomic,retain) UILabel *label declared in the interface.
-(void)loadView {
// Origin's y is 20 to take the status bar into account, height is 460 for the very same reason.
UIView *aView = [[UIView alloc]initWithFrame:CGRectMake(0,20,320,460)];
[aView setAutoresizingMask:UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight];
[aView setAutoresizeSubviews:YES];
// The 150x50 label will appear in the middle of the view.
UILabel *aLabel = [[UILabel alloc]initWithFrame:CGRectMake((320-150)/2,(460-50)/250,150,50)];
// Label will maintain the distance from the bottom and right margin upon rotation.
[aLabel setAutoresizingMask:UIViewAutoresizingFlexibleTopMargin|UIViewAutoresizingFlexibleLeftMargin];
// Add the label to the view hiearchy.
[self setLabel:aLabel];
[aView addSubview:aLabel];
// Set aView outlet to be the outlet for this view controller. This is critical.
[self setView:aView];
// Cleanup.
[aLabel release];
[aView release];
}
-(void)viewDidLoad {
// Additional and conditional setup.
// labelText is an istance variable that hold the current text for the label. This way, if you
// change the label text at runtime, you will be able to restore its value if the view has been
// unloaded because of a memory warning.
NSString *text = [self labelText];
[label setText:text];
}
-(void)viewDidUnload {
// The superclass implementation will release the view outlet.
[super viewDidUnload];
// Set the label to nil.
[self setLabel:nil];
}
The biggest difficulty is probably understanding how IB settings map to UIView variables and methods, for example the autoresizing mask. Apple's UIView and UIViewController class references are full of useful informations.
Without using Interface builder or xib files, what is the correct way to instantiate two classes which inherit from UIView such that they can switch between themselves using UIButtons located on the views themselves?
I think this involves setting up a UIViewController from the app delegate and adding two instances of my classes which implement UIView into the controller (perhaps from inside the controller?)
I'm also not sure how to raise events from UIButtons on the custom UIViews to switch the views. I suspect I would need to add a method to the view controller but I'm not sure how to get a reference to the view controller from inside the scope of my UIView.
Also, I'm wondering that,if the use of a UIViewController is necessary, should the switch method could be in the scope of the main app delegate?
Some code examples would be great!
Your main problem is that you don't conceptually understand the role of UIViewControllers versus UIViews. Most people don't when they first start out.
Views are stupid and ideally, they should be composed of generic objects. They contain virtually none of the logic of the interface. They do not know or care about the existence of other views. The only logic you put in views is logic that pertains to the immediate and generic functioning of the view itself, regardless of the data it displays or the state of other parts of the app. You seldom need to subclass UIView. This is why views can be completely configured in Interface builder without any code.
ViewControllers contain the logic of the interface and connect the interface to the data (but they do not contain or logically manipulate the data.) They are "intelligent" and highly customized. The viewControllers do understand the place of the view in the context of the app. The viewControllers load and configure the views either from nib or programmatically. The viewControllers control when the views are displayed or hidden and it what order. The viewControllers determine what action is taken in response to events and what data gets displayed where.
VictorB's example code shows how this is all done pragmatically. The important thing to note is that the viewController and view are entirely separate objects from two entirely separate classes. There is no overlap and no need to subclass UIView. All the customization is in the controller.
All this is because of the MVC design patter. It decouples the interface from the data model, making them both modular and independent of each other. This makes it easy to design, debug, and reuse each independent module.
If you want to get it done in code, here is an example I just drummed up using lazy loaded UI elements. I'm only making one button here and swapping it between whichever view is active. It's slightly awkward, but it reduces the amount of code necessary to demonstrate this.
I've created two UIViews to represent your custom classes, one with a blue background and one with a red. The button swaps between the two. If you have a unique button already in each of your custom views, you just need to either expose those buttons as properties of your UIView subclasses so your view controller can access them, or add the view controller as a target for the button's action from within your UIView's loading code.
I've tested this code in my simulator and it seems to work fine, but you really should try to understand what's going on here so you can implement it yourself.
ToggleViewController.h:
#import <UIKit/UIKit.h>
#interface ToggleViewController : UIViewController {
UIView *firstView;
UIView *secondView;
UIButton *button;
}
- (void)addButton;
- (void)toggleViews;
#property (nonatomic, readonly) UIView* firstView;
#property (nonatomic, readonly) UIView* secondView;
#property (nonatomic, readonly) UIButton* button;
#end
ToggleViewController.m:
#import "ToggleViewController.h"
#implementation ToggleViewController
// assign view to view controller
- (void)loadView {
self.view = self.firstView;
}
// make sure button is added when view is shown
- (void)viewWillAppear:(BOOL)animated {
[self addButton];
}
// add the button to the center of the view
- (void)addButton {
[self.view addSubview:self.button];
button.frame = CGRectMake(0,0,150,44);
button.center = self.view.center;
}
// to toggle views, remove button from old view, swap views, then add button again
- (void)toggleViews {
[self.button removeFromSuperview];
self.view = (self.view == self.firstView) ? self.secondView : self.firstView;
[self addButton];
}
// generate first view on access
- (UIView *)firstView {
if (firstView == nil) {
firstView = [[UIView alloc] initWithFrame:CGRectZero];
firstView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
firstView.backgroundColor = [UIColor redColor];
}
return firstView;
}
// generate second view on access
- (UIView *)secondView {
if (secondView == nil) {
secondView = [[UIView alloc] initWithFrame:CGRectZero];
secondView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
secondView.backgroundColor = [UIColor blueColor];
}
return secondView;
}
// generate button on access
- (UIButton *)button {
if (button == nil) {
// create button
button = [[UIButton buttonWithType:UIButtonTypeRoundedRect] retain];
// set title
[button setTitle:#"Toggle Views"
forState:UIControlStateNormal];
// set self as a target for the "touch up inside" event of the button
[button addTarget:self
action:#selector(toggleViews)
forControlEvents:UIControlEventTouchUpInside];
}
return button;
}
// clean up
- (void)dealloc {
[button release];
[secondView release];
[firstView release];
[super dealloc];
}
#end
Use Interface Builder. It's there for a reason.