Setting the frame of an UIView does not work - ios

I have a problem with the frame-property in iOS 7.
I wanna resize some UIViews in the viewDidLoad-method of my UIViewController, but if I do it like int screenHeight = [[UIScreen mainScreen] bounds].size.height;
[self.leftSideTableView setFrame: CGRectMake(0, 0, 320, screenHeight)];
the height is set as I want it till the end of the method, but in every other method it is as is has been before!
What's wrong with it or is it just a bug of the compiler or anything else?

One has to put view resizing into -viewDidLayoutSubviews:! (documentation)
Placing view frame changes into -viewWillAppear: or -viewDidLoad: will not work, because the views are not laying out yet!

Check if you are using autolayout in your xib file. If you don't want to use autolayout, uncheck it in your xib file.
Change your self.leftSideTableView frame in -viewWillAppear:.

Check to make sure that auto layout isn't activated in your storyboard file.
To turn it off, look at the inspector in interface builder. Click the icon that looks like a page all the way on the left. In the section "Interface Builder Document" uncheck "Use Auto Layout."
I find it's best to an entire view controller in IB with auto layout, or completely in code. Mixing the two can lead to weird behavior that is hard to debug.

There are several reasons why this might be happening. First of all, you need to make sure that your tableview isn't nil. If you're creating it programmatically, you need to be sure that you're calling alloc/init somewhere before you attempt to set the frame. If self.leftSideTableView is an IBOutlet, this can be caused by forgetting to actually link the outlet to the interface object.
Then, second and less likely, you are creating the table view programmatically and initializing it properly, but you forgot to add it as a subview of one of your on screen views.

Related

Xcode Interface Builder Height/Width Settings vs. View Controller

I'm having a hard time wrapping my head around how frame properties (height, width, posX, posY) work in terms of setting them in a View Controller vs on the Storyboard (Interface Builder).
For instance, let's say I have a UICollectionView object that I set to have a width of 400 and a height of 800. Then, in my code, I set the frame of that same object to 600 x 400. I haven't really found a consistent behavior. I tried setting the frame in viewDidLayoutSubviews and it sort of worked - but it seem to 'jump' back and forth between that and what was set on the storyboard.
Basically my question is, when do the properties on the storyboard change the UI object? I assume that I just need to know that, and then reset them in the View Controller after the fact. Or, is there a way to set the height and width empty so that I can do it all in the code?
Any insight into this would be very helpful!
If you use AutoLayout in your project then setting frames of the view objects you configured in storyboard won't work. Because after you set the frames, AutoLayout will update frames again which makes the frames set by you not working. If you want detail, you can check this article:Advanced Auto Layout Tools But you can set frames of view objects created programmatically to position them.
You can check if you have turned on AutoLayout in you storyboard file's file inspector. There is one thing though, if you do want to use AutoLayout, be sure to not set view's translatesAutoresizingMaskIntoConstraints to NO. The default value of this property is YES. If you use AutoLayout, this property is set by storyboard for you, because constraints created in storyboard are enough to layout the views.
EDIT:
1) if you are not using AutoLayout then setting frames in code should work as expected.
2) yes you can, a little tricky though. you must create UICollectionView yourself using [[UICollectionView alloc] init] or load it from nib. and then configure cell in IB with a xib file. you can use AutoLayout to layout subviews of cell in xib file. and register the class of cell to UICollectionView or load cell object from nib yourself. then you should calculate the size of every cell and let AutoLayout layout subviews of cells.
although this is easier than layout interface entirely in code, it's still a little complicated. the better way is using AutoLayout. Since not all the layout detail can be done in the design time since some views' frame may be different depending on data. you can make a basic layout with AutoLayout first, then IBOutlet the constraints you want to configure on the fly. and change the constant property of constraint objects later. this way, you can 100% control the layout process and also let AutoLayout do the dirty jobs you don't want to do yourself. I suggest you read official docs of AutoLayout and other good resources about it. The learning curve is steep at first, it may make you want to kill yourself too. But it's really powerful and easy to use. once you figured out how AutoLayout works, it will make your iOS development life much easier.
If you want to set the size of things through code, you could try creating outlets from the storyboard to the View Controller. Then in the View Controller, you can use viewDidLoad or viewDidAppear to set the size properties of your object(s).
viewDidLoad will get called when that view is first created. viewDidAppear will get called each time that view comes back onto the screen (like if you are going back with a navigation controller).

