Adding a background imageview programmatically with autolayout - ios

I need to add a background image view for my views for a project I've done using storyboards + autolayout. I want to add this image programmatically using code. so basically it should be from top layoutguide to bottom layoutguide, without going under them. I've tried few ways which failed horribly.
one way I first adjust the VC'c view before adding like this
id topGuide = self.topLayoutGuide;
UIView *superView = self.view;
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings (superView, topGuide);
[self.view addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"V:[topGuide]-20-[superView]"
options:0
metrics:nil
views:viewsDictionary]
];
[self.view layoutSubviews];
but for some reason my imageview still goes under statusbar.
this is how I add the bg imageview
self.backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"Default"]];
self.backgroundView.contentMode = UIViewContentModeTop;
[self.view insertSubview:self.backgroundView atIndex:0];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[backgroundImageView]|" options:0 metrics:nil views:#{#"backgroundImageView":self.backgroundView}]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[backgroundImageView]|" options:0 metrics:nil views:#{#"backgroundImageView":self.backgroundView}]];

Adding the constraint related to the topLayoutGuide to self.view is useless. The view controller layout its root view (self.view) independently from AutoLayout, and will override the constraints effects (don't quote me on this, this is an observation more than a real understanding of the layout system).
Instead, add the first constraint (#"V:[topGuide]-20-[superView]") to self.backgroundView:
self.backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"Default"]];
self.backgroundView.contentMode = UIViewContentModeTop;
[self.view insertSubview:self.backgroundView atIndex:0];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[topGuide]-(20)-[backgroundImageView]|" options:0 metrics:nil views:#{#"backgroundImageView":self.backgroundView, #"topGuide": self.topLayoutGuide}]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[backgroundImageView]|" options:0 metrics:nil views:#{#"backgroundImageView":self.backgroundView}]];
[self.view layoutSubviews];

Related

How to fill iOS Stack View (UIStackView) with an image as a background?

