I am creating an app with a simple UIImageview and scroll view for zooming.
Everything is perfect on my iPhone5/s. However, when I test the app on the iPhone 4S it does not work correctly. When I use a modal to pop the image full screen, the image is half way off the screen.
Again, this ONLY happens on the 4S and not the 5.
I tried changing the anchor points, forcing sizing but nothing works
Also, after I change my orientation from portrait to land scape and back, it goes back into "position"
I am also using constraints as my scrollview is horizontal and vertical center in my main view and the image view is horizontal vertical center in the scroll view.
Thank you in advance.
some relevant code:
image = [UIImage imageNamed:self.myImage];
[self.scrollView setTranslatesAutoresizingMaskIntoConstraints:YES];
[self.imageView setTranslatesAutoresizingMaskIntoConstraints:YES];
[self.myView setTranslatesAutoresizingMaskIntoConstraints:YES];
[im setBackgroundColor:[UIColor blackColor]];
im.contentMode = UIViewContentModeScaleAspectFit;
[self.scrollView setZoomScale:self.scrollView.minimumZoomScale
animated:YES];
NSLog(#"%F",img.size.height);
CGFloat imgheight = image.size.height;
CGFloat imgwidth = image.size. width;
CGSize imageSize = [self currentScreenBoundsDependOnOrientation];
self.scrollView.frame = CGRectMake(0,
0,
imageSize.width,
imageSize.height);
self.imageView.frame = CGRectMake(0,
0,
imageSize.width,
imageSize.height);
dan = true;
self.scrollView.delegate = self;
UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(handleDoubleTap:)];
[doubleTap setNumberOfTapsRequired:2];
[self.scrollView addGestureRecognizer:doubleTap];
self.scrollView.minimumZoomScale = 1;
self.scrollView.maximumZoomScale = 2.4;
self.scrollView.scrollEnabled = YES;
self.scrollView.zoomScale = self.scrollView.minimumZoomScale;
Related
I want to convert frame from UIScrollView to UIView, but not exaclty.
This is my code:
//Create UIScrollView addSubview self.view
scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
[scrollView setBouncesZoom:YES];
[scrollView setMinimumZoomScale:1];
[scrollView setZoomScale:4];
scrollView.delegate = self;
//create UIView overlay UIScrollView and addSubview self.view
overlayView = [[UIView alloc] initWithFrame:self.view.bounds];
overlayView.backgroundColor = [UIColor blueColor];
overlayView.alpha = 0.5;
[self.view addSubview:overlayView];
//create UIView addSubView overlayView, can move change postion
UIView *moveView = [[UIView alloc] initWithFrame:CGRectMake(10, 10, 100, 100)];
moveView.backgroundColor = [UIColor redColor];
[overlayView addSubView:moveView];
If zoom in, zoom out scrollView, moveView change position by ratio when scrollView zoom in zoom out.
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale {
[scrollView setZoomScale:scale+0.01 animated:NO];
[scrollView setZoomScale:scale animated:NO];
CGRect visibleRect;
visibleRect.origin = scrollView.contentOffset;
visibleRect.size = CGSizeMake(scrollView.bounds.size.width * scrollView.zoomScale, scrollView.bounds.size.height * scrollView.zoomScale);
visibleRect.origin.x = 0;
visibleRect.origin.y = 0;
overlay.frame = visibleRect;
CGRect moveRect = moveView.frame;
moveRect.origin.x *= scrollView.zoomScale;
moveRect.origin.y *= scrollView.zoomScale;
moveRect.size.width *= scrollView.zoomScale;
moveRect.size.height *= scrollView.zoomScale;
moveView.frame = moveRect;
}
I cannot change postion moveView exactly when scrollView zoom in zoom out. Please help me resolve this issue.
First I'd say, you really need to be using autolayout for all this stuff. Or none of this will work if you rotate the device, or on different devices, etc. Just using frames to position views is just not the done way anymore. It will work to a degree though. Yes autolayout is a learning curve but you just need to know it and use it all the time.
But looking at the code there two issues stand out : at the start of the didEndZooming method, you set the scale twice, Im not sure why -
[scrollView setZoomScale:scale+0.01 animated:NO];
[scrollView setZoomScale:scale animated:NO];
Then also later, you set the origin of the visibleRect property twice in different ways :
visibleRect.origin = scrollView.contentOffset;
visibleRect.size = CGSizeMake(scrollView.bounds.size.width * scrollView.zoomScale, scrollView.bounds.size.height * scrollView.zoomScale);
visibleRect.origin.x = 0;
visibleRect.origin.y = 0;
Setting the x and y values there is the same as setting the origin to the scrollView.contentOffset - so first you set the origin to the content offset position, then to 0,0 - which doesnt make sense. Clearing up those problems might sort things out.
With the code below, I see a red square appear which I can scroll around (because scrollview's content is 4x screen).
But once I start zooming out (scale < 1.0), the scrollview's contentSize immediately jumps to scale times the iPad's screen size. For example:
0.985783 757.081249 1009.441665
After which I cannot move the square around anymore. I can only move the square when scale get's above 1.0; in which case the scroll indicators show that the contentSize is still scale times screen size.
Another effect is that when zooming out below 1.0, the red square moves toward the top-left corner normally by zooming out. However, after I release the screen (without any further zooming), it moves even further to the corner. EDIT: This must be the rubber banding of the scrollview.
(The square itself does scale as expected.)
What I am missing here? Here's my view controller code:
- (void)viewDidLoad
{
[super viewDidLoad];
self.scrollView = [[UIScrollView alloc]initWithFrame:self.view.bounds];
self.scrollView.contentSize = CGSizeMake(self.view.bounds.size.width * 2, self.view.bounds.size.height * 2);
self.scrollView.backgroundColor = [UIColor colorWithWhite:0.5f alpha:1.0f];
self.scrollView.minimumZoomScale = 0.1f;
self.scrollView.maximumZoomScale = 4.0f;
self.scrollView.clipsToBounds = YES;
self.scrollView.delegate = self;
self.contentView = [[UIView alloc] initWithFrame:self.scrollView.bounds];
[self.scrollView addSubview:self.contentView];
[self.view addSubview:self.scrollView];
UIView* view = [[UIView alloc] initWithFrame:CGRectMake(400, 400, 100, 100)];
view.backgroundColor = [UIColor redColor];
[self.contentView addSubview:view];
}
#pragma UIScrollViewDelegate
- (UIView*)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return self.contentView;
}
- (void)scrollViewDidEndZooming:(UIScrollView*)scrollView
withView:(UIView*)view
atScale:(double)scale
{
NSLog(#"%f %f %f", scale, self.scrollView.contentSize.width, self.scrollView.contentSize.height);
}
I have the following code to create a scroll view with additional area at the beginning and the end of the images (50 points).
UIScrollView* scroll = [[UIScrollView alloc] initWithFrame: CGRectMake(0,0,200,100)];
scroll.contentSize = CGSizeMake(400,100);
UIImageView* img1 = [[UIImageView alloc] initWithFrame: CGRectMake(50,0,100,100);
UIImageView* img2 = [[UIImageView alloc] initWithFrame: CGRectMake(150,0,100,100);
UIImageView* img3 = [[UIImageView alloc] initWithFrame: CGRectMake(250,0,100,100);
//Adding images to ImageViews
scroll.pagingEnabled = YES;
[scroll addSubView:img1];
[scroll addSubView:img2];
[scroll addSubView:img3];
The first time I see the view, I will see the additional area on the left (0-50), then the first image (50-150) and then half of the second image (150-200).
When I swipe left, I want to see half of the first image on the right, the second image at the center, and half of the third image on the right.
When I swipe left again, I want to see the third image at center, with half of the second image on the left, and the additional area on the right.
Can it be possible?
It's possible as long as you define everything correctly. Make sure that the assigned content size is equal to the overall size that you wish to scroll through. Make sure that each page size is equal to the scroll view's frame. You can set the clipsToBounds to NO if the frame clips your subviews.
You can do this by adjusting the contentSize of UIScrollView
- (void)addImagesToScrollView{
//An offset from where imageViewStarts
CGFloat xOffset = 50.0f;
CGRect imageViewFrame = CGRectMake(xOffset, 0.0f, 100.0f, 100.0f);
for (NSString *imageName in #[#"image1.jpg",#"image2.jpg",#"image3.jpg"])
{
UIImageView *imageView = [[UIImageView alloc]initWithFrame:imageViewFrame];
UIImage *image = [UIImage imageNamed:imageName];
imageView.image = image;
[self.scrollView addSubview:imageView];
imageViewFrame.origin.x+=imageViewFrame.size.width;
}
CGSize contentSize = self.scrollView.frame.size;
//Content size is calculate from the imageViewFrame and an offset is added to it
contentSize.width = imageViewFrame.origin.x+xOffset;
[self.scrollView setContentSize:contentSize];
}
Source Code
You can scroll to a particular place in the scrollview using the following code
[YOURSCROLLVIEW setContentOffset:CGPointMake(x, y) animated:YES];
I have an application which has a fullscreen UIScrollView, and within it there are seven images. The images are also meant to be full screen, and the scroll view is set to enable pagination.
I have a method which either creates or moves the image views:
-(void)rebuildImageView{
// set up images
float screenW = self.view.bounds.size.width;
float screenH = self.view.bounds.size.height;
int numImgs = self.soundNames.count;
self.mainScrollView.contentSize = CGSizeMake(screenW * numImgs, screenH);
for(int i=0; i<numImgs; i++){
UIImageView* imageView = (UIImageView*)[self.mainScrollView viewWithTag:i+100];
if(imageView == nil){
imageView = [[UIImageView alloc] init];
imageView.tag = i+100;
[self.mainScrollView addSubview:imageView];
imageView.image = [UIImage imageNamed:[NSString stringWithFormat:#"image%d.jpg",i]];
imageView.contentMode = UIViewContentModeScaleAspectFill;
imageView.clipsToBounds = YES;
[imageView release];
}
imageView.frame = CGRectMake(i * screenW, 0, screenW, screenH);
}
// scroll to the current one
[self.mainScrollView scrollRectToVisible:CGRectMake(self.currentSound*screenW, 0, screenW, screenH) animated:YES];
}
I also have this on the view controller:
-(void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{
[UIView animateWithDuration:duration animations:^{
[self rebuildImageView];
}];
}
This code works fine when I autorotate while image 0 is being shown, but when I'm on image 7, you can briefly see most of image 6 when rotating. This video shows what's happening:
http://www.youtube.com/watch?v=1O3jOcTgVP8
Is there a better method I should use to reconfigure the scroll view and images when rotating the device?
Any frame changes put in the willAnimateRotationToInterfaceOrientation:duration method should automatically animate. So you could try removing it from the block?
Personally, I've had a lot more luck with this type of thing subclassing UIScrollView and putting the equivalent layout subview frame code in an override of the layoutSubviews method (don't forget to call super or you might end up with misplaced scroll bars).
I have an interesting problem. I have one UIScrollView and inside only one UIImageView with image. I need this for zooming and panning.
Ok. On a button touch I want to replace image within UIImageView (which is within UIScrollView, like a mentioned) with the next one in line.
So far I've done this like that:
self.imageView = nil;
self.imageView = [[UIImageView alloc] initWithImage:nextImage];
The reason that I always set self.imageView to nil is that if I don't do that than the next image is scaled or zoomed like the previous one was.
But I don't think thats fine solution for memory efficiency.
Is there any other way to set new image on UIImageView with "reset" option for zooming, scale, etc... ?
UPDATE:
The code that still does not work:
[self.scrollView setZoomScale:1.0];
[self.imageView setImage:photoImage];
[self.imageView setFrame:rect];
[self.scrollView setContentSize:[self.imageView frame].size];
CGFloat minZoomScale = [self.scrollView frame].size.width / [self.imageView frame].size.width;
if (minZoomScale < [self.scrollView frame].size.height / [self.imageView frame].size.height) {
minZoomScale = [self.scrollView frame].size.height / [self.imageView frame].size.height;
}
[self.scrollView setMinimumZoomScale:minZoomScale];
[self.scrollView setZoomScale:[self.scrollView minimumZoomScale]];
UPDATE 2
Just figured out what I was doing wrong. I did not reset the minimum zoom scale.
[self.scrollView setMinimumZoomScale:1.0];
Now it works!
There is a property called zoomScale in UIScrollView
Set 1.0 to the zoomScale of your scrollview. No need to release the old UIImageView and to allocate a new UIImageView everytime. You can directly set the image to the imageview and set the zoomScale of the UIScrollView to 1.0.
Just want to make one thing clear because I ran into the same problem: reset the minimum zoom scale to 1.0 BEFORE changing the image of the imageView:
[self.scrollView setMinimumZoomScale:1.0];
[self.scrollView setZoomScale:1.0];
[self.imageView setImage:photoImage];
[self.photoView setFrame:(CGRect){.origin=CGPointMake(0.0f, 0.0f), photoImage.size=photo.size}];
[self.scrollView setContentSize:[self.imageView frame].size];
CGFloat minZoomScale = MIN([self.scrollView frame].size.width / [self.imageView frame].size.width, [self.scrollView frame].size.height / [self.imageView frame].size.height)
[self.scrollView setMinimumZoomScale:minZoomScale];
[self.scrollView setMaximumZoomScale:1.0];
[self.scrollView setZoomScale:[self.scrollView minimumZoomScale]];
[self centerScrollViewContents];
and for centring the image:
- (void)centerScrollViewContents {
CGSize boundsSize = self.scrollView.bounds.size;
CGRect contentsFrame = self.imageView.frame;
if (contentsFrame.size.width < boundsSize.width) {
contentsFrame.origin.x = (boundsSize.width - contentsFrame.size.width) / 2.0f;
} else {
contentsFrame.origin.x = 0.0f;
}
if (contentsFrame.size.height < boundsSize.height) {
contentsFrame.origin.y = (boundsSize.height - contentsFrame.size.height) / 2.0f;
} else {
contentsFrame.origin.y = 0.0f;
}
self.imageView.frame = contentsFrame;
}
You can just use the zoomScale property of the scrollView object and set it to 1.0 everytime change the image. This'll probably go before loading the new image. This way you'll not have to alloc and init a new UIIMageVIew object each time you want to change just the image. Additionally you can put this change into an animation block to make the image change not sharp but gradual.
Whey you use this code, every time you create new object, but previous UIImageView stay on your UIScrollView. This is not efficient in memory usage.
Use next code:
[self.imageView removeFromSuperview];
[self.imageView release];
self.imageView = [[UIImageView alloc] initWithImage: nextImage];
[self addSubview: imageView];
or
[self.imageView setImage: nextImage];
[self.imageView setFrame: CGRectMake(0, 0, nextImage.size.width, nextImage.size.height)];
and then, you can set content size for you ScrollView:
topScrollView.contentSize = CGSizeMake(nextImage.size.width, nextImage.size.height);