Creating UIScrollView Programmatically - ios

I am trying to create a UIScrollView programatically. I set a property and synthesize it.
#property (weak, nonatomic) IBOutlet UIScrollView *topScrollView;
#synthesize topScrollView;
I then have a method that does this.
[topScrollView setFrame:CGRectMake(320, 0, 320, 65)];
[topScrollView setContentSize:CGSizeMake(500, 100)];
[topScrollView setBackgroundColor:[UIColor greenColor]];
[topScrollView setScrollEnabled:YES];
[topScrollView setShowsHorizontalScrollIndicator:YES];
[topScrollView setShowsVerticalScrollIndicator:NO];
[[self view] addSubview:topScrollView];
I put this in viewDidLoad.
This does not create the scroll view. I think it is because the scroll view has not been initialized. I can do the allocation and initialization in the above method but then when I want to use it in another method it wont work. I looked at Programmatically added UIScrollView used in multiple methods but did not help too much. There is probably a simple solution that I am not aware of. What can I do to programmatically create this scroll view and be able to use it in another method(mainly to conduct animations with it).
Thanks,
Ivan

I think it is because the scroll view has not been initialized.
Right.
I can do the allocation and initialization in the above method but then when I want to use it in another method it wont work.
It will if you assign your newly minted scroll view to a property or instance variable. That's what they're for.
Oh, one other thing. You'll need to make sure that your scroll view is retained somehow. That means changing your topScrollView property to strong, or adding it to a view (which will then retain it), or both.

Simple Method:
You can created multiple times if you need means
- (void)scrollView
{
int x = 0;
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);
x=x+55;
}
}