I want to set stack view background to an image. For example, in Android, if I use 'Linear Layout' (equivalent to UIStackView), I can set a background image to the 'Linear Layout' irrespective of whatever the content (views) I add to it.
How could I do this using XCode?
You can't do this, UIStackView is a non-drawing view, meaning that drawRect() is never called. If you want a background image, consider placing the stack view inside a UIImageView.
As the answers stated above you can't add an image background to a stackview.
The way to achieve it is by adding a UIView and then nest an ImageView and the existing stackview to the UIView just added. Also, you need to apply the corresponding constraints to the imageview in order to fit the full background (0, 0 , 0 , 0)
The outline should look like this:
If you program UIStackview in code maybe you can try below ~
UIScrollView *scrollview = [[UIScrollView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
[[scrollview layer] setContents:(__bridge id)[[UIImage imageNamed:#"your_background.jpg"] CGImage]];
[[scrollview layer] setContentsGravity:kCAGravityResizeAspectFill];
[self.view addSubview:scrollview];
UIStackView *stackview = [[UIStackView alloc] init];
[stackview setTranslatesAutoresizingMaskIntoConstraints:NO];
[stackview setAxis:UILayoutConstraintAxisVertical];
[stackview setDistribution:UIStackViewDistributionFill];
[scrollview addSubview:stackview];
NSDictionary *dic_vfl = NSDictionaryOfVariableBindings(stackview,scrollview);
NSArray *array = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|[stackview(scrollview)]|" options:0 metrics:nil views:dic_vfl];
[scrollview addConstraints:array];
array = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|[stackview]|" options:0 metrics:nil views:dic_vfl];
[scrollview addConstraints:array];
//add view to stackview for test
srand48(arc4random());
for(int i=0;i<50;i++){
UIColor *rc = [UIColor colorWithRed:drand48() green:drand48() blue:drand48() alpha:0.5];
UIView *view_test = [[UIView alloc]init];
[view_test setTranslatesAutoresizingMaskIntoConstraints:NO];
[view_test setBackgroundColor:rc];
[stackview addArrangedSubview:view_test];
dic_vfl = NSDictionaryOfVariableBindings(view_test);
array = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|[view_test]|" options:0 metrics:nil views:dic_vfl];
[stackview addConstraints:array];
array = [NSLayoutConstraint constraintsWithVisualFormat:#"V:[view_test(50)]" options:0 metrics:nil views:dic_vfl];
[stackview addConstraints:array];
}
hope this is useful!!

UIScrollView does not scroll with AutoLayout

I can't make UIScrollView scroll vertically when using Autolayout, I've tried many suggestions. I've read Apple's technical note but even that doesn't seem to work with my pure auto layout approach.
Here is my simple code where I'm adding two UIView blocks1 & 2 in a container view. The container view itself is the only child of the UIScrollView as suggested by many references online. I set the heights of block 1 and 2 as 800 points each, but the scroll view won't scroll.
- (void)viewDidLoad {
[super viewDidLoad];
// Test UIScrollView with Autolayout, scrolling should word
UIView *mainView = self.view;
UIScrollView* scrollView = [UIScrollView new];
scrollView.translatesAutoresizingMaskIntoConstraints = NO;
scrollView.backgroundColor = [UIColor redColor];
[self.view addSubview:scrollView];
UIView* contentView = [UIView new];
contentView.translatesAutoresizingMaskIntoConstraints = NO;
contentView.backgroundColor = [UIColor greenColor];
[scrollView addSubview:contentView];
UIView* block1 = [[UIView alloc] init];
block1.translatesAutoresizingMaskIntoConstraints = NO;
[block1 setBackgroundColor:[UIColor blackColor]];
[contentView addSubview:block1];
UIView* block2 = [[UIView alloc] init];
block2.translatesAutoresizingMaskIntoConstraints = NO;
[block2 setBackgroundColor:[UIColor blackColor]];
[contentView addSubview:block2];
NSDictionary* viewDict = NSDictionaryOfVariableBindings(mainView,scrollView, contentView, block1, block2);
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[scrollView]|" options:0 metrics:0 views:viewDict]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[scrollView]|" options:0 metrics:0 views:viewDict]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[contentView(==mainView)]|" options:0 metrics:0 views:viewDict]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[contentView(==mainView)]|" options:0 metrics:0 views:viewDict]];
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[block1(==300)]" options:0 metrics:0 views:viewDict]];
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[block1(==800)]" options:0 metrics:0 views:viewDict]];
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[block2(==300)]" options:0 metrics:0 views:viewDict]];
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-810-[block2(==800)]" options:0 metrics:0 views:viewDict]];
}
Looking to your code, It seems that the size of you content view will be of same size as UIView.
e.g. : IF Device screen height is 568 then your content view size will be 568, and you are set your block2 at y position = 180.
Your Code:
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[contentView(==mainView)]|" options:0 metrics:0 views:viewDict]];
change:
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[contentView(2000)]|” options:0 metrics:0 views:viewDict]];
Hope it solves your problem if any further query please let me know.
If all you want to do is make a scroll view work with auto layouts, you can try this.
http://natashatherobot.com/ios-autolayout-scrollview/
Do the following changes in your code
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[contentView]|" options:0 metrics:0 views:viewDict]];
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-810-[block2(==800)]|" options:0 metrics:0 views:viewDict]];
Hope this helps

Set auto layout dynamically to scroll view in iOS

i am creating scroll view dynamically now i want to set auto layout for that
i implemented following code to set auto layout but its not working at all.
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationItem.title=#"Insta SMS Collection";
scrollView = [[UIScrollView alloc]initWithFrame:self.view.frame];
[self.view addSubview:scrollView];
scrollView.translatesAutoresizingMaskIntoConstraints = NO;
NSDictionary *views = #{#"scrollView":scrollView};
[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:
[scrollView]"
options:kNilOptions
metrics:nil
views:views;
[self.view addSubview:scrollView];
}
add horizontal and vertical autolayout.
[self.view addSubview:scrollView];
NSDictionary *dictScrollConst = NSDictionaryOfVariableBindings(scrlView);
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[scrlView]|" options:0 metrics:nil views:dictScrollConst]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[scrlView]|" options:0 metrics:nil views:dictScrollConst]];
Maybe this will help you.

Autolayout issue with top & bottom layout guide

I have some problem to set NSLayoutconstraints programmatically from my view and controller.topLayoutGuide & controller.bottomLayoutGuide.
with this code in viewDidLoad:
_mainView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:_mainView];
[self.view removeConstraints:self.view.constraints];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-0-[_mainView]-0-|" options:NSLayoutFormatAlignAllBaseline metrics:nil views:NSDictionaryOfVariableBindings(_mainView)]];
id top = self.topLayoutGuide;
id bottom = self.bottomLayoutGuide;
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[top]-0-[_mainView]-0-[bottom]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(top, _mainView, bottom)]];
[self.view layoutSubviews];
the result is that:
(_mainView has dark gray background color)
if i set up the constraints relative to the superview it works:
_mainView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:_mainView];
[self.view removeConstraints:self.view.constraints];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-0-[_mainView]-0-|" options:NSLayoutFormatAlignAllBaseline metrics:nil views:NSDictionaryOfVariableBindings(_mainView)]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-0-[_mainView]-0-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_mainView)]];
[self.view layoutSubviews];
where is my error?
the view controller hierarchy is uitabbarcontroller -> uinavigationController -> myViewController
SOLVED!
the error was:
[self.view removeConstraints:self.view.constraints];
I changed the code with
for(NSLayoutConstraint *c in self.view.constraints)
if(c.firstItem == _mainView || c.secondItem == _mainView)
[self.view removeConstraint:c];
and it worked.
thanks anyway! ;)
Check if self.topLayoutGuide and self.bottomLayoutGuide is not nil. You might have forgotten to set the outlet in Interface Builder.

