how to set subview of UIScrollView using autolayout - ios

i am trying to implement Horizontal scrollview with ten views.
its working fine in portrait mode but i want same scenario in landscape too.
can anyone help me by which constraint i can achieve this?
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.title=#"Demo.....";
self.myScrollView.pagingEnabled = YES;
NSInteger numberOfViews = 10;
for (int i = 0; i < numberOfViews; i++) {
CGFloat myOrigin = i * self.view.frame.size.width;
UIView *myView = [[UIView alloc] initWithFrame:CGRectMake(myOrigin, 0, self.view.frame.size.width, self.view.frame.size.height)];
myView.backgroundColor = [UIColor purpleColor];
CGRect myFrame = CGRectMake(10.0f, 70.0f, 200.0f, 25.0f);
UILabel *myLabel = [[UILabel alloc] initWithFrame:myFrame];
myLabel.translatesAutoresizingMaskIntoConstraints = NO;
myLabel.text = [NSString stringWithFormat:#"This is page number %d", i];
myLabel.font = [UIFont boldSystemFontOfSize:16.0f];
myLabel.textAlignment = NSTextAlignmentLeft;
[myView addSubview:myLabel];
//set the scroll view delegate to self so that we can listen for changes
self.myScrollView.delegate = self;
//add the subview to the scroll view
[self.myScrollView addSubview:myView];
NSLayoutConstraint *constraintLeft = [NSLayoutConstraint constraintWithItem:myView
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:self.myScrollView
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:0.0];
// 0px to the right of the UIScrollView
NSLayoutConstraint *constraintRight = [NSLayoutConstraint constraintWithItem:myView
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:self.myScrollView
attribute:NSLayoutAttributeRight
multiplier:1.0
constant:0.0];
[self.myScrollView addConstraint:constraintLeft];
[self.myScrollView addConstraint:constraintRight];
}
//set the content size of the scroll view, we keep the height same so it will only
//scroll horizontally
self.myScrollView.contentSize = CGSizeMake(self.view.frame.size.width * numberOfViews,
self.view.frame.size.height);
//we set the origin to the 3rd page
CGPoint scrollPoint = CGPointMake(self.view.frame.size.width * 2, 0);
//change the scroll view offset the the 3rd page so it will start from there
[myScrollView setContentOffset:scrollPoint animated:YES];
[self.view addSubview:self.myScrollView];
}
in my above code,
ten view is completely added into scrollview and working fine in portrait mode but when i change the orientation to horizontal it will not resize and not scrolled.

You need to tie subviews width constraint equal to scrollView width
for (UIView *subView in subViews)
{
NSLayoutConstraint *constraintEqualWidth = [NSLayoutConstraint constraintWithItem:subView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self.myScrollView
attribute:NSLayoutAttributeWidth
multiplier:1.0
constant:0.0];
[subView addConstraint:constraintEqualWidth];
}

Related

iOS Objective C ScrollView

