I have some custom buttons I add to the view in -viewDidLoad. I want them centered vertically inside the view, like so:
self.customButton.center = CGPointMake(98.f, self.view.center.y);
However, the view height is 504.0 even on a 3.5" device, where I expect it to be 416 (480 - 20 (status bar height) - 44 (nav bar height)).
The view in xib is using "Retina 4 Fullscreen", but setting it to freeform does not help. I certainly don't want two xibs just to account for the height difference.
I know I can use UIScreen but I would prefer that the view be aware of the height difference.
EDIT: the odd thing is, in -viewWillAppear, view height is correctly set to 416.0. Why this isn't handled after [super viewDidLoad] is beyond me.
How are people handling this? Autolayout would probably handle this but I need a iOS5 compatible method. Can autoresizing handle "centering" somehow?
You don't have Autolayout in iOS5, but you do still have the autoresizing mask. If you center a view vertically in IB, go to the inspector and deselect the vertical struts and springs in the autolayout masks, the view will stay centered vertically on both 4" and 3.5" screens.
Or you can do this in code in your viewDidLoad method. Just center the view as you are now and set the autoresizingMask like this:
self.customButton.autoresizingMask = (UIViewAutoresizingFlexibleBottomMargin | \
UIViewAutoresizingFlexibleTopMargin);
EDIT: the odd thing is, in -viewWillAppear, view height is correctly set to 416.0. Why this isn't handled after [super viewDidLoad] is beyond me.
Because resizing the view is not part of loading the view! -viewDidLoad gets called from -view in a method that probably looks a bit like this:
-(UIView*)view
{
if (!_view)
{
[self loadView];
[self viewDidLoad];
}
return _view;
}
-(void)loadView
{
// Some code to load the view from a nib/storyboard...
}
The view will get resized with a call like yourViewController.view.frame = ..., but this can only happen after the view is loaded (and thus after -viewDidLoad is called).
Set some autoresizing masks. That's what they're there for.
Related
I'm loading a view from a storyboard programatically, but there's an issue with my subview heights (I'm using Autoresize Subviews etc).
When the view loads, the main UIView appears to be the correct size, but the subviews that depend on the constraints to dynamically calculate their final height don't seem to be resizing from the sizes they were set at in interface builder.
Loading the view like so:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
ApplicationRecordingViewController* recordingController = (ApplicationRecordingViewController*)[storyboard instantiateViewControllerWithIdentifier:#"recordView2"];
[self presentViewController:recordingController animated:NO completion:nil];
Orange area's height is from top of main view to top of grey area. Grey area is fixed height
The strange thing is, if I set the simulated size in interface builder as the correct view for my phone, the sizes work out. I guess the initial heights for subviews are 'baked' into the view, but then (usually) resized when the view actually loads?
Note: The orange area should be completely filled by the camera view I'm loading into it.
Incorrect height
Correct height if I change simulated layout
Would love to know what I'm doing wrong!
Edit 1: Constraints
Camera is the orange bit. The Camera bottom constraint is currently set as the bottom of the view + enough room for the gray bar. Have also tried setting the bottom to match the top of the gray bar!
Link to Storyboard with Scene
I know you're all better than me, but I experienced the same in my last project. My Camera View inside a UIView is not in full screen or resizing and fitting the whole UIView, and I also posted it on my daily blog.
Implement viewDidLayoutSubviews -- Inside that method, layout again your video preview layer.Do not alloc init anything inside that method, just re-layout your views. After that, your video preview will fill the height and width of its parent, or whatever you've been targetting.
The issue also has something to do with the hierarchy of the constraints and views.
You already got the answer.
'if I set the simulated size in interface builder as the correct view for my phone, the sizes work out.'
You may call some functions to get height in viewDidLoad or viewWillAppear.
Change Code like this
- (void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.view setNeedsLayout];
[self.view layoutIfNeeded];
CGFloat height = [self getHeight];
}
or
- (void) viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
CGFloat height = [self getHeight];
}
As i can see in your storyboard constrain you have set bottom space to bottom layout guide 150 . i think this is the issue . if you give fixed height to bottom view then no need to set constant as from bottom layout guide --> 150 .
Your old constrain :- >
Just set bottom space of orangeview from greenview = 0.
Check new constrain ,
Here is your storyboard link ,
Storyboard Download link
Here is your output,
After investigating the layout a little more, I found the camera view (orange) seemed to be the correct size, but the live camera preview wasn't.
Solution was to move the camera library (SCRecorder)'s init method into viewDidAppear (was in viewDidLoad before).
My thinking is iOS doesn't autolayout the camera preview view in the same way as normal UIViews, etc.
#interface ApplicationRecordingViewController () {
SCRecorder *_recorder;
}
#end
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
_recorder.previewView = _previewView;
}
Perhaps you can try to get rid of these two hook.
My problem is very similar to UIToolbar not displaying on iPhone 4 and iPhone 4s
I am using Xcode 7 and am trying to get a 'legacy' UINavigationController based iPhone app up and running on various iPhone screen sizes. By legacy , I mean it does not use Storyboards etc. The views are loaded from an .xib.
The app is a classic UINavigationController app with UITableViewControllers but with a UIToolBar at the bottom underneath the table view. The TableView and ToolBar are subviews of the view of the ViewController.
Works great on iPhone5/5s/6/6s.
But on the iPhone 4/4s the toolbar is off the screen. Oddly if I rotate the screen to landscape, the toolbar appears. Rotate back, it vanishes. I know this seems like prehistoric iOS code, but I am completely at a loss here and have wasted hours fiddling in Xcode and IB. I know I am missing something obvious.
It seems like the height of your table view is set to be a fixed height where the toolbar is visible beneath the table view on iPhone 5 and up. To get the toolbar to display on iPhone 4/4s requires reducing the height of the table view's frame using something like the following code.
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
if (onIphone4 && portraitOrientation) {
CGFloat height = 480 - self.toolbar.frame.size.height;
self.tableView.frame = CGRectMake(0, 0, 320, height); // for iPhone 4/4s
} else if (onIphone5 && portraitOrientation) {
CGFloat height = 568 - self.toolbar.frame.size.height;
self.tableView.frame = CGRectMake(0, 0, 320, height); // for iPhone 5 and up
}
}
You will probably need to change the y position in the frame origin to account for the status bar and navigation bar if they are also present. In that case, the overall frame height will need to be adjusted for those changes. This is the frame-based way of adjusting view sizes.
The alternative and preferred method is to add auto layout constraints in the XIB for the toolbar and the table view in Interface Builder so that they will maintain the proper size and position relative to the screen dimensions.
I found the issue.. it was the "Full Screen at Launch" checkbox in IB, for the main "UIWindow"... and I quote from Apple documentation....
"If you choose to create a window in Interface Builder, be sure to select the Full Screen at Launch option in the Attributes inspector so that the window is sized appropriately for the current device."
Now please excuse me while I slam head against wall..way to waste a Sunday!
I'm writing an app in Objective-C using Xcode 6 and iOS 8. The app needs to be able to be deployed on an iPhone 5, 6, or 6+.
If you want to get straight to answering my question, jump down to the last sentence. If you want to understand why I have the question I do, or maybe how I can alter my UI layout in order to solve my problem another way, read on.
In one of my view controllers, I have a scroll view whose top is constrained to the bottom of the navigation bar, and whose bottom is constrained to the top of a table view. The table view's bottom is constrained to the bottom of the view controller's main view (i.e. to the bottom of the phone).
The scroll view contains subviews that expand/contract when the user taps on them. I want the scroll view to grow as its subviews grow, but obviously I don't want the scroll view to grow off screen because it looks bad and because it would cause unsatisfiable constraints (the table view's top--which is constrained to the bottom of the scroll view--would cross below its bottom--which is constrained to the bottom of the main view...this causes an error). So, I use the following code to make the scroll view resize itself according to its subviews sizes without growing right off the screen:
// The max height before the scroll view would go off screen, which would
// mess up the table view's constraints and cause all sorts of problems
CGFloat maxHeight = self.view.size.height
- self.navigationController.navigationBar.frame.size.height
- [UIApplication sharedApplication].statusBarFrame.size.height;
// The height of all the subviews in the scroll view.
CGFloat height = _scrollContentView.frame.size.height;
if (height > maxHeight) {
height = maxHeight;
}
self.scrollViewHeightConstraint.constant = height;
Now for the fun part. Originally, I called this code to re-evaluate and reset the size of the scroll view whenever I rotated the device from portrait to landscape, or vice versa. However, when I would rotate the phone from portrait to landscape, I was getting constraints errors. I determined that it was because I was calling this code after the rotation, when the main view's height was smaller, but the scroll view's height was still large (causing the table view's top to go below the bottom, etc. as I explained before). So, I just moved the code to be called before the rotation (I called the code in the viewWillTransitionWithSize:withTransitionCoordinator: method). This all makes sense so far.
However, now, the problem is that the navigation bar's height changes when the rotation occurs, but the viewWillTransitionWithSize:... method does not include any details on this change (it only gives the new size that the main view will be when rotation is completed, not the new size the navigation bar will be as well).
So, I need someway to determine the new size of the navigation bar before the device's orientation actually changes (just like I can determine the main view's new size before the device's orientation actually changes using the viewWillTransitionWithSize:... method).
Any ideas? TIA!
So, here's my work around in its simplest form:
/*
* This method gets called when the device is about to rotate.
*/
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
// Set the scroll view's height to 0 to avoid constraints errors as described
// in the question.
self.scrollViewHeightConstraint.constant = 0;
}
/*
* At the point when this method gets called, the device rotation has finished altering
* the frames of the views in this view controller, but the layout has not finished
* so nothing has changed on screen.
*/
- (void)viewWillLayoutSubviews
{
// The max height before the scroll view would go off screen, which would mess up
// the table view's constraints and cause all sorts of problems
CGFloat maxHeight = self.view.size.height
- self.navigationController.navigationBar.frame.size.height
- [UIApplication sharedApplication].statusBarFrame.size.height;
// The height of all the subviews in the scroll view.
CGFloat height = _scrollContentView.frame.size.height;
if (height > maxHeight) {
height = maxHeight;
}
// Reset the scroll view's height to the appropriate height.
self.scrollViewHeightConstraint.constant = height;
[super viewWillLayoutSubviews];
}
I have two questions related to UITableViews.
1) The first one is, what is the gap at the top of the UITableView? My app always starts with the top cell not flush with the top of the tableview (as shown in the second image), it starts about one cell lower, i.e. where that gap is in the interface builder. I can't find where that is coming from, or why.
2) I can't seem to resize the uitableview programmatically, I'm trying to reduce the height after a popup appears. I've shown an example of it not working in the second picture.
Here is (an example of) what I am trying at the moment:
- (void)viewDidLoad
{
[super viewDidLoad];
self.table_view.delegate = self;
CGRect tableBounds = self.table_view.bounds;
tableBounds.size.height -= 100;
self.table_view.bounds = tableBounds;
CGRect tableFrame = self.table_view.frame;
tableBounds.size.height -= 100;
self.table_view.frame = tableFrame;
}
Thanks!
UITableView Selected:
Simulation:
In your xib (or storyboard) put your UITableView at position (0,0). ( the same position as the navigation bar).
The first image shows that your table view has problems even in Interface Builder. It looks as if you've set the top inset incorrectly; check your edge insets.
The reason your resizing code is not working is probably that it is too early (viewDidLoad); put it in viewDidAppear: and see if that works, and if it does, try moving it back to viewWillAppear: so the user doesn't see the resizing. If it doesn't work, it might be because you're using auto layout; you can't manually alter the frame of something whose frame is dictated by auto layout. (Your resizing code is also silly; you want to set the frame, not the bounds.) But it might also be because you're using a UITableViewController in a UINavigationController; if you do that, the table view is under the navigation controller's direct control and its size is not up to you.
I cannot resize the uitableview properly when changing the orientation of the screen. I can make one orientation work fine but not both.
I've tried 2 different methods: 1) using the autoresizingMask on the tableview and 2) using the layoutSubviews method and here are the results of each:
1) using the autoresizing almost works if I use
self.myTableView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin |UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleBottomMargin;
It shows correctly when in portrait and then I move to landscape it also shows correctly but not all the way to the end of the screen. In landscape I see a space on the left side and on the right side.
So, I thought let's just add the UIViewAutoresizingFlexibleWidth to the line of code above. It does make the uitableviewcell expand to the full screen in landscape but when I change the orientation it does not maintain the same cell on the screen and sometimes it displays half of one cell and half of another cell in the middle of the screen.
2) Using auto layout. I get the same behavior as above when I use this code:
-(void)layoutSubviews {
[super layoutSubviews];
onLoadSize=self.contentView.bounds.size;
self.myTableView.frame = CGRectMake(0, 0, onLoadSize.width, onLoadSize.height);
self.selectedBackgroundView.frame = self.myTableView.frame;
}
If I add an if statement testing for the orientation, then it works fine for that orientation in specific but not the other. If I test for both orientations I get the same behavior as described above.
Any help will be very welcome.
Thanks!
UITableViews usually occupy the full space of the parent view controller and typically don't need constraints or active resizing, unless you have more than one table view in a single view.
Consider managing the content of UITableViewCells (instead of managing the table view) by:
(1) Applying constraints to each custom cell in Interface Builder.
(2) Redrawing the cell's frame to fit the parent UItableView when the cell renders its subviews (layoutSubviews method).
(3) Calling [cell.contentView needsUpdateConstraints] to force the cell contents to redraw.
Here is some sample code for cell's custom class:
-(void)layoutSubviews
{
resizeCell:self forView:[the cell's UITableViewController.view]
}
Here is a generic function you can add anywhere for reuse:
-(void)resizeCell:(UITableViewCell *)cell forView:(UIView *)view
{
cell.contentView.frame=CGRectMake(cell.contentView.frame.origin.x,
cell.contentView.frame.origin.y,
view.frame.size.width,
view.frame.size.width,
cell.contentView.frame.size.height);
}
Please note the cell's UITableViewController must be passed to the cell's custom class (as a member variable) for the code above to work.
Good luck!