Need assistance setting UIButton in centre using Autolayouts - ios

I am trying to programmatically set the UIButton vertically and horizontally in centre using AutoLayouts here is my code:
- (void)awakeFromNib {
UIView *av = [[UIView alloc]initWithFrame:CGRectMake(0, 0, self.bounds.size.width, 35.0)];
UIButton *doneButton = [[UIButton alloc]initWithFrame:CGRectMake(0, 0, 37, 30)];
//UIButton title, color, backgroung color, targer action ...
NSLayoutConstraint* cn3 = [NSLayoutConstraint constraintWithItem:doneButton
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:av
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0];
[av addConstraint:cn3];
NSLayoutConstraint* cn4 = [NSLayoutConstraint constraintWithItem:doneButton
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:av
attribute:NSLayoutAttributeCenterY
multiplier:1.0
constant:0];
[av addConstraint:cn4];
[av addSubview:doneButton];
self.theTextField.inputAccessoryView = av;
}
The above code is working fine except one thing, I am getting this warning in console:
The view hierarchy is not prepared for the constraint:
NSLayoutConstraint:0x7fcfc494efb0
UIButton:0x7fcfc2624420'Done'.centerX ==
UIView:0x7fcfc2624230.centerX
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(UIConstraintBasedLayout) _viewHierarchyUnpreparedForConstraint:] to debug.
And getting same warning for Y.
Please tell me what is going wrong why I am getting this warning?

Add doneButton to the av view before setting your constraints.
[av addSubview:doneButton];
[av addConstraint:cn3];
[av addConstraint:cn4];

Related

Interaction of View Created in Code with Autolayout

