Custom UIViewController and custom UIView - ios

i have three views which have a couple of similarities. For example a UIImageView at position 30,30 and three UILabels at the same position.
Whats the best way to solve this without copy and paste or redundant code?
When i create all three views with a nib i have to copy and paste the same parts...
I started to implement my own UIViewController and a base UIView for that. My code looks like this:
- (id)init {
if((self = [super init])) {
image = [[UIImageView alloc] init];
header = [[UILabel alloc] init];
[self addSubview:image];
[self addSubview:header];
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
image.frame = CGRectMake(30, 30, 20, 20);
header.frame = CGRectMake(30, 100, 200, 30);
}
When i push the controller with that view onto the stack, the view is displayed wrong. Image and label are covered by the titleheader (navigationcontroller)...
Can someone please explain me how this layout system works... ios sdk is the worst sdk i ever worked with :(

I don't see the necessity for copy and paste while creating views in a nib? It definitely removes the necessity to position views per code. And as aside comment: ususally the "worst" sdks are those that we don't know well.
And if they need a common set of housekeeping code factot a method out that will take a view as a parameter and do it without offending the DRY principle (donz repeat yourself)

When i create all three views with a
nib i have to copy and paste the same
parts...
Do you realize that it's possible -- simple, even -- to have an object like a view controller load more than one nib? All three of your view controllers could load the common part of the interface from the same nib file, and also load their own interfaces from their own nib files. One way to do that would be to put the outlet(s) for the common interface in one class along with whatever code (setup, actions, etc.) you need to manage the common interface. You could then create a subclass of that class for each different view controller, and have each subclass load its own nib. The last step is to either insert the interface from the subclass into the superclass's view hierarchy or insert the interface from the superclass into the subclass's view hierarchy, whichever makes sense in your situation.

Related

Add custom UIView programmatically created inside XIB UIView

I've created a UIView programmatically that embeds several UIControls (UIButtons, UISwitchs and UILabels mainly). I set in the -(id)initWithFrame: of this class the background color.
I have created a message to add the UIControls in the view in order to put inside of my custom view. It's something like that:
-(void) CreateGuiObjects
{
UIView *container = self;
//create uiswitch
mOnOffSwitch = [[UISwitch alloc] initWithFrame:CGRectMake(10, 20, 0, 0)];
mOnOffSwitch.translatesAutoresizingMaskIntoConstraints = NO; //used for constraint testing
//add parent view (self)
[container addSubview: mOnOffSwitch];
/*
other stuff like above
*/
}
then in my view controller there is an event handler for an external button; the action is to add the above custom view in an empty UIView created with Storyboard Interface Builder in Xcode.
the code is like the following:
-(void)CreateButton
{
MyCustomView *view = [MyCustomView alloc] initWithFrame:CGRectMake(20,20,300,200)];
[self.view addSubview: view];
//now call my create method
[view CreateGuiObjects];
}
now, the problem is that it draws the controls, but it seems to position them in a different place...i set the frame origin for the container view in (20,20) and then put the switch in (10,20) where this point is relative to the custom view origin. Instead of that position the view seem to be far away from that position and the second problem is that the background color (gray) set in the initWithFrame is totally ignored.
If i remove every call to addSubview inside the CreateGuiObjects, it draws the empty view with the correct background color and in the correct position.
Edit:
if remove `mOnOffSwitch.translatesAutoresizingMaskIntoConstraints = NO; it works fine...but if i put again it doesn't work. Need to understand deeply the meaning of this property.
Could someone can help me? i think it is a silly question but i'm new to iOS development :(
thanks in advice
Method translatesAutoresizingMaskIntoConstraints = YES means that the UIView is using Auto Layout.
The fundamental building block in Auto Layout is the constraint.
Constraints express rules for the layout of elements in your
interface; for example, you can create a constraint that specifies an
element’s width, or its horizontal distance from another element. You
add and remove constraints, or change the properties of constraints,
to affect the layout of your interface.
When calculating the runtime positions of elements in a user
interface, the Auto Layout system considers all constraints at the
same time, and sets positions in such a way that best satisfies all of
the constraints.
read more about Auto Layout Concepts
If you don't know how to use Auto Layout I would recommend you to turn it off.

Where and when to initialize/create view programatically

I had this question when/where to create and initialize views that are created programatically, so I hope some discussions here will shed more light on this topic for me.
This slide:
says: "not to initialize something based on the geometry of the view in viewDidLoad" and suggests viewDidAppear.
Imagine my view controller has view. I want to add 10 dynamic UIButtons to it.
Shall I put the code like below to the viewDidAppear?
-(void) viewDidAppear:(BOOL)animated
{
...
UIButton *button1 = [[UIButton alloc] initWithFrame: rect1];
[self.view addSubview: button1];
UIButton *button2 = [[UIButton alloc] initWithFrame: rect2];
[self.view addSubview: button2];
...
}
But this creates the buttons each time the view is shown. Is it what we want?
On the other hand if I put the code in viewDidLoad slide suggest not to initialize geometry of these views there.
Or shall we create buttons in viewDidLoad and set their frames in viewDidAppear?
What approach do you usually take?
But this creates the buttons each time the view is shown. It's true.
So the best thing you can do is to add a boolean (lets name it isLaunched). You set it to FALSE in the method -(void)viewDidLoad
Then add a if condition in your -(void)viewDidAppear where you perform creation of buttons (or other stuff) and set the boolean to true at the end.
You should have something like that :
-(void)viewDidLoad
{
//some settings
isLaunched = FALSE;
}
-(void)viewDidAppear:(BOOL)animated
{
if(!isLaunched)
{
//creating and adding buttons
isLaunched = TRUE;
}
}
zbMax (and now Amar) offered good solutions to implement the view creations in viewDidAppear: I will provide the rational for doing this (over viewDidLoad).
It is pretty simple actually. In viewDidLoad none of the views are actually setup yet, so any attempt to set/create frames or bounds will be extremely inconsistent. Struts and springs (or autolayout) will take effect after this method which will create additional changes to your views. viewDidAppear: is the correct place to do this because you can now rely on existing views and setting frames.
Reason for not playing with the geometry in viewDidLoad is because view is still in the memory and not on the window. Once the view is put on the window, then you can specify geometry. That happens when viewDidAppear is called for your controller.
As recommended, you should do all the initialisation in viewDidLoad as this is one time task and need not be repeated. Hold references to the added subviews and give them appropriate frame in viewDidAppear.
When you are dealing with custom UIView and its subviews, layoutSubviews is the method you need to override in the custom view in order to rearrange the geometry of its subviews.
Hope that helps!

If I don't want to use a nib file, what is the proper implementation of loadView that sizes the root view correctly?

There are a bunch of related questions here, but none that feels like a concise or correct answer. Here's the situation:
I am creating a new ViewController and don't want to use a nib file. My understanding from the Apple docs is that if I don't want to use a nib, I should implement loadView to manually create my own view hierarchy.
However, its not clear to me how I should properly instantiate self.view with the proper bounds (given this view controller might be used in a bunch of different situations, setting it simply to the screen bounds doesn't feel right).
Somehow the default UIViewController loadView does seem to properly initiate the frame size, but its not clear if I'm writing my own version what I should be doing to do this.
There is no need to implement loadView. Instead, implement viewDidLoad and create and add any and all desired subviews you want. Just add them to the default self.view.
If you want to add a view that fills the view controller's view then do something like the following:
- (void)viewDidLoad {
[super viewDidLoad];
UIView *someView = [[UIView alloc] initWithFrame:self.view.bounds];
someView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.view addSubview:someView];
}
In loadView, you should set the view property of viewController, and nothing else. Adding subviews should be done in viewDidLoad.
- (void)loadView {
self.view = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, 80, 40)];
}
Roopesh Chander has an interesting blog post on which strategy to choose: loadView vs viewDidLoad
for Programmatic UI Setup. He recommends setting the frame in loadView rather than viewDidLoad for maximum efficiency.

adding Multiple subviews efficiently on a scroll view

If we need to add multiple views say - a tableview, a mapview and 2 or 3 more views on a single scrollable screen, then what would be most efficient way to do it ?
And will it be suitable for an app from the point of memory management?
Please share your thoughts.
Here's how you can do it programmatically: just add them in your viewDidLoad (or possibly your viewWillAppear) methods.
- (void) viewWillLoad
{
self.myScrollView = [[UIScollView alloc] init];
//Configure scrollview here (frame, contentsize, contentoffset...etc)
UITableView *table = [[UITableView alloc] init];
//Configure table here (frame...etc)
[self.myScrollView addSubview:tableView];
//Continue adding other subviews here
}
Or you can do it visually using storyboards. Just drag the views that you want onto your storyboard and then cntrl drag to your .m or .h files.
And yes, iOS is designed for displaying multiple views at once. It is suitable.

Adding subviews on top of a programmatically-created UIView hierarchy

I have a UIView subclass that programmatically creates and adds a number of subviews. However, I want to give users of the class the ability to place new subviews over it (and its subviews) using addSubview and ideally from within Interface Builder as well.
What is the best way to do this?
Now, I suppose it would be acceptable to only add the views programmatically using addSubview, but I'm not sure how I would go about this either.
NOTE: I've tried adding additional subviews using interface builder, but they're hidden once the custom UIView subclass I refered to above creates its other views programmatically, presumably since they're added last.
Just drag a generic UIView into a nib in Interface Builder and set its class to your custom subclass. You can still do everything you could with a plain old UIView, including drag-dropping subviews.
You won't be able to see what the custom subview actually looks like in Interface Builder; it will just be a gray rectangle. You could have (I think) done this in Xcode 3 with IB plugins, but, at least for the time being, Xcode 4 does not support such plugins.
EDIT:
To address the OP's follow-up, you need to manage z-ordering programmatically. Probably the easiest thing to do is to leave your code largely as-is, but instead of doing something like:
UIView *mySubview1 = ...;
UIView *mySubview2 = ...;
UIView *mysubview3 = ...;
[self addSubview:mySubview1];
[self addSubview:mySubview2];
[self addsubview:mySubview3];
(which, as you noted, may well layer the subviews on top of the user's subviews)
Do something like:
UIView *container = [[UIView alloc] initWithFrame:[self bounds]];
UIView *mySubview1 = ...;
UIView *mySubview2 = ...;
UIView *mysubview3 = ...;
[container addSubview:mySubview1];
[container addSubview:mySubview2];
[container addsubview:mySubview3];
[self addSubview:container];
[self sendSubviewToBack:container];
[container release];
You could alternatively use [self insertSubview:container atIndex:0] but I think that adding and moving to the back is a little clearer during a rapid skim of the code.
The end result is that you don't have to mess with the existing z-ordering of your subviews; by putting them in a single, easily-managed container, you can move the whole collection to wherever you would like in the subview hierarchy.

Resources