I have two UIScrollViews. Both the scroll views contain an image. Now, I want to zoom second scroll view programmatically if I zoom in the first scroll view manually. I am using -(UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView method to manually zoom the first scroll view. The second scroll view should be zoomed to the same point and scale. How can I achieve this?
By using the tag or object of those two scrollviews you can identify the which scroll view you need zoom in viewForZoomingInScrollView method.
e.g
//need to set the tag for scrollviews
-(UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
if(scrollView.tag == 1)
{
return firstImageView;
}
return secondImageView;
}
OR
//need to create the object of used scrollviews
-(UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
if(scrollView == objScrollViewFirst)
{
return firstImageView;
}
return secondImageView;
}
How about something like this:
CGRect zoomRect;
zoomRect.size.height = manualScrollView.frame.size.height / manualScrollView.zoomScale;
zoomRect.size.width = manualScrollView.frame.size.width / manualScrollView.zoomScale;
zoomRect.origin.x = manualScrollView.center.x - (zoomRect.size.width / 2.0);
zoomRect.origin.y = manualScrollView.center.y - (zoomRect.size.height / 2.0);
[autoScrollView zoomToRect:zoomRect animated:YES];
[autoScrollView setZoomScale:manualScrollView.zoomScale animated:YES];
Try something like this in the UIScrollViewDelegate method:
override func scrollViewDidScroll(scrollView: UIScrollView){
otherScrollView.contentOffset = scrollView.contentOffset
otherScrollView.zoomScale = scrollView.zoomScale
}
Related
I'm trying to move a view while the scrollview is being scrolled with the same intensity.
I'm trying:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
// here I detect when user scroll
}
How could I get the intensity that the scrollview is being scrolled?
Try to use these methods:
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
//scrolling on
NSLog(#"scrollViewWillBeginDragging");
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
if(!decelerate) {
//not scrolling
}
NSLog(#"scrollViewDidEndDragging");
}
You can do that by reading the contentOffset property on the scrollView
// You can save this as a property or an iVar
CGPoint lastContentOffset;
-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{
lastContentOffset = scrollView.contentOffset;
}
-(void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGPoint difference = CGPointSubstract(scrollView.contentOffset, lastContentOffset);
lastContentOffset = scrollView.contentOffset;
// Use difference to know by how much the scroll view scrolled
// IMPORTANT: CGPointSubstract is not a standard function, you can implement it yourself.
}
I don't want to give you the full code but i'll help with how to achieve it.
1) You need to use the time.
2) When the time difference is greater than an amount of time passed to measure (0.05s?)
3) Check what the old content offset is compared to the new one
4) The difference is your velocity
5) reset your current timer
6) set current offsets to last offset
Hope this helps
Ben
distance calcs
CGFloat distanceY = currentOffset.y - lastOffset.y;
CGFloat distanceX = currentOffset.X - lastOffset.X;
I am using a UITableView to achieve a scale/unblur effect on my user's profile picture (like in the Spotify App) when I pull down the UITableView to a negative contentOffset.y. This all works fine...
Now, when a user pulls down to a contentOffset.y smaller or equal to a certain value -let's call it maintainOffsetY = 70.0- and he "lets go" of the view, I would like to maintain this contentOffset until the user "pushes" the view up again, instead of the view to automatically bounce back to contentOffset = (0,0) again.
In order to achieve this, I must know when the touches began and ended, which I can do with the following delegate methods:
- (void) scrollViewWillBeginDragging:(UIScrollView *)scrollView {
// is like touches begin
if ([scrollView isEqual:_tableView]) {
NSLog(#"touches began");
_touchesEnded = NO;
}
}
- (void) scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
// is like touches ended
NSLog(#"touches ended");
if ([scrollView isEqual:_tableView]) {
_touchesEnded = YES;
}
}
Then in
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
I check if the contentOffset.y is smaller than or equal to maintainOffsetY and if the user has already "let go" of the table, so it is about to bounce (or already bouncing) back to contentOffset = (0,0).
It does not seem to be possible to let it bounce back only to the maintainOffsetY. Does anybody know a workaround or have an idea on how to solve this?
Any help is much appreciated!
To set a negative offset to a scroll view you can use this.
You need to save the initial content inset of scroll view. It can be not 0 already, if you have translucent nav bar for example.
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
contentInsetTopInitial = self.tableView.contentInset.top;
}
Then write this.
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
CGPoint contentOffset = scrollView.contentOffset; // need to save contentOffset before changing contentInset
CGFloat contentOffsetY = contentOffset.y + contentInsetTopInitial; // calculate pure offset, without inset
if (contentOffsetY < -maintainOffsetY) {
scrollView.contentInset = UIEdgeInsetsMake(contentInsetTopInitial + maintainOffsetY, 0, maintainOffsetY, 0);
[scrollView setContentOffset:contentOffset animated:NO];
}
}
Hope this will help.
I am creating an app where I am trying to have a pagescrollviewcontroller swipe through screens and have the top "nav" bar titles scroll real time with the titles
i have the title bar view as a custom view and I am able to access the scroll delegate methods for both the custom scroll view and the pageview controller. However. I don't see how to access the real time scroll pos? I know it is possible because twitter does it (really can't shouldn't be in anyone's vocabulary) but I am not sure how to achieve this.
a picture
the home title swipes at the same scroll pos as the pagecontrollers.
current code:
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
if(scrollView.tag == 100) {
CGPoint point = CGPointMake(mainScrollView.contentOffset.x * 1,0);
[titleSwipe setContentOffset:point animated:YES];
}
else {
mainScrollView.contentOffset = CGPointMake(0,1);
[mainScrollView setContentOffset:titleSwipe.contentOffset animated:YES];
}
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate: (BOOL)decelerate {
CGPoint point = CGPointMake(mainScrollView.contentOffset.x * 2,0);
[titleSwipe setContentOffset:point animated:YES];
}
You'll find the real time position in scrollViewDidScroll:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
// X scroll
//
CGFloat percentage = scrollView.contentOffset.x / scrollView.contentSize.width;
NSLog(#"Scrolled percentage: %f", percentage);
}
Hope that helps!
I am doing a module where there is a pin drop functionality.
The pin[Image view] is dropped on a scroll view which is zoom-able.
When zoomed the the image view moves in accordance with its center orgin.
I would like to make it moved with its bottom point.
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale;
{
zoomLevel=scale;
for (SSSmartPinView *smartPinView in smartPinViewMutArray) {
smartPinView.zoomScale = zoomLevel;
smartPinView.parrentOffset = drawingScrollView.contentOffset;
smartPinView.center = CGPointMake(([smartPinView.coresmartPin.xpoint floatValue]*zoomLevel),(([smartPinView.coresmartPin.ypoint floatValue]+(zoomLevel))*zoomLevel));
}
}
- (void)scrollViewDidZoom:(UIScrollView *)scrollView{
zoomLevel=scrollView.zoomScale;
for (SSSmartPinView *smartPinView in smartPinViewMutArray){
smartPinView.zoomScale = zoomLevel;
smartPinView.parrentOffset = drawingScrollView.contentOffset;
smartPinView.center = CGPointMake(([smartPinView.coresmartPin.xpoint floatValue]*zoomLevel),(([smartPinView.coresmartPin.ypoint floatValue]+(zoomLevel))*zoomLevel));
}
}
You can set the anchor (center point for rotation / scaling) with
smartPinView.layer.anchorPoint = CGPointMake(0.5, 1.0);
See the anchorPoint property
I'm using storyboard (iOS 6.0) to create a photo gallery viewer for my app. This is how my imageViewController is set up in storyboard:
I've made sure to enable userInteraction and multiple touches on both the imageView and scrollView. What I want to do is, on pinch I want to zoom into the imageView (maximum scale 3) and be able to pan around. This is what I currently have, however, even though the pinch gesture is detected the scale does not change.
- (IBAction)imagePinched:(id)sender {
if (pinchRecognizer.state == UIGestureRecognizerStateEnded || pinchRecognizer.state == UIGestureRecognizerStateChanged) {
NSLog(#"gesture.scale = %f", pinchRecognizer.scale);
CGFloat currentScale = self.fullScreenView.frame.size.width / self.fullScreenView.bounds.size.width;
CGFloat newScale = currentScale * pinchRecognizer.scale;
if (newScale < 1) {
newScale = 1;
}
if (newScale > 3) {
newScale = 3;
}
CGAffineTransform transform = CGAffineTransformMakeScale(newScale, newScale);
self.fullScreenView.transform = transform;
pinchRecognizer.scale = 1;
}
}
Most questions and tutorials online deal with programmatically creating the views and doing this, but the less code the better (in my eyes). What's the best way to get this to work with storyboard? Thank you in advance!!!
UPDATED:
Here is my full .m file code:
- (void)viewDidLoad
{
[super viewDidLoad];
//Assign an image to this controller's imageView
fullScreenView.image = [UIImage imageNamed:imageString];
//Allows single and double tap to work
[singleTapRecognizer requireGestureRecognizerToFail: doubleTapRecognizer];
}
- (IBAction)imageTapped:(id)sender {
NSLog(#"Image Tapped.");
//On tap, fade out viewController like the twitter.app
[self dismissViewControllerAnimated:YES completion:nil];
}
- (IBAction)imageDoubleTapped:(id)sender {
NSLog(#"Image Double Tapped.");
//On double tap zoom into imageView to fill in the screen.
[fullScreenView setContentMode:UIViewContentModeScaleAspectFill];
}
- (IBAction)imagePinched:(id)sender {
if (pinchRecognizer.state == UIGestureRecognizerStateEnded || pinchRecognizer.state == UIGestureRecognizerStateChanged) {
NSLog(#"gesture.scale = %f", pinchRecognizer.scale);
CGFloat currentScale = self.fullScreenView.frame.size.width / self.fullScreenView.bounds.size.width;
CGFloat newScale = currentScale * pinchRecognizer.scale;
if (newScale < 1) {
newScale = 1;
}
if (newScale > 3) {
newScale = 3;
}
CGAffineTransform transform = CGAffineTransformMakeScale(newScale, newScale);
self.fullScreenView.transform = transform;
pinchRecognizer.scale = 1;
}
}
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return self.fullScreenView;
}
-(void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale {
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
I think better solution in Apple Documentation
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return self.imageView;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.scrollView.minimumZoomScale=0.5;
self.scrollView.maximumZoomScale=6.0;
self.scrollView.contentSize=CGSizeMake(1280, 960);
self.scrollView.delegate=self;
}
Check Apple Documentation
The first step is to make sure your views have the correct delegates implemented. For example in the .m file
#interface myRootViewController () <.., UIGestureRecognizerDelegate, UIScrollViewDelegate, ...>
From the documentation, make sure you have this implemented:
The UIScrollView class can have a delegate that must adopt the UIScrollViewDelegate protocol. For zooming and panning to work, the delegate must implement both viewForZoomingInScrollView: and scrollViewDidEndZooming:withView:atScale:; in addition, the maximum (maximumZoomScale) and minimum ( minimumZoomScale) zoom scale must be different.
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return self.fullScreenView;
}
-(void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale
{}
The scrollViewDidEndZooming method can stay empty for now. If you already did that or it still doesnt work, please post more of your code and then it is easier to help more specifically.
You need to do what Wadi suggested above but also set the setMinimumZoomScale to be different from setMaximumZoomScale. They are both 1.0f by default.
After that, the UIImageView should be pinchable
I have created a fully working demo application (similar in nature to Facebook's default photo gallery) which demonstrates pinch to zoom of Image View nested inside a scroll view with AutoLayout and storyboards. View my project here: http://rexstjohn.com/facebook-like-ios-photo-modal-gallery-swipe-gestures/.
It also includes async photo loading via MKNetworkKit and gesture based swiping through a photo gallery. Enjoy and I hope it saves everyone time trying to figure this out because it is somewhat annoying.
Here is how Apple recomends to do what you want to do. I used the code provided by Apple and it worked like a charm! If you want to optimize memory management, you can use iCarousel (made by nicklockwood), which have a cache management for dealloc unused views!
I've created a class to handle zooming of UIImageViews similar to Instagram. Might be what you're looking for, otherwise you can look at how I achieved it. https://github.com/twomedia/TMImageZoom