UIScrollView scrolls too far after initial zoom - iphone

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.

Related

Scrollbar doesn't show up

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

UIScrollview ContentSize magic

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

Is it possible to add fixed content to a UIScrollView?

I want to create a subclass of UITableView or UIScrollView that will have some shading at the top when the content offset is > 0 to indicate that the content is scrollable. (See image attached)
The way I'm implementing it right now is using the UIViewController that is the delegate of the tableView. I simply have a GradientView on top of the tableView, and I intercept scrollViewDidScroll: to animate the visibility of that top gradient.
My problem with this implementation is that it's not "clean". I want my UIViewControllers to take care of logic, and not to deal with applying gradients and stuff. I wish I could just drop a subclass of UITableView that will do that for me.
The challenge for me is that I can't figure out how the tableView could add to itself a fixed content on top of the scrollable content.
Another question is what method/s of UIScrollView should I override to intercept the scrolling event. Obviously I don't want the tableView to be the delegate of itself...
Any ideas?
Thanks!
Ok, so I found the solution on Apple's WWDC 2011 Session 104 video - Advanced Scroll View Techniques.
There is a whole section in this video about "Stationary Views" inside a scroll view.
According to Apple, the way to go here is to override layoutSubviews and put there all the code to position whatever you want - wherever you want.
I tried it and it's actually pretty easy and it's working as expected.
So for example if I would like a shadowed header on top of the table when the content is being scrolled, this is the code I should write:
-(void) layoutSubviews
{
[super layoutSubviews];
[self positionTopShadow];
}
-(void) positionTopShadow
{
CGFloat yOffset = self.contentOffset.y;
// I'm doing some limiting so that the maximum height of the shadow view will be 40 pixels
yOffset = MIN(yOffset, 40);
yOffset = MAX(0, yOffset);
CGRect frame = self.topShadowView.frame;
// The origin should be exactly like the content offset so it would look like
// the shadow is at the top of the table (when it's actually just part of the content)
frame.origin = CGPointMake(0, self.contentOffset.y);
frame.size.height = yOffset;
frame.size.width = self.frame.size.width;
self.topShadowView.frame = frame;
if (self.topShadowView.superview == nil)
{
[self addSubview:self.topShadowView];
}
[self bringSubviewToFront:self.topShadowView];
}
I've managed to figure out a much simpler way of doing this then what Avraham did.
I use the fact that the UIScrollView calls scrollViewDidScroll: ever pixel the scrolling changes to set the object at the location of the offset. Below is my full code to keep a gray bar at the top of the scrollview as you move around:
- (void)viewDidLoad {
UIScrollView* scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(5.0, 50.0, self.bounds.size.width - 15.0, self.bounds.size.height - 60.0)];
[scrollView setBackgroundColor:[UIColor colorWithRed:251.0/255.0 green:251.0/255.0 blue:251.0/255.0 alpha:1.0]];
[scrollView setContentSize:CGSizeMake(scrollView.frame.size.width + 500, 1000.0)];
[scrollView setDelegate:self];
[self addSubview:scrollView];
UIView* header = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, scrollView.contentSize.width, 40.0)];
[header setTag:100];
[header setBackgroundColor:[UIColor darkGrayColor]];
[scrollView addSubview:header];
}
-(void)scrollViewDidScroll:(UIScrollView *)scrollView {
UIView* header = [self viewWithTag:100];
[header setFrame:CGRectMake(0.0, scrollView.contentOffset.y, header.bounds.size.width, header.bounds.size.height)];
}
You could try using viewForHeaderInSection method of tableView for the shaded view(and also heightForHeaderInSection)... Make the shaded portion as a header.That way there is a fixed content on top of the scrollable content.
#define kImageOriginHight 300
- (void)scrollViewDidScroll:(UIScrollView *)scrollView1{
CGFloat yOffset = scrollView1.contentOffset.y;
// NSLog(#" y offset := %f", yOffset);
//zoom images and hide upper view while scrooling to down position
if (yOffset < 0) {//-kImageOriginHight
CGRect f = imgV.frame;
f.origin.y = yOffset;
f.size.height = -yOffset + kImageOriginHight;
imgV.frame = f;
//viewTableUpperView.alpha = 1.5 - (yOffset/-kImageOriginHight);
//viewTableUpperView.userInteractionEnabled = NO;
if(yOffset+0.5 == -kImageOriginHight){
[UIView animateWithDuration:0.1 animations:^{
//viewTableUpperView.alpha = 1.0;
}];
//viewTableUpperView.userInteractionEnabled = YES;
}
}
}

