I want to have a UIScrollView on a panoramic image so that you can pan across the image horizontally. For some reason, the code I'm using does not allow the user to scroll horizontally.
Why won't it scroll horizontally?
Here is the code:
[scrollView setFrame:CGRectMake(0,160,1338,269)];
scrollView.scrollEnabled = YES;
scrollView.userInteractionEnabled = YES;
UIImageView *panorama = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, 1338, 269)];
panorama.image = [UIImage imageNamed:#"Panorama.png"];
scrollView.contentSize = CGSizeMake(320, 269);
[scrollView addSubview:panorama];
[self.view addSubview:scrollView];
The scrollView's frame is stationary, the contentSize is the one that allows the scrolling.
scrollView.contentSize = CGSizeMake(320, 269);
Should be replaced with
scrollView.contentSize = CGSizeMake(1338, 269);
To allow a large contentSize to be scrolled through. The frame should be smaller.
scrollView.frame = CGRectMake(0,0,320, 269);
Think of the frame as the area that you see on the screen at any given time. Think of the contentSize as the area that the scrollView is capable of showing (much larger).
You are doing it other way around. Scroll view's content size must be set to the size of the large image you are trying to scroll.
Change contentsize to size of the uiimageview and change scrollview frame to 320,269
Related
I am using UIScrollview and set it's content size same as view's height.
I need to make my scrollview scrolls same in both side (Up / Down).
It will come back in same position I mean whenever we scrolls in top direction and leave scrolling it will come y = 20 pixels so when we scroll in bottom direction it will come back on the 20 pixels when we leave scrolling...
For setting scrollview content size
CGSize scrollViewContentSize = CGSizeMake(self.view.frame.size.width, self.view.frame.size.height);
[self.scrollViewBg setContentSize:scrollViewContentSize];
Also I have tried to give content offset
[scrollViewBg setContentOffset:CGPointMake(0, 0) animated:YES];
but not solved my problem.
I just updated and make scrollview's height similar to view's height and increase it's content height like this and it solved my problem.
-(void)viewDidAppear:(BOOL)animated{
float space = 2.5; // This is for making scroll view scrolls and on same positions from both sides.
CGSize scrollViewContentSize = CGSizeMake(self.view.frame.size.width,
self.view.frame.size.height+space);
[self.scrollViewBg setContentSize:scrollViewContentSize];
[scrollViewBg setContentOffset:CGPointMake(0, 0) animated:YES];
}
After 2 days behind the implementation of a UIScrollView, I give up to do it by myself, despite It should be simple but I am not able to make it.
I need a vertical scrollview with Autolayout (I have 2 backgrounds images that need to scale), I have Xcode 7.2, iOs deployment target 9.0 and I am using the storyboard
The scrollview is a "new account" form with a lot of content, So it can have a fixed height
I tried lots of options but no one of them work, at the momment I have a scrollview with a "content view" with all of the components.
Problems:
1.- How can I put more content in the scrollview? If I try to put more compoments(more than the height of ViewControler), it automatically moves my components to be inside the ViewController rectangle
2.- How I can make it scrollable? If I pin the content view to the top, bottom, left, right and make it with fixed height (like 1000) it doesn't scroll
Any help would be appreciated
Scrollview will scroll until the end of last UI object. But this is not the default behaviour of the scrollview. You should explicitly set the contentSize on your scrollview. Check below example.
-(void)addImage {
int count = 20;
UIScrollView * scView = [[UIScrollView alloc] init];
scView.frame = CGRectMake(30, 60, self.view.frame.size.width, 60);
UIButton * btn;
UIImageView * imgV;
float imgWidth = 44;
for(int i=0;i<count;i++) {
imgV = [[UIImageView alloc] init];
imgV.frame = CGRectMake(i*imgWidth, 0, imgWidth, 44);
imgV.image = [UIImage imageNamed:#"gitar"];
[scView addSubview:imgV];
}
[scView setContentSize:CGSizeMake(imgWidth*count, 50)];
[scView setBackgroundColor:[UIColor redColor]];
[self.view addSubview:scView];
}
What the above method does is, it will add 20 images in scrollview and at the end after adding all the images to scrollview in for loop I set contentSize to Scrollview. This step makes my scrollview to scroll until the content available. My example showing for Horizontal scroll, you have to change this behaviour to vertical scroll.
Consider a UIScrollView with a single subview. The subview is an UIImageView with the following size constraints:
Its height must be equal to the height of the UIScrollView.
Its width must be the width of the image scaled proportionally to the height of the UIImageView.
It is expected that the width of the UIImageView will be bigger than the width of the UIScrollView, hence the need for scrolling.
The image might be set during viewDidLoad (if cached) or asynchronously.
How do you implement the above using autolayout, making as much use as possible of Interface builder?
What I've done so far
Based on this answer I configured my nib like this:
The UIScrollView is pinned to the edges of its superview.
The UIImageView is pinned to the edges of the UIScrollView.
The UIImageView has a placeholder intrinsic size (to avoid the Scrollable Content Size Ambiguity error)
As expected, the result is that the UIImageView is sized to the size of the UIImage, and the UIScrollView scrolls horizontally and vertically (as the image is bigger than the UIScrollView).
Then I tried various things which didn't work:
After loading the image manually set the frame of UIImageView.
Add a constraint for the width of the UIImageView and modify its value after the image has been loaded. This makes the image even bigger (?!).
Set zoomScale after the image is loaded. Has no visible effect.
Without autolayout
The following code does exactly as I want, albeit without autolayout or interface builder.
- (void)viewDidLoad
{
{
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.view addSubview:scrollView];
self.scrollView = scrollView;
}
{
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, self.scrollView.frame.size.width, self.scrollView.frame.size.height)];
imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.scrollView addSubview:imageView];
self.scrollView.contentSize = imageView.frame.size;
self.imageView = imageView;
}
}
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
[self layoutStripImageView];
}
- (void)layoutStripImageView
{ // Also called when the image finishes loading
UIImage *image = self.imageView.image;
if (! image) return;
const CGSize imageSize = image.size;
const CGFloat vh = self.scrollView.frame.size.height;
const CGFloat scale = vh / imageSize.height;
const CGFloat vw = imageSize.width * scale;
CGSize imageViewSize = CGSizeMake(vw, vh);
self.imageView.frame = CGRectMake(0, 0, imageViewSize.width, imageViewSize.height);
self.scrollView.contentSize = imageViewSize;
}
I'm trying really hard to move to autolayout but it's not being easy.
Under the autolayout regime, ideally the UIScrollView contentSize is solely determined by the constraints and not set explicitly in code.
So in your case:
Create constraints to pin the subview to the UIScrollView. The constraints have to ensure the margin between the subview and the scroll view are 0. I see that you have already tried this.
Create a height and a width constraint for your subview. Otherwise, the intrinsic size of the UIImageView determines its height and width. At design time, this size is only a placeholder to keep Interface Builder happy. At run time, it will be set to the actual image size, but this is not what you want.
During viewDidLayoutSubviews, update the constraints to be actual content size. You can either do this directly by changing the constant property of the height and width constraint, or calling setNeedsUpdateConstraints and overriding updateConstraints to do the same.
This ensures that the system can derive contentSize solely from constraints.
I've done the above and it works reliably on iOS 6 and 7 with a UIScrollView and a custom subview, so it should work for UIImageView too. In particular if you don't pin the subview to the scroll view, zooming will be jittery in iOS 6.
You may also try creating height and width constraints that directly reference a multiple of the height and width of the scroll view, but I haven't tried this other approach.
translatesAutoresizingMaskIntoConstraints is only required when you instantiate the view in the code. If you instantiate it in the IB it's disabled by default
In my opinion the UIImageView should fill the ScrollView. Later I'd try setting the zoom of the scrollview to the value that suits you well so the image can only be panned in one direction
In my case it was a full width UIImageView the had a defined height constraint that causing the problem.
I set another constraint on the UIImageView for the width that matched the width of the UIScrollView as it is in interface builder then added an outlet to the UIViewController:
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *imageViewWidthConstraint;
then on viewDidLayoutSubviews I updated the constraint:
- (void) viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
self.imageViewWidthConstraint.constant = CGRectGetWidth(self.scrollView.frame);
}
This seemed to do the trick.
I faced a strange problem, the scrollview does not scroll down, only scroll up. I have scrollview in my app, please look at my coding
.....
self.scrollView = [[UIScrollView alloc] initWithFrame: CGRectMake(0, 0, 320,427)];
[self.view addSubViews: self.scrollView];
UIView *blueView = [[UIView alloc] initWithFrame: CGRectMake(0, 47, 320, 320)];
blueView.backgroundColor = [UIColor blueColor];
[self.scrollView addSubViews: blueView];
self.scrollView.contentSize = CGSize(320, 640);
....
My problem is no matter what value I changed contentSize, my ScrollView only scroll up, not scroll down. I want user can move blueView to the top or bottom of iPhone screen from the original position.
do you have this problem?
The Problem
It looks like your issue is with how you're orienting blueView within scrollView. You're setting the frame of blueView to the CGRect (0, 47, 320, 320). When you set the frame like this, one of the things you're implicitly saying is:
The top edge of blueView is 47 points below the top edge of scrollView.
That's a perfectly valid thing to say, but it's what's causing the problem you describe. scrollView won't scroll down because it is designed to start, by default, with the rect (0, 0, 320, 480) in view. The contentSize property only indicates the size of the content within the UIScrollView, not its positioning. When you set it, you're basically telling scrollView:
Starting from your content origin, the content is 320 points wide and 640 points tall.
Thus, scrollView won't scroll up because, as far as it knows, there's no content above the coordinate (0, 0).
The Solution
There are three steps you'll need to take to get the functionality you want.
Set the contentSize to be just big enough to allow blueView to scroll all the way up and down.
Put blueView in the vertical center of scrollView.
Scroll the scrollView so that it is initially centered on blueView.
Set the contentSize to be just big enough to allow blueView to scroll all the way up and down.
We'll want to calculate the correct value of the contentSize property. It is of the type CGSize, so we need two parts: width and height. width is easy – since you don't seem to want horizontal scrolling, just make it the width of the screen, 320. Height is a little more tricky. If you want blueView to just touch the top and bottom of the screen when scrolled up or down, you need to do some math. The correct total height will be double the height of the screen, minus the height of blueView. So:
scrollView.contentSize = CGSizeMake(320, 480 * 2.0 - blueView.frame.size.height);
Put blueView in the vertical center of scrollView.
That's easy; just set the center property of blueView:
blueView.center = CGPointMake(160, scrollView.contentSize.height / 2.0);
Scroll the scrollView so that it is initially centered on blueView.
If you check the Apple UIScrollView documentation, you'll see an instance method - (void)scrollRectToVisible:(CGRect)rect animated:(BOOL)animated. This is exactly what you need to scroll scrollView programmatically. The rect you want is the one centered on blueView, with the size of the iPhone screen. So:
CGRect targetRect = CGRectMake(0, scrollView.contentSize.height / 2.0 - 240,
320, 480);
[scrollView scrollRectToVisible:targetRect animated:NO];
Make sure you do this scrolling in viewWillAppear, so it's ready right when the user sees the view.
That should be it. Let me know if you have any questions!
The content size of the scrollView should be the size of the view it is holding. This is how the code should be, try something like this.
self.scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(X, Y, W, H1)];
UIView * blueView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, W, H2)];
self.scrollView .contentSize = blueView.frame.size;
[self.scrollView addSubview:blueView];
[self.view addSubView: self.scrollView];
Thanks to Riley. Here, the H1 is the height of the UIScrollVIew and H2 is the height of the blueView and (H1 < H2).
I was wondering what was the real meaning of using initWithFrame with this scrollView, because we also set the dimensions of the scrollView after that, and we add the scrollView as a subView of the view.
So why do we need to specify this initWithFrame? I actually don't really understand it when the frame is self.view.frame (I would understand it better if we set a different rectangle, such as 0,0 50,50)
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:self.view.frame];
scrollView.contentSize = CGSizeMake(847, 1129);
UIImageView *imageView = [[UIImageView alloc] initWithImage: image];
[scrollView addSubview:imageView];
[self.view addSubview:scrollView];
Thanks
self.view in this case is the view containing the scrollview, so the scrollview fills the entire view when set to self.view.frame. Frame and content size are different things - frame of scrollview defines visible part of scrollview, and content size defines the size of scrollable (zoomable) content inside your scrollview.