I am trying to implement a UIScrollView and load it with images from an array of images in Xcode using objective-C, each image in the UIScrollView must be full screen both in portrait and in the landscape mode.I have been able to make it work in portrait mode but not in landscape mode. It should be fullscreen in all iOS device sizes. Below is the code I have written so far. I have UIScrollView in my storyboard, a button and a label. Any answer or pointing to a tutorial that implements this will be appreciated. Thanks in advance.
CGRect screen = [[UIScreen mainScreen] bounds];
CGFloat widthInPixel = screen.size.width;
CGFloat heightInPixel = screen.size.height;
float increaseAmount = widthInPixel;
self.imageScrollView.contentMode = UIViewContentModeScaleAspectFit;
self.imageScrollView.pagingEnabled = YES;
[self.imageScrollView setAlwaysBounceVertical:NO];
[self.imageScrollView setAlwaysBounceHorizontal:NO];
imageViews = [[NSMutableArray alloc] init];
self.imageScrollView.clipsToBounds = YES;
NSInteger imageNumbers = [self.images count];
UIImageView *image;
for(NSInteger i = 0; i < imageNumbers; i++) {
CGFloat xOrigin = i * self.view.frame.size.width;
image = [[UIImageView alloc] initWithFrame:
CGRectMake(xOrigin, 0,
widthInPixel,
self.imageScrollView.frame.size.height)];
image.contentMode = UIViewContentModeScaleAspectFit;
image.clipsToBounds = YES;
image.image = self.images[i];
[image setAutoresizingMask:
UIViewAutoresizingFlexibleWidth |
UIViewAutoresizingFlexibleHeight];
[self.imageScrollView addSubview:image];
}
self.imageScrollView.contentSize = CGSizeMake(image.frame.size.width *
imageNumbers,
self.imageScrollView.frame.size.height);
You really should learn how to use auto-layout and constraints. Use your favorite search engine and search for ios auto layout tutorial ... you'll find plenty of material.
Edit:
Scroll offset is an inherent issue when rotating a scroll view with paging enabled. See the edit below for an implementation of viewWillTransitionToSize.
But, to give you an idea, this will do what you want, including auto-resizing on device rotation:
//
// ViewController.m
// ScrollingImages
//
// Created by Don Mag on 7/19/18.
//
#import "ViewController.h"
#interface ViewController ()
#property (strong, nonatomic) IBOutlet UIScrollView *theScrollView;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSArray *images = #[#"a", #"b", #"c", #"d", #"e"];
[_theScrollView setPagingEnabled:YES];
[_theScrollView setAlwaysBounceVertical:NO];
[_theScrollView setAlwaysBounceHorizontal:NO];
// we'll use this to hold the most recently added view
UIImageView *prevImageView = nil;
for (int i = 0; i < images.count; i++) {
// create an image view with named image from array
UIImageView *v = [[UIImageView alloc] initWithImage:[UIImage imageNamed:images[i]]];
// we want to use auto-layout
v.translatesAutoresizingMaskIntoConstraints = NO;
// we want aspect-fit
v.contentMode = UIViewContentModeScaleAspectFit;
// add it to the scroll view
[_theScrollView addSubview:v];
// set width and height constraints equal to the scroll view
[[NSLayoutConstraint
constraintWithItem:v
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:_theScrollView
attribute:NSLayoutAttributeWidth
multiplier:1.0
constant:0.0] setActive:YES];
[[NSLayoutConstraint
constraintWithItem:v
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:_theScrollView
attribute:NSLayoutAttributeHeight
multiplier:1.0
constant:0.0] setActive:YES];
if (i == 0) { // if it's the first image
// add top constraint
[[NSLayoutConstraint
constraintWithItem:v
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:_theScrollView
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0.0] setActive:YES];
// and leading constraint
[[NSLayoutConstraint
constraintWithItem:v
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:_theScrollView
attribute:NSLayoutAttributeLeading
multiplier:1.0
constant:0.0] setActive:YES];
} else {
// constrain leading to previous image view trailing
[[NSLayoutConstraint
constraintWithItem:v
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:prevImageView
attribute:NSLayoutAttributeTrailing
multiplier:1.0
constant:0.0] setActive:YES];
// and top to previous image view top
[[NSLayoutConstraint
constraintWithItem:v
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:prevImageView
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0.0] setActive:YES];
}
if (i == images.count - 1) { // if it's the last image
// add trailing constraint
[[NSLayoutConstraint
constraintWithItem:v
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:_theScrollView
attribute:NSLayoutAttributeTrailing
multiplier:1.0
constant:0.0] setActive:YES];
// and bottom constraint
[[NSLayoutConstraint
constraintWithItem:v
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:_theScrollView
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0.0] setActive:YES];
}
// reference to most recently added view
prevImageView = v;
}
}
- (void) viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
// execute before rotation
// get the "index" of the current image in the scroll view
NSUInteger idx = (unsigned)(_theScrollView.contentOffset.x / _theScrollView.frame.size.width);
[coordinator animateAlongsideTransition:^(id _Nonnull context) {
// execute during rotation
// update the scroll view's contentOffset, based on the "index"
self.theScrollView.contentOffset = CGPointMake(idx * self.theScrollView.frame.size.width, 0);
} completion:^(id _Nonnull context) {
// execute after rotation (if additional code wanted)
}];
}
#end
You can download a working example project here: https://github.com/DonMag/ScrollingImages

