How to constrain UIScrollView to only zoom vertica

2019-05-07 04:47发布

I have a UIScrollView which I'm using to represent an axis on a graph. I'd like the user to be able to zoom in on the axis using the usual pinch motion, but for it to only scale in the vertical direction, not horizontally.

My question is similar to this one, but I've tried the solution suggested there (overriding the subview's SetTransform method so that it ignores scaling in one direction) and it works perfectly when constraining scaling horizontally, but not vertically. When I try implementing it vertically the first pinch action works fine, but subsequent pinches seem to reset the zoom scale to one before having any effect.

Does anyone know what might be causing this behaviour, and more importantly how I can get around it?

I'm using MonoTouch but answers using Objective-C are fine.

4条回答
姐就是有狂的资本
2楼-- · 2019-05-07 05:26

It will be more helpful if you provide a sample code of what you are trying, but i am giving you some lines to try. Actually you have to make the content size width equal to "320" i.e. equal to the the screen size of iPhone.

scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 45,320,480)];   
scrollView.contentSize = CGSizeMake(320,1000);
scrollView.showsVerticalScrollIndicator = YES;

The MonoTouch version follows:

scrollView = new UIScrollView (new RectangleF (0, 45, 320, 480)) {
    ContentSize = new SizeF (320, 1000),
    ShowVerticalScrollIndicator = true
};

Hope it helps.. :) and Yes dont forget to accept the answer if it helps :D

查看更多
成全新的幸福
3楼-- · 2019-05-07 05:32

After struggling with the same issue, I was able to come up with a workaround. Use this code for the setTransform method.

-(void)setTransform:(CGAffineTransform)transform
{
    CGAffineTransform constrainedTransform = CGAffineTransformIdentity;
    constrainedTransform.d = self.initialScale * transform.d;
    constrainedTransform.d = (constrainedTransform.d < MINIMUM_ZOOM_SCALE) ? MINIMUM_ZOOM_SCALE : constrainedTransform.d;
    
    [super setTransform:constrainedTransform];
}
                                                                          

Set the initialScale property from within the scrollViewWillBeginZooming delegate method.

查看更多
Explosion°爆炸
4楼-- · 2019-05-07 05:40

I know this question was posted quite a while ago, but here's an answer for anyone stuck on this problem.

I looked over the question you linked to, rankAmateur, and I think the simple way to fix the solution found there to suit your needs is to replace the CGAffineTransform's "a" property with its "d" property in the setTransform: method.

- (void)setTransform:(CGAffineTransform)newValue;
{
 CGAffineTransform constrainedTransform = CGAffineTransformIdentity;
 // constrainedTransform.a = newValue.a;
 constrainedTransform.d = newValue.d;
 [super setTransform:constrainedTransform];
}

I'm not very well versed in CGAffineTransorm, but this worked for me and after browsing the documentation it seems the "a" property corresponds to a view's x-axis and the "d" property corresponds to a view's y-axis.

EDIT

So after going back and realizing what the question really was, I did some more digging into this and I'm a bit stumped, but having experienced the same behavior that rankAmateur mentions above, it seems incredibly unusual for the CGAffineTransform to work perfectly well with zoomScale when zooming is constrained to only horizontally, but not when constrained to only vertically.

The only hypothesis I can offer, is that it might have something to do with the differing default coordinate systems of Core Graphics and UIKit, since in those coordinate systems the x-axis functions in the same way, while the y-axis functions oppositely. Perhaps somehow this gets muddled up in the previously mentioned overriding of setTransform.

查看更多
爷的心禁止访问
5楼-- · 2019-05-07 05:42

This answer depends heavily on the answer from starryVere (thumbs up!)

this is starryVere's code in Swift. It is in the zoomed UIView subclass:

var initialScale: CGFloat = 1.0
override var transform: CGAffineTransform {
    set{
        //print("1 transform... \(newValue), frame=\(self.frame), bounds=\(self.bounds)")
        var constrainedTransform = CGAffineTransformIdentity
        constrainedTransform.d = self.initialScale * newValue.d // vertical zoom
        //constrainedTransform.a = newValue.a // horizontal zoom
        super.transform = constrainedTransform
        //print("2 transform... \(constrainedTransform), frame=\(self.frame), bounds=\(self.bounds)")
    }
    get{
        return super.transform
    }
}

The commented out prints are very helpful to understand what happens with bounds and frame during the transformation.

Now to the scale problem:

the method scrollViewDidEndZooming of the containing UIScrollViewDelegate has a parameter scale. According to my tests this parameter scale contains the value zoomedView.transform.a which is the horizontal scale factor that we set to 1.0 using CGAffineTransformIdentity. So scale is always 1.0.

The fix is easy:

func scrollViewDidEndZooming(scrollView: UIScrollView, withView view: UIView?, atScale scale: CGFloat) {
    let myScale = zoomView.transform.d

}

use myScale like you would use scale in cases with horizontal zoom.

查看更多
登录 后发表回答