UIScrollView with iOS Auto Layout Constraints: Wrong size for subviews

I'm trying to generate a view in code. Here's the hierachy of my view object
UIScrollView
UIView
UIButton
The ScrollView should be the same size as the window.
The button should be as big as possible.
I'm using iOS auto layout, so the constraint strings for all of my objects look like this
H:|[object]|
V:|[object]|
I've also set translatesAutoresizingMaskIntoConstraints to NO for each object.
The problem is that the button only gets the default button-size. Its parent view object (UIView) only gets the size its subviews need.
red: UIScrollView / yellow: UIView
How can I force those views to be as big as the scrollView?
When I use a UIView instead of th UIScrollView everything works great...
Here's some code:
- (void) viewDidLoad {
[super viewDidLoad];
// SCROLL VIEW
UIScrollView* scrollView = [UIScrollView new];
scrollView.backgroundColor=[UIColor redColor];
scrollView.translatesAutoresizingMaskIntoConstraints = NO;
//CONTAINER VIEW
UIView *containerView = [UIView new];
containerView.translatesAutoresizingMaskIntoConstraints = NO;
containerView.backgroundColor = [UIColor yellowColor];
[scrollView addSubview:containerView];
// CONSTRAINTS SCROLL VIEW - CONTAINER VIEW
[scrollView addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[containerView]|"
options:0 metrics:nil
views:#{#"containerView":containerView}]];
[scrollView addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[containerView]|"
options:0 metrics:nil
views:#{#"containerView":containerView}]];
// BUTTON
UIButton* button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.translatesAutoresizingMaskIntoConstraints = NO;
[button setTitle:#"I'm way to small" forState:UIControlStateNormal];
[containerView addSubview:button];
// CONSTRAINTS CONTAINER VIEW - BUTTON
[containerView addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[button]|"
options:0 metrics:nil
views:#{#"button":button}]];
[containerView addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[button]|"
options:0 metrics:nil
views:#{#"button":button}]];
self.view = scrollView;
}
UPDATE:
I really don't know, why this is happening. If you set up the view in IB, connect the outlets and instanciate the view in code, the scrollview behaves like a normal view (which bounces vertically). Its contentSize is not calculated correctly. More here. But how to do it correctly?
A couple of observations:
Constraints for subviews in scroll views don't work like constraints in other views. They're used to set the contentSize of the scroll view. (See TN2154.) That way, you throw a bunch of stuff on a scroll view, set the constraints for the stuff inside it, and the contentSize is calculated for you. It's very cool feature, but it's antithetical to what you're trying to do here.
Worse, buttons will, unless you set an explicit constraint for their width and height of a button, will resize according to their content.
The net effect of these two observations is that your existing constraints say "(a) set my container to be the size of my button; (b) let my button resize itself dynamically to the size of the text; and (c) set my scrollview's contentSize according to the size of my container (which is the size of the button)."
I'm unclear as to what the business problem is. But here are some constraints that achieve what I think your technical question was:
- (void)viewDidLoad
{
[super viewDidLoad];
UIView *view = self.view;
UIScrollView *scrollView = [[UIScrollView alloc] init];
scrollView.backgroundColor = [UIColor redColor]; // just so I can see it
scrollView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:scrollView];
UIView *containerView = [[UIView alloc] init];
containerView.backgroundColor = [UIColor yellowColor]; // just so I can see it
containerView.translatesAutoresizingMaskIntoConstraints = NO;
[scrollView addSubview:containerView];
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.translatesAutoresizingMaskIntoConstraints = NO;
[button setTitle:#"I'm the right size" forState:UIControlStateNormal];
[containerView addSubview:button];
NSDictionary *views = NSDictionaryOfVariableBindings(scrollView, button, view, containerView);
// set the scrollview to be the size of the root view
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[scrollView]|"
options:0
metrics:nil
views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[scrollView]|"
options:0
metrics:nil
views:views]];
// set the container to the size of the main view, and simultaneously
// set the scrollview's contentSize to match the size of the container
[view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[containerView(==view)]|"
options:0
metrics:nil
views:views]];
[view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[containerView(==view)]|"
options:0
metrics:nil
views:views]];
// set the button size to be the size of the container view
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[button(==containerView)]"
options:0
metrics:nil
views:views]];
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[button(==containerView)]"
options:0
metrics:nil
views:views]];
}
Frankly, I don't understand the business intent of your UI, as this feels like a contortion of auto layout to achieve a very simply UI. I don't know why you have a scroll view if you have "screen sized" content in it (unless you were paging through buttons). I don't know why you'd have a content view with a single item in it. I don't understand why you're using a full-screen button (I'd just put a tap gesture on the root view at that point and call it a day).
I'll assume you have good reasons for all of this, but it might make sense to back up, ask what is your desired user experience is, and then approach the problem fresh to see if there's a more efficient way to achieve the desired effect.

Resources