How can I vertically center an image inside a scrollView?
I'm using storyboards in Xcode 5. The main view is embedded inside a navigation controller, and "Adjust scroll view insets" option is enabled in main Storyboard. This main view has a scrollView which size is equal to the main view size.
The imageView is inside the scrollView and it's the same size as the scrollView. Content mode is set to AspectFit.
So, hierarchy is as follows:
- UINavigationController
- UIView
- UIScrollView
- UIImageView
The image may be landscape or portrait, and can be any size (it's loaded at runtime). This is why imageView is the same size as the scrollView.
How can I vertically center the image inside the scrollView?
EDIT:
As commented before, I have set imageView's contentMode to AspectFit because the image may be too big, so I need it resized. The problem I have is that the image is not center of the scrollView.
You can check screenshot at link and download source code at link.
It will be good to use auto layout as mentioned by #Douglas. However, if you prefer the traditional way, you can also make it work.
I'll first give you the answer and then explain it to you. You should first delete the image view from the storyboard ( I'll explain it later), and then add the viewWillAppear method.
- (void)viewDidLoad
{
[super viewDidLoad];
// 1. Add the image view programatically
UIImageView * imageView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:#"portrait.jpg"]];
[_scrollView addSubview:imageView];
_imageView = imageView;
}
- (void)viewWillAppear:(BOOL)animated
{
// 2. calculate the size of the image view
CGFloat scrollViewWidth = CGRectGetWidth(_scrollView.frame);
CGFloat scrollViewHeight = CGRectGetHeight(_scrollView.frame);
CGFloat imageViewWidth = CGRectGetWidth(_imageView.frame);
CGFloat imageViewHeight = CGRectGetHeight(_imageView.frame);
CGFloat widthRatio = scrollViewWidth / imageViewWidth;
CGFloat heightRation = scrollViewHeight / imageViewHeight;
CGFloat ratio = MIN(widthRatio, heightRation);
CGRect newImageFrame = CGRectMake(0, 0, imageViewWidth * ratio, imageViewHeight * ratio);
_imageView.frame = newImageFrame;
// 3. find the position of the imageView.
CGFloat scrollViewCenterX = CGRectGetMidX(_scrollView.bounds);
CGFloat scrollViewCenterY = CGRectGetMidY(_scrollView.bounds) + _scrollView.contentInset.top / 2 ;
_imageView.center = CGPointMake(scrollViewCenterX, scrollViewCenterY);
}
Here is the explanation:
You should not put the imageView in the storyboard, otherwise the frame of the imageView will be fixed by the storyboard, and will not change with the size of the image. Even if you choose UIViewContentModeScaleAspectFill, the frame of the imageView is still not changed. It just add some white space around the image.
Now the imageView has the same size as your image. If you want it to be fully displayed, you need to calculate the frame yourself.
Pay attention to the _scrollView.contentInset.top / 2, this is why you need to put the codes in viewWillAppear instead of viewDidLoad. The _scrollView.contentInset.top is the height of the navigation bar and is calculated automatically for you before willViewAppear.
You put your image view in a scrollView, I guess you want to zoom in and out. If this is true, add self.imageView = imageView; and the bottom of viewDidLoad. Set the delegate of _scrollView to self and add the following method:
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return _imageView;
}
I made a comment, but then took a look at your project. You are almost there. I ran through the following steps and have gotten the result you are looking for.
First, make sure you have auto layout turned ON!!!
In your storyboard click on your scroll view. You had a scroll view that was the same size as the view. You are going to put on some constraints. Down at the bottom of the story board you will see some icons.
The fourth one over looks sort of like an I-beam on its side, it is the pin button. After selecting the scroll view, click on this and it will bring up a pop up menu.
For the scroll view click on all the bars around the middle block so you pin the scroll view to the sides of the main view.
You will notice they are all red now.
Then go and click on the imageview. Once again you had it set to the size of the view. Using the pin button again, you are going to pin just the Width at 320 and the Height at 568. When you are done you are then going to use the next button over.
This is the align button. Click on that after you have selected your image view. You are going to click on Horizontal Center in Container, and Vertical Center in Container.
Next you will need to add one method to your ViewController.m file.
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
[_scrollView setContentSize:CGSizeMake(self.view.frame.size.width, self.view.frame.size.height)];
}
Start up the simulator and let her rip! You will get one warning though. It says the content size is ambiguous for the scroll view. But that's OK, because you will set it on viewDidLayoutSubviews.
Hope that helps, or helps someone out. Autolayout and scroll views are a bit tough!!
EDIT#1
if you want to then make the image view scalable, by pinch zooming you can do the following.
Make sure you made the .h file follow the UIScrollViewDelagate.
#interface ViewController : UIViewController <UIScrollViewDelegate>
This will allow the scroll view to be able to access the delegate methods of the scroll view. The method you are looking for is called..
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return self.imageView;
}
Then in the viewDidLoad method of your .m file do the following.
_scrollView.minimumZoomScale = 0.5;
_scrollView.maximumZoomScale = 4.0;
_scrollView.delegate = self;
The underscore and the variable name is the same as self.variable. Either will work.
That should do it. Let me know if it works or if you have any other questions. ENJOY!
These are the ones u can use, the 3 modes of ImageView content display.You can do this by dynamically setting them or u can set them in storyboard too, click on the ImageView and go to properties tab-bar and choose from there, run them and select which output u want.
imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.contentMode = UIViewContentModeScaleToFill;
imageView.contentMode = UIViewContentModeScaleAspectFill;
Hope this helps
If you want to center image in an imageview use
imageView.contentMode=UIViewContentModeCenter;
image retains it's size in this this content mode. Alternatively you can use other content modes as per your requirement.
UIViewContentModeScaleToFill,
UIViewContentModeScaleAspectFit, // contents scaled to fit with fixed aspect. remainder is transparent
UIViewContentModeScaleAspectFill, // contents scaled to fill with fixed aspect. some portion of content may be clipped.
UIViewContentModeRedraw, // redraw on bounds change (calls -setNeedsDisplay)
UIViewContentModeCenter, // contents remain same size. positioned adjusted.
UIViewContentModeTop,
UIViewContentModeBottom,
UIViewContentModeLeft,
UIViewContentModeRight,
UIViewContentModeTopLeft,
UIViewContentModeTopRight,
UIViewContentModeBottomLeft,
UIViewContentModeBottomRight,
Related
I have a button I added manually on the storyboard. I would like when the screen loads, to this button sets its center with the coordination I give to it by
-(void)viewWillLayoutSubviews{
NSLog(#"Will Layout");
CGPoint buttonPoint;
buttonPoint.y = self.view.frame.size.height - 50;
buttonPoint.x = self.view.frame.size.width /2;
[menuButton setCenter:buttonPoint];
}
But nothing changes, the button stays still on the place I first dragged it.
What am I doing wrong?
It's very simple for both the modern AutoLayout based and old legacy Springs&Struts based layout management approach.
AUTOLAYOUT
You simply set up two constraints that will keep the button in the horizontal middle and in the vertical middle minus 50 points up.
SPRINGS & STRUTS
You will make sure the button is set to be in the center by setting the autoresizing mask properly in the Size inspector. Then in code, in view controller's viewDidLoad method, you will fix the 50 points offset.
(Some might suggest viewWillLayoutSubviews, but let's not complicate it for now.)
- (void)viewDidLoad
{
[super viewDidLoad];
self.button.frame = CGRectMake(self.button.frame.origin.x,
self.button.frame.origin.y - 50,
self.button.frame.size.width,
self.button.frame.size.height);
/// other code....
}
I have a scroll view with an image view. And I want to zoom the image in all direction equally. But when I am trying to do it's not happening.
But its zooming image only in vertical direction not in horizontal direction. I have done this:
[monthView setImage:[monthArray objectForKey:dateString] ];
scrollView1.contentSize=CGSizeMake(1280, 960);
#pragma scrollview delegates
-(UIView *) viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return monthView;
}
Write minimumZoomScale and maximumZoomScale value of scrollView in viewDidLoad
_scrollview.minimumZoomScale=1.0;
_scrollview.maximumZoomScale=2.0;
Set zooming scale for scrollview. Like:
self.myScrollView.minimumZoomScale = 1.0;
self.myScrollView.maximumZoomScale = 5.0f;
And Make sure monthView is a subview of scroll view.
Hope this helps. :)
1 Set your view controller up as a .
2 Draw your UIScrollView the size you want for the rectangle at the center of the view. Set the max zoom in the inspector to something bigger than 1. Like 4 or 10.
3 Right click on the scroll view and connect the delegate to your view controller.
4 Draw your UIImageView in the UIScrollView and set it up with whatever image you want. Make it the same size as the UIScrollView.
5 Ctrl + drag form you UIImageView to the .h of your View controller to create an IBOutlet for the UIImageView, call it something clever like imageView.
6 add this code.
-(UIView *) viewForZoomingInScrollView:(UIScrollView *)scrollView {return self.imageView;}
7 Run project.
Inside a UIViewController, I need to have the bottom half scrollable. So I added a UIScrollView and positioned it halfway down the view's height. And in the viewDidAppear method, I have put the below two code lines to make it scrollable.
self.scrollView.contentSize = CGSizeMake(self.view.frame.size.width, self.scrollView.frame.size.height);
self.scrollView.frame = self.view.frame;
This way works if the scroll view fills the entire view, I've tested. But this method didn't work for my need. The scroll view would automatically move up and take up the entire screen. I assumed it was the second line of code which causes this.
So I removed the scroll view, added two UIViews to the view controller. To the bottom view, I added the UIScrollView. And in the viewDidAppear method, I have put the same two code lines changing the second line to refer the frame of the UIView that contains the scroll view..
self.scrollView.contentSize = CGSizeMake(self.view.frame.size.width, self.scrollView.frame.size.height);
self.scrollView.frame = self.containerView.frame;
But it wouldn't scroll either.
Can anyone please tell me how to do this correctly?
Thank you.
Dude, you keep setting the frame of the scrollView to something completely different from what you're actually trying to achieve.
If all you want to do is setup your scroll view so that it only occupies half the space then why dont you just set the frame so that the height only covers the portion of the screen that you want it to cover; and then set the x & y coordinates so that you draw the scroll view from the right position.
Do something like this:
//Shortcut to view's frame.
CGRect viewsFrame = self.view.frame;
/**
CGRectMake takes 4 parameters: x, y, width, height
x: is set to 0 since you want the scrollview to start from the left with no margin
y: you want the y position to start half way, so we grab the view's height and divide by 2
width: you want your scrollview to span from left to right, so simply grab the view's width
height: you want your scrollview's height to be half of your screen height, so get view's height and divide by 2.
*/
CGRect frameForSV = CGRectMake(0, viewsFrame.size.height/2, viewsFrame.size.width, viewsFrame.size.height/2);
UIScrollView *myScrollView = [[UIScrollView alloc] initWithFrame:frameForSV];
[self.view addSubview:myScrollView];
Then set your content size not based on an ansolute value, its best to have it based on the size of the content that's actually inside your scrollview so that your scrollview always scrolls to cover all your content inside it.
Also, remember that your scrollview will only scroll if the contentsize is greater than the scrollview's frame
UPDATE 1 after reading your comment in this post simply comment out any code in your viewController.m file related to your scrollview since youre setting up everything in interface builder.
This is the result:
I made a simple test project to show the problem:
there is an imageview which is contained in scrollview
view -> scrollview -> imageview
scrollview's content size is same as imageview size = (AllWidth, AllHeight).
The problem is that if scrollview is scaled EXACTLY to fit image by width (scrollView.zoomScale = 320.0/AllWidth;) then calling zoomToRect
[scrollView zoomToRect:CGRectMake(0.0, 0.0, AllWidth, AllHeight) animated: NO];
does scroll imageview to the bottom for some reason. But nothing is expected to happen with UI
if scrollview is NOT scaled EXACTLY to fit image by width (scrollView.zoomScale = (320.0-1)/AllWidth;), then calling zoomToRect does what is expected - image is scaled and not scrolled to the bottom.
i noticed that in 'buggy' case ContentOffset.y is changed, but i have no any idea why.
to reproduce the issue, start new project, in viewcontroller.h file add <UIScrollViewDelegate>; viewcontroller.m is here: http://pastebin.com/bPUtuYn1 (in test project you will need double tap green image, then change "320.0-1" to "320.0" and try again)
I have a custom view with some drawing in drawRect added as subview in a UIView.On tapping on the subview I change the height of the base view.This is done by -
CGRect rect = self.bounds;
rect.size.height = 400.0;
self.bounds = rect;
This also repositions the custom view (sets it to the top of the base view) which I don't want , the subview should remain fixed at the bottom of the UIView.
Y position for the custom view is set to the height of the base view -
y = self.bounds.size.height - 40.0;
This works when the custom control is added for the first time, but does not work when the base view changes height.
I have set self.autoresizesSubviews = NO; for the base view.
If I remove the custom view from the base view and add it again after the height changes nothing is shown , just a blank base view.
Any ideas to fix the position of the custom view to the bottom of the base view.
Try to use
self.bounds = CGRectMake(0, 0, 320, 400);
It will make the bounds from top left corner to point on the right side of the screen that is 400 pixels below the initial point.
You can also try using method
-(void)layoutSubviews
I don't remember the correct syntax, but I'm pretty sure it was like that.
hope it helps
What about firing setNeedsDisplay method on that view.