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
Related
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).
so lets say I have a UIScrollView, within it are 3 UIViews, within those there is a UISlider in each one. they are positioned vertically in the UIScrollView.
I now have a 4th UIView also in the UIScrollView which I wish to move around depending on the position of the slider which has been used.
so within my sliderChanged method which i pass the sender, i get the position of the slider, and adjust the position of the 4th UIWindow to its y. This works great on the first UIView, but once on another UIView which has forced me to scroll down, using the slider moves the 4th UIView but stays at the beginning of the UIScrollView
I am using:
[4thView setCenter:CGPointMake([4thView center].x, [slider center].y+10)];
what I need is to get the position of the slider relative to the content of the scrollView and not relative to its UIView, so that I may set the 4th view again relative to the scrollView content.
You can convert the points by UIView's instance methods.
- (CGPoint)convertPoint:(CGPoint)point toView:(UIView *)view
- (CGPoint)convertPoint:(CGPoint)point fromView:(UIView *)view
For example, I want to convert a ponint on the viewA to the scrollViewB's coordinates.
CGPoint aPtInScrollView = [viewA convertPoint:aPoint toView:scrollViewB];
Or, I want to know the position of viewA in scrollViewB.
CGPoint aPosViewA = [scrollViewB convertPoint:CGPointZero fromView:viewA];
Using the previous answer I took it a bit further to solve the issue I was having.
If you have multiple UITextView's within multiple UIView's all inside a single UIScrollView: this will animate and scroll to them.
This could be also be applied to UITextField's as well.
-(void)textViewDidBeginEditing:(UITextView *)textView {
[self.theScrollView setContentOffset:[self.theScrollView convertPoint:CGPointMake(textView.frame.origin.x, textView.frame.origin.y) fromView:textView.superview] animated:YES];
}
If you have labels above your textviews, just offset the x or y by that amount.
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.
I want to have images inside of a UIScrollView (or at least appear to be inside a UIScrollView) that don't change their size when zooming in and out. I do want the images to maintain their positions when zooming or scrolling.
The use case is that I have a customized map view with pushpins. I want the pushpins to stay in place when zooming and scrolling the map, but I don't want them to be "zoomed in" with the content.
What is the best way to do this?
You can have your pushpin image(s) as a subview of your UIScrollView but not a subview of the view returned by
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
Then, in scrollViewDidZoom adjust the pushpin x and y by the zoomScale to keep the pins correctly positioned:
- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
imageView.frame = CGRectMake(pushPinX * scrollView.zoomScale, pushPinY * scrollView.zoomScale, imageView.frame.size.width, imageView.frame.size.height);
}
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;