Positioning UIView in UIViewController using NSLayoutConstraints causes aliased UILabel? - cocoa-touch

I'm trying to manually animate in a UIView, contained in a UIViewController, from the top of the screen. This view will have a height of 150px.
I setup layout constraints for when it's "collapsed" and when it's "expanded" (shown). I animate between the two constraints when I want to hide/show, respectively.
I setup the view contained in this UIViewController in IB with a simple UILabel in the upper-left of the view.
CGFloat closeUpViewHeight = 150.f;
self.closeUpViewController = [[[CloseUpViewController_Phone alloc] initWithNibName:#"CloseUpViewController_Phone"
bundle:nil]
autorelease];
[self addChildViewController:self.closeUpViewController];
[self.view addSubview:self.closeUpViewController.view];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.closeUpViewController.view
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeHeight
multiplier:0.f
constant:closeUpViewHeight]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.closeUpViewController.view
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeWidth
multiplier:1.f
constant:0.f]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.closeUpViewController.view
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1.f
constant:0.f]];
self.expandedCloseUpViewConstraint = [NSLayoutConstraint constraintWithItem:self.closeUpViewController.view
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeTop
multiplier:0.f
constant:0.f];
self.collapsedCloseUpViewConstraint = [NSLayoutConstraint constraintWithItem:self.closeUpViewController.view
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeTop
multiplier:1.f
constant:-1.f];
Anyone know why the resulting UILabel has aliasing problems?

Fixed my problem, pretty dumb mistake.
Inside the setup for my view controller, I added a drop shadow to the UIView and accidentally had carried over this:
self.view.layer.masksToBounds = NO;
self.view.layer.shadowOffset = CGSizeMake(0, 2);
self.view.layer.shadowRadius = 3;
self.view.layer.shadowOpacity = 0.8;
self.view.layer.shouldRasterize = YES; //<--- Don't rasterize.
Removing the shouldRasterize line fixes the issue here.

Related

Xcode version to 9, iOS11 and works fine except iPhone x. UI got collapsed

I have Created Two UIView one headerview and second is UICollectionView both subview of UIScrollView and I have hidden my NavigationBar entire app.
Now I have added below code for set "SafeAreaLayoutGuides" in iOS11 but here application crass due to superview nil found.
I have added below code.
UIView *parentView = self.view.superview;
UIView *childView = scrollViewMain.superview;
childView.translatesAutoresizingMaskIntoConstraints = NO;
NSLayoutConstraint *topConstraint;
NSLayoutConstraint *bottomConstraint;
if (#available(iOS 11, *)) {
topConstraint = [NSLayoutConstraint constraintWithItem:childView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:parentView.safeAreaLayoutGuide attribute:NSLayoutAttributeTop multiplier:1.0 constant:0];
bottomConstraint = [NSLayoutConstraint constraintWithItem:childView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:parentView.safeAreaLayoutGuide attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0];
} else {
topConstraint = [NSLayoutConstraint constraintWithItem:childView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:parentView attribute:NSLayoutAttributeTop multiplier:1.0 constant:0];
bottomConstraint = [NSLayoutConstraint constraintWithItem:childView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:parentView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0];
}
[parentView addConstraint:topConstraint];
[parentView addConstraint:bottomConstraint];
If parentView is nil (because self.view.superview is nil), then that means that self.view hasn't been added to the view hierarchy yet.
Make sure that whatever view contains this one has called self.addSubview(childView); that'll fix the "nil superview" issue.

Setting auto layout constraints programmatically on two views in ViewController

I am trying to add two UIView on ViewController. One of them is plain UIView and other is UITableView. I am not quite sure how to set height constraint on them in such away that UITableView will be of height required for its content and other view can resize itself based on UITableView height.
Below is the simple sketch and code I am using for setting up those constraints.
NSDictionary *views = NSDictionaryOfVariableBindings(_infoView,_infoTableView);
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"[_infoView]"
options:0
metrics:nil
views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[_infoView]-[_infoTableView]"
options:0
metrics:nil
views:views]];
// Width constraint
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.infoView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeWidth
multiplier:1.0
constant:0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.infoTableView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeWidth
multiplier:1.0
constant:0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.infoView
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.infoTableView
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0]];
Any comments / feedback would be highly appreciated.
Thanks.
You are on the right track. The trick here is that you need to dynamically update the height of tableView to its contentSize.height so infoView will know it should either be vertically expanded or shrunk:
First you define a property that will hold the height constraint:
#property (nonatomic, strong) NSLayoutConstraint *tableViewHeightConstraint;
- (void)viewDidLoad {
...
self.tableViewHeightConstraint = [NSLayoutConstraint constraintWithItem:self.tableView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:0.0
constant:350.0];
}
Then you can update the height constraint right after tableView knows its contentSize (ex: viewDidAppear:animated):
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
self.tableViewHeightConstraint.constant = self.tableView.contentSize.height;
[self.view layoutIfNeeded];
}
I created a sample project for you so you can play around with it to have a better understanding about what I intend to do.

