I need to locate a subview vertical align middle in a view without hardcode the top value 150 using constraint programmatically. I wish to achieve something like below:
Below is my code so far, Please advise.
import UIKit
class MainViewController: UIViewController {
var viewInner:UIView = UIView()
var viewOuter:UIView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
viewInner.backgroundColor = UIColor.redColor()
viewOuter.backgroundColor = UIColor.purpleColor()
viewOuter.frame = CGRectMake(100, 100, 400, 400)
viewInner.setTranslatesAutoresizingMaskIntoConstraints(false)
let viewsDictionary = ["viewInner":viewInner]
viewOuter.addSubview(viewInner)
self.view.addSubview(viewOuter)
//Add Constraint
var constOuterH = NSLayoutConstraint.constraintsWithVisualFormat("H:|-10-[viewInner(>=200)]-10-|", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDictionary)
var constOuterV = NSLayoutConstraint.constraintsWithVisualFormat("V:|-150-[viewInner(100)]", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDictionary)
viewOuter.addConstraints(constOuterH)
viewOuter.addConstraints(constOuterV)
}
}
You need to use the non-visual formatting way of adding constraints:
[self.view addConstraint:[NSlayoutConstraint constraintWithItem:self.viewOuter attribute:NSLayoutAttributeCenterY relateBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterY .......]];
to center align things.
The above type of "relational constraints" (it's what I personally call them) is what I tend to use for specifying relations between two views e.g. center alignment, relative positioning.
Visual Formatting language on the other hand is more useful for pinning views to each other from my own observation.
Here's a demo app:
ViewController.h
#interface ViewController : UIViewController
#property (nonatomic, strong) UIView *outerView;
#property (nonatomic, strong) UIView *innerView;
#property (nonatomic, strong) NSLayoutConstraint *outerViewHeightConstraint;
#end
ViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self initViews];
[self initConstraints];
// change height of outerView after 3 seconds
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self changeHeight];
});
}
-(void)changeHeight
{
self.outerViewHeightConstraint.constant = 150;
[UIView animateWithDuration:1.0 animations:^{
[self.innerView layoutIfNeeded];
[self.view layoutIfNeeded];
}];
}
-(void)initViews
{
self.outerView = [[UIView alloc] init];
self.outerView.backgroundColor = [UIColor purpleColor];
self.innerView = [[UIView alloc] init];
self.innerView.backgroundColor = [UIColor redColor];
[self.outerView addSubview:self.innerView];
[self.view addSubview:self.outerView];
}
-(void)initConstraints
{
self.outerView.translatesAutoresizingMaskIntoConstraints = NO;
self.innerView.translatesAutoresizingMaskIntoConstraints = NO;
id views = #{
#"outerView": self.outerView,
#"innerView": self.innerView
};
// outer view constraints
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:[outerView(300)]" options:0 metrics:nil views:views]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.outerView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.outerView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0]];
// give outerView a default height e.g. 300
// note we can animate the height of outerview later using this var
self.outerViewHeightConstraint = [NSLayoutConstraint constraintWithItem:self.outerView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeHeight multiplier:1.0 constant:300.0];
[self.view addConstraint:self.outerViewHeightConstraint];
// inner view constraints
[self.outerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-10-[innerView]-10-|" options:0 metrics:nil views:views]];
[self.outerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[innerView(50)]" options:0 metrics:nil views:views]];
[self.outerView addConstraint:[NSLayoutConstraint constraintWithItem:self.innerView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.outerView attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0]];
[self.outerView addConstraint:[NSLayoutConstraint constraintWithItem:self.innerView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.outerView attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0]];
}
After running the app for 3 seconds, you'll see the outer view (purple view) shrink in height while the red inner view remains centered within the purple view and maintains its height.
You can also rotate the view too.
Related
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];
SecondViewController adds a UIView that contains a MKMapView as a subview inside an IBAction method:
if(_tagTwo == 4){
seg2_buttonImg = #"Maps.png";
UIImage *btnImage1 = [UIImage imageNamed:seg2_buttonImg];
[_left_button setImage:btnImage1 forState:UIControlStateNormal];
[_table removeFromSuperview];
[mapVC layoutMapView];
[self.view addSubview:mapVC.view];
return;
}
mapVC is created in ViewDidLoad with mapVC = [[CollectionMapViewController alloc] init];
Edit: new code for adding constraints, taken from #Reinier Melian:
#implementation CollectionMapViewController
#synthesize mapView;
- (void)viewDidLoad
{
if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_6_1) {
//[segmentedControl setBackgroundColor:[UIColor whiteColor]];
//[segmentedControl setBackgroundColor:[UIColor leafletBrown]];
[segmentedControl setBackgroundColor:[UIColor leafletLightBrown]];
segmentedControl.layer.cornerRadius = 5;
}
self.mapView.translatesAutoresizingMaskIntoConstraints = NO;
NSArray * verticalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|[mapView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(self.mapView)];
NSArray * horizontalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|[mapView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(self.mapView)];
//Add constraints to the Parent
[self.view addConstraints:verticalConstraints];
[self.view addConstraints:horizontalConstraints];
}
CollectionMapViewController.h:
#interface SecondViewController : UIViewController <NSFetchedResultsControllerDelegate, CollectionMapViewControllerDelegate>{
CollectionMapViewController* mapVC;
}
#property (nonatomic, retain) CollectionMapViewController* mapVC;
The problem is that it adds the MKMapView to the top of the screen:
I tried to constrain the MKMapView to the bottom of the screen by adding following code to viewDidLoad of MapViewController
mapView.translatesAutoresizingMaskIntoConstraints = NO;
NSLayoutConstraint *bottom =[NSLayoutConstraint
constraintWithItem:mapView
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeBottom
multiplier:1.0f
constant:0.f];
//Add constraints to the Parent
[self.view addConstraint:bottom];
But it just makes the map go black (or more likely moves it completely off the screen?).
What I am trying to get is this:
I would appreciate any help! Thank you so much!
When you add mapView.translatesAutoresizingMaskIntoConstraints = NO; your map rect size is equal to zero if there are not enough constraints to define the correct frame, that is why you have your screen black, you need define all the constraints needed by your MKMapView
Updated using dynamic height constraints
try with this code
self.mapView.translatesAutoresizingMaskIntoConstraints = NO;
CGFloat mapHeigthValue = self.view.frame.size.height/3; //setting the mapView heigth = 1/3 of view height this is an example
NSArray * verticalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:#"V:[mapView(%f)]|",mapHeigthValue] options:0 metrics:nil views:#{#"mapView":self.mapView}];
NSArray * horizontalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|[mapView]|" options:0 metrics:nil views:#{#"mapView":self.mapView}];
//Add constraints to the Parent
[self.view addConstraints:verticalConstraints];
[self.view addConstraints:horizontalConstraints];
In viewDidLoad:
mapView.translatesAutoresizingMaskIntoConstraints = NO;
/*Set width and height of mapview */
NSLayoutConstraint *widthConst = [NSLayoutConstraint constraintWithItem:mapView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:400];
NSLayoutConstraint *heightConst = [NSLayoutConstraint constraintWithItem:mapView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:300];
[self.view addConstraint:widthConst];
[self.view addConstraint:heightConst];
/*creater X and Y constraints for mapview */
NSLayoutConstraint *centreHorizontallyConstraint = [NSLayoutConstraint
constraintWithItem:mapView
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0];
[self.view addConstraint:centreHorizontallyConstraint];
NSLayoutConstraint *bottomConstraint = [NSLayoutConstraint
constraintWithItem:mapView
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:140];
[self.view addConstraint:bottomConstraint];
I am trying to use the animation from this library:
https://github.com/shu223/PulsingHalo
I have a UIScrollView, and within it a UIImageView that has a larger width
UIScrollView *scrollView;
scrollView = [[UIScrollView alloc] init];
scrollView.showsHorizontalScrollIndicator = NO;
scrollView.showsVerticalScrollIndicator = NO;
scrollView.delegate = self;
self.scrollView = scrollView;
[self.view insertSubview:scrollView belowSubview:self.optionsButton];
scrollView.translatesAutoresizingMaskIntoConstraints = NO;
imageView = [[UIImageView alloc] init];
self.mapImageView = imageView;
UIImage* mapImg = [UIImage imageNamed:#"..."];
// Add image (larger than screen size) to the image view.
[imageView setImage: mapImg];
// Set the constraints for the scroll view and the image view.
NSDictionary* viewsDictionary = NSDictionaryOfVariableBindings(scrollView, imageView);
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[scrollView]|" options:0 metrics: 0 views:viewsDictionary]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[scrollView]|" options:0 metrics: 0 views:viewsDictionary]];
[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[imageView]|" options:0 metrics: 0 views:viewsDictionary]];
[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[imageView]|" options:0 metrics: 0 views:viewsDictionary]];
After this, I add a bunch of buttons to my scrollview, programmatically. For instance:
UIButton* flagButton = [UIButton new];
[self.scrollView addSubview: flagButton];
flagButton.translatesAutoresizingMaskIntoConstraints = NO;
NSLayoutConstraint* xc = [NSLayoutConstraint constraintWithItem:flagButton attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.mapImageView attribute:NSLayoutAttributeLeft multiplier:1.f constant:x];
NSLayoutConstraint* yc = [NSLayoutConstraint constraintWithItem:flagButton attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.mapImageView attribute:NSLayoutAttributeTop multiplier:1.f constant:y];
[self.view addConstraints#[xc, yc]];
Now I want to create my halo for this button. I create a specific view for it, centered around my button. I still haven't quite wrapped my head around positioning, so I don't position it for now.
UIView* haloView = [UIView new];
haloView.backgroundColor = [UIColor clearColor];
NSLayoutConstraint* constraintHeight = [NSLayoutConstraint constraintWithItem:haloView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:flagButton attribute:NSLayoutAttributeHeight multiplier:2.0 constant:0.f];
NSLayoutConstraint* constraintWidth = [NSLayoutConstraint constraintWithItem:haloView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:flagButton attribute:NSLayoutAttributeWidth multiplier:2.0 constant:0.f];
NSLayoutConstraint* constraintCenterX = [NSLayoutConstraint constraintWithItem:haloView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:flagButton attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.f];
NSLayoutConstraint* constraintCenterY = [NSLayoutConstraint constraintWithItem:haloView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:flagButton attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.f];
[self.view insertSubview:haloView belowSubview:flagButton];
haloView.translatesAutoresizingMaskIntoConstraints = NO;
constraintCenterX.active = YES;
constraintCenterY.active = YES;
constraintHeight.active = YES;
constraintWidth.active = YES;
PulsingHaloLayer *halo = [PulsingHaloLayer layer];
[haloView.layer insertSublayer:halo atIndex:0];
halo.radius = 50.0;
halo.haloLayerNumber = 4;
halo.duration = 4.0;
This should normally provide me with the pulsing animation shown in the linked library. And I do get it, somewhat. The behavior is hard to describe, but it appears to start correctly, then abruptly stop when it should loop, then restart again a second later. Basically, it does not seem to loop properly as it should, and I have no idea why.
This is the view hierarchy. The last "UIView" is the one with the Halo layer.
These are the UIView's properties as shown by the debugger.
These are the UIScrollView's properties.
Thank you for your help.
I have a UIView that I want to mask with another UIView, punching a hole out of its center. Here's my viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
[self.view addSubview:self.viewToMask];
[self.view addSubview:self.theMask];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.viewToMask attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.viewToMask attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"[cyan(200)]" options:0 metrics:nil views:#{#"cyan": self.viewToMask}]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[cyan(200)]" options:0 metrics:nil views:#{#"cyan": self.viewToMask}]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.theMask attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.viewToMask attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.theMask attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.viewToMask attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"[mask(100)]" options:0 metrics:nil views:#{#"mask": self.theMask}]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[mask(100)]" options:0 metrics:nil views:#{#"mask": self.theMask}]];
}
It gives me exactly what I'm looking for, minus the masking:
If I add one more line:
[self.viewToMask setMaskView:self.theMask];
both views disappear --- the small view (self.theMask) masks out the whole larger view (self.viewToMask) even though it's only half the size. Does anyone understand what's going on here? Can you not use UIView.maskView with Auto Layout?
As Zev explained, the mask view lives outside of the ordinary view hierarchy and so can't be used together with Auto Layout. I got around this by placing it manually in my view controller's viewDidLayoutSubviews:
-(void)viewDidLayoutSubviews{
[super viewDidLayoutSubviews];
CGRect viewToMaskRect = self.viewToMask.bounds;
CGRect maskRect = CGRectMake(viewToMaskRect.origin.x + 50.0, viewToMaskRect.origin.y + 50.0, 100.0, 100.0);
[self.theMask setFrame:maskRect];
[self.viewToMask setMaskView:self.theMask];
}
Based on Russ' answer I did my own research and got an alternative workaround.
The code sample below will mask the left and right side of the main view with 10pt. Important
class SomeViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.whiteColor()
self.setupMaskView()
}
private func setupMaskView() {
let maskView = UIView()
maskView.translatesAutoresizingMaskIntoConstraints = false
maskView.backgroundColor = UIColor(white: 0.0, alpha: 1.0)
let maskContainerView = UIView()
maskContainerView.addSubview(maskView)
self.view.addSubview(maskContainerView)
maskView.leftAnchor.constraintEqualToAnchor(self.view.leftAnchor, constant: 10).active = true
maskView.widthAnchor.constraintEqualToAnchor(self.view.widthAnchor, constant: -20).active = true
maskView.heightAnchor.constraintEqualToAnchor(self.view.heightAnchor).active = true
self.view.maskView = maskContainerView // this will not work it we place this line above the constraint code !!!
/* I have no idea why this does not work and why nesting is required, maybe some sort of bug? */
}
}
This code is using AutoLayout syntax available from iOS 9.0 and Swift 2.0. (Written in Xcode 7 beta 4)
You can make auto layout work with your maskView by making the maskView just a normal subview (Eg. in a storyboard with relevant constraints) then only setting it as maskView in viewWillLayoutSubviews. Ie:
-(void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
self.viewToBeMasked.maskView = self.maskViewWithLayoutConstraints;
}
Within my root view controller, I'm adding a child view controller's view as a subview, as follows:
ChildViewController *cvc = [[ChildViewController alloc] init];
[self addChildViewController:cvc];
[self.view addSubview:cvc.view];
[cvc didMoveToParentViewController:self];
and I'd now like to use a NSLayoutConstraint to position the cvc.view within the parent view (self.view), such that cvc.view is positioning 25 pts above the bottom of the parent view. My understanding is that the following should work:
UIView *superview = self.view;
UIView *childview = cvc.view;
NSLayoutConstraint *cn =
[NSLayoutConstraint withItem:childview
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:superview attribute:NSLayoutAttributeBottom
multiplier: 1.0
constant: -25.0];
[superview addConstraint: cn];
But the constraint fails at runtime. I thought initially maybe the autoresizing mask in the child view was causing problems (and following the WWDC 2012 Intro video on auto layout), so I set [childview setTranslatesAutoresizingMaskIntoConstraints:NO], but then the childview simply fails to appear.
What am I doing wrong?
I'm not sure but the following should work, or something very similar:
UIView *superview = self.view;
UIView *childview = cvc.view;
NSDictionary *constrainedViews = NSDictionaryOfVariableBindings(childview);
NSArray *constraints =
[superview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[childview]-25-|"
options:0
metrics:nil
views:constrainedViews];
If not make sure that you are actually setting a size for the childview, something like:
UIView *superview = self.view;
UIView *childview = cvc.view;
NSDictionary *constrainedViews = NSDictionaryOfVariableBindings(childview);
NSArray *constraints =
[superview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[childview]|"
options:0
metrics:nil
views:constrainedViews];
Say, to make it fill the width of the view. Or:
UIView *superview = self.view;
UIView *childview = cvc.view;
NSDictionary *constrainedViews = NSDictionaryOfVariableBindings(childview);
NSArray *constraints =
[superview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[childview(>100,<304)]-|"
options:0
metrics:nil
views:constrainedViews];
Which means something like childview should have a width bigger than 100 but less than 304 with default margins to the superview. Please note I don't know if the above constraint actually makes sense (e.g. it may just always give you 304 width childview as that would leave default margins), but it serves as an example.
To properly setup and size your child view controller with auto layout do the following:
childView.translatesAutoresizingMaskIntoConstraints = NO;
NSLayoutConstraint *left = [NSLayoutConstraint constraintWithItem:superview attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:childview attribute:NSLayoutAttributeLeft multiplier:1 constant:0];
[self.view addConstraint:left];
NSLayoutConstraint *top = [NSLayoutConstraint constraintWithItem:superview attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:childview attribute:NSLayoutAttributeTop multiplier:1 constant:0];
[self.view addConstraint:top];
NSLayoutConstraint *width = [NSLayoutConstraint constraintWithItem:superview attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:childview attribute:NSLayoutAttributeWidth multiplier:1 constant:0];
[self.view addConstraint:width];
NSLayoutConstraint *height = [NSLayoutConstraint constraintWithItem:superview attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:childview attribute:NSLayoutAttributeHeight multiplier:1 constant:0];
[self.view addConstraint:height];
If you prefer the visual format you can do the following:
childView.translatesAutoresizingMaskIntoConstraints = NO;
NSDictionary *views = #{#"childview": childview};
[superview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|-0-[childview]-0-|" options:0 metrics:nil views:views]];
[superview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-0-[childview]-0-|" options:0 metrics:nil views:views]];