Center SubViews in ParentView in Objective-C

I have the following code:
UIView *parentView = [[UIView alloc] init];
parentView.frame = CGRectMake(0, 0, 100, 100);
parentView.center = self.view.center;
parentView.backgroundColor = [UIColor greenColor];
UIView *subView = [[UIView alloc] init];
subView.backgroundColor = [UIColor redColor];
subView.frame = CGRectMake(0, 0, 50, 50);
subView.center = parentView.center;
[parentView addSubview:subView];
[self.view addSubview:parentView];
Which produces the following result:
Why is the red view not centered in the green view since they have the same center?
The Apple UIView documentation states for the center property:
The center is specified within the coordinate system of its superview and is measured in points. Setting this property changes the values of the frame properties accordingly.
That means the parentView center will be relative to its superview (the white background view by the looks of your screenshot).
To get the desired result you need to do something like this:
subview.center = CGPointMake(CGRectGetMidX(parentView.bounds),
CGRectGetMidY(parentView.bounds));
But you should really be using autolayout for this type of thing.
You are setting the green view to be the center of it's superview, but you are setting the red view to be in the center of it's superview's superview...
The way to fix that with the smallest amount of code change would be to change the red view's superview.
[self.view addSubview:parentView];
[self.view addSubview:subView];
EDIT: To explain a little further what is going on. Say when you set the center of your green view it gets set to (500,500). That's inside it's superview, so it gets set to the middle of the screen. Then you set your red view's center to the same value as the green view's center, (500,500). But it's parent that it is relative to is the red view, so it gets placed (500,500) from the origin point of the green view. It gets placed near the bottom right of the screen.
You are Doing Like this
UIView *parentView = [[UIView alloc] init];
parentView.frame = CGRectMake(0, 0, 100, 100);
parentView.center = self.view.center;
parentView.backgroundColor = [UIColor greenColor];
UIView *subView = [[UIView alloc] init];
subView.backgroundColor = [UIColor redColor];
CGFloat SubviewX = (parentView.frame.size.width - 50)/2;
CGFloat SubviewY = (parentView.frame.size.height - 50)/2;
subView.frame = CGRectMake(SubviewX, SubviewY, 50, 50);
[parentView addSubview:subView];
[self.view addSubview:parentView];
The best way to center a view, and maintain it that way even if the view changes its view frame is using constraints:
UIView *parentView = [[UIView alloc] init];
parentView.translatesAutoresizingMaskIntoConstraints = NO;
// You might want to add constraints for the view's size
parentView.frame = CGRectMake(0, 0, 100, 100);
parentView.backgroundColor = [UIColor greenColor];
[self.view addSubview:parentView];
NSContraint *xContraint = [NSLayoutConstraint constraintWithItem:parentView
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0.0];
[self.view addConstraint:xContraint];
NSContraint *yContraint = [NSLayoutConstraint constraintWithItem:parentView
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterY
multiplier:1.0
constant:0.0];
[self.view addConstraint:yContraint];
UIView *subView = [[UIView alloc] init];
subView.translatesAutoresizingMaskIntoConstraints = NO;
// You might want to add constraints for the view's size
subView.frame = CGRectMake(0, 0, 50, 50);
subView.backgroundColor = [UIColor redColor];
[parentView addSubview:subView];
NSContraint *xSubContraint = [NSLayoutConstraint constraintWithItem:subView
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:parentView
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0.0];
[parentView addConstraint:xSubContraint];
NSContraint *ySubContraint = [NSLayoutConstraint constraintWithItem:subView
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:parentView
attribute:NSLayoutAttributeCenterY
multiplier:1.0
constant:0.0];
[parentView addConstraint:ySubContraint];

How to change scrollView content sizes using auto-layouts in ios