UIScrollView and image thumbnails

I want to have a scroll view, where you display 5 thumbnails on the scroll view each time.
Basically it's a table with one row and in each row we have 5 thumbnails in it.
You slide your finger to the right then you display the next set of 5 thumbnails.
What do I need to do this?? Is it true that we need a scroll view to do this? How can I start?
In your UIView that will contain your UIScrollView, place (something like) this code in -viewDidLoad:
UIScreen *screen = [UIScreen mainScreen];
pageWidth = screen.bounds.size.height;
pageHeight = screen.bounds.size.width;
scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, pageWidth, pageHeight)];
[scrollView setAutoresizingMask:UIViewAutoresizingNone];
[scrollView setContentSize:CGSizeMake(numPages * pageWidth, pageHeight)];
[scrollView setContentOffset:CGPointMake(pageWidth, 0)];
[scrollView setPagingEnabled:YES];
[scrollView setDelaysContentTouches:YES];
[scrollView setDelegate:self];
[scrollView setClearsContextBeforeDrawing:NO];
[scrollView setOpaque:YES];
[self.view insertSubview:scrollView atIndex:0];
[scrollView release];
// iterate through your thumbnails - which are UIViews
thumbnailViews = [NSMutableArray array];
[thumbnailViews retain];
NSUInteger pageCounter = 0;
for (...)
{
ThumbnailView *thumbnailView = [[ThumbnailView alloc] initWithFrame:CGRectMake( pageCounter*pageWidth, 0, pageWidth, pageHeight )];
[thumbnailView setMultipleTouchEnabled:YES];
[thumbnailView setViewController:self]; // this may be necessary to push/pop on navigation controller stack.
[thumbnailView restoreState];
[scrollView thumbnailView];
[thumbnailViews thumbnailView];
++pageCounter;
}
In my case, pageWidth, pageHeight, thumbnailViews and scrollView are all ivars for my UIView subclass.
I just looked in here as I'm learning about UIScrollViews myself. I started to try and get westsider's code to work, but I noticed that it is practically gobblygook. It should be in a UIViewController that implements the <UIScrollViewDelegate> protocol, and things like [scrollView thumbnailView]; is meaningless as here thumbnailView is a method name. I believe http://www.codeproject.com/Articles/46556/How-To-Use-UIScrollView-in-Your-iPhone-App.aspx is more coherent.
To answer the question, I think you want to create a UIScrollView in a standard manner--such as in the link, and you want to create a subclass of UIView called, say, UIView5 that has 5 UIImageViews. Then, instead of adding a normal UIView to the UIScrollView, like [scrollView addSubview:standardUIView];, you add UIView5 objects.

Setting the size of a UIView

I have a UIViewController subclass to control a UIView that I want to add to a UIScrollView. I only want the view to be 100px high, but when I add it to the scroll view it gets made 460px high, ignoring the frame size I set:
MyViewController *vc = [[MyViewController alloc] init];
vc.view.frame = CGRectMake(0, 0, 320, 100);
myScrollView.autoresizesSubviews = NO
[myScrollView addSubview:vc.view];
[vc release];
I have set the scroll view to not autoresize subviews but it seems this is still happening! What can I do?
I have also tried setting the frame size inside loadView: in the UIViewController (which is where I will add all my controls and will need access to the size of the view) but that doesnt work either!
- (void)loadView {
[super loadView];
self.view.backgroundColor = [UIColor redColor];
self.view.frame = CGRectMake(0, 0, 320, 100); // still doesnt work
}
Any help would be greatly appreciated!
You are using loadView incorrectly, im even suprised you see a view (you shouldnt since in load view you arent assigns the vc view to anything), in loadView you must assign your view to a new UIView i nstance, anyway, you should be doing the same but in viewDidLoad instead of load view, that might work for you
Here is a snippet of how I do it. Note that the origin is with respect to the view you are adding to (in my case 'self').
appRect.origin=CGPointMake(0.0, 0.0);// origin
appRect.size = CGSizeMake(320.0f, 100.0f); //size
CGRect frame = CGRectInset(appRect, 0.0f, 0.0f);
gv=[[GraphicsView alloc] initWithFrame:appRect object:[model me]];
[gv setFrame:frame];
[self.view addSubview:gv];
[gv release];