I have an UIScrollview that is zoomable, the subview is one UIView (viewTexto) that contains an UILabel inside (messageLabel).
This is the code
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollViewtmp{
return viewTexto;
}
-(void)scrollViewDidEndZooming:(UIScrollView *)scrollViewtmp withView:(UIView *)view atScale:(float)scale{
messageLabel.contentScaleFactor=scale;
[scrollView setContentSize:CGSizeMake(scrollView.frame.size.width, messageLabel.frame.origin.y + messageLabel.frame.size.heightt)];
}
With this code I can zoom, and the text is not blurry, there is no horizontal scroll but the size of the UILabel continues to be too large, so it is cut. I need that the width of the UILabel adopts to the scrollView width again as at the beginning.
I have read any question about UIScrollViews in SO and having found exactly what I need.
OK, so finally I found the answer as Ismael suggested I had to adjust the width, the problem was to find the equation.
The way the scaling works and the width of the scrollview's subviews is not obvious at the beginning.
Once you scale a UIView in a scrollview, and you want to have the same width for it, you have to divide the width of the element by the scale.
That is to say that if your initial width was 600, once you scale you can think that the width has changed and you only have to resize it again to 600, but it is not true. 600 is going to be multiplied by the scale automatically. So the right answer would be to resize it to 600 / scale.
Here we divide.
Now the code:
Everything happens in the method:
-(void)scrollViewDidEndZooming:(UIScrollView *)scrollViewtmp withView:(UIView *)view atScale:(float)scale{}
The first thing was to get rid of the blurry fonts:
messageLabel.contentScaleFactor=scale;
In another method I saved the initial width of the UILabel messageLabel (inside scrollview), I called the variable "initialWidth", that was 600. This is important, because I started using the current messageLabel width once in the scrollViewDidEndZooming method.
To readjust the width of the subviews of the scrollview to 600, we only have to divide the initial width by the zoomscale, and readjust the label:
[messageLabel setFrame:CGRectMake(0,0,(initialWidth/scale), messageLabel.frame.size.height)];
[messageLabel sizeToFit];
At this point, we have a scrollview that can be zoomed, with a Label that readjust the text to the initial width of the scrollview, we don't have horizontal scrollbars, but we have a problem, the vertical scrollbars have a wrong height: we can only scroll a part of the text.
This was a difficult second problem to solve.
If you pass the messageLabel height to the contentsize, curiously it seems that it doesn'work, and even if I get a height multiplied by the scale in my NSLogs, it does not change the height of the scrolling, as if internally it was divided again. For example, if the initial height was 500, after scaling by 2, I get 1000 height, if I pass this value to the ContentSize it remains the same, as if it was divided again by 2.
So the solution was this time to multiply by the scale.
We only have to add this line:
[scrollView setContentSize:CGSizeMake(scrollView.frame.size.width, (messageLabel.frame.origin.y+messageLabel.frame.size.height)*scale)];
As it is seen, the difficult part was to understand this mess with dividing or multiplying by the scale.
After you zoom, the contentSize of a UIScrollView increases proportionally (originalContentSize * zoomScale), so when you adjust it again, it will become smaller
Example:
Your scrollView.frame is (0, 0, 100, 100), and your contentSize is (100, 100).
After you zoom to 2.0, your contentSize will be (200, 200).
You are then putting it at (100, 100) manually, which equals to a contentSize of (50, 50) without zoom.
Since (contentSize.width <= scrollView.frame.size.width), there will be no horizontal scrolling, and the right-most part of your label is not visible.
Try not adjusting the contentSize. The scrolling will be possible then, and the user can reach the whole of the label
Edit: Re-reading your question, if you want the label to adjust and become visible entirely after the zoom, what you have to do in addition to adjusting the contentSize, is adjusting the messageLabel's frame. The label should wrap itself properly
Related
How do you handle centering and scrolling content in a UIScrollView when the dimensions are both larger and smaller than the containing scroll view?
I have been trying to set values in layoutSubviews to handle the initial centering of the content, while still allowing for scrolling.
If the content is smaller in both dimensions, I can just set the frame and the image is properly centered for all rotations and orientations. Setting the contentInset will also work. contentOffset does not seem to work.
If the content is larger in both dimensions, I can set contentOffset for the initial display, and not modify it again to support scrolling.
What do I do if I have an image with one dimension larger, and the other smaller, than the scroll view?
contentOffset uses a CGPoint, and contentInset uses UIEdgeInsets (top, left, bottom, right). I have tried mixing positive and negative, since one dimension needs to be moved in and the other out, but haven't gotten anything to work.
My next thought is to resize the scroll view (and modify constraints I suppose) so that the content is never smaller than the container and use contentOffset.
I would really like to have a single approach that will work regardless of larger or smaller dimensions.
What is the best solution (a solution) to this problem?
After stepping back from this, getting a better understanding of UIScrollView, and rethinking, I have solved my problem.
For starters, layoutSubviews is the wrong way to go, at least for what I need.
Trying to resize the UIScrollView and update constraints seemed more trouble than it was worth.
For whatever reason, it didn't initially occur to me than I could use both contentOffset and contentInset at the same time (thought it was either/or for some reason), but that was my exact solution.
CGRect rectContent = CGRectMake(0, 0, image.size.width, image.size.height);
UIImageView *imageView = [[UIImageView alloc] initWithFrame:rectContent];
self.scrollView.contentSize = rectContent.size;
CGFloat fOffsetWidth = (rectContent.size.width < self.scrollView.bounds.size.width) ? (self.scrollView.bounds.size.width - rectContent.size.width)/2 : 0;
CGFloat fOffsetHeight = (rectContent.size.height < self.scrollView.bounds.size.height) ? (self.scrollView.bounds.size.height - rectContent.size.height)/2 : 0;
self.scrollView.contentInset = UIEdgeInsetsMake(fOffsetHeight, fOffsetWidth, fOffsetHeight, fOffsetWidth);
self.scrollView.contentOffset = CGPointMake((rectContent.size.width - self.scrollView.bounds.size.width)/2, (rectContent.size.height - self.scrollView.bounds.size.height)/2);
imageView.image = image;
[self.scrollView addSubview:imageView];
All image dimension possibilities (larger/smaller, one/both) are centered in the scroll view, and a larger image dimension is scrollable while a smaller dimension remains centered.
Perfect!
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:
What does this expression "minimum x-value of the superview’s bounds" mean in the Apple Documentation regarding Autolayout in a UIScrollView?
...some notes regarding Auto Layout support for UIScrollView:
In general, Auto Layout considers the top, left, bottom, and right edges of a view to be the visible edges. That is, if you pin a view to
the left edge of its superview, you’re really pinning it to the
minimum x-value of the superview’s bounds. Changing the bounds origin
of the superview does not change the position of the view.
The UIScrollView class scrolls its content by changing the origin of its bounds. To make this work with Auto Layout, the top, left,
bottom, and right edges within a scroll view now mean the edges of its
content view.
1) As I know by default bounds of a view is (0, 0, width, height). So x is 0.
2) How could a view have more x values to take minimum on it?
The Autolayout constraints for any view is generally in relation with the visible bounds of the view but when it comes to a UIScrollView, the Autolayout constraints are in relation with the scrollView's contentView.
Obviously, because a scrollView is, well... supposed to scroll.
Suppose a scrollView has a contentSize of something like:
myScrollView.frame = CGRectMake(0, 0, 100, 100);
myScrollView.contentSize = CGSizeMake(1000, 1000);
now...
Say, in this scrollView you have a subView, something like:
UILabel *lblTest = [UILabel alloc] initWithFrame:CGRectMake(0, 0, 50, 50)];
[lblTest setText:#"Where am I?"];
[myScrollView addSubView:lblTest];
Before Scrolling:
myScrollView.contentOffset.y will be 0
myScrollView.bounds.y will be 0
myScrollView.frame.origin.y will be 0
lblTest.frame.origin.y will be 0 (and will be visible)
After Scrolling (to bottom):
myScrollView.contentOffset.y will change to 900
myScrollView.bounds.y will also be 900
myScrollView.frame.origin.y will still be 0
lblTest.frame.origin.y will be -900 (and no longer visible)
This is what the Apple Documentation means by:
The UIScrollView class scrolls its content by changing the origin of
its bounds.
Now...
If Autolayout was in relation with the visible bounds of UIScrollView then no matter how much you scrolled, the UILabel would not scroll up.
but...
Since Autolayout in a UIScrollView goes in relation with the scrollView's contentSize, the Autolayout contraints work within it with relation to the scrolling functionality.
So...
When you use Autolayout for subViews in a scrollView, it will be relational to the scrollView's actual size and not the visible size.
apple docs says ask: The UIScrollView class scrolls its content by changing the origin of its bounds.
1) so scrolling can change bounds.x, and than x will be not 0
bounds property is usually (0, 0, width, height), but not always
2) the minimum means: the top, left, bottom, and right edges within a scroll view now mean the edges of its content view.
I have set the contentSize of the UIScrollView as follows:
self.scrollView.contentSize = CGSizeMake(320,800);
But as you can see in the image below it still displays one last line under the UITabbar. Why?
I am using iOS 7.
The scrollView's contentSize refers to the size of the content that can be scrolled through. For example you could have a contentSize height of 2000 px, because you have 2000 px worth of content to scroll through.
Your contentSize is not the problem; you indeed want it to be 800 (I'm assuming that's the height of all your text and graphics, not including the tab bar at the bottom.) What you want to change is the actual size of the scrollView object itself. Then the scrollView will be the correct height (something like scrnHeight - tabBarHeight) but the contentSize will still reflect the size of the content (the stuff inside the scrollView).
[self.scrollView setFrame:CGRectMake(self.scrollView.frame.origin.x,
self.scrollView.frame.origin.Y, 320, scrnHeight - tabBarHeight)];
self.scrollView.contentSize = CGSizeMake(320, 800);
And then you should be all set. Now it's possible that 800 isn't actually the number you want; the number you want is the exact height from the top of the scrollview to the very botom of that text at the bottom. But this is the idea :)
I have a UIScrollView that contains several dynamically resizing subviews. I can resize and layout the subviews just fine, but when I set the content size of the scroll view itself, the bottom subviews are clipped. Is there some reason why a scroll view's content size height should be larger than the sum of the heights of the views it contains?
Here's my situation in more detail:
I have a superview containing a UIScrollView containing several subviews. In the superview's layoutSubviews method, I calculated the needed size of each subview, then set the frames so the subviews are tiled vertically down the screen with a bit of space between them. When done, I set the height of the UIScrollView's content size to be the end of the last subview (origin.y + size.height). In theory, this means the bottom of the scroll view's content area should exactly line up with the bottom of the last subview.
But it doesn't. Instead, a nice chunk of the last subview is clipped. It's still there - if I scroll down I can see the remaining portion during the "bounce". The problem is even worse in landscape mode - a much larger portion of the bottom subview simply isn't visible.
The subviews are all being arranged and positioned properly. The problem is that the UIScrollView's contentSize seems to need to be significantly larger than the sum of the heights of the subviews (plus the space between them). This doesn't make any sense to me. Furthermore, the amount the size is "off" varies - I reuse this view several times with different subviews, and they're all off by a different amount. Therefore, simply adding a constant to the content view height won't help.
What is causing the content size (or my height calculations) to not function correctly?
Code:
- (void)layoutSubviews
{
[super layoutSubviews];
CGFloat width = self.bounds.size.width - [self subviewLeftMargin] - [self subviewRightMargin]; // All subviews have same width as parent view
CGFloat x = [self subviewLeftMargin]; // All subviews should start at the far left of the view
CGFloat y = [self spaceBetweenSubviews]; // Running tally of the y coordinate for the next view
/* Adjust the subviews */
for(UIView *view in self.theVariousSubviews) {
/* Resize the view with the desired width, then let it size its height as needed */
view.frame = CGRectMake(view.frame.origin.x, view.frame.origin.y, width, view.frame.size.height);
CGSize newSize = [view sizeThatFits:view.frame.size];
/* Set the origin */
//The subviews are positioned correctly, so this doesn't seem to be a problem
view.frame = CGRectMake(x, y, newSize.width, newSize.height);
/* Have the view refresh its own layout */
[view setNeedsLayout];
/* Update the y value for the next subview */
y += newSize.height + [self spaceBetweenSubviews];
}
/* Resize the scroll view to ensure it fits all of the content */
CGFloat scrollViewHeight = y;
self.scrollView.contentSize = CGSizeMake(self.scrollView.contentSize.width, scrollViewHeight);
//Content size is set to the same total height of all subviews and spacing, yet it is too small. Why?
}
hi it seems to me that your calculation and resizing timing is wrong.
Without the missing code for the layout change I could not fully understand the problem.
What strikes me is that you are assigning view.frame twice and between the new calculation you intercept the process with sublayouting which might change some of the values your calculation is depending on.
I could only advice you to separate the calculation from layouting and not invoke methods while you are calculating. To bring light into it you should either drop a sample app with the missing calculation or for yourself add some NSLog statement showing you the frame origin size of any subview and the contentOffset for the scrollview.
On my experiences the scrollview is working properly in general so I would expect a bug within your code.