UIScrollView with autolayout mixed approach? - ios

I am trying to work an example code thats using the 'mixed approach' mention on apple dev link:
I am trying to stack 3 views vertically in UIScrollView. In below example the UIScrollView shows the red view on load but does not scroll as expected. I can scroll a little bit to see the green view below the red view - but the scroll view springs back up and does not scroll to the green view or view below it(blue view). I understand I need a constraint I tried to add one between view 1 & 2 so that view2.top = view1.bottom ,but seems I am missing something.
Also I noticed the content size of the scrollview is zero ( in viewDidAppear method).
Any tips on what I am missing or help on how to get this mixed approach working would be of great!!
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIScrollView* scrollView = ((UIScrollView*)self.view);
scrollView.translatesAutoresizingMaskIntoConstraints = NO;
CGFloat w = self.view.frame.size.width;
CGFloat h = self.view.frame.size.height;
contentView = [[UIView alloc] initWithFrame:CGRectMake(0,0, w, h*3)];
[scrollView addSubview:contentView];
v1 =[[UIView alloc] initWithFrame:CGRectMake(0,0, w, h)];
v2 =[[UIView alloc] initWithFrame:CGRectMake(0,h, w, h)];
v3 =[[UIView alloc] initWithFrame:CGRectMake(0,h*2, w, h)];
v1.backgroundColor = [UIColor redColor];
v2.backgroundColor = [UIColor greenColor];
v3.backgroundColor = [UIColor blueColor];
[contentView addSubview:v1];
[contentView addSubview:v2];
[contentView addSubview:v3];
NSLayoutConstraint *myConstraint =[NSLayoutConstraint
constraintWithItem:v1
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:v2
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0];
[contentView addConstraint:myConstraint];
scrollView.contentSize = contentView.bounds.size;
}

I dont understand why you would need a constraint if you are just trying to stack views in a scroll view. I tried your code and it does not scroll well like you said, but I think it is being caused because you are using a UIScrollView as the main view for the view controller, is there a specific reason you want to do it like this?
I changed the code to instead add a UIScrollView to the normal UIView and it works perfectly as expected without using any complicated constraints. Just remember to define the scroll view at the top by using UIScrollView *scrollView;
- (void)viewDidLoad {
[super viewDidLoad];
scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
[self.view addSubview:scrollView];
CGFloat w = self.view.frame.size.width;
CGFloat h = self.view.frame.size.height;
contentView = [[UIView alloc] initWithFrame:CGRectMake(0,0, w, h*3)];
[scrollView addSubview:contentView];
v1 =[[UIView alloc] initWithFrame:CGRectMake(0,0, w, h)];
v2 =[[UIView alloc] initWithFrame:CGRectMake(0,h, w, h)];
v3 =[[UIView alloc] initWithFrame:CGRectMake(0,h*2, w, h)];
v1.backgroundColor = [UIColor redColor];
v2.backgroundColor = [UIColor greenColor];
v3.backgroundColor = [UIColor blueColor];
[contentView addSubview:v1];
[contentView addSubview:v2];
[contentView addSubview:v3];
scrollView.contentSize = contentView.bounds.size;
}

I had to struggle alot with stupid pure based auto-layout approach for UIScrollView. I wanted a scrollview that is based on subviews that are auto-layout constraint based and works with rotation. In the end the following "Mixed Approach" code worked for me for a vertical scrolling and kept things simple:
- (void)viewDidLoad {
UIView *contentView;
contentView = [[UIView alloc] initWithFrame:self.scrollView.bounds];
//don't add UIViewAutoresizingFlexibleHeight as that messes things up but next line helps with rotation
contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
[scrollView addSubview:contentView];
// DON'T change contentView's translatesAutoresizingMaskIntoConstraints,
// which defaults to YES;
// Set the content size of the scroll view to match the size of the content view
[scrollView setContentSize:CGSizeMake(contentWidth, contentHeight)];
/* the rest of your code here... pre auto-layout style and you can change the origins of subviews to position them within contentView */
[contentView addSubview:subView1];
[contentView addSubview:subView2];
}
The key point to realize is that DO NOT change size of contentView any point later after initialization and adding it to scrollview as that would cause its internal constraints to break but you can still continue to add subviews without worrying about its size.
You'll calculate the scroll view content size independently from size of contentView unless you use the systemLayoutSizeFittingSize: method which I haven't tested still

