I am really getting tired of this:
I have a simple UIView called statesComboxBox. I have connected it on the IB using interface builder. I run the following line inside viewDidAppear method and it prints out zero.
NSLog(#"%f",self.statesComboBox.frame.size.width);
I am lost.. completely lost!
NOTE:
I am using Storyboards with Auto Layouts in iOS 6
Related
Since I've seen many other IOS projects written in obj-C, I found that many of them do not use interface builder or storyboard file. It's hard to see what's going on. And I've known that loadview method should do the things similarly to drag and drop objects in IB. So what is the difference between loadview method and doing some dragging objects in interface builder?
Well, you don't have AutoLayout (unless you use hard code to constraints too) and Size Classes without interface builder.
Working with IB is much less coding and more clarity.
If you add a Label to IB then the IB is responsible to release that object. And it's added to the view of course so you don't have to.
I would never go without IB now and would recommend to do so for other devs.
I use Size Classes + AutoLayout and all my screens looks good on every device plus that iOS9 is coming out: I have multiple screens auto enabled because I used Size Classes. App works without maintenance for many years..
Simple difference between IB and loadView
IB:
All u can do is drag and drop things without having any code written.
loadView:
It is first viewCycle method which calls on loading any view of corresponding viewcotroller, You should do all your stuff programmatically here. like adding subviews to your view. and adding constraints etc.
Thanks
In my xib file, I have a constraint for height for the label in my xib file.
And in my objective c, I tried to change it using
self.heightConstraint.constant = newHeight;
But nothing is changed when I run it in simulator. I have used debugger and make sure that line is executed. And in Spark tool, I see the label height is the old height.
How can I adjust the height dynamically?
Updated:
I have changed my code to add a nil check.
if (self.heightConstraint != nil) {
self.heightConstraint.constant = newHeight;
}
My code still get executed, but nothing get changed.
There's nothing wrong with your code itself:
self.heightConstraint.constant = newHeight;
But the reason this changes nothing in your interface, even though it is being executed, is that self.heightConstraint is not a reference to a constraint in your interface. (It is probably nil.)
I agree with Matt. The likely reason your code isn't working is that you have a broken outlet link. In general, when code that tries to do something with an outlet or action doesn't work, the most likely cause is a broken outlet or action connection.
Rule of thumb: When debugging stereo or computer hardware, check the cables.
Corollary for for iOS or Mac OS apps in Xcode: When debugging UI code, check your IBOutlet and IBAction connections.
At what part of the view controller lifecycle are you changing the constraints? You may need to call setNeedsLayout on the parent view.
Currently I'm experiencing a strange behavior. I removed the auto resizing mask from a view for viewForHeaderInSection. When I'm doing this the app crashes because I tried to remove a constraint which is nil.
I'm creating the concerned constraints in viewDidLoad in my parent controller. In debug mode I found out that when the system tries to create a constraint where my child view controller is involved (which has the removed auto resizing mask from the section header view) it directly jumps to updateViewConstraints. Of course the constraints are nil because the weren't created yet.
If I add again the auto resizing mask the app works, but I can't do what I'm trying to do (to layout my views correctly).
If I create my constraints in updateViewConstraints the app also works.
I don't understand why this is happening. On a similar view controller it is working without problems. Sometimes I think auto layout is more a pain than a gain. On a server error an alert was displayed. Here the view could be correctly loaded. Seems that this is a kind of timing problem.
I want to know why this is happening and how should I proceed in future that such an error doesn't happen anymore. Am I doing something wrong?
Edit:
Don't know if it helps but if I call setNeedsLayout and layoutIfNeeded on the view of my child view controller in viewDidLoad of my parent then also the app crashes.
Edit 2:
Seems that it occurs when I add multiple views with constraints on different places to my view controller. For my table I add an empty message if there are currently no entries. If I don't add the label as subview to my table everything works fine.
So when I'm allowed to add my constraints? Currently I add them right after the view was added as subview. For the empty message it is in viewDidLoad and for the section header it is viewForHeaderInSection. Do I have to use something like setNeedsLayout?
Edit 3:
Adding a subview to the table view isn't a good idea at all (especially when using auto layout). For now I'm using the background view, but that's not the solution I'm looking for.
You do not need to change the value of translatesAutoresizingMaskIntoConstraints unless you actually have set the autoresizingMask to something other than 0. If you are using constraints, the solution is to completely ignore autoresizingMask and translatesAutoresizingMaskIntoConstraints.
Now I got the same problem again. My workaround didn't worked:
// on iOS 7 this would bring "Auto Layout still required after executing -layoutSubviews. UITableView's implementation of -layoutSubviews needs to call super." and the app crashes
// on iOS 8 this is needed, otherwise "Unable to simultaneously satisfy constraints."
// bug?
if (UIDevice.CurrentDevice.CheckSystemVersion (8, 0)) {
sectionHeader.TranslatesAutoresizingMaskIntoConstraints = false;
}
What I've found out so far is that it has to do with the headerview and the empty message (view). When both are used I get this problem. Either the app crashes or I get
Unable to simultaneously satisfy constraints
depending if I have the autoresizing mask turned off or not. Interestingly, only iOS 8 made this problem with my workaround. A solution would be to set a boolean variable in viewDidLoad if the constraints have been set up and check for this in updateViewConstraints. But I wanted to know the real cause for this and not using a workaround for a workaround ...
Every time I retrieve new data for my table I checked if the number of records is zero or not. If they were zero I showed my empty message. The problem seems to be caused by the reloadData, which I called before this check. Than UITableViewHeaderFooterContentView got a height of zero from the NSAutoresizingMaskLayoutConstraint. Restructuring my code to this variant
if (myList.Count == 0) {
this.TableView.BackgroundView = emptyMessage;
this.TableView.SeparatorStyle = UITableViewCellSeparatorStyle.None;
} else {
this.TableView.BackgroundView = null;
this.TableView.SeparatorStyle = UITableViewCellSeparatorStyle.SingleLine;
if (emptyMessage != null) {
emptyMessage.RemoveFromSuperview ();
}
}
// call this afterwards!
TableView.ReloadData();
seems to solve my issue.
If I want to initialize views programmatically, where in the viewcontroller lifecycle should this happen?
The initial intuition is loadView. However, here, we don't yet have the frame of the view itself (necessary for calculating the sizes/positions of the views). Ditto for viewDidLoad.
Next intuition is viewWillAppear- here we DO (finally) have a guarantee of the frame of the view. However, this has potential to be called many times throughout the vc lifecycle. Ditto for viewDidAppear, etc...
Finally, I found viewWillLayoutSubviews. This works for the initialization of most static layouts- however, whenever any view moves this gets called again (same problem as viewWillAppear).
I've seen recommendations to init the views in loadView and set their frames in viewWillLayoutSubviews (since setting frames should be idempotent, who cares if it gets called a couple times). But then why does apple so strongly encourage initWithFrame: as the standard initialization method of UIViews (https://developer.apple.com/library/ios/documentation/windowsviews/conceptual/viewpg_iphoneos/CreatingViews/CreatingViews.html)?
Would it be crazy to subclass all my UIViewControllers to have an initWithViewFrame: method? That way I can pass in a frame, manually set it immediately in loadView and be done with it? Or is it better to have a viewHasBeenFormatted flag in viewWillAppear that, if not set, calls the formatting of views and then sets it?
Or is this just apple's way of saying "use interface builder or you're screwed"?
Any help is appreciated!
edit- accidentally wrote loadView where I meant viewWillAppear (in final paragraph)
update- I guess I've come to terms with the fact that there is no place where
The frame is confidently known
The code will only be run once (on setup)
Looks like you're expected to initWithFrame: all your views in viewDidLoad (but then I guess the contents of that view shouldn't treat that frame as even remotely final? because how could it be when it was derived on an assumption? ugh...). Then re-set their frames in layoutSubviews. And make sure to manually handle the differences between initial layout and layout as a result of a moved view there... Man I feel like I've GOT to be missing something... (lol denial...)
I guess that, OR submit and use IB.
update2- viewWillLayoutSubviews WILL get called when one of its subviews is resized. So it is still disqualified as it fails property 2 of the required characteristics that I'm looking for. :(
If you're doing layout with IB, it's fine to do additional view initialization in viewDidLoad (for example, if you need to do stuff that IB doesn't handle well, or if you have UIView subclasses with properties not supported by IB). Alternatively, if you're not using IB, the documentation says you should use loadView to manually initialize your view hierarchy.
You're right, though, that you can't rely on the frame being accurate at that point. So you can accomplish layout via each view's autoResizingMask property, layout constraints (if you're iOS 6 and later), and/or overriding layoutSubviews.
My usual approach is to do layout to some degree in IB, then do anything else I need to (nontrivial layout, custom classes, etc) in viewDidLoad. Then, if I have layout to figure out that autoResizingMask doesn't cover (I'm supporting down to iOS 5), I override viewWillAppear (or layoutSubviews if I'm subclassing UIView) and do some pixel math. I've got a category on UIView to help with this that has things like:
-(void)centerSubviewHorizontally:(UIView *)view pixelsFromTop:(float)pixels;
-(void)centerSubviewHorizontally:(UIView *)view pixelsBelow:(float)pixels siblingView:(UIView *)sibling;
View controllers should not have initWithFrame: methods. What I do in all of my code (I never use IB) is to let the default loadView do its own thing. I create and setup all subviews in viewDidLoad. At this point the view controller's frame has at least a sane value. All subviews can be created with their own sane frames based on the initial size of the view controller's view. With proper autoresizingMask values this may be all you need.
If you need more specific subview layout, put the appropriate layout code in the viewWillLayoutSubviews method. This will deal with any view controller view frame changes including rotation, in-call status bars, etc.
If you don't use interface builder you should override loadView and initialize the views there. If you use autolayout you can also add your constraints there. If you don't use autolayout you can override the layoutSubviews method of your views to adjust the frames.
I'm sorry if I missed something here, but I thought UIView objects that were created in IB should have their frames created in viewDidLoad so you can do initial setup based off of this view in viewDidLoad or awakeFromNib or viewWillAppear. I logged the output in each method:
NSLog(#"%# %s", NSStringFromCGRect(self.zoomView.frame), __FUNCTION__);
And in all I get {0, 0, 0, 0}.
This is the first nib in my UIStoryboard, and I'm using Autolayout and iOS 6. I could have sworn on previous apps I have used the frame of other UIView objects created in IB to set things up. Is there something that has changed? Or do I just remember it incorrectly? Thanks!
I had the exact same issue. Yes, you remember correctly - it used to be different in iOS 5. I always set up my views in viewDidLoad: and the frame was already the way it was going to be when the view was actually on screen.
Now in iOS 6, you'll need to put your code into viewDidAppear: to have a valid frame to work with, if auto layout is enabled. Apparently the laying-out is done in between those two calls.
I think it has to do with the device you are using and possibly other things but the frames and bounds "could" sometimes be set by the time viewDidLoad is invoked and sometimes not (depends on ....) On the other hand, frames and bounds are guaranteed to have been set by the time "ViewDidAppear" has been called. And you want to put your code related to frames and bounds where it is guaranteed!