Is there a way to add a UIView from a function (in this case, within a statically built library) to a view without passing a frame for the view itself.
Paypal's library achieves this, and I was wondering how the implementation would go.
I don't want to change the root controller, I would just like to add my view over the current controller.
in the style of
[something addSubview:myNewView];
Where something is a view that I don't have access too.
Yes. You simply use the default init method instead of initWithFrame::
MyView *mySubview = [[MyView alloc] init];
[otherView addSubview:mySubview];
Note that this will result in a frame at origin (0/0) with zero size for mySubview and though it would be invisible. You could either specify the frame later, once you know it, or MyView can override the init method and pass a default frame to [super initWithFrame:defaultFrame].
While DrummberB's answer is correct, the view would not show up anywhere because a view initialized without a frame defaults to a frame of CGRectZero. Sooner or later you will need to assign a frame to the view.
Related
I have a UIView based control which I need to resize depending on various criteria and properties of the control. I'm not sure if the way I'm doing it is the best way but so far it's the only one I've found that seems to work. Because the size of the control is dependant on various properties, I cannot set the size in a constructor.
I have a method called setupControl: which contains all the code to finish the setup based on the properties set. I don't want setupControl: called manually, so in drawRect I detect if I need to call it and then queue a selector like this:
[self performSelector:#selector(setupControl)withObject:self afterDelay:0];
return;
At the bottom of setupControl: I then do:
[self setNeedsDisplay];
self.hidden = NO;
I've also overridden the initWithFrame: and initWithDecoder: constructors so that they set the UIView to be hidden to start with until the setup code is executed. The idea being to eliminate any "flashes" on the display as the control resizes.
As I said this works fine, the controls can be drawn ay size is the xib file and then set themselves to the correct size at run time.
My question is whether this method of queuing a selector, exiting the drawRect: and then using a setNeedsDisplay is the only way to do this? O is there some method I haven't found that I can override? or something else?
What you probably want to override is layoutSubviews rather than drawRect as you're changing the layout, not implementing custom drawing.
You may also have to use a custom setter for any properties that change the size of the view and call [self setNeedsLayout] in the setter to make sure your layoutSubviews method is called before your view size is computed.
I have a UIView in a UIViewController to which I add a custom subview in the viewDidAppear method.
MyView *myView = [[MyView alloc] initWithLabelText:text];
[self.view addSubview:myView];
[myView release];
The text variable is a string used on a label in myView. This text changes every time you come back to the current view. But it seems that viewDidAppear does not reload the view - it rather loads a new view over the old one - so I have two labels over each other.
I tried to use viewWillAppear but it doesn't make any difference. I also tried to use [self.view setNeedsDisplay] - doesn't help. I also tried to make myView an instance variable, but it also didn't help.
What worked was to remove the view explicitly, when I declared it as an instance variable:
- (void)viewDidDisappear:(BOOL)animated
{
[_myView removeFromSuperview];
}
Although there is this workaround I would like to simply reset the view when getting back to it. Does anybody know how to do that? I would appreciate it ;)
Don't alloc and init the custom subview every time, only the first time viewDidAppear is called. Then retain it in a property for subsequent use.
The followings thing can ben considered.
viewDidLoad --> alloc and init your sub views
viewDidAppear --> update sub views
dealloc --> release sub views.
I know there is a seemingly exact duplicate of this question here: iPhone SDK: what is the difference between loadView and viewDidLoad?
However, I have read that question and still it was not fully answered.
I'm not using IB as the UI is dynamic.
So should I create the self.view and then add the subviews in loadView,
or should I create the self.view in loadView and add the subviews in viewDidLoad?
When you load your view from a NIB and want to perform further customization after launch, use viewDidLoad.
If you want to create your view programatically (not using Interface Builder), use loadView.
For your specific question, you should add the subview in viewDidLoad. Because, if you overwrite the loadView, you have to do all the jobs, loading all the views.
Here is the explanation from Apple's documentation:
The steps that occur during the load cycle are as follows:
1.
* Some part of your application asks for the view in the view
controller’s view property.
2.
* If the view is not currently in memory, the view controller calls its loadView
method.
3.
* The loadView method does one of the following:
If you override this method, your implementation is
responsible for creating all
necessary views and assigning a
non-nil value to the view property.
If you do not override this method, the default implementation uses
the nibName and nibBundle properties of the view controller to try to load the view
from the specified nib file. If the
specified nib file is not found, it
looks for a nib file whose name
matches the name of the view
controller class and loads that file.
If no nib file is available, the method creates an empty UIView object
and assigns it to the view property.
4.
* The view controller calls its viewDidLoad method to perform any
additional load-time tasks.
It is very simple actually. If you do it without IB, then your UIViewController's view property is empty. So set it at loadView!
I only do setting of view at loadView and nothing else.
Other than that, do all thing inside viewDidLoad. Here is some example:
- (void)loadView {
CGRect frame = [[UIScreen mainScreen] applicationFrame];
baseView = [[UIView alloc] initWithFrame:frame];
[self setView:baseView];
[baseView release];
}
That's it! I am done. And would never want to add more to it. Then at the viewDidLoad, I add all those subviews I want to.
- (void)viewDidLoad {
[super viewDidLoad];
msg = [[UILabel alloc] initWithFrame:CGRectMake(0, 200, 320, 50)];
[msg setText:#"Your profile is empty!"];
[[self view] addSubview:msg]; // hey, I have done my view at loadView, so I have it now
[msg release];
}
I could be wrong in my understanding :)
loadView is the method that actually sets up your view (sets up all the outlets, including self.view).
viewDidLoad you can figure out by its name. It's a delegate method called after the view has been loaded (all the outlets have been set) that just notifies the controller that it can now start using the outlets.
viewDidLoad:
"This method is called after the view controller has loaded its associated views into memory. This method is called regardless of whether the views were stored in a nib file or created programmatically in the loadView method."
loadView:
"If you create your views manually, you must override this method and use it to create your views."
Add subviews in viewDidLoad. That way you are 100% sure than the view did indeed load and is ready for consumption.
Use viewDidLoad for initialize views and constrols. And use loadView if you don't have Nib/Xib and would like your ViewController has custom (not UIView) view.
Only use loadView when you want to create a view yourself.
Don't use loadView after you use interface builder or init with nib since these actions have already called loadView in the underly implementation.
Also, when use loadView, assign view first before doing any other settings:
-(void)loadView {
[super loadView];
// if you do any things here before assigning a view
// it will try to get a view first by calling loadView()
// and ends up with a crash since a dead loop.
self.view = ...;//assign your view here
//do other settings
}
I have a UIViewController that is initialised with a correct frame, however somewhere in my code the frame gets mangled and I'm having difficulty finding out where.
In situations like this it is usually handy to watch a variable in the debugger, however I have no way of accessing the controller->view->frame property in my variable view, since it isn't a variable, it's a property (surprisingly enough)
Drilling into the UIView in the variables display shows a few things but nothing I can relate to the frame, I thought perhaps that would be in layer but it isn't.
Is there any way to watch for changes in a private API? I guess not, since the variables are essentially 'hidden' and so you can't specify exactly what to watch.
Alternatively, what other approach could I use? I already tried subclassing UIView, setting my UIViewController's view to point to this subclass and breaking on the setFrame method but it didn't seem to work.
EDIT: the subclassing UIView method DID work, I just had to set the view to point to my test subclass in viewDidLoad and not the init method. Leaving this question open as I'm not sure if this is the best way of approaching this kind of problem...
Subclass your the view you want to track and rewrite the setFrame method:
#implementation MyTableView
- (void)setFrame:(CGRect)frame;
{
NSLog(#"%#", frame);
[super setFrame:frame];
}
#end
Then use the debugger to add a breakpoint to it and check when it gets called. Eventually, you'll see when the frame gets changed and where does the change comes from.
I discovered this can be done using key value observers.
http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/KeyValueObserving/KeyValueObserving.html
You could create an ivar, view2, and just assigned it to your view in your loadView method. That should enable you to watch it like a normal variable.
i set my view controller to the particular view through Interfacebuilder.
but initWithFrame is not calling , but drawRect is being called?when i put break point?
Right, because it's not guaranteed that initWithFrame: will be called when unarchiving the xib. Try using awakeFromNib or viewWillLoad or viewDidLoad. Which one you choose will mainly depend on at what stage during the display process you need to insert your code.
Also, check out the answer to this question.
According to the documentation - http://developer.apple.com/library/ios/#documentation/uikit/reference/UIView_Class/UIView/UIView.html - initWithFrame: is not called when your view objects are subsequently loaded from the nib file. Objects in a nib file are reconstituted and then initialized using their initWithCoder: method, which modifies the attributes of the view to match the attributes stored in the nib file.