Table View moves around after reorientation - uitableview

I'm using autolayout and i've set the options leading space to superview (constant 0), trailing space to superview (constant 0) and center horizontally to supeview in a table view.
Even though, when coming back from landscape mode, the table view becomes movable you can drag it around i doesn't seem to be attached to the edges of the superview, and it seems that happens because the table view was wider in landscape mode and when it goes back to portrait mode there's some blank room left to the right of the table view to fill the space is not longer ocuppying.
I call this code in every re orientation:
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
[General addLeftRightConstraintsToView:_detail1TableView inRelationToSuperview:self.navigationController.view];
}
That's a class method i'm using to update layout constraints:
+ (void)addLeftRightConstraintsToView:(UIView *)view inRelationToSuperview:(UIView *)superview
{
// Left Space to Superview
NSLayoutConstraint *leftSpaceConstraint =
[NSLayoutConstraint constraintWithItem:view
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:0.0];
// Right Space to Superview
NSLayoutConstraint *rightSpaceConstraint =
[NSLayoutConstraint constraintWithItem:view
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeRight
multiplier:1.0
constant:0.0];
[superview addConstraint:leftSpaceConstraint];
[superview addConstraint:rightSpaceConstraint];
}
Any ideas?

This works:
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
[_details2TableView reloadData];
}

Related

UIStackView: Loading views from xib and updating height constraint of subView did not reflecting any changes?

