How to get constraints from UIView Programmatically - ios

I want get UILabel constraints from UIView but I can't get any constraints.
I set the constraints in CustomView.m like this:
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
_titleLabel = [UILabel new];
[self addSubview:_titleLabel];
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
_titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
NSLayoutConstraint *titleLabelBottom = [NSLayoutConstraint constraintWithItem:_titleLabel
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeCenterY
multiplier:1
constant:0];
[self addConstraints:#[titleLabelBottom]];
...more code
}
in ViewController.m
CustomView *view = [CustomView alloc] initWithFrame:viewFrame];
NSLog(#"%#",view.titleLabel.constraints); // nil

you can get constraints in NSArray like,
NSArray *constraintArr = self.backButton.constraints;
NSLog(#"cons : %#",constraintArr);
And you can set instance variable like,
NSLayoutConstraint *titleLabelBottom;
and then use,
titleLabelBottom = [NSLayoutConstraint constraintWithItem:_titleLabel
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeCenterY
multiplier:1
constant:0];
[self addConstraints:#[titleLabelBottom]];
so, you can use titleLabelBottom anywhere in class.
hope this will help :)

You are getting nil because the constraint has not been created yet.
Try logging your constraints in:
- (void)viewDidLayoutSubviews;
Assumming that constraint is essential to your CustomView, you should create that constraint in your initWithFrame method method.

Related

IOS/Objective-C: Center Line under Button Programmatically

I am trying to do a simple line under three buttons to create a Tabs effect. While I could do this in Storyboard, I'd like to do it in code and ideally make the line a subview of an element already in the view to avoid having to give it special constraints.
My approach has been to create a UILabel with no text and a background color. I can make the UILabel a subview of the View, however, that does not attach it to the bottom of the buttons.
On the other hand, if I make the UILabel a subview of one of the buttons, I can't see it.
Can anyone suggest a simple way to do this?
[centerButton setTitle:#"Favorites" forState:UIControlStateNormal];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(20, 120, 280, 2)];
label.backgroundColor = [UIColor redColor];
label.textAlignment = NSTextAlignmentCenter;
label.numberOfLines = 1;
label.text = #"";
[self.view addSubview: label];
Thanks in advance for any suggestions.
Edit:
I tried adding some NSLayoutConstraints Programmatically:
NSLayoutConstraint *con3 = [NSLayoutConstraint
constraintWithItem:label attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual toItem:centerButton
attribute:NSLayoutAttributeBottom multiplier:1 constant:0];
[label addConstraints:#[con3]];
//tried with and without
[self.view layoutIfNeeded];
This returned the exception:
[LayoutConstraints] The view hierarchy is not prepared
for the constraint: <NSLayoutConstraint:0x174e85500 V:
[UIButton:0x100b0baf0'Now']-(0)-[UILabel:0x108555160]
(inactive)>
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.
The button created in storyboard is a subview of self.view and I add the label to self.view as a subview in code so not sure why the exception is occurring.
I wonder from the error message if you're possibly attempting to add the NSLayoutConstraint prior to the redLabel view being added to it's parentView (or too early in the lifecycle). In any event, I think this is pretty close to what you're attempting to accomplish:
#import "ViewController.h"
#interface ViewController ()
#property (strong, nullable) UILabel *previousRedLabel;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (IBAction)buttonClicked:(id)sender {
if ([sender isKindOfClass:[UIButton class]]) {
UIButton *clickedButton = (UIButton *)sender;
UIView *buttonSuperview = [clickedButton superview];
if (buttonSuperview != nil) {
[self _putRedLineWithHeight:3.0f atTheBottomOfView:buttonSuperview animate:YES];
}
}
}
- (void)_putRedLineWithHeight:(CGFloat)height atTheBottomOfView:(UIView *)viewToPutUnder animate:(BOOL)animate {
// remove our previous red line
if (self.previousRedLabel) {
// if you want it to be a no-op here if they click the same button
// you'll need to add some logic to check if the superView == viewToPutUnder
[self.previousRedLabel removeFromSuperview];
self.previousRedLabel = nil;
}
UILabel *redLabel = [[UILabel alloc] init];
// we're using autolayout so we don't want any resizing from it
redLabel.translatesAutoresizingMaskIntoConstraints = NO;
redLabel.backgroundColor = [UIColor redColor];
// start out with alpha = 0
redLabel.alpha = 0.0f;
// add it to our parentView
[viewToPutUnder addSubview:redLabel];
// height (determined by passed in value)
NSAssert(height >= 0, #"Height must be a positive number");
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:redLabel attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0f constant:height];
// width equal to parentView's width
NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:viewToPutUnder attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:redLabel attribute:NSLayoutAttributeWidth multiplier:1.0f constant:0.0f];
// center x == parentView's center x
NSLayoutConstraint *centerConstraint = [NSLayoutConstraint constraintWithItem:viewToPutUnder attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:redLabel attribute:NSLayoutAttributeCenterX multiplier:1.0f constant:0.0f];
// now the bottom constraint (place it at the bottom of the parent view)
NSLayoutConstraint *bottomConstraint = [NSLayoutConstraint constraintWithItem:viewToPutUnder attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:redLabel attribute:NSLayoutAttributeBottom multiplier:1.0f constant:0.0f];
// add the height constraint to our label
[redLabel addConstraint:heightConstraint];
// and all the other constraints to our parent view
[viewToPutUnder addConstraints:#[widthConstraint, centerConstraint, bottomConstraint]];
redLabel.alpha = 1.0f;
if (animate) {
[UIView animateWithDuration:0.6f animations:^{
[redLabel layoutIfNeeded];
}];
}
self.previousRedLabel = redLabel;
}
Example of Animated:
And one of Non-Animated:
EDITED ANSWER TO HANDLE THE CASE IF EACH BUTTON ISN'T IN IT'S OWN SUPERVIEW
Adjusted for if all buttons are in one superview (width based on button width, center to button center, and pinning the top of the label to the bottom of the button)
#import "ViewController.h"
#interface ViewController ()
#property (strong, nullable) UILabel *previousRedLabel;
- (void)_putRedLineWithHeight:(CGFloat)height atTheBottomOfButton:(UIButton *)button animate:(BOOL)animate;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (IBAction)buttonClicked:(id)sender {
if ([sender isKindOfClass:[UIButton class]]) {
UIButton *clickedButton = (UIButton *)sender;
// if you want it to be a no-op here if they click the same button
// you'll need to add some logic to store the previous clicked button and check whether it's the same button
[self _putRedLineWithHeight:3.0f atTheBottomOfButton:clickedButton animate:YES];
}
}
- (void)_putRedLineWithHeight:(CGFloat)height atTheBottomOfButton:(UIButton *)button animate:(BOOL)animate {
UIView *buttonSuperview = button.superview;
NSAssert(buttonSuperview != nil, #"Button has to have a superview");
// remove our previous red line
if (self.previousRedLabel) {
[self.previousRedLabel removeFromSuperview];
self.previousRedLabel = nil;
}
UILabel *redLabel = [[UILabel alloc] init];
// we're using autolayout so we don't want any resizing from it
redLabel.translatesAutoresizingMaskIntoConstraints = NO;
redLabel.backgroundColor = [UIColor redColor];
// start out with alpha = 0
redLabel.alpha = 0.0f;
// add it to our parentView
[buttonSuperview addSubview:redLabel];
// height (determined by passed in value)
NSAssert(height >= 0, #"Height must be a positive number");
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:redLabel attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0f constant:height];
// width equal to button's width
NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:redLabel attribute:NSLayoutAttributeWidth multiplier:1.0f constant:0.0f];
// center x == button's center x
NSLayoutConstraint *centerConstraint = [NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:redLabel attribute:NSLayoutAttributeCenterX multiplier:1.0f constant:0.0f];
// now pin the top of the label to the bottom of the button
NSLayoutConstraint *bottomConstraint = [NSLayoutConstraint constraintWithItem:redLabel attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:button attribute:NSLayoutAttributeBottom multiplier:1.0f constant:0.0f];
// add the height constraint to our label
[redLabel addConstraint:heightConstraint];
// and all the other constraints to our parent view
[buttonSuperview addConstraints:#[widthConstraint, centerConstraint, bottomConstraint]];
redLabel.alpha = 1.0f;
if (animate) {
[UIView animateWithDuration:0.6f animations:^{
[redLabel layoutIfNeeded];
}];
}
self.previousRedLabel = redLabel;
}
#end
Animated:
Not Animated:

Constraints programmatically with Objective C

I don't know what I'm doing wrong: I'm creating a UIView that occupies all the screen (it has already constraints) and then, programmatically I'm creating an UI Image View:
_panel = [[UIImageView alloc] initWithImage:[self loadImageForKey:#"registerPanel"]];
_panel.frame = CGRectMake(0, 0, 100, 100);
_panel.exclusiveTouch = YES;
_panel.userInteractionEnabled = YES,
[self.scrollView addSubview:_panel];
And here it comes the problem: I'm adding constraints to the panel I created but it crashes (I'm doing it on the ViewWillAppear):
NSLayoutConstraint *centreHorizontallyConstraint = [NSLayoutConstraint
constraintWithItem:_panel
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0];
NSLayoutConstraint *centreVerticalConstraint = [NSLayoutConstraint
constraintWithItem:_panel
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0];
[_panel addConstraint:centreHorizontallyConstraint];
[_panel addConstraint:centreVerticalConstraint];
Error message:
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.
You can constrain a scrollView's subview to the scrollView's parent (self.view in this case), but that's probably not what you want.
Edit: For clarification, the reason you were getting the error was because you initialize your constraints:
toItem:self.view
and then you try to add them:
[_panel addConstraint:centreHorizontallyConstraint];
[_panel addConstraint:centreVerticalConstraint];
You want to add them to the toItem object:
[self.view addConstraint:centreHorizontallyConstraint];
[self.view addConstraint:centreVerticalConstraint];
Again, you probably don't want to center _panel in the main view, but this will compile and run:
#import "AddPanelScrollViewController.h" /// just default .h
#interface AddPanelScrollViewController ()
#property (strong, nonatomic) UIScrollView *scrollView;
#property (strong, nonatomic) UIImageView *panel;
#end
#implementation AddPanelScrollViewController
- (void)viewDidLoad {
[super viewDidLoad];
_scrollView = [UIScrollView new];
_scrollView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:_scrollView];
[_scrollView.topAnchor constraintEqualToAnchor:self.view.topAnchor constant:20.0].active = YES;
[_scrollView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor constant:-20.0].active = YES;
[_scrollView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:20.0].active = YES;
[_scrollView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-20.0].active = YES;
_scrollView.backgroundColor = [UIColor blueColor];
_panel = [UIImageView new];
// required
_panel.translatesAutoresizingMaskIntoConstraints = NO;
[self.scrollView addSubview:_panel];
// frame will be ignored when using auto-layout / constraints
// _panel.frame = CGRectMake(0, 0, 100, 100);
_panel.exclusiveTouch = YES;
_panel.userInteractionEnabled = YES;
_panel.backgroundColor = [UIColor redColor];
// _panel needs width and height constraints
[_panel.widthAnchor constraintEqualToConstant:100.0].active = YES;
[_panel.heightAnchor constraintEqualToConstant:100.0].active = YES;
NSLayoutConstraint *centreHorizontallyConstraint = [NSLayoutConstraint
constraintWithItem:_panel
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0];
NSLayoutConstraint *centreVerticalConstraint = [NSLayoutConstraint
constraintWithItem:_panel
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterY
multiplier:1.0
constant:0];
// if constraints are releated to "self.view" that's where they need to be added
[self.view addConstraint:centreHorizontallyConstraint];
[self.view addConstraint:centreVerticalConstraint];
}
First you can't create constraints between panel & self.view because there is no common parent , instead you want to create them with the scrollview
NSLayoutConstraint *centreHorizontallyConstraint = [NSLayoutConstraint
constraintWithItem:_panel
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.scrollView
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0];
NSLayoutConstraint *centreVerticalConstraint = [NSLayoutConstraint
constraintWithItem:_panel
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self.scrollView
attribute:NSLayoutAttributeCenterY
multiplier:1.0
constant:0];
[_scrollView addConstraint:centreHorizontallyConstraint];
[_scrollView addConstraint:centreVerticalConstraint];
Also both constraints are centerX , you need also width & height , or better top , leading , trailing and bottom to scrollView ,,, with width and height static or proportional to self.view
//
Also for any view you want to add constraints programmatically you must set
[self.scrollView setTranslatesAutoresizingMaskIntoConstraints: NO];
[self.panel setTranslatesAutoresizingMaskIntoConstraints: 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];
}

Add subview to UITextView and using autolayout

I have problem with adding subview to UITextView using autoLayout:
in SubTextView : UITextView .m file:
- (instancetype)initWithFrame:(CGRect)frame
{
if(self = [super initWithFrame:frame]) {
_baseline = [[UIView alloc] init];
[self addSubview:_baseline];
[self setBackgroundColor: [UIColor blackColor]];
_baseline.translatesAutoresizingMaskIntoConstraints = NO;
[self setNeedsUpdateConstraints];
}
}
- (void)updateConstraints
{
[super updateConstraints];
NSLayoutConstraint *constraint = nil;
constraint = [NSLayoutConstraint constraintWithItem:self.baseline
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeTrailing
multiplier:1.f
constant:0.f];
[self addConstraint:constraint];
constraint = [NSLayoutConstraint constraintWithItem:self.baseline
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeLeading
multiplier:1.f
constant:0.f];
[self addConstraint:constraint];
constraint = [NSLayoutConstraint constraintWithItem:self.baseline
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeCenterY
multiplier:1.f
constant:0.f];
[self addConstraint:constraint];
}
But, this do not work... the self.baseline's frame is always (0, 0, 0, 0)...
Anyone can help?
Edited:
I set a break point at layout subviews, at that time, when layoutSubviews the subviews are inited, and the UITextView is :
<InventoryOverviewListingNameTextView: 0x7fd92b2a1800;
baseClass = UITextView;
frame = (234 36; 399 70.2);
text = '11111111';
clipsToBounds = YES;
gestureRecognizers = <NSArray: 0x7fd92cbd8ca0>;
layer = <CALayer: 0x7fd92cb08900>;
contentOffset: {-0, 10};
contentSize: {399, 70.199997425079346}>
But the frame of the baseline is:
<UIView: 0x7fd92cb90710;
frame = (0 -10; 399 0.5);
layer = <CALayer: 0x7fd92cb907e0>>.
Do not know how to make the y = -10 to y = 69.7.
I feel it is a little like scrollView, to which I solve by add a UIView as contentView on UIScrollView, and UIScrollView itself is added on another UIView. Then I set the contentView's top to scrollView's top, contentView's heigh, and !!!(this is important or autoLayout won't work properly)
contentView's left and right to UIView rather than scrollView.
But I do not see it works for UITextView...
Anyone can help?
The frame will always be (0,0,0,0) because that is the default one that comes whit [[UIView alloc] init].
Have you tried defining the view setting a frame?
- (instancetype)initWithFrame:(CGRect)frame
{
if(self = [super initWithFrame:frame]) {
//Add a frame, does not matter the size
_baseline = [[UIView alloc] initWithFrame:CGRectMake(0,0,20,20)];
//Just to make sure it's there let us give it a color
[_baseline setBackgroundColor:[UIColor green]];
[self addSubview:_baseline];
//Don't run this line just yet.
//[self setNeedsUpdateConstraints];
}
}
If that works, then we found the problem, if it does not, maybe the problem is on the - (void)updateConstraints method.
UPDATE:
So, I sat down and ran your code, I got a few crashes but eventually I managed to make the black box apear inside the text view. I found that the box was not instantiated when the view was looking to layout its subviews, so I tinkered around and here goes, this works, it throws a few constraints warnings but I guess you can work those by yourself.
- (instancetype)initWithFrame:(CGRect)frame
{
if(self = [super initWithFrame:frame]) {
}
return self;
}
-(void)layoutSubviews{
[super layoutSubviews];
if (!_baseline) {
_baseline = [[UIView alloc] init];
[self addSubview:_baseline];
[self setBackgroundColor: [UIColor blackColor]];
[self setNeedsUpdateConstraints];
}
}
- (void)updateConstraints {
[super updateConstraints];
if (_baseline) {
NSLayoutConstraint *constraint = nil;
constraint = [NSLayoutConstraint constraintWithItem:self.baseline
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeTrailing
multiplier:1.f
constant:0.f];
[self addConstraint:constraint];
constraint = [NSLayoutConstraint constraintWithItem:self.baseline
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeLeading
multiplier:1.f
constant:0.f];
[self addConstraint:constraint];
constraint = [NSLayoutConstraint constraintWithItem:self.baseline
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeCenterY
multiplier:1.f
constant:0.f];
[self addConstraint:constraint];
}
}

Automatically resized imageview

I have a scrollView and imageView in it.
The app works in landscape and portrait mode.
And i use auto layout.
If i use constant for imageview.
images that I loaded into imageviev it automatically expands under its size.
What i have now:
Landscape mode:
and Portrait mode:
I want to make it as in these pictures
Landscape mode:
and Portrait mode:
How do I fix the auto resize in imageview?
P.S. App for iPad
Thank you all for answers!
Here is an example of code based Autolayout that should help you
#import "UniversalViewController.h"
#interface UniversalViewController ()
#property (strong, nonatomic) UIImageView *myImageView;
#end
#implementation UniversalViewController
#synthesize myImageView;
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor blackColor];
UIImage *myImage = [UIImage imageNamed:#"Velvet_Underground_and_Nico.jpg"];
myImageView = [[UIImageView alloc] initWithImage:myImage];
[myImageView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addSubview:myImageView];
[self setWidth:300 andHeight:300 toView:myImageView];
[self centerView1:myImageView toView2:self.view];
}
- (void) willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
[self.view removeConstraints:self.view.constraints];
[self centerView1:myImageView toView2:self.view];
if (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft || toInterfaceOrientation == UIInterfaceOrientationLandscapeRight) {
[self setWidth:450 andHeight:250 toView:myImageView];
} else {
[self setWidth:300 andHeight:300 toView:myImageView];
}
}
#pragma mark custom Autolayout methods
- (void) setWidth:(float)width andHeight:(float)height toView:(UIView *)view {
NSLayoutConstraint *myConstraint;
myConstraint = [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:width];
[self.view addConstraint:myConstraint];
myConstraint = [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:height];
[self.view addConstraint:myConstraint];
}
- (void) centerView1:(UIView *)view1 toView2:(UIView *)view2 {
NSLayoutConstraint *myConstraint;
myConstraint = [NSLayoutConstraint constraintWithItem:view1 attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:view2 attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0];
[self.view addConstraint:myConstraint];
myConstraint = [NSLayoutConstraint constraintWithItem:view1 attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:view2 attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0];
[self.view addConstraint:myConstraint];
}
You should be able to set the size of the images as you require... I hope it helps...

Resources