I have a tableview that I create in code. Previously, I assigned it absolute coordinates but as I switch over to autolayout in Storyboard- I don't know how to link it to the element above it.
Basically, the tableView is supposed to be a subview of the container view and it is supposed to be about 12 points below a textField. Can anyone suggest correct way to do this?
This is how I currently create and position the tableview.
_autocompleteTableView = [[UITableView alloc] initWithFrame:
CGRectMake(20, 200, 280, 300) style:UITableViewStylePlain];
[self.view addSubview:_autocompleteTableView];
The constraints in storyboard for the textfield above it are:
textfield.leading = superview.leading+20
textfield.trailing+20 =superview.trailing
textfield.top = elementAbove.bottom+12
textfield.height=30;
Thanks for any suggestions.
Try using anchors. It's super easy to use as simple as constraints in SB.
I think this guide is the best to show how it works. Scroll down to part with title Creating Constraints with Layout Anchors.
Hope it helps!
Try this
Declare
bool once = YES;
then
-(void)viewDidLayoutSubviews
{
if(once)
{
_autocompleteTableView = [[UITableView alloc] initWithFrame:
CGRectMake(20, 200, 280, 300) style:UITableViewStylePlain];
[self.view addSubview:_autocompleteTableView];
self.autocompleteTableView.translatesAutoresizingMaskIntoConstraints = NO;
NSLayoutConstraint* con1=[NSLayoutConstraint constraintWithItem: self.view attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.autocompleteTableView attribute:NSLayoutAttributebottom multiplier:1 constant:20];
NSLayoutConstraint* con2=[NSLayoutConstraint constraintWithItem: self.view attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.autocompleteTableView attribute:NSLayoutAttributeLeading multiplier:1 constant:-20];
NSLayoutConstraint* con3=[NSLayoutConstraint constraintWithItem: self.view attribute:NSLayoutAttributeTraling relatedBy:NSLayoutRelationEqual toItem:self.autocompleteTableView attribute:NSLayoutAttributeTraling multiplier:1 constant:20];
NSLayoutConstraint* con4=[NSLayoutConstraint constraintWithItem: self.autocompleteTableView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.textfield attribute:NSLayoutAttributeBottom multiplier:1 constant:12];
[self.view addConstraints:#[con1,con2,con3,con4]];
[self.view layoutIfNeeded];
once = NO;
}
}

Custom UIView (with xib) autolayout width and pushing viewcontroller from delegate

I have problem with setting constraints to custom UIView and pushing new ViewController by delegate when I tap the view. I tried to find solution for these problems but couldn't find one that answers to these questions.
Autolayout problem
I have custom xib-file which has been set Size: Freeform, Width: 600 and Height: 25, it also includes one label and one button with constraints in this view. I have added this view successfully below navigation bar where I want it. Problem is, that it don't make anything to fit it's width equally with navigation bar / window size (I have tried multiple choices eg. making new frame for view that is width of window / navigation bar). It only appears to have static 600 width all the time whatever I try.
First two constraints are working, it appears 25 points below navigation bar and it centers it. But last one won't make anything.
How should I do this properly? So far have this:
[self.subView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addSubview:self.subView];
[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:self.subView
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self.navBar
attribute:NSLayoutAttributeBottom
multiplier:1
constant:25.0]];
[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:self.subView
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0.0]];
[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:self.subView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeWidth
multiplier:1
constant:0]];
Should I do something more with xib-file that it will make this width to fit it's parent view? I have also implemented initWithFrame, initWithCoder and intrinsicContentSize to my custom view.
Solution
I ended up to make containerView for my subView and center it vertically and horizontally and found right constraint for width. I also forgot to update my subView's view frames to match navigation bar width. Here is what I ended up to (if there is better way to do this, I take critic with pleasure):
self.containerView = [[UIView alloc] initWithFrame:CGRectMake(0, 62, self.navBar.frame.size.width, 25)];
[self.view addSubview:self.containerView];
self.subView = [[SubView alloc]init];
[self.subView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.containerView addSubview:self.subView];
self.subView.view.frame = CGRectMake(0, 0, self.containerView.frame.size.width, self.containerView.frame.size.height);
[self.containerView addConstraint:
[NSLayoutConstraint constraintWithItem:self.subView
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self.containerView
attribute:NSLayoutAttributeCenterY
multiplier:1.0
constant:0.0]];
[self.containerView addConstraint:
[NSLayoutConstraint constraintWithItem:self.subView
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.containerView
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0.0]];
[self.containerView addConstraint:
[NSLayoutConstraint constraintWithItem:self.subView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self.containerView
attribute:NSLayoutAttributeWidth
multiplier:1.0f
constant:0]];
Delegate problem (solved)
For answer to this problem: check MisterGreen's answer below.
Another problem occured when I made UITapGestureRecognizer with delegate in my custom view. What I want is when I tap the view, it opens another ViewController. The delegate function is like this where I implement my custom view:
-(void)pushViewControllerUsingDelegate
{
NSLog(#"DELEGATE WAS : %#", self.subView.delegate);
[self pushViewController:self.anotherViewController animated:YES];
}
Now it gives exception when I tap the view:
DELEGATE WAS : <MasterViewController: 0x7fc96132e7d0> <-- Delegate is OK
*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<AnotherViewController 0x7fc961248230> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key subViewButton.'
What this actually means? I have this subViewButton IBOutlet with weak property, does it have something to do with this? Or is there another way to make this happen?
Tutorial which I followed: https://www.youtube.com/watch?v=TfKv1MYxnA4
Because there is not enough data to be exactly sure what is the problem you encountered, i have just created a code snippet that is working and doing exactly what you are trying to get.
About the constraints i think the problem is the hight constraint that is missing(unless you determined it elsewhere),
try to remember that when you add constraints provide enough data to the compiler to understand how to resize and position your subview according to it's superview, in your case it didn't know what is the hight cause you didn't supply nor bottom or hight constraint to determine it.
About the delegate method you didn't supply enough data to exactly determine what is the problem, so i've written something that i think is doing what you are trying to get.
This code snippet is tested and working:
The subview:
View.h
#protocol viewManager <NSObject>
#optional
- (void)subviewWasTapped;
#end
#interface View : UIView
#property (nonatomic, strong) id<viewManager>delegate;
#end
View.m
#implementation View
- (void)awakeFromNib{
[super awakeFromNib];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(viewWasTapped:)];
[self addGestureRecognizer:tap];
}
- (void)viewWasTapped:(NSNotification *)notification
{
[self sendViewWasTappedToDelegate];
}
- (void)sendViewWasTappedToDelegate
{
#synchronized(_delegate)
{
if([_delegate respondsToSelector:#selector(subviewWasTapped)])
{
[_delegate subviewWasTapped];
}
}
}
#end
FirstViewController:
#interface ViewController () <viewManager>
#property (nonatomic, strong) View *subview;
#end
#implementation ViewController
#synthesize subview;
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *subviewArray = [[NSBundle mainBundle] loadNibNamed:#"View" owner:self options:nil];
subview = [subviewArray objectAtIndex:0];
[subview setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addSubview:subview];
[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:subview
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.topLayoutGuide
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0.0]];
[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:subview
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0.0]];
[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:subview
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeWidth
multiplier:1
constant:0.0]];
// Height constraint to determine the
[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:subview
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1
constant:25.0]];
[self.view layoutIfNeeded];
[subview setDelegate:self];
}
#pragma mark - viewManager delegate method
- (void)subviewWasTapped{
SecondeViewController *secondeVC = [self.storyboard instantiateViewControllerWithIdentifier:#"SecondeViewController"];
[self.navigationController pushViewController:secondeVC animated:YES];
}

How to set three buttons horizontally without gap using autolayout

I have a requirement where , i try to set 3 buttons at the bottom of the view horizontally placed without any gap . I have attached a screen shot of how i need to show and another showing how it displays currently.
i am using the following constraint programatically to set this
NSDictionary *views = NSDictionaryOfVariableBindings(btnCreateAccount,btnForgotuserid,btnForgotPassword);
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:btnCreateAccount attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeBottom multiplier:1 constant:0]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[btnCreateAccount][btnForgotuserid(==btnCreateAccount)][btnForgotPassword(==btnCreateAccount)]|" options:NSLayoutFormatAlignAllBottom metrics:nil views:views]];
Please help me in solving this issue
EDIT : In iOS 7 , see the screen shot
Thanks,
Vinod.
I have tried your code and the constraints seem to work fine. The problem might be somewhere else.
This is the code I have tried, created all buttons programmatically:
UIButton *b1 = [[UIButton alloc] init];
UIButton *b2 = [[UIButton alloc] init];
UIButton *b3 = [[UIButton alloc] init];
for (UIButton *b in #[b1, b2, b3]) {
[b setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addSubview:b];
[b.layer setBorderWidth:1];
}
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:b1 attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeBottom multiplier:1 constant:0]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-0-[b1]-0-[b2(==b1)]-0-[b3(==b1)]-0-|" options:NSLayoutFormatAlignAllBottom metrics:nil views:#{ #"b1":b1, #"b2":b2, #"b3":b3 }]];
Make sure you are calling setTranslatesAutoresizingMaskIntoConstraints:NO on your buttons. If you create them in the storyboard, you need to remove the implicit constraints that are added there.
Let me know how this goes and if you need more help.