Related

Autoresizing not working for UIView added programatically

I have followed this SO link for autoresizing, but Autoresizing not working.
How to set frame programmatically with autoresizing?
I have set frame for iPhone 4
UIView *box = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 320, 120)];
[box setBackgroundColor:[UIColor redColor]];
[box setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
[box setAutoresizingMask:UIViewAutoresizingFlexibleWidth];
[box setAutoresizingMask:UIViewAutoresizingFlexibleTopMargin];
[box setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin];
[box setAutoresizingMask:UIViewAutoresizingFlexibleRightMargin];
[self.view addSubview:box];
But it is not working in iPad or iPhone 6
First thing first : As per open suggestion, you should use constraints.
Add autoresizing mask after adding it to view.
UIView *box = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 320, 120)];
[box setBackgroundColor:[UIColor redColor]];
[self.view addSubview:box];
[box setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
[box setAutoresizingMask:UIViewAutoresizingFlexibleWidth];
[box setAutoresizingMask:UIViewAutoresizingFlexibleTopMargin];
[box setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin];
[box setAutoresizingMask:UIViewAutoresizingFlexibleRightMargin];
If I understand you question correctly you're trying to set red view on top with height = 120
ADD EXPLANATION
You could achieve it with using constraints:
UIView *box = [[UIView alloc] init];
// Prevent creating constraints from masks automatically
box.translatesAutoresizingMaskIntoConstraints = NO;
box.backgroundColor = [UIColor redColor];
[self.view addSubview:box];
// Define metrics (constants) which will be used to create constraints.
// Key #"boxSize" - name which will be used in constraints, Value - constant
NSDictionary *metrics = #{#"boxSize" : #(120)};
// Define views that will participate in auto layout constraints.
// Key #"readBox" - name which will be used in constraints, Value - real UIView object
NSDictionary *views = #{ #"redBox" : box };
// Here we create constraints. For Vertical, and for Horizontal
// I'm using Visual language format (you can find it in Apple Documentation
// In a few words:
// H:|-0-[redBox]-0-|
// "H" - means horizontal
// "|" - short cut for parent view (in our case it is UIViewController.view)
// "[redBox]" - view name from view's dictionary
// "-0-" - gap between views (you could set number), in our case it is "|" and "[redBox]"
// "[redBox(boxSize)]" - means that view (redBox) size should be qual to "boxSize" from metrics' dictionary
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-0-[redBox]-0-|" options:NSLayoutFormatAlignAllCenterY metrics:metrics views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-0-[redBox(boxSize)]" options:NSLayoutFormatAlignAllCenterY metrics:metrics views:views]];
Apple Documentation
You are setting a UIView with a frame (0, 0, 320, 120). This frame will fit the iPhone 4 screen, as the phone screen width is 320 pixels. But you cant expect same when you run the code in iPhone 6/6s. Setting Autoresizing will not handle this. You need to use constraints/autolayout for that.
Autoresizing masks describe how a subview will resize or move when its superview is resized.
So after adding this view, if you change the phone orientation, this will resize the view in position accordingly. But you need to set the frame according to the superview first. You can set the width dynamically, like: (0, 0, self.view.frame.size.width, 120).

UIScrollView is not scrolling even with setting contentSize