Adding subview to tableview using autolayout

I was trying to add a subview to tableview using autolayout, but I get this Assertion failure when I do so.
" * Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Auto Layout still required after executing -layoutSubviews. UITableView's implementation of -layoutSubviews needs to call super.'"
My code
UIView *view = [[UIView alloc] init];
[view setBackgroundColor:[UIColor redColor]];
[parentView addSubview:view];
[view setTranslatesAutoresizingMaskIntoConstraints:NO];
NSLayoutConstraint *c1 = [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:parentView attribute:NSLayoutAttributeLeft multiplier:1.0 constant:0.0];
NSLayoutConstraint *c2 = [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:parentView attribute:NSLayoutAttributeLeft multiplier:0.0 constant:10.0];
NSLayoutConstraint *c3 = [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:parentView attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0.0];
NSLayoutConstraint *c4 = [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:parentView attribute:NSLayoutAttributeTop multiplier:1.0 constant:50.0];
[parentView addConstraint:c1];
[parentView addConstraint:c3];
[parentView addConstraint:c2];
[parentView addConstraint:c1];
but if I set frame to the view without any autolayout constraint, it works fine.
I couldn't figure out what I am missing.

Autolayout when adding subview to UIWindow

I am creating a customalertview that needs to be added as a subview to UIWindow. I've setup the constraints for it's subviews correctly but I'm confused as to when/how to set the constraints on the view itself in relation to the window.
I would like the width of the alertview to be 70% of the width of the screen. The height is already in relation to it's subviews.
- (void)show {
UIWindow *window = [[UIApplication sharedApplication] keyWindow];
[window addSubview:self];
[self addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:[UIApplication sharedApplication].keyWindow attribute:NSLayoutAttributeWidth multiplier:0.7 constant:0]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:_messageLabel attribute:NSLayoutAttributeBottom multiplier:1.0 constant:10]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:(_imageView.image ? _imageView : _titleLabel) attribute:NSLayoutAttributeTop multiplier:1.0 constant:-10]];
}
What am I doing wrong? I am getting the following error message:
NSLayoutConstraint:0x181cd7c0 WFSoftAlertView:0x16e384c0.width == 0.7*UIWindow:0x16e7c8c0.width>
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.
WFSoftAlertView.translatesAutoresizingMaskIntoConstraints=NO;
NSDictionary *views = NSDictionaryOfVariableBindings(WFSoftAlertView);
float width30 = self.view.frame.size.width*.3;
width30 = width30/2; // divided by 2 for padding of its left/rigth
NSString *str =[NSString stringWithFormat:#"%f",width30];
NSDictionary *metrics = #{#"width":str};
[self.view addSubview:WFSoftAlertView];
//add the WFSoftAlertView in center(x) of superview with a padding of "width"
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|-width-[WFSoftAlertView]-width-|" options:NSLayoutFormatAlignAllTop | NSLayoutFormatAlignAllBottom metrics:metrics views:views]];
// if you want center(y)
NSLayoutConstraint *centerY = [NSLayoutConstraint
constraintWithItem:WFSoftAlertView
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterY
multiplier:1.0f
constant:0.f];
Try changing your constraintWithItem from
self
to
self.view

UIView Element Layout Constraints

I'm fairly new to iOS development but have reached the point where I want to create my own composite UIView as a custom UIButton. I would like to layout a UILabel and 2x UIImageViews as follows;
But I would also like to anchor the controls (subviews) in such a way that as the label expands, due to translations say, the view automatically handles the extra real estate. For example;
Ideally -
the right hand side UIView is anchored to the right; and remains a fixed width/hight (right aligned).
the label and bottom image divide the remaining left hand space
the label is vertically centered in the top half of the upper remaining space
the bottom image remains centered (both vertically and horizontally) in the lower remaining space
if the label is wider than the bottom image then the view should expand
I'm happy to construct this in pure code if required. I used a XIB in the above images to play with attributes and to visualize my question.
I'm from a C#/XAML background so I would typically use grid layouts with fixed/auto/* columns and rows but I'm guessing I need to use something like NSLayoutConstraints here - unfortunately I don't know where to start or how to search for the solution. Any help appreciated!
in your UIButton
(code not tested)
//put the code below in your button's init method
UILabel *label = [[UILabel alloc] init];
UIImageView *bottomImageView = [[UIImageView alloc] init];
UIImageView *rightImageView = [[UIImageView alloc] init];
// we define our own contraints,
// we don't want the system to fall-back on UIView's autoresizingMask property (pre-iOS 6)
self.translatesAutoresizingMaskIntoConstraints = NO;
label.translatesAutoresizingMaskIntoConstraints = NO;
bottomImageView.translatesAutoresizingMaskIntoConstraints = NO;
rightImageView.translatesAutoresizingMaskIntoConstraints = NO;
//fixed sizes => SET THESE as you want
CGFloat labelHeight, bottomWidth, rightImageWidth;
// 1 - Label constraints :
// labelWidth = 1 *self.width - rightImageWidth
NSLayoutConstraint *labelWidth = [NSLayoutConstraint constraintWithItem:label
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeWidth
multiplier:1
constant:(- rightImageWidth)];
// label must be at the top :
NSLayoutConstraint *labelTop = [NSLayoutConstraint constraintWithItem:label
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeTop
multiplier:1
constant:0];
//label must be on the left border
NSLayoutConstraint *labelLeft = [NSLayoutConstraint constraintWithItem:label
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeLeft
multiplier:1
constant:0];
//label must be of 1/2 height
NSLayoutConstraint *labelHeight = [NSLayoutConstraint constraintWithItem:label
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeHeight
multiplier:0.5
constant:0];
[self addSubview:label];
[self addConstraints:#[labelWidth, labelTop, labelLeft, labelHeight]];
//2 - botom view
// width constant
NSLayoutContraint *bottomWidth = [NSLayoutConstraint constraintWithItem:bottomImageView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:0
constant:bottomWidth];
// same height constraint as label
NSLayoutConstraint *bottomHeight = [NSLayoutConstraint constraintWithItem:bottomImageView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeHeight
multiplier:0.5
constant:0];
// sticks at container's bottom (pun intended)
NSLayoutConstraint *bottomBottom = [NSLayoutConstraint constraintWithItem:bottomImageView
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeBottom
multiplier:1
constant:0];
//we have height, width, y contraints, just x remains
// NOTE : this one is between bottom view and label
NSLayoutConstraint *bottomCenteredXAsLabel = [NSLayoutConstraint constraintWithItem:bottomImageView
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:label
attribute:NSLayoutAttributeCenterX
multiplier:1
constant:0];
[self addSubview:bottomImageView];
[self addConstraints:#[bottomWidth, bottomHeight, bottomBottom, bottomCenteredXAsLabel]];
// 3 - last one !
NSLayoutConstraint *rightAligned = [NSLayoutConstraint constraintWithItem:rightImageView
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeRight
multiplier:1
constant:0];
//right height
NSLayoutConstraint *rightHeight = [NSLayoutConstraint constraintWithItem:rightImageView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeHeight
multiplier:1
constant:0];
//constant width...
NSLayoutConstraint *rightWidth = [NSLayoutConstraint constraintWithItem:rightImageView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:0
constant:rightImageWidth];
//width, height, x constraints...
//we still need one on y, let's say it sticks at the top
NSLayoutConstraint *rightTop = [NSLayoutConstraint constraintWithItem:rightImageView
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeTop
multiplier:1
constant:0];
[self addSubview:rightImageView];
[self addConstraints:#[rightAligned, rightHeight, rightWidth, rightTop]];
Et voilĂ  !
The method is always the same : you need at least 4 constraints on each view, setting width, height, x, and y (CGRect 4 dimensions).
Think of a constraint as a relation :
item1.layoutAttribute1 >= a*item2.layoutAttribute2 + b
to translate in this form
[NSLayoutConstraint constraintWithItem:item1
attribute:layoutAttribute1
relatedBy:NSLayoutRelationGreaterThanOrEqual
toItem:item2
attribute:layoutAttribute2
multiplier:a
constant:b];
Note that using visual format, you might express all that with less code. (but I've not played with it yet).