You can use below code to add UIScrollView on yourView :-
Step 1:
Delegate "UIScrollViewDelegate" to your ViewController.h
for example:
#interface yourViewController:UIViewController<UIScrollViewDelegate>
Step 2:
//create you UIScrollView
UIScrollView *MyScrollVw= [[UIScrollView alloc]initWithFrame:CGRectMake(0 ,0 ,320 ,480)];
MyScrollVw.delegate= self;
[MyScrollVw setShowsHorizontalScrollIndicator:NO];
[MyScrollVw setShowsVerticalScrollIndicator:YES];
MyScrollVw.scrollEnabled= YES;
MyScrollVw.userInteractionEnabled= YES;
[yourView addSubview:MyScrollVw];
MyScrollVw.contentSize= CGSizeMake(320 ,1500);//(width,height)
Step 3:
you want to implement the scrollView Delegates in ViewController.m
-(UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView{
return imgView;
}
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
NSLog(#"Did end decelerating");
//do your code here
}
-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
NSLog(#"Did scroll");
//do your code here
}
-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{
NSLog(#"Did end dragging");
//do your code here
}
-(void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView{
NSLog(#"Did begin decelerating");
//do your code here
}
-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{
NSLog(#"Did begin dragging");
//do your code here
}

The straightforward programmatically way in SWIFT
To wrap it up
Create a UIScrollView
let scrollView = UIScrollView()
scrollView.translatesAutoresizingMaskIntoConstraints = false
Use a Single Child View to Hold All of Your Content Subviews
let contentView = UIView()
contentView..translatesAutoresizingMaskIntoConstraints = false
Usually, you only want your content to scroll in one direction. In most cases to scroll vertically. Therefore set the width of the content view to be the width of the scroll view.
NSLayoutConstraint.activate([
contentView.widthAnchor.constraint(equalTo: scrollView.widthAnchor)
Attach four constraints (top, bottom, left, right) from our single content view to the scroll view.
contentView.topAnchor.constraint(equalTo: scrollView.topAnchor),
contentView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
contentView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
Make sure you have constraints attached to all four sides of the content view so that it will expand to the size of your content.
// After Adding your subviews to the contentView make sure you've those two constraints set:
firstSubView.topAnchor.constraint(equalTo: contentView.topAnchor),
.
.
.
lastSubView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
])
Reference: Using UIScrollView with Auto Layout in iOS

Related

Where should I put AutoLayout code?

I'm using PureLayout to implement AutoLayout of subviews in a UIView. But I don't know the best practice of organizing the code.
Should I put the AutoLayout related code in the init of the UIView, or the overridden methods such as updateConstraints and layoutSubviews?
For example, I want to create a subclass of UIView called PHView, and for any phview, there is a subview called centerView, it is always at the center of phview, and width/height is 0.3*phview's width/height.
https://www.dropbox.com/s/jaljggnymxliu1e/IMG_3178.jpg
#import "PHView.h"
#import "Masonry.h"
#interface PHView()
#property (nonatomic, assign) BOOL didUpdateConstraints;
#property (nonatomic, strong) UIView *centerView;
#end
#implementation PHView
- (instancetype)init {
self = [super init];
if (self) {
self.backgroundColor = [UIColor redColor];
self.translatesAutoresizingMaskIntoConstraints = NO;
}
return self;
}
- (UIView *)centerView {
if (!_centerView) {
_centerView = [UIView new];
_centerView.backgroundColor = [UIColor yellowColor];
[self addSubview:_centerView];
}
return _centerView;
}
-(void)updateConstraints {
if (!_didUpdateConstraints) {
_didUpdateConstraints = YES;
[self.centerView mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerX.equalTo(self.mas_centerX);
make.centerY.equalTo(self.mas_centerY);
make.width.equalTo(self.mas_width).multipliedBy(0.3);
make.height.equalTo(self.mas_height).multipliedBy(0.3);
}];
}
[super updateConstraints];
}
#end
'didUpdateConstraints' aims to indicate you have added constraints, so you will only add constraints once.
in UIViewController:make phview top bottom left right 20 to the margin.
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor greenColor];
PHView *myView = [PHView new];
[self.view addSubview:myView];
[myView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.view).with.insets(UIEdgeInsetsMake(20, 20, 20, 20));
}];
}
You should add constraints when you are sure that view has been added to its superview. Basically, you should do it in superview's class any point after addSubview: is called.
To answer your questions:
1- in init methods, can you be sure of that view has been added as subview to a superview? it wouldn't be safe to assume that. maybe you can add constraints in init method of superview
2- layoutSubviews is in where autolayout code actually works. you can't add constraints in layoutSubviews. already playing with autolayout constraints are not cheap, therefore you should add/remove them as few as possible, doing so in a method that is called multiple times (i.e. layoutSubviews) is not the best practice.
Mechanism of autolayout is going to inner view from outer view, so subviews do not actually concern about constraints. it is superview's responsibility
Hope this helps you by understanding controller’s view hierarchy
How View Controllers Participate in the View Layout Process
The view controller’s view is resized to the new size.
If autolayout is not in use, the views are resized according to their autoresizing masks.
The view controller’s viewWillLayoutSubviews method is called.
The view’s layoutSubviews method is called. If autolayout is used to configure the view hierarchy, it updates the layout constraints by executing the following steps:
a.The view controller’s updateViewConstraints method is called.
b.The UIViewController class’s implementation of the updateViewConstraints method calls the view’s updateConstraints method.
c. After the layout constraints are updated, a new layout is calculated and the views are repositioned.
The view controller’s viewDidLayoutSubviews method is called.
Please refer this for more details

UIScrollView disable scrolling when removeFromSuperView