My UIScrollView isn't scrolling. (I'm trying to get it to scroll horizontally) I'm setting the contentSize to be (960, 300). I have set the framesize of the UIScrollView to be width:320 height:300 in the storyboard.
I have a container view inside the scroll view that has a width of 960 points.
At 320 points, I have a subview that has a background color of brown. When I scroll, I can see the brown subview, but it bounces back when I let go of the drag.
This is my viewDidLoad method:
- (void)viewDidLoad
[super viewDidLoad];
self.scrollView.scrollEnabled = YES;
[self.scrollView setFrame:CGRectMake(0,0,320,300)];
self.scrollView.contentSize = CGSizeMake(960, 300);
UIView *subview1 = [[UIView alloc] initWithFrame:CGRectMake(320, 0, 320, 300)];
[subview1 setBackgroundColor:[UIColor brownColor]];
UIView *containerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 960, 300)];
[containerView addSubview:subview1];
[self.scrollView addSubview:containerView];
}
Here is a sample code for create a scroll view programmatically:
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIScrollView* scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 320, 300)];
scrollView.backgroundColor = [UIColor redColor];
scrollView.scrollEnabled = YES;
scrollView.contentSize = CGSizeMake(960, 300);
[self.view addSubview:scrollView];
UIView *subview1 = [[UIView alloc] initWithFrame:CGRectMake(320, 0, 320, 300)];
[subview1 setBackgroundColor:[UIColor brownColor]];
UIView *containerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 960, 300)];
containerView.backgroundColor = [UIColor greenColor];
[containerView addSubview:subview1];
[scrollView addSubview:containerView];
So, there is no issue in your code, but something wrong with your storyboard configure.
I have a few thoughts about what you are trying to accomplish. I don't know how old is your code, but today we have many better ways to work with scrollview and they don't include setting up the intrinsic size or fame.
Well, despite my doubt about the objective of the code, I tried to run it here, using storyboard, a single view, a default configured scrollView and the experiment went well, your code actually works, I think maybe you have some problem with your scrollView. I know this will sound weird but did you checked if the scrollView has the property "Scrolling Enabled" checked?
If you can give me more information about this issue, I can help you.

How to create UIScrollview inside UIScrollview programmatically

Please tell me how to set scrollview inside a scrollview like nested process.
The following code works partially.
int x=10;
int y=10;
for(int i=0; i<5; i++)
{
UIScrollView *scrollview=[[UIScrollView alloc]initWithFrame:CGRectMake(x, y, 50, 50)];
scrollview.showsVerticalScrollIndicator=YES;
scrollview.scrollEnabled=YES;
scrollview.userInteractionEnabled=YES;
scrollview.backgroundColor = [UIColor greenColor];
[self.view addSubview:scrollview];
scrollview.contentSize = CGSizeMake(50,50);
y=y+95;
}
Now all I can see are 3 scrollviews and the others are hidden. How can I create main scroll to view so that the child scrollViews are not hidden?
You need to have an initial scrollView that you then put these scrollViews in.
UIScrollView * mainScrollView = [[UIScrollView alloc] initWithFrame:self.view.frame];
mainScrollView.contentSize = CGSizeMake(50, (y + 95) * 5);
// further configure
[self.view addSubview: mainScrollView];
Then change
[self.view addSubview:scrollview];
To
[mainScrollView addSubview: scrollView];
//I have created Two Scroll view programmatically this way
UIScrollView *scrollViewOuter = [[UIScrollView alloc] initWithFrame:CGRectMake(100.0f, 100.0f, 600.0f, 600.0f)];
scrollViewOuter.backgroundColor = [UIColor scrollViewTexturedBackgroundColor];
scrollViewOuter.contentSize = CGSizeMake(2000.0f, 2000.0f);
UIScrollView *scrollViewInner = [[UIScrollView alloc] initWithFrame:CGRectMake(10.0f, 10.0f, 200.0f, 200.0f)];
scrollViewInner.backgroundColor = [UIColor whiteColor];
scrollViewInner.contentSize = CGSizeMake(2000.0f, 2000.0f);
[scrollViewOuter addSubview:scrollViewInner];
[self.window addSubview:scrollViewOuter];
//You can change frame and use in your own way
Just create a parent scrollview that is big enough to hold the 5 smaller ones, then change this line:
[self.view addSubview:scrollview];
to
[parentScrollView addSubview:scrollview];

UIScrollView won't stay in place after user finishes scrolling