UIScrollView Frame Always CGRectZero

I have a nib that has the standard UIView and I've also included a UIScrollView as an IBOutlet (yes, it is hooked up), not as a subview of the main UIView, but just out on its own. Autolayout is turned off. The scroll view has several subviews and is larger than the main view. In viewWillAppear:, I set the content size of the UIScrollView to its current frame size, and then set its frame to the size of the main view, and add it as a subview of the main view.
Unfortunately, nothing is showing up. When I NSLog the frame of my UIScrollView, it is coming back as {0, 0, 0, 0} (CGRectZero). I thought this was odd, so I went back and tried logging the frame before I do any changes to it. Still zeroes. Logged it out in viewDidLoad before anything is done to any of my view elements. Still zeroes. (FWIW in my nib, the frame is {0, 0, 320, 896})
I've had this issue with several of my controllers, but it seems to be hit or miss. Sometimes it works, other times I get the empty frame. Typically, recreating everything from scratch seems to fix the issue, but I don't know why, as I'm setting everything up the same both times.
Running Xcode version 6.1 (6A1052d), iOS SDK 8.1 with a deployment target of 7.0
Let me know if there is any other relevant information I can give that might help.
EDIT 1: To address the questions about my UIScrollView being a "subview", here is what my view heirarchy looks like in the document outline:
As you can see, the UIScrollView is a "subview" of the view controller, but is not a subview of the "main" UIView which has the controller's view outlet.
EDIT 2: More images and some code. Here is a better look at how my nib is set up:
I add my scrollView to the main UIView as follows:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
scrollView.contentSize = scrollView.frame.size;
scrollView setFrame:CGRectMake(0, 100, self.view.frame.size.width, self.view.frame.size.height - 100); // I slightly modified this because there are other variables that determine its Y position and height
[self.view addSubview:scrollView]
}
But I still don't think any of that code matters- for whatever reason, my UIScrollView is coming back nil. If I have the view created in my nib, the outlet is connected properly, how would the view still be nil? I'm creating my controller via initWithNibName I've tried cleaning the project, and removing the app and reinstalling.
Best guess on an answer, and some comments:
Nibs are simply serialised object trees. You can have as many view as you want as root. The problem (big one!) is that if they are not "connected" to outlets that retain them, at some point you will loose those references.
Since your UIScrollView is not a subview of the main view controller view on the Nib, it's not retained by it (views retain their subviews). So, it's up to you to retain it on your view controller.
My guess is that at the point you are trying to set the frame in the View Controller, the scroll view is already gone. I'm not sure how sure when are you doing it, but it might be after some run loops, so the unretained scroll view is dealloced.
Workarounds:
Instead of a variable for the IBOutlet, use a strong property (#property (strong, nonatomic) IBOutlet UIScrollView *scrollView).
Add the __strong qualifier to the variable.
Add the UIScrollView as a subview within the nib, so when the object tree is deserialised it's already retained by its parent.
Given that you purposefully put the UIScrollView outside of the main view hierarchy, I'm assuming you don't want option 3, so I'll just go with 1.
First, you might want to consider using autolayout. It makes things a lot easier once you make the investment to understand how it works. Check out this Apple doc on using UIScrollview with autolayout.
If that's not an option, the problem is that you're setting up your scrollView at the wrong point in the cycle. The scrollView needs a chance to lay itself out. By the time you hit viewWillAppear, you've missed sub view layout.
So, you could try:
Configure your scrollView in willLayoutSubviews and set its setNeedsLayout property.
Explicitly call layoutIfNeeded on your scrollView in viewWillAppear. The idea is to force another subview layout cycle. But, I'm not 100% sure it would work -- it might be too late at that point.
Note that the size of the main view (self.view) isn't determined until viewDidAppear. It's not always correct in viewWillAppear. So, you have a problem in your setup: you can't really set up your scrollView until you know your view size, but you don't know that until it's too late! You might want to redesign to avoid that dependency.

Can't set size of UIButton frame with auto layout enabled

I really tried hard to solve this apparently simple problem by myself, but I couldn't fix it yet!
So here's the deal:
I am using a storyboard with auto layout enabled.
But because of to many objects in one of my views, I decided to implement the
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
duration:(NSTimeInterval)duration
method for this view controller and set the frames for the views explicit like so:
self.startStopBtn.frame = CGRectMake(10, 223.5, 300, 30);
And it works fine as long as I keep the size of this UIButton object the same (as in the storyboard) and only change its origin. As soon as I change the size the button is gone!
Finally enough I can change the frames of the labels, views and image views without any problems in the same method in the exact same way.
I really would appreciate your help!
Hopefully somebody can guide me in the right direction.
if you are using autolayout you should not manimulate UIView frame. You should modify NSLayoutConstaints to change something in view from code.

Using interface builder to layout a scrolling view with images and text

I have a requirement in my app to display a bunch of information that includes both text and images. It will be quite long, so it will need to be scrollable to access all the content.
I know that I can achive this by programmatically adding different UILabels, UIImages etc to a UIScrollView. But this is a proof of concept, so I'm looking for something a little quicker than having to work out all the positioning and code required. The information is static anyway, and does not need to interact with code.
Is there a way to do this using the interface builder (storyboard or xib is fine)?
you definitely can do that if you simply want a quick interface
1.> you might need to know how long is your scroll view, for example in my case, i set it to 1568
2.> Then i drag all the controls that will fit for the first 568 pixel view onto the scroll view and position them.
3.> Then change the Y value for that scroll view to something like - 500, so you can see the rest of the scroll view, and put everything you need there.
4.> After you have all your controls, and remember to set the frame back to 0,0,320,568
5.> last step, in your code, set SCROLLVIEW.contentSize = CGSizeMake(320, 1568);
I would still suggestion don't hard code all those values, but if you are looking for a quick way to do your interface, hope that gives you some ideas.
Just start a new project with a single view, it will come with a xib or storyboard for its single ViewController.
Create a UIView by dragging it into the workspace and place as many Labels, Images and UI Elements as you want.
Open the xib / storyboard and drag a UIScrollView in as your root VC's root view. Drag the view containing your layout into the scrollview, making it the scrollviews only subview.
Done (almost)!
If you launch your app at this point, you'll notice you can't scroll. That is because the scrollview is "too stupid" to adjust the size of its contentSize property on its own.
You'll need some code here, but it is only a tiny snippet and you won't need to touch it again:
Create a new Category on UIScrollView.
In your category's implementation, do:
#implementation UIScrollView (MyHandyCategory)
-(void)awakeFromNib {
NSArray *subViews = [self subviews];
UIView *contentView = [subViews objectAtIndex:0];
[self setContentSize:contentView.frame.size];
}
#end
Done (for real this time)! This will check the size of the view your scrollview contains and ajust the contentSize after it has been initialized. You can change the size of your content view as you like, no need to play around with hardcoded values or even Interface Builder values!
If it’s just proof of concept I’d have a WebView and a local HTML page you load. Easy-peasy.
I would suggest UICollectionView. It's fairly straightforward. There's a good tutorial here:
http://www.raywenderlich.com/22324/beginning-uicollectionview-in-ios-6-part-12

Accessing parent view when adding subview

I have a ViewController that has 2 NSViews in it of different sizes. I'm wanting to add the view of a custom ViewController as a subView to both of these NSViews and have it dynamically size to fill (the 2 parent views). I can accomplish this just fine in the implementation file for the main layout, but its a lot of code. I would like to instead have my custom ViewController do all the work. To do that, I need to know the height and width of the view that I am adding my custom view to. There is a parentViewController property, but it isn't doing anything for me. Is there a way to reference the view that a view is being added to?
In my custom ViewController viewDidLoad method I would like to be able to have
[self.view setFrame:CGRectMake(0, 0,
self.parentViewController.view.frame.size.width,
self.parentViewController.view.frame.size.height)];
but the height and width are both nil here.
I've been digging through documentation for hours and I am still confused. Any help would be much appreciated.
The parentViewController property should be set in the first place. It will be nil if it is not set. You have not specified if it is set correctly. There is a simpler way to do this though, assuming that your view's superview is set correctly. Try this:
[self.view setFrame:self.superview.frame];
or make the rect from the superview's frame if you need to alter something in there.

Resources