I have a .xib file who have one view, one tab bar and other 3 scrollViews, when the user select a new tab bar item I execute this code:
//Views e Scrolls
IBOutlet UIView *myView;
IBOutlet UIScrollView *myScroll;
IBOutlet UIScrollView *myScroll2;
IBOutlet UIScrollView *myScroll3;
#property (nonatomic) UIScrollView *scroll;
-(void)tabBar:(UITabBar *)tabBar
didSelectItem:(UITabBarItem *)item{
NSArray *viewsToRemove = [myView subviews];
for (UIView *v in viewsToRemove) {
[v removeFromSuperview];
}
if(item.tag == 1){
self.title = #"scroll 1";
scroll = myScroll;
}
if(item.tag == 2){
self.title = #"scroll 2";
scroll = myScroll2;
}
if(item.tag == 3){
self.title = #"scroll 3";
scroll = myScroll3;
}
scroll.contentSize = scroll.frame.size;
scroll.frame = myView.frame;
scroll.scrollEnabled = YES;
[myView addSubview:scroll];
[myView setBackgroundColor:[UIColor clearColor]];
}
This code works great, but when I select one of the scrolls that had previously been removed from view, they lose their scrolling (which had not happened before), why this is happening and how to solve?
It's this line here:
scroll.contentSize = scroll.frame.size;
When your UIScrollView's contentSize matches its frame.size, it doesn't need to scroll (by definition). You need to figure out the actual size of its content and use that. You can at least demonstrate the correct functionality (even if it isn't with correct values) by doing this:
scroll.contentSize = CGSizeMake(scroll.frame.size.width*2, scroll.frame.size.height*2);
You'll have to find the real content size for yourself, though.
Assuming I understand your intention correct: Removing a scollview from it superview will also remove it from the responderchain. It cant no longer respond to touch event - thus wont scroll.
The question itself and your purpose of code is not very clear. You may try to explain further. And check this line -
scroll.contentSize = scroll.frame.size;
A UIScrollView whose contentSize <= frame.size, will not scroll. To make it scroll, contentSize should be bigger than frame.size
You should usually set the contentSize depending on the amount of content (subviews) for the scrollView.
In the next line you changed the frame.size, but I do not think that makes the frame size smaller than contentSize in your case.

Objective-C - When I add a subview, my subview is going out of the screen at the bottom. Misunderstanding of frames?

Im practicing objective-C, and I try to do everything programmatically.
I'm making a simple view that I add on my view of the ViewController, but this subview is going out of the screen.
When I set my frame, the position for the X and Y are respected, but the rest, no...
Here is the screenshot of my result :
As you can see... The red subview is going out of the screen.
Here is my loadView where I add that subview :
HomeViewController.m - loadView
-(void)loadView
{
self.view = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIView *subview = [[UIView alloc] initWithFrame:CGRectMake(15, 15, self.view.frame.size.width - 30, self.view.frame.size.height - 30)];
[subview setBackgroundColor:[UIColor redColor]];
[self.view addSubview:subview];
}
For the padding, I did put 15 for the position x and y... And for the frame, I did calculate with the size of the self.view by removing paddings... As you see, it works well for the width, but for the height, it is a big fail. It goes outside the screen.
In my AppDelegate.h, I set the navigationController.navigationBar.translucent = NO;, in order to that when I set position for x, and y, it starts well after the navigationBar .
I don't understand this weird behavior for the height... If someone has a good explanation for this please.
Thanks
First, you shouldn't rely on the value of self.view in viewDidLoad. It is set to a correct value later, in viewWillAppear:. You can keep your code, if you make your subview resize automatically when self.view is displayed. For that, set autoresizingMask on the subview:
subview.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
(or add an equivalent set of constraints if you use Auto Layout.)
Also, I recommend to use bounds instead of frame:
UIView *subview = [[UIView alloc] initWithFrame:CGRectMake(15, 15, self.view.bounds.size.width - 30, self.view.bounds.size.height - 30)];
It doesn't make a difference here, but it often does, e.g. if you calculate the frame's x and y based on the parent frame.
loadView method just creates the view. At the point when loadView gets called there is no information about final view frame hence its children views cannot be placed properly.
The right place to update your children views' frames is viewDidLayoutSubviews:
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
// update child view frame here
}
Remarks: you can define auto-layout constraints of your child view in code and they will be automatically applied to child views when view controller's view gets resized.

Page scrolling not working with Autolayout enabled