Within my UIView, I have a UIScrollView which fills the first view, so than when the content is bigger than the iPhone screen size, the user can scroll the page down. It works well, but when the user finishes the scroll movement - i.e. removes his fingers, the page snaps back into it's original position. Obviously that is not what I want, how can it be avoided?
Here is the relevant code in the UIView class which declares and uses the UIScrollView class.
#implementation TestView
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Initialization code.
}
CGRect scrollViewFrame = CGRectMake(0, 0, 320, 460);
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:scrollViewFrame];
scrollView.canCancelContentTouches=NO;
[self addSubview:scrollView];
CGSize scrollViewContentSize = CGSizeMake(320, 500);
[scrollView setContentSize:scrollViewContentSize];
CGRect rectForBigRedSquare = CGRectMake(50, 50, 200, 200);
UILabel *redSquare = [[UILabel alloc] initWithFrame:rectForBigRedSquare];
[redSquare setBackgroundColor:[UIColor redColor]];
[scrollView addSubview:redSquare];
return self;
}
An additional question is this: how is it possible to make it such that the user can only scroll down, that is to see content at the bottom which was out of view, but not to scroll up so that there is space before the start of the content. In
Basically you just have to set contentSize of your scrollview according to the contents.
CGSize scrollViewSize = CGSizeMake(newWidth, newHeight);
[self.myScrollView setContentSize:scrollViewSize];
Okay, the easiest way to get this scrollview working as you desire is to ensure that content size of the scrollview is identical to the frame size of the content you wish to scroll.
You can achieve this by having a content view into which you add all the views you wish to be visible and then add that content view to the scrollview while ensuring that the content size of the scrollview is set to the content view's frame size.
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 320, 460)];
UIView* contentView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 1280, 460)];
UIView* redView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 460)];
[redView setBackgroundColor:[UIColor redColor]];
[contentView addSubview:redView];
[redView release];
UIView* blueView = [[UIView alloc] initWithFrame:CGRectMake(960, 0, 320, 460)];
[redView setBackgroundColor:[UIColor blueColor]];
[contentView addSubview:blueView];
[blueView release];
CGSize contentViewSize = contentView.frame.size;
[scrollView addSubview:contentView];
[scrollView setContentSize:contentViewSize];
[contentView release];
[self addSubview:scrollView];
The app I was working on had similar symptoms. The user could scroll down but on release the view would snap back to the initial position. The page was set up as follow:
[VIEW]
[SAFE AREA]
[SCROLL VIEW]
[CONTENT VIEW]
I strongly suspect that a combination of Auto-Layout and manual constraints caused by several adjustment iterations was causing the issue. To resolve this all constraints where removed from the View.
The Scroll View was assigned the following constraints:
Scroll View.leading = Safe Area.leading
Scroll View.top = Safe Area.top
Scroll View.trailing= Safe Area.trailing
Scroll View.bottom = Safe Area.bottom
The Content View was then assign the following constraints
ContentView.Leading = Scroll View.Leading
ContentView.top = Scroll View.top
ContentView.centerX = ScrollView.centerX
The Content View was also given the following self constraint
height = 1000

How to create a UIScrollView?

I have 4 labels on UIViewController. Now I want to know how can i make my current view to scrollable view using UIScrollView ?
You have to create and add these labels there. Something like this (Warning: untested)
UIScrollView * content = [[UIScrollView alloc] initWithFrame:self.view.bounds];
[content setContentSize:CGSizeMake(width, height)];
//Here you can add those labels to content view
[self.view addSubView:content];
To get scrolling to work you must specify size of the content (setContentSize:) larger than main view size. And don't forget to release the scroll view.
Simplest method:
-(void)scrollview
{
UIScrollView *scrollview=[[UIScrollView alloc]initWithFrame:CGRectMake(10, 10, 250, 150)];
scrollview.showsVerticalScrollIndicator=YES;
scrollview.scrollEnabled=YES;
scrollview.userInteractionEnabled=YES;
scrollview.backgroundColor = [UIColor greenColor];
[mainScrollView addSubview:scrollview];
scrollview.contentSize = CGSizeMake(250,150);
}

Resources