I am zooming an UIImageView in a UIScrollView on double tap. Works fine when it is set up in Interface Builder. The magic that I see here is, the contentSize of the scrollView increses/decreases by itself as I zoom in/out. I check for the contentSize in viewDidLoad, its not zero.
I tried the same by removiing the scrollView and imageView from IB and created them programmatically. After adding the imageView and tapGestureRecognizer, I fail to zoom here. When I checked for the reason, the contentSize remains zero everywhere.
When I add them in IB/xib, I am doing nothing with the contentSize. I am not setting it anywhere. I find its woking fine, the contentSize adjusts automatically where I need. But, its is not replicating when I create the scrollView programmatically.
How Can I make it work? I welcome your suggestions.
Here is my code for the reference.
ScrollView created in IB
- (void)viewDidLoad {
[super viewDidLoad];
UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleDoubleTap:)];
[doubleTap setNumberOfTapsRequired:2];
[imageView addGestureRecognizer:doubleTap];
imageScrollView.minimumZoomScale=1.01;
imageScrollView.maximumZoomScale=5;
imageScrollView.zoomScale=1.01;
NSLog(#"height = %f width = %f", imageScrollView.contentSize.height, imageScrollView.contentSize.width);
}
NSLog says
height = 449.479767 width = 320.000000
ScrollView created programmatically
- (void)viewDidLoad {
[super viewDidLoad];
imageScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 320, 460)];
imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 320, 460)];
UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleDoubleTap:)];
[doubleTap setNumberOfTapsRequired:2];
[imageView addGestureRecognizer:doubleTap];
imageScrollView.minimumZoomScale=1.01;
imageScrollView.maximumZoomScale=5;
imageScrollView.zoomScale=1.01;
[self.view addSubview:imageScrollView];
[self.imageScrollView addSubview:imageView];
NSLog(#"height = %f width = %f", imageScrollView.contentSize.height, imageScrollView.contentSize.width);
}
NSLog says
height = 0.000000 width = 0.000000
These properties
self.minimumZoomScale=1.01;
self.maximumZoomScale=5;
self.zoomScale=1.01;
should be properties of your scrollView:
imageScrollView.minimumZoomScale=1.01;
imageScrollView.maximumZoomScale=5;
imageScrollView.zoomScale=1.01;
Then additionally you need to set the contentSize property (when using IB you don't need to set it as it is loaded from the xib file):
imageScrollView.contentSize = CGSizeMake(width,height);
using appropriate values.
Additionally you need to set the delegate:
imageScrollView.delegate = self;
And at least one delegate method indicating which subview to zoom:
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return scrollView.subviews[0];
}
(actually you are better off setting a tag on the subview or assigning it to a property to get a reference to it, as scrollViews also have built-in subviews)
Is auto layout enabled in your xib? If so, IB is setting up constraints betwixt the image view and the scroll view, and at runtime auto layout uses the constraints to set the scroll view's content size. When you create the views in code, you're not adding enough constraints and not setting the content size directly, so the content size remains zero.
Read Tech note TN2154 for more information.
The similar answer is given by #Pheel about the zooming of image inside a scrollview using coding.
The link of the answer is given below:
IOS: add imageview in a scrollview to have zoom
Related
I'm new to ios programming.
I want users to be able to scroll screen, so I initialized UIScrollView and add other views into the instance of UIScrollView.
But, I'm not able to scroll screen and I don't see a scrollbar.
This is my code I wrote.
This view controller extends from UIViewController.
What is wrong with this?
Please help me.
Thank you very much!!
- (void)viewDidLoad
{
[super viewDidLoad];
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:self.view.frame];
scrollView.contentSize = self.view.frame.size;
[self.view addSubview:scrollView];
UIImage *img = [UIImage imageNamed:#"avatar.png"];
UIImageView *profileImg = [[UIImageView alloc] initWithImage:img];
CGPoint newPoint = self.view.center;
newPoint.y = 300;
profileImg.center = newPoint;
[scrollView addSubview:profileImg];
}
you need to set a content size and scroll enabled
[scrollView setFrame:self.view.bounds]; // not needed to be able to scroll
[scrollView setScrollEnabled:YES]; // needed to be able to scroll, defaults to YES
[scrollView setContentSize:CGSizeMake(320, 500)]; // the important part that is needed to be able to scroll
the 320 and 500 are desired maxed distances of scrolling to, this is not the actual scroll distance but the width and height of pixels that will be shown, you can think of the scroll view being a window and these values being the world outside
that is where you main error is, the width and height of your scroll view is the same width and height of the context size, therefore; it's like you are looking through a 1ft by 1ft window in to a room that is only 1ft by 1ft
UIScrollView Documentation
My UIScrollView is populated with a large content and then zoomed with "setZoomScale:animated", such that it fits into the scrollview frame. The view appears properly zoomed out, and properly positioned in the scrollview. However, the user is able to scroll outside the content (the scrollview background color shows). It seems that he can scroll as far as the original content size (as if the content was not zoomed out).
Strangely enough, after the user zooms manually the first time, everything works fine, and the scrollview is constrained to the content size again.
I have searched the web, and gone through the Apple docs and examples, but could not find anyone having the same problem. The Apple examples seem to show the same thing as I do.
My content is a custom view that is derived from UIView, and whose CALayer is replaced by a CATiledLayer (as in the Apple examples). The drawing I do myself in drawLayer:inContext:.
Here is a code snippet from MyScrollViewController:
- (void)loadView {
CGRect frame = [UIScreen mainScreen].applicationFrame;
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:frame];
scrollView.maximumZoomScale=1.0;
scrollView.delegate = self;
... setup theContentView ...
scrollView.contentSize = theContentView.bounds.size;
scrollView.contentInset = UIEdgeInsetsMake(20, 20, 20, 20);
[scrollView addSubview:theContentView];
self.view = scrollView;
double zoomScale = 0.5; // hardcoded for testing
[scrollView setZoomScale:zoomScale animated:NO];
NSLog(#"zoomscale=%g contentview=%# contentsize=%#", zoomScale, NSStringFromCGSize(theContentView.bounds.size), NSStringFromCGSize(scrollView.contentSize));
[scrollView release];
}
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return theContentView;
}
The NSLog statement shows a correctly set contentSize and zoomscale...
Hoping there is something obvious that I am missing...
I ended up setting the zoomScale on the next runloop (instead of directly from loadView), which worked:
- (void) loadView {
CGRect frame = [UIScreen mainScreen].applicationFrame;
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:frame];
scrollView.maximumZoomScale=1.0;
scrollView.delegate = self;
... setup theContentView ...
scrollView.contentSize = theContentView.bounds.size;
scrollView.contentInset = UIEdgeInsetsMake(20, 20, 20, 20);
[scrollView addSubview:theContentView];
self.view = scrollView;
// set the zoom level on the next runloop invocation
[self performSelector:#selector(initialZoom) withObject:nil afterDelay:0];
[scrollView release];
}
- (void) initialZoom {
double zoomScale = 0.5; // hardcoded for testing
UIScrollView *scrollView = (UIScrollView *) self.view;
[scrollView setZoomScale:zoomScale animated:NO];
}
Another possibility, suggested by Maverick1st, is to set the zoomscale from viewDidAppear, rather than LoadView. That approach has the added advantage that it shows an animation of the zooming, indicating to the user that the view starts in a zoomed state.
- (void) viewDidAppear {
double zoomScale = 0.5; // hardcoded for testing
UIScrollView *scrollView = (UIScrollView *) self.view;
[scrollView setZoomScale:zoomScale animated:YES];
}
I'm new at this but you could try clipping subviews. I created a scroll view with interface builder, and there in the attributes inspector was a checkbox clip subviews, so i think you should do something like that, only programmatically. Hope that helps.
Ok so this is kind of a strange issue, but I have got two scrollViews that each have a nested imageView so as to allow for panning and zooming of an image. The tow scrollView/imageView combo's are showing up just great, and because the image is bigger than my scrollView size, I can pan just great. Here is my viewDidLoad:
-(void) viewDidLoad
{
// set the image to be displayed, pic your own image here
imageView = [[MyImage alloc] initWithImage: [UIImage imageNamed: #"newBackground.png"]];
// yes we want to allow user interaction
[imageView setUserInteractionEnabled:YES];
// set the instance of our myScrollView to use the main screen
scrollView = [[MyScroll alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
// turn on scrolling
[scrollView setScrollEnabled: YES];
// set the content size to the size of the image
[scrollView setContentSize: imageView.image.size];
// add the instance of our myImageView class to the content view
[scrollView addSubview: imageView];
// flush the item
[imageView release];
// set max zoom to what suits you
[scrollView setMaximumZoomScale:1.0f];
// set min zoom to what suits you
[scrollView setMinimumZoomScale:0.25f];
// set the delegate
[scrollView setDelegate: self];
// scroll a portion of image into view (my image is very big) :)
//[scrollView scrollRectToVisible:CGRectMake(100, 100, 320, 440) animated:NO];
// yes to autoresize
scrollView.autoresizesSubviews = YES;
// set the mask
scrollView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
// set the view
//self.view =scrollView;
[scrollView setFrame:CGRectMake(0, 0, 320, 230)];
[self.view addSubview:scrollView];
imageView1 = [[MyImage alloc] initWithImage: [UIImage imageNamed: #"newBackground.png"]];
// yes we want to allow user interaction
[imageView1 setUserInteractionEnabled:YES];
// set the instance of our myScrollView to use the main screen
scrollView1 = [[MyScroll alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
// turn on scrolling
[scrollView1 setScrollEnabled: YES];
// set the content size to the size of the image
[scrollView1 setContentSize: imageView1.image.size];
// add the instance of our myImageView class to the content view
[scrollView1 addSubview: imageView1];
// flush the item
[imageView1 release];
// set max zoom to what suits you
[scrollView1 setMaximumZoomScale:1.0f];
// set min zoom to what suits you
[scrollView1 setMinimumZoomScale:0.25f];
// set the delegate
[scrollView1 setDelegate: self];
// scroll a portion of image into view (my image is very big) :)
//[scrollView scrollRectToVisible:CGRectMake(100, 100, 320, 440) animated:NO];
// yes to autoresize
scrollView1.autoresizesSubviews = YES;
// set the mask
scrollView1.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
// set the view
//self.view =scrollView;
[scrollView1 setFrame:CGRectMake(0,240,320,230)];
[self.view addSubview:scrollView1];
//[[self view] addSubview:scrollView];
}
No as for zooming, when I pinch to zoom on the FIRST scrollView/imageView pair, it works beautifully, no problems whatsoever. It zooms and pans no problem. BUT when I pinch to zoom my second scrollView, it pans the image of the SECOND, and zooms the image of the FIRST. SO my SECOND scrollView zooms the FIRST's image. Huh? I have no idea why that would be happening.
All I want is to have to separate images viewed that can be independently panned and zoomed.
Any thoughts as to what might be happening?
Thanks!
Your viewForZoomingInScrollView: method is probably returning the same image view for both scroll views. It needs to look at which scroll view is being passed in and return the corresponding image view.
Ok I have been working on this feature for some time now and it is driving me nuts. I have looked all around the web and can't seem to find any answer. I want to have a view that has two UIScrollviews, that themselves contain a UIImageView each. So I can have to images on my view that are SEPARATELY scroll/zoomable.
It seems pretty simple, but I just can't get it to work. I setup the ScrollView/ImageView pairs in IB and hooked them up ok. Now my viewDidLoad code looks like this:
-(void) viewDidLoad
{
// set the image to be displayed, pic your own image here
imageView = [[MyImage alloc] initWithImage: [UIImage imageNamed: #"newBackground.png"]];
// yes we want to allow user interaction
[imageView setUserInteractionEnabled:YES];
// set the instance of our myScrollView to use the main screen
scrollView = [[MyScroll alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
// turn on scrolling
[scrollView setScrollEnabled: YES];
// set the content size to the size of the image
[scrollView setContentSize: imageView.image.size];
// add the instance of our myImageView class to the content view
[scrollView addSubview: imageView];
// flush the item
[imageView release];
// set max zoom to what suits you
[scrollView setMaximumZoomScale:1.0f];
// set min zoom to what suits you
[scrollView setMinimumZoomScale:0.25f];
// set the delegate
[scrollView setDelegate: self];
// scroll a portion of image into view (my image is very big) :)
//[scrollView scrollRectToVisible:CGRectMake(100, 100, 320, 440) animated:NO];
// yes to autoresize
scrollView.autoresizesSubviews = YES;
// set the mask
scrollView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
// set the view
//self.view =scrollView;
[scrollView setFrame:CGRectMake(0, 0, 320, 240)];
self.view =scrollView;
imageView1 = [[MyImage alloc] initWithImage: [UIImage imageNamed: #"newBackground.png"]];
// yes we want to allow user interaction
[imageView1 setUserInteractionEnabled:YES];
// set the instance of our myScrollView to use the main screen
scrollView1 = [[MyScroll alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
// turn on scrolling
[scrollView1 setScrollEnabled: YES];
// set the content size to the size of the image
[scrollView1 setContentSize: imageView1.image.size];
// add the instance of our myImageView class to the content view
[scrollView1 addSubview: imageView1];
// flush the item
[imageView1 release];
// set max zoom to what suits you
[scrollView1 setMaximumZoomScale:1.0f];
// set min zoom to what suits you
[scrollView1 setMinimumZoomScale:0.25f];
// set the delegate
[scrollView1 setDelegate: self];
// scroll a portion of image into view (my image is very big) :)
//[scrollView scrollRectToVisible:CGRectMake(100, 100, 320, 440) animated:NO];
// yes to autoresize
scrollView1.autoresizesSubviews = YES;
// set the mask
scrollView1.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
// set the view
//self.view =scrollView;
[scrollView1 setFrame:CGRectMake(0, 300, 320, 220)];
//I do this only because I don't know what else to do. This effectively adds the scrollview to the scrollview...
self.view =scrollView1;
And the first scrollView loads, but then the second one is nested in that one! I tried doing[self.view addSubView:scrollView1]; but that just made the app crash. I'm trying my best to really dig in and figure out scrollViews as best I can, but I am just getting owned here.
Please help!
Thanks
** To further clarify, all I want is to have a scrollView on the top half of an image that can pan and zoom with an image inside of it, and then another scrollView separate from the firt that can pan and zoom on the bottom half of the screen, independent of the first.**
You need to add both scroll views as subviews of the ViewController's view (self.view):
[self.view addSubview:scrollView];
[self.view addSubview:scrollView1];
You'll then need to manually position those views so they're positioned like you want them (the above code will put the two scroll views on top of each other, with some arbitrary default size which may or may not be what you want).
I'm using a UIView to control the layout of my view (along with a view controller). I want UIScrollView to only use half of the vertical screen. That works fine if I use the upper half of the screen, but not the bottom half.
Here's the relevant code from the UIViewController:
- (void)loadView {
CGRect fullFrame = [[UIScreen mainScreen] applicationFrame];
//trying to put the scroll view on the bottom half of the screen, but does not work.
CGRect halfFrame = CGRectMake(0, fullFrame.size.height / 2 ,
fullFrame.size.width, fullFrame.size.height / 2);
//use this instead for the scroll view to go to the top half of the screen (and work properly)
//CGRect halfFrame = CGRectMake(0, 0 , fullFrame.size.width, fullFrame.size.height / 2);
UIScrollView* sv = [[UIScrollView alloc] initWithFrame:halfFrame];
[sv setContentSize:CGSizeMake(3 * halfFrame.size.width, halfFrame.size.height)];
CGRect stencilFrame = halfFrame;
UIView *leftView = [[UIView alloc] initWithFrame:stencilFrame];
stencilFrame.origin.x += stencilFrame.size.width;
UIView *centerView = [[UIView alloc] initWithFrame:stencilFrame];
stencilFrame.origin.x += stencilFrame.size.width;
UIView *rightView = [[UIView alloc] initWithFrame:stencilFrame];
//mix up the colors
[leftView setBackgroundColor:[UIColor redColor]];
[centerView setBackgroundColor:[UIColor greenColor]];
[rightView setBackgroundColor:[UIColor blueColor]];
//add them to the scroll view
[sv addSubview:leftView];
[sv addSubview:centerView];
[sv addSubview:rightView];
//turn on paging
[sv setPagingEnabled:YES];
UIView *containerView = [[UIView alloc]initWithFrame:fullFrame];
[containerView addSubview:sv];
[self setView:containerView];
}
Thank you in advance for any advice or help.
I figured it out. The crux of the problem is that views within the scroll view are initialized with the same frame as the scroll view itself. When the scrollView is initialized with halfFrame, the origin is (0, half the full screen size), which is ok since that is relative to the application window itself. However, the views that are put inside the scrollView (like leftView) are initialized to halfFrame, but in this case the origin is relative to the scrollView, effectively placing them off the screen. Setting the origin to (0,0) fixes this:
CGRect stencilFrame = CGRectMake(0, 0, fullFrame.size.width , fullFrame.size.height / 2);
contentSize must contain the rectangle of the view inside the scroll view. That is, the total size of all scrollable controls within. The frame of the UIScrollView decides how much scrolling is needed to let the user browse everything.
You don't have the "full frame" available if you have a nav bar or a tab bar. In general, code that uses [UIScreen mainScreen] for layout information is probably wrong.
Additionally, the status bar can change size if (for example) a call is in progress or tethering is enabled.
Instead, use any sane value for full frame and enable autoresizing:
CGRect fullFrame = {{0,0}, {320,480}};
...
sv.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleTopMargin;
EDIT: You also probably need to subclass UIScrollView and implement -setFrame: so that it also sets the content size and -layoutSubviews to do the correct layout.