I am adding 3 UITextfields and 1 UIButton on my scrollview.
My main requirement is when I click on UITextfield scroll must scroll up-to all fields visible to user above keyboard.
And when I click return button on keyboard scroll must scroll by default what in set for scrollview contentSize using auto-layouts.
my code:
#interface ViewController10 ()
{
UIScrollView * scrollView;
UITextField * emailTextField;
UITextField * nameTextField;
UITextField * passwword;
UIButton * submit;
}
#end
#implementation ViewController10
- (void)viewDidLoad {
[super viewDidLoad];
scrollView = [[UIScrollView alloc] init];
scrollView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:scrollView];
emailTextField = [self createLabelWithText];
emailTextField.delegate = self;
[scrollView addSubview: emailTextField];
nameTextField = [self createLabelWithText];
nameTextField.delegate = self;
[scrollView addSubview: nameTextField];
passwword = [self createLabelWithText];
passwword.delegate = self;
[scrollView addSubview: passwword];
submit = [[UIButton alloc]init];
submit.backgroundColor = [UIColor orangeColor];
[submit setTitle: #"Submit" forState: UIControlStateNormal];
submit.translatesAutoresizingMaskIntoConstraints = NO;
[scrollView addSubview:submit];
NSDictionary * viewsDic = NSDictionaryOfVariableBindings(scrollView,emailTextField,nameTextField,passwword,submit);
//Applying autolayouts for scrolview
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:#"H:|-0-[scrollView]-0-|"]
options:0
metrics:nil
views:viewsDic]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:#"V:|-0-[scrollView]-0-|"]
options:0
metrics:nil
views:viewsDic]];
//Applying autolayouts for textfields and button
[scrollView addConstraint:[NSLayoutConstraint constraintWithItem:emailTextField
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:scrollView
attribute:NSLayoutAttributeCenterX
multiplier:1
constant:0]];
NSArray * keys = #[#"emailTextField",#"nameTextField",#"passwword",#"submit"];
for (NSString * key in keys) {
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:#"H:|-10-[%#]-10-|",key]
options:0
metrics:nil
views:viewsDic]];
}
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-30-[emailTextField(30)]-130-[nameTextField(30)]-130-[passwword(30)]-60-[submit(30)]-20-|"
options:0
metrics:nil
views:viewsDic]];
}
-(UITextField *)createLabelWithText{
UITextField * textfield = [[UITextField alloc] init];
textfield.textColor = [UIColor whiteColor];
textfield.backgroundColor = [UIColor lightGrayColor];
textfield.translatesAutoresizingMaskIntoConstraints = NO;
return textfield;
}
- (void)textFieldDidBeginEditing:(UITextField *)textField{
scrollView.contentSize = CGSizeMake(320, 700);
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField{
[textField resignFirstResponder];
return YES;
}
Please check this awsome tutorial , it is definetly helpfull for you.
**
Using UIScrollView with Auto Layout in iOS
**
as said in blog you need to set constrain in this way when you are using autolayout.
NSLayoutConstraint *leftConstraint = [NSLayoutConstraint constraintWithItem:self.contentView
attribute:NSLayoutAttributeLeading
relatedBy:0
toItem:self.view
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:0];
[self.view addConstraint:leftConstraint];
NSLayoutConstraint *rightConstraint = [NSLayoutConstraint constraintWithItem:self.contentView
attribute:NSLayoutAttributeTrailing
relatedBy:0
toItem:self.view
attribute:NSLayoutAttributeRight
multiplier:1.0
constant:0];
[self.view addConstraint:rightConstraint];
change value and name according to your requirement.
When keyBoards is appears .
CGRect frame = currentActiveView.frame; // your scrollview frame
frame.size.height = SCREEN_HEIGHT - 216 - keyboardAccesssory.frame.size.height;
currentActiveView.frame = frame;
[yourScrollView setContentSize:CGSizeMake(SCREEN_WIDTH, SCREEN_HEIGHT)];
When keyboard is resign ..
CGRect frame = currentActiveView.frame; // your scrollview frame
frame.size.height = SCREEN_HEIGHT;
currentActiveView.frame = frame;
[yourScrollView setContentSize:CGSizeMake(SCREEN_WIDTH, SCREEN_HEIGHT)];
You need to play with the frame of the view.
Move the frame above keyboard when keyboard is shown whereas reset its position when keyboard is hidden.
You need to do it programmatically.
You can also go through Apple's Programming guide. https://developer.apple.com/library/ios/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html#//apple_ref/doc/uid/TP40009542-CH5-SW3

UILabel is not centered in my UIView

I'm adding a UILabel to a UIView on initialization and the label will not center itself. I've tried centering it is layoutSubviews and adding constraints programmatically with no luck. When it appears it is about 200 pixels to the left of the center. Here is my code. Thanks in advance.
- (instancetype)initWithTitle:(NSString *)title andBody:(NSString *)body Andwidth:(CGFloat)width {
self = [super initWithFrame:CGRectMake(0, 0, width, 60)];
if (self) {
UIView *containerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, width, 60)];
containerView.backgroundColor = [UIColor orangeColor];
[self addSubview:containerView];
self.winningLabel = [[UILabel alloc] initWithFrame:CGRectMake(0,0, 300, 40)];
self.winningLabel.textColor = [UIColor whiteColor];
self.winningLabel.font = [UIFont fontWithName:#"Avenir" size:30];
self.winningLabel.backgroundColor = [UIColor clearColor];
self.winningLabel.lineBreakMode = NSLineBreakByTruncatingTail;
self.winningLabel.numberOfLines = 2;
self.winningLabel.text = #"YOU WIN";
self.winningLabel.hidden = YES;
[containerView addSubview:self.winningLabel];
NSLayoutConstraint *xCenterConstraint = [NSLayoutConstraint constraintWithItem:self.winningLabel attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0];
[self addConstraint:xCenterConstraint];
NSLayoutConstraint *yCenterConstraint = [NSLayoutConstraint constraintWithItem:self.winningLabel attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0];
[self addConstraint:yCenterConstraint];
}
return self;
}
- (void)layoutSubviews {
[self.winningLabel setCenter:self.center];
}
I think you forget the text alignment. By default the text in a label is aligned to the left. If you want it to be in the center, you should specify it explicitly.
self.winningLabel.textAlignment = NSTextAlignmentCenter;

