I have a UIScrollView, with several levels of subviews of the view returned by viewForZoomingInScrollView. During zooming, I want some of those subviews to resize, and others to not resize. No matter what I try, all subviews resize. On the superview of the subviews I want to not resize, I've set autoResizesSubviews = NO, and also tried contentMode = UIViewContentModeCenter. I've also tried setting the autoresizingMask for all views involved to UIViewAutoresizingNone. Any thoughts? I can't restructure the hierarchy of subviews.
The subviews of UIScrollView's zooming view are "resized" by changing their transforms, not by changing their bounds, etc. One simple solution to your question is to catch the transform being set on the zooming view, invert it, and apply it to the subviews, canceling out the zooming transform applied by the scroll view.
In the view returned by viewForZoomingInScrollView, override -setTransform:, and go through all the subviews in question and apply the inverted transform:
- (void)setTransform:(CGAffineTransform)transform
{
[super setTransform:transform];
CGAffineTransform invertedTransform = CGAffineTransformInvert(transform);
for (UIView *view in subviewsNotToBeResized)
{
[view setTransform:invertedTransform];
}
}
Swift 4.2 version of Andrew Madsen's excellent answer:
/* maintains subview size during zoom */
class NonResizingView: UIView {
override var transform: CGAffineTransform {
get {return super.transform}
set {
super.transform = newValue
for v in subviews {
v.transform = self.transform.inverted()
}
}
}
}
UIScrollView zooming is done with a transform, which will ignore your content mode and auto resizing mask. One possible solution is to place the content that you don't want resized into a sibling view of the scroll view instead of a sub view. Another is to try to invert the transform in real time with a UIView animation (see Andrew Madsen's answer).
Related
I am using a UIView animation to resize and translate a view containing multiple subviews. The animation for the parent view happens perfectly; however, the subviews exhibit strange behaviour. When the animation begins, the subviews are immediately resized and then moved to their final position.
For example, if the duration and delay of the animation was five-seconds, as soon as the animation was called, the subviews would move to the desired end-of-animation values. After five-seconds, the superview would be resized and translated to the desired.
My code for the animation is:
[UIView animateWithDuration:0.5 animations:^{
if (UIDeviceOrientationIsLandscape(self.interfaceOrientation)) {
self.leftPaneView.frame = leftPaneLandscapeFrame;
self.rightPaneContainerView.frame = rightPaneLandscapeFrame;
}
if (UIDeviceOrientationIsPortrait(self.interfaceOrientation)) {
CGFloat offset = 300;
self.leftPaneView.frame = CGRectOffset(leftPanePortraitFrame, -offset, 0);
self.rightPaneContainerView.frame = rightPanePortraitFrame;
}
}];
Any ideas?
Note: rightPaneContainerView contains the view of a UIViewController that is a child of the view controller that calls this animation.
I managed to solve the problem. The content mode for some of the views was set to Left. When the animation started, the views would jump the left, and then be animated to the desired end-of-animation value.
An amateur mistake. Thanks everyone who took a look.
I have a big content view within UIScrollView that contains subviews. When I zoom, I want to keep frames of that subviews unchanged. In my current solution I use the following:
I override setTransform in my content view and for each subview I apply inverted transform as follows:
- (void)setTransform:(CGAffineTransform)newValue {
for (SubView *subView in self.subviews) {
subView.transform = CGAffineTransformInvert(newValue);
}
[super setTransform:newValue];
}
it works for my subviews sizes (frame.size.), but I always have some small (sometimes not small) offset in frame.origin. of my subviews.
How should I calculate correct frame.origin for my subviews?
I need to get the same functionality as in maps application when after zoom we have the same initial position of annotation view.
Thanks
i have an scroll view and i am adding few views to that
when the user scrolls how to get the current visible view's tag.
then i can add some thing to that view...
its just like getting the indexpathrow in table view..
how to do that..?
Thanks
You basically want to check if the frame of the subviews inside the UIScrollView intersect the scrollview's frame (if you only want to determine partial visibility), or if the frame is contained in the other frame (if you want to determine full visibility).
However, in order to check if the subview's frame intersects and/or is contained in the scrollview's frame you need to translate it from the local coords inside the scrollview to the global coordinates outside the scrollview.
That is probably pretty confusing, so here is some code. This will loop through all the subviews of a scrollview and print out whether it is fully visibile or partially visible:
for (UIView *subview in scrollView)
{
CGRect globalRect = CGRectOffset(subview.frame, -scrollView.contentOffset.x, -scrollView.contentOffset.y);
CGRect scrollViewBounds = CGRectMake(0.0f, 0.0f, scrollView.bounds.size.width, scrollView.bounds.size.height);
if (CGRectContainsRect(scrollViewBounds, globalRect)) {
NSLog(#"FULLY VISIBLE");
} else if (CGRectIntersectsRect(scrollViewBounds, globalRect)) {
NSLog(#"PARTIALLY VISIBLE");
}
}
You could put this in a UIScrollViewDelegate method to do these checks while the user is scrolling the content around.
In my app I have some logic that changes the UIScrollView's contentSize dynamically. The problem I'm running into is that when I change the contentSize it sets the contentOffset to CGPointZero or something like that.
I guess you can't set contentSize to be in the middle of the UIScrollView. Any ideas on how to get this to work?
In other words I want to change the contentSize of my UIScrollView half way and keep its contentOffset.
thanks in advance,
fbr
In my application, i had a UIViewController which had a UIScrollView as its root subview. That scrollView contains a UIView which acts a a container for a UITextView i had embedded.
Entering a long enough string would cause the textView to expand, subsequently changing the contentSize of the scrollView causing it to reset it's content offset to 0. Looking at the stack trace i was able to see _adjustContentOffsetIfNecessary attempts to adjust the content offset immediately after a contentSize change.
I created a subclass of UIScrollView that blocks _adjustContentOffsetIfNecessary initial attempt to adjust the offset.
class CustomScrollView: UIScrollView {
private var oldContentSize: CGSize = .zero
override var contentOffset: CGPoint {
set {
if oldContentSize == contentSize {
super.contentOffset = newValue
} else {
super.contentOffset = super.contentOffset
oldContentSize = contentSize
}
}
get {
return super.contentOffset
}
}
}
When the contentSizes are the same, we adjust the offset as normal. If it differs, we keep the offset the same and update oldContentSize to its new size so the next time around it's receptive to changing its offset.
As you've already seen, you can't do this by changing contentSize, but you can fake it by changing the contentOffset and moving your subviews around. An example of this in the DTInfiniteGridView class of Daniel Tull's DTKit library.
setContentSize: of UIScrollView will set its contentOffset( _adjustContentOffsetIfNecessary, an internal method is called,which does the mysterious offset change on your scrollview.)
One way to set the required content offset is to do setContentOffset:reqiredOffset animation:NO on the scrollview.
(requiredOffset is calculated for new contentSize).
I think you can just trick this by saving your current contentOffset and after you set the new contentSize, you can just use the setContentOffset:animated: to return the scroll where it was. Hope this helps.
I don't know about which is the method will used,
- (void)viewDidLoad {
[super viewDidLoad];
srcv.contentSize = ?;
}
Here what will come and replace with "?" to scroll the simulator
I just created an animation to zoom in or zoom out a UIScrollView, but my problem is that the zoomScale is not updating. Like for example, I zoom out my UIView inside of my UIScrollView using the animation I created, but when I zoom in using expand gesture the UIView will automatically change to its big size or zoom in size. I saw the my zoomScale is not updating and still in 1.0 scale. Then I update my zoomScale after the animation but the problem is the zoom out UIView will automatically go to the top of the screen. Can I update this zoomScale property of UIScrollView?
Thanks.
Did you implement viewForZoomingInScrollView function ?
This function of your scroll view delegate is called when you change the zoomScale. It returns the view that must be rescaled when the scale of your scrollview is changed.
In general you just return the scrollView given in parameters :
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView; {
return scrollView.childView;
}
Don't forget to set a delegate to your scrollview : yourScrollView.delegate = self;