I have the following hierarchy in my application
- UIScrollView
- UIStackView
- UIView 1 // load with xib and added in arrangedSubviews
- UIScrollView 1.1 // horizontal scrolling, fixed height constraint 38
- UIView 1.2 // called it childView. has fixed height 0 (I load the view from xib and add it here dynamically and update its height)
- UIView 1.2.1 // called it New View
- UIView 2
- UIView 3
So my problem is when I have loaded a view from xib and added it to UIView1.2 also increased height constraint 0 to a height of newly added sub-view but nothing will happen.UIView1.2height did not update expectedly .
self.constraintChildViewHeight.constant = 95;
[self layoutIfNeeded];
NewView *newView = (NewView *)[[[NSBundle mainBundle]loadNibNamed:NSStringFromClass([FieldPhotoView class]) owner:self options:nil]objectAtIndex:0];
[newView setTranslatesAutoresizingMaskIntoConstraints:false];
[self.childView addSubview:newView];
[self applyConstraintsToParent:self.childView andSubView:newView];
Method
- (void)applyConstraintsToParent:(UIView *)parentView andSubView:(UIView *)subView {
//constraints
NSLayoutConstraint *leading = [NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:parentView attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0];
[parentView addConstraint:leading];
NSLayoutConstraint *trailing = [NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:parentView attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:0];
[parentView addConstraint:trailing];
NSLayoutConstraint *top = [NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:parentView attribute:NSLayoutAttributeTop multiplier:1.0 constant:0];
[parentView addConstraint:top];
NSLayoutConstraint *bottom = [NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:parentView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:-8];
[parentView addConstraint:bottom];
NSLayoutConstraint *equalWidth = [NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:parentView attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0];
[parentView addConstraint:equalWidth];
leading.active = true;
trailing.active = true;
top.active = true;
bottom.active = true;
equalWidth.active = true;
}
#Edit1 - Child view constraints
#Edit2 - For better understanding, I want to achieve this functionality programmatically using xib's(In UIStoryBoard is just working fine.)
From your question what i understood is that you are not able to scroll your content in the scrollview 1.1.
Try the following steps :
For the scrollview 1.1
give top, bottom,leading, trailing constraints w.r.t View1.
give scrollview height constraint = 38
For the childview UIView 1.2
give top, bottom,leading, trailing constraints w.r.t scrollview1.1
pin childview w.r.t View1 for leading & trailing edges
give childview height constraints
give childview vertically center constraint.
For the newView UIView 1.2.1
Load view from nib.
Add it to the childview
Set its constraints - top, bottom, leading & trailing w.r.t childview.
This will make your content scrollable.
I have shared a sample project here: https://github.com/Abhie87/StackExchangeSample
Hope this will be helpful.
Tried to do the same and it worked as expected.
What I did:
Initial views hierarchy looks like this:
Stack view constraints are on the next image:
Each view in stack view has only its' height set by constraint (the button in last view I use to add/remove the new view to the stack view at index 0)
The view to add is loaded from .xib file called SomeView.xib view hierarchy and constraints for which are on the next image (constraint called View Height Constraint is set to 1 and is changed to 95 when SomeView is added to the stack view):
The function that is called when the button is tapped looks like this:
- (IBAction)buttonTap:(UIButton *)sender {
if (self.theView) {
[self.stackView removeArrangedSubview:self.theView];
[self.theView removeFromSuperview];
self.theView = nil;
} else {
self.theView = [[[NSBundle mainBundle] loadNibNamed:#"SomeView" owner:nil options:nil] firstObject];
self.theView.translatesAutoresizingMaskIntoConstraints = NO;
[self.stackView addSubview:self.theView];
[self.stackView insertArrangedSubview:self.theView atIndex:0];
self.theView.viewHeightConstraint.constant = 95;
}
}
Hope this will give you any suggestions in how to fix your situation

ios ipad: rotating TabBar to left side in landscape mode, with transform and constraint update

I'm creating an ios IPad app that has a UITabBar control positioned at the bottom with 3 MenuItems in it. Normally, when I rotate the device, the tabbar will remain at the bottom in landscape. I'd like to have the TabBar rotate to the left side in Landscape mode, and become a vertical representation. I don't want to create a UIView with buttons if possible, because I'd like the same object to manage both, and I like the convenience of the normal TabBar.
My Plan was to detect the device position, and use a UITransform to rotate the TabBar 90 degrees when in landscape. Let's ignore the icon and text rotation inside for now, and just focus on the overall TabBar itself.
Rotation works, except that it leaves the vertical TabBar in the bottom center of the screen looking very out of place. The next task was to constrain it to the left wall. Normally I am using 3 constraints to hold the TabBar to the Left/Bottom/Right sides. I'm having a lot of trouble finding the right constraints to keep it on the left side correctly.
One main problem is that once the transform is done, height and width attributes are reversed (or also transformed) on the tabbar. So high makes it wider on the screen, and width makes it taller. Using top/bottom constraints along with a height of 49 is what is seemingly required, except the top/bottom +height combo causes a constraint conflict.
I'm attaching my current constraint logic which is reasonably working, yet I can't figure out why I need to hardcode these values as shown. This was the only way to get the visual output on the left side. I'm wondering if there is a cleaner way to do this, perhaps without hardcoded values in there for the constants.
I'm also not sure how I'll end up rotating the text+icons as a next step.
Attached also are how it currently appears. It is green to illustrate clearly but will ultimately be transparent, so the cutoff at the top of landscape won't be a problem.
- (void) adjustViewsForOrientation:(UIInterfaceOrientation) orientation {
switch (orientation)
{
case UIInterfaceOrientationPortrait:
case UIInterfaceOrientationPortraitUpsideDown:
{
//load the portrait view
// revert to normal transform
_tabBar.transform = CGAffineTransformRotate(CGAffineTransformIdentity, M_PI*2);
[self.view removeConstraint:_tabbarConstraintT];
[self.view removeConstraint:_tabbarConstraintR];
[self.view removeConstraint:_tabbarConstraintB];
[self.view removeConstraint:_tabbarConstraintL];
// right space
_tabbarConstraintR = [NSLayoutConstraint constraintWithItem:_tabBar
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeTrailing
multiplier:1.0
constant:0.0];
// bottom space
_tabbarConstraintB = [NSLayoutConstraint constraintWithItem:_tabBar
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0.0];
// left space
_tabbarConstraintL = [NSLayoutConstraint constraintWithItem:_tabBar
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeLeading
multiplier:1.0
constant:0.0];
//[self.view addConstraint:_tabbarConstraintT];
[self.view addConstraint:_tabbarConstraintR];
[self.view addConstraint:_tabbarConstraintB];
[self.view addConstraint:_tabbarConstraintL];
}
break;
case UIInterfaceOrientationLandscapeLeft:
case UIInterfaceOrientationLandscapeRight:
{
//load the landscape view
_tabBar.transform = CGAffineTransformRotate(CGAffineTransformIdentity, M_PI/2);
[self.view removeConstraint:_tabbarConstraintT];
[self.view removeConstraint:_tabbarConstraintR];
[self.view removeConstraint:_tabbarConstraintB];
[self.view removeConstraint:_tabbarConstraintL];
NSLog(#"frame: %f,%f",_tabBar.frame.size.height,_tabBar.frame.size.width);
// top space
_tabbarConstraintT = [NSLayoutConstraint constraintWithItem:_tabBar
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:[UIScreen mainScreen].bounds.size.height/2+49];
// left space
_tabbarConstraintL = [NSLayoutConstraint constraintWithItem:_tabBar
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeLeading
multiplier:1.0
constant:-360.0];
// effective width
_tabbarConstraintR = [NSLayoutConstraint constraintWithItem:_tabBar
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem: nil
attribute:NSLayoutAttributeHeight
multiplier:1.0f
constant:49];
// effective height
_tabbarConstraintB = [NSLayoutConstraint constraintWithItem:_tabBar
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem: nil
attribute:NSLayoutAttributeWidth
multiplier:1.0f
constant:[UIScreen mainScreen].bounds.size.height];
[self.view addConstraint:_tabbarConstraintR];
[self.view addConstraint:_tabbarConstraintB];
[self.view addConstraint:_tabbarConstraintT];
[self.view addConstraint:_tabbarConstraintL];
}
break;
case UIInterfaceOrientationUnknown:break;
}
}

UIButton change dimensions at run time

I have a xib file of UIView which has 2 buttons in it. Like this :
Button1 will be either hidden on shown. When it is hidden I want other button to take away whole space.
I also want the view to occupy all available space (orientation change also to be managed). In IB for view, I have selected Size as Freeform, Orientation as Portrait.
For that I added Constraints to both buttons setting 0 to top, bottom and left/right. But with this, when orientation is changed their is gap between both the buttons which I don't want.
I tried many ways but am not able to handle the above two matters. Both are also inter-related. If Button1 is hidden, then should add left constraint to other button also. Right now, I have removed all constraints, so in landscape it doesn't utilize full space.
Can you let me know how do I handle this inter-related issues.
UPDATE
Added the following method to the view (UIView) class. Adding it like this is new for me, with reference have written code to add for top, bottom and left/right setting 0 on both buttons. Left with main role - button1 is hidden then what to add and shown then what.
-(void) addCustomConstriants:(BOOL) hideFirstBtn {
// http://technet.weblineindia.com/mobile/ui-design-of-ios-apps-with-autolayout-using-constraints-programmatically/2/
//NSDictionary *viewsDict = NSDictionaryOfVariableBindings(self.button1, self.button2);
//NSArray *constriaints =
if (hideFirstBtn) {
// ONLY 2ND BUTTON - NO 1ST BUTTON
} else {
// SHOW BOTH BUTTONS
// BUTTON 1
// ADD TOP CONSTRAINT - 0
[self addConstraint:[NSLayoutConstraint constraintWithItem:self.button1 attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeTop multiplier:1.0 constant:0.0] ];
// ADD BOTTOM - 0
[self addConstraint: [NSLayoutConstraint constraintWithItem:self.button1 attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0.0 ] ];
// LEFT
[self addConstraint: [NSLayoutConstraint constraintWithItem:self.button1 attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeLeft multiplier:1.0 constant:0.0 ] ];
// BUTTON 2
// ADD TOP CONSTRAINT - 0
[self addConstraint:[NSLayoutConstraint constraintWithItem:self.button2 attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeTop multiplier:1.0 constant:0.0] ];
// ADD BOTTOM - 0
[self addConstraint: [NSLayoutConstraint constraintWithItem:self.button2 attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0.0 ] ];
// ADD RIGHT - 0
[self addConstraint: [NSLayoutConstraint constraintWithItem:self.button2 attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeRight multiplier:1.0 constant:0.0 ] ];
// Width constraint, half of parent view width
[self addConstraint:[NSLayoutConstraint constraintWithItem:self.button1
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeWidth
multiplier:0.5
constant:0]];
// Width constraint, half of parent view width
[self addConstraint:[NSLayoutConstraint constraintWithItem:self.button2
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeWidth
multiplier:0.5
constant:0]];
// AFTER ADDING WIDTH ALSO FOR BOTH BUTTONS, WILL HAVE TO ADD X SOME HOW ??? PUZZLE ???
}
}
I believe I will also have to set Width for both the buttons. That should be of equal width. If only button2, then that width also will have to set here only right. How to add constraints for them ?
This is how I call this view from other screen :-
vov1 = [visitorOptView objectAtIndex:0];
// note the origin of the frame is (0, 0) since you are adding it to the cell instead of the table view
vov1.frame = CGRectMake(0, 0, selectedCell.frame.size.width, selectedCell.frame.size.height);
btnWidth = selectedCell.frame.size.width;
[vov1.button2 addTarget:self action:#selector(showVisitorDetailsView) forControlEvents:UIControlEventTouchUpInside];
// show/Enable "Start Chat" btn, else Disable it
if (tableView.tag == 0 || tableView.tag ==1) {
// START
[vov1.button1 setTitle:#"Accept" forState:UIControlStateNormal];
[vov1.button1 addTarget:self action:#selector(acceptBtnClicked) forControlEvents:UIControlEventTouchUpInside];
} else if (tableView.tag == 3) {
// CLOSED LIST - HIDE ALL BTNS
[vov1.button1 setHidden:YES];
}
///// HERE addCustomConstraints SHOULD BE CALLED I THINK
// add overlay view to this row
[selectedCell.contentView addSubview:vov1];
Can you help me with the above.
In that case you should add the constraints by code. So when the two buttons are shown. the first button trailing constraint should be bonded to the leading to the second button constraint. If the "View Details" button is the only one shown then hide the first one and the leading constraint to that button will be bonded with the leading of the self.view .
Create a method called addCustomConstraints and call it in viewDidLoad.

Why am I unable to set Auto Layout constraints when adding constraints to a view I added to UIWindow?

To create a dark overlay on the screen, I add a view over all the other views onto UIWindow:
UIView *darkOverlayView = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];
darkOverlayView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.85];
I then want to center a UIImageView in the new darkOverlayView, and try to as follows:
UIImageView *imageFromLink = [[UIImageView alloc] initWithImage:responseObject];
imageFromLink.translatesAutoresizingMaskIntoConstraints = NO;
CGFloat widerThanHeightBy = imageFromLink.bounds.size.width / imageFromLink.bounds.size.height;
[darkOverlayView addConstraint:[NSLayoutConstraint constraintWithItem:imageFromLink attribute:NSLayoutAttributeWidth relatedBy:0 toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:[UIScreen mainScreen].bounds.size.width]];
[darkOverlayView addConstraint:[NSLayoutConstraint constraintWithItem:imageFromLink attribute:NSLayoutAttributeHeight relatedBy:0 toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:[UIScreen mainScreen].bounds.size.height / widerThanHeightBy]];
But every time it runs, I get the error:
The view hierarchy is not prepared for the constraint:
When added to a view, the constraint's items must be descendants of that view (or the view itself). This will crash if the constraint needs to be resolved before the view hierarchy is assembled. Break on -[UIView _viewHierarchyUnpreparedForConstraint:] to debug.
2013-12-07 00:59:03.626 Jupiter[58008:70b] View hierarchy unprepared for constraint.
Constraint:
Container hierarchy:
>
View not found in container hierarchy: > - (null)
That view's superview: NO SUPERVIEW
How would I get around this? Or do I have to use frames?
How say #rdelmar:
First: Where you add your imageFromLink like addSubView on to superview?
Seccond:
Your constraints:
[darkOverlayView addConstraint:[NSLayoutConstraint constraintWithItem:imageFromLink attribute:NSLayoutAttributeWidth relatedBy:0 toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:[UIScreen mainScreen].bounds.size.width]];
[darkOverlayView addConstraint:[NSLayoutConstraint constraintWithItem:imageFromLink attribute:NSLayoutAttributeHeight relatedBy:0 toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:[UIScreen mainScreen].bounds.size.height / widerThanHeightBy]];
setup only Width and height, what about x and y
just add two constraints more:
[darkOverlayView addConstraint:[NSLayoutConstraint constraintWithItem:imageFromLink attribute:NSLayoutAttributeCenterX relatedBy:0 toItem:darkOverlayView attribute:NSLayoutAttributeCenterX multiplier:1.0f constant:0.0f]];
[darkOverlayView addConstraint:[NSLayoutConstraint constraintWithItem:imageFromLink attribute:NSLayoutAttributeCenterY relatedBy:0 toItem:darkOverlayView attribute:NSLayoutAttributeCenterY multiplier:1.0f constant:0.0f]];
it's set position in center of darkOverlayView.
When adding constraints to view that involves two views, the one view has to be a subview of the other, so you would need to add imageFromLink as a subview of darkOverLayView before you add the constraints. However, in this case, where you're adding a width and height constraint, those constraints should be added to imageFromLink, not to its superview. This type of fixed width or height constraint doesn't involve any other views, so it should belong to the view itself, not the superview.
Adding a height and width constraint doesn't center it in its superview however. You need to add (after making imageFromLink a subview) a centerX and centerY constraint to darkOverlayView as well.

Center custom UIView vertically and horizontally using Auto Layout

I'm trying to build a rather simple animated custom UI using the Auto Layout API newly available iOS 6. The custom view I'm building has a circle that I want to be both vertically and horizontally centered.
Unfortunately I can't figure out why my constraints appear to work fine for UIButton, and UILabel elements but yield weird results when I use a custom view with and custom CALayer (in this case a circle, that will eventually be animated).
To be clear I don't want my view to expand to fill the whole screen, but rather to have dynamic "padding" so that the view is vertically centered both on the iPhone 4 and 5. I should also note that I'm very new to Cocoa and UIKit.
RootViewController.m:
...
- (void)viewDidLoad {
[super viewDidLoad];
// Create Circle View
CGRect circle_view_rect = CGRectMake(0, 0, 100, 100);
UIView *circle_view = [[UIView alloc] initWithFrame:circle_view_rect];
// Create Circle Layer
CircleLayer *circle_layer = [[CircleLayer alloc] init];
circle_layer.needsDisplayOnBoundsChange = YES;
circle_layer.frame = circle_view.bounds;
[circle_view.layer addSublayer:circle_layer];
// Enable Auto Layout
[circle_view setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addSubview:circle_view];
// Center Vertically
NSLayoutConstraint *centerYConstraint =
[NSLayoutConstraint constraintWithItem:circle_view
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterY
multiplier:1.0
constant:0.0];
[self.view addConstraint:centerYConstraint];
// Center Horizontally
NSLayoutConstraint *centerXConstraint =
[NSLayoutConstraint constraintWithItem:circle_view
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0.0];
[self.view addConstraint:centerXConstraint];
}
...
CircleLayer.m:
...
- (void)drawInContext:(CGContextRef)context {
CGContextAddArc(context, 50, 50, 50, 0.0, 2*M_PI, 0);
CGContextSetFillColorWithColor(context, [UIColor yellowColor].CGColor);
CGContextFillPath(context);
}
...
Basically the constraints I've implemented are:
center vertically inside parent view
center horizontally inside parent view
And this is the result I get:
Any help would be greatly appreciated, I've been pondering this one for a few days now.
Thanks
Try adding a height and width constraint to your circle_view. I couldn't even get just a pain square view to appear at all without adding those (using your code, minus the layer stuff).
NSLayoutConstraint *heightConstraint =
[NSLayoutConstraint constraintWithItem:circle_view
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0
constant:100.0];
[circle_view addConstraint:heightConstraint];
NSLayoutConstraint *widthConstraint =
[NSLayoutConstraint constraintWithItem:circle_view
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0
constant:100.0];
[circle_view addConstraint:widthConstraint];
Just to add to rdelmar's answer:
The core issue is that as soon as you go the NSLayoutConstraint route, and specify setTranslatesAutoresizingMaskIntoConstraints:NO, the frame you made with CGRectMake is rendered irrelevant for AutoLayout purposes. That's why it didn't use the info from the frame's height and width.

Resources