how content inset affects layout

While working with UIScrollView, I noticed some weird behavior about auto layout and content inset; take the following code for example:
- (void) loadView
{
UIScrollView * scroller = [[UIScrollView alloc] init];
scroller.backgroundColor = [UIColor orangeColor];
UILabel * label = [[UILabel alloc] init];
label.backgroundColor = [UIColor blueColor];
label.translatesAutoresizingMaskIntoConstraints = NO;
[scroller addSubview:label];
[scroller addConstraint: [NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:scroller attribute:NSLayoutAttributeLeft multiplier:1.0 constant:40]];
[scroller addConstraint: [NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:scroller attribute:NSLayoutAttributeTop multiplier:1.0 constant:20]];
[scroller addConstraint: [NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:240]];
[scroller addConstraint: [NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:44]];
self.view = scroller;
scroller.contentInset = UIEdgeInsetsMake(44, 0, 0, 0); //this is the game changer
}
Without the last line that changes the content inset, the UILabel is at (40, 20) as expected. When altering the content inset, the label moved to (40, 108), instead of (40, 64); the top content inset is somehow doubled.
If the UILabel is created with set frame and without those constraints as:
- (void) loadView
{
UIScrollView * scroller = [[UIScrollView alloc] init];
scroller.backgroundColor = [UIColor orangeColor];
CGRect frame = CGRectMake(40, 20, 240, 44);
UILabel * label = [[UILabel alloc] initWithFrame:frame];
label.backgroundColor = [UIColor blueColor];
[scroller addSubview:label];
self.view = scroller;
scroller.contentInset = UIEdgeInsetsMake(44, 0, 0, 0);
}
Then it behave as expected (40, 20) and (40, 64) depending on the content inset.
I checked with the documents and tried flipping some switches, but still can't figure out why.
BTW, that was the result from the IOS(6) simulator, I did not try it on device yet.
P.S.
Things appear as expected on simulator with IOS7, except the difference of the new status bar behavior.

Resources