I have the following ViewController:
It contains two UILabels at top, an UIImageView, below it a UITextView and below this a UIButton. I have arranged them using the Interface Builder following the blue line. All of this controls are inside a UIScrollView:
[self.scrollView setContentSize:CGSizeMake(320, 660)];
[self.scrollView addSubview:self.descriptionText];
[self.scrollView addSubview:self.descriptionImage];
[self.scrollView addSubview:self.titleLabel];
[self.scrollView addSubview:self.feedNameLabel];
[self.scrollView addSubview:self.load];
So when enabling Autolayout option, I just selected the ViewControler and then "Reset to Suggested Constraints in Description View Controller". But when I run the app, the scroll still appears for the entire page, but the only control scrolling is the UIButton. When scrolling up it will scroll below the UITextView.
I have made the UITextView to resize depending on the text, so I want my UIButton to always have the same distance to the UITextView. For that I have also set Vertical Spacing to the UIButton, but like this I don't have any scroll to my page.
Using the Autolayout for the first time, can I get some suggestions on what am I doing wrong ?
edit:
- (void)viewDidLoad
{
[super viewDidLoad];
UIView *contentView = [[UIView alloc]initWithFrame:CGRectMake(0,0,320,660)];
[contentView addSubview:self.descriptionText];
[contentView addSubview:self.descriptionImage];
[contentView addSubview:self.titleLabel];
[contentView addSubview:self.feedNameLabel];
[contentView addSubview:self.load];
contentView.translatesAutoresizingMaskIntoConstraints = YES;
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 320, 660)];
//[scrollView setContentSize:CGSizeMake(320, 660)];
[scrollView addSubview:contentView];
[self.view addSubview:scrollView];
// to resize UITextField
[self.descriptionText sizeToFit];
[self.descriptionText layoutIfNeeded];
self.descriptionText.scrollEnabled = NO;
}
Autolayout is a bit tricky when it comes to UIScrollView subviews. I would recommend:
1-Embed all your controls (descriptionText + descriptionImage + titleLabel + feedNameLabel + load) into a UIView first, say contentView:
UIView *contentView = [[UIView alloc]initWithFrame:CGRectMake(0,0,320,660)];
//add all controls as subviews to contenView
2-Add contentView as subview of self.scrollView.
3-Keep the translatesAutoresizingMaskIntoConstraints property of contentView to YES.
I recommend you read this technical note from Apple here.
If you are using AutoLayout you don't have to set the content size of your scroll view. In fact, I believe it has no effect at all. It is set automatically from the constraints you are setting up. The trick is that you have to have at least one constraint related to every side of the scroll view, otherwise the content size will be wrong and it won't scroll. So for example, if you would have a really large image in it you would need 4 constraints connecting the sides of the UIImageView to the sides of the UIScrollView. You can read about this more here.

UIButton does not work in uiscrollView

I have a scrollView that has a UIView as subview. This has as UIView subview a UIButton. Only the scrollView is connected to the outlet, the rest is all in code. The button does not respond to touches, not turns blue when touched. What can I do to make it work?
This is the code:
- (void)viewDidLoad
{
[super viewDidLoad];
//......
self.buttonHome = [UIButton buttonWithType:UIButtonTypeCustom];
[self.buttonHome addTarget:self action:#selector(pressedHome:)
forControlEvents:UIControlEventTouchUpInside];
//....
self.containerView =[[UIView alloc]initWithFrame:self.scrollView.frame];
self.containerView.userInteractionEnabled=YES;
[self.scrollView addSubview:self.containerView];
[self.containerView addSubview:self.buttonHome];
}
-(void) pressedHome:(id)sender{
//....
}
You must set content size of your view. That must be greater than or equal to the content size of scrollView.
Because default size of your view is 320*480 (3.5" retina) and 320*568 (4" retina). So increasing height of view as-
self.view.frame=CGRectMake(0, 0, 320, 700);
Then adding this as the subview of your scrollView.
will get you to the solution.
I resolve it
- (BOOL)touchesShouldCancelInContentView:(UIView *)view
{
return ![view isKindOfClass:[UIButton class]];
}
Since the problem is to put a button UIButton
Make sure your scrollview isn't swallowing the touches:
Check this other SO question
Make containerView view height same as scrollView Height.

Resources