After adding constrain scrolling not work

i am trying to add button inside the scrollview with center constrain,
constrain working but scroll not working scroll become stuck,
Anyone can help me where is mistake,
-(void)viewDidLoad{
scrllview = [[UIScrollView alloc] initWithFrame:
[[UIScreen mainScreen] applicationFrame]];
scrllview.backgroundColor = [UIColor orangeColor];
self.view=scrllview;
[scrllview setContentSize:CGSizeMake(300, 1000)];
submitButton = [UIButton buttonWithType:UIButtonTypeCustom];
[submitButton setTitle:#"connect" forState:UIControlStateNormal];
[submitButton.titleLabel setFont:[UIFont fontWithName:#"HelveticaNeue-Bold" size:14.0]];
[submitButton addTarget:self
action:#selector(myMethod:)
forControlEvents:UIControlEventTouchUpInside];
submitButton.backgroundColor = [UIColor blackColor];
submitButton.translatesAutoresizingMaskIntoConstraints = NO ;
[scrllview addSubview:submitButton];
NSLayoutConstraint *constraint = [NSLayoutConstraint
constraintWithItem:submitButton
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:scrllview
attribute:NSLayoutAttributeCenterX
multiplier:1.0f
constant:0.0f];
[scrllview addConstraint:constraint];
constraint = [NSLayoutConstraint
constraintWithItem:submitButton
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:scrllview
attribute:NSLayoutAttributeCenterY
multiplier:1.0f
constant:0.0f];
[scrllview addConstraint:constraint];
}
While you are using scrollview with autolayout, you should not use contentsize.
You can create a view inside scroll view, which should have your required height and constraint relative to scrollview. You can add your button inside that view.
It will work. I have implemented it with xib. Same issue while adding control directly in scrollview. I used view inside scrollview and added required control to view.
Auto layout UIScrollView with subviews with dynamic heights

Autolayout stops UITapGestureRecognizer?

I setup a UIImageView to accept tap gesture, which is tapped, the button shall go hidden and shows a UIActivityIndicatorView at the same position. The logic is very simple. but I have two problems:
I need to add either [fb setTranslatesAutoresizingMaskIntoConstraints:NO]; or [loading setTranslatesAutoresizingMaskIntoConstraints:NO]; to avoid constraint conflict. in order to be safe, i added both. seems UIImageView and UIActivityIndicatorView have their own builtin constraints:
"<NSLayoutConstraint:0x8d69e80 UIImageView:0x8d68700.centerX == UIView:0x8d626f0.centerX>",
"<NSLayoutConstraint:0x8d6a0f0 UIActivityIndicatorView:0x8d629b0.centerX == UIView:0x8d626f0.centerX>",
"<NSAutoresizingMaskLayoutConstraint:0x8b48d30 h=--& v=--& UIActivityIndicatorView:0x8d629b0.midX == + 18.5>",
"<NSAutoresizingMaskLayoutConstraint:0x8b495f0 h=--& v=--& UIImageView:0x8d68700.midX == + 60>"
I have to comment off either the line [fb
setTranslatesAutoresizingMaskIntoConstraints:NO]; or [loading
setTranslatesAutoresizingMaskIntoConstraints:NO];. otherwise, the UITapGestureRecognizer doesn't work anymore, why?
code:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
// setup the loading activity indicator;
loading = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
[self.view addSubview:loading];
loading.hidden = YES;
// setup the fb button
fb = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"facebook.png"]];
fb.userInteractionEnabled = YES;
UITapGestureRecognizer* tap = [[UITapGestureRecognizer alloc] init];
[tap addTarget:self action:#selector(login:)];
[fb addGestureRecognizer:tap];
[self.view addSubview:fb];
fb.hidden = YES;
NSLayoutConstraint *c;
c = [NSLayoutConstraint constraintWithItem:fb
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1
constant:0];
[self.view addConstraint:c];
c = [NSLayoutConstraint constraintWithItem:fb
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterY
multiplier:1
constant:0];
[self.view addConstraint:c];
c = [NSLayoutConstraint constraintWithItem:loading
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1
constant:0];
[self.view addConstraint:c];
c = [NSLayoutConstraint constraintWithItem:loading
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterY
multiplier:1
constant:0];
[self.view addConstraint:c];
[fb setTranslatesAutoresizingMaskIntoConstraints:NO];
[loading setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view setTranslatesAutoresizingMaskIntoConstraints:NO];
}
You need to call setTranslatesAutoresizingMaskIntoConstraints:NO because the autoresizing mask gets converted to constraints which conflict with the autolayout system. You usually cannot add auto layout constraints to views with autoresizing masks constraints or you'll get into incompatible constraints.
The main issue in your case is that the width and height of those issues cannot be determined. You need to add width and height constraints as well. The layout can't be determined using the centering constraints if the auto layout system can't calculate them.

Resources