使用绘图在UIScrollView内缩放UIView

时间:2019-02-20 13:45:09

标签: ios objective-c uiscrollview drawing

我有一个滚动视图(灰色),里面有一个缩放视图(橙色)。问题是如果我缩放此视图,则绘制在其上的红色形状也会被缩放,包括线宽和蓝色正方形大小。我想要的是保持恒定的线宽和蓝色正方形的大小(如第一张图片所示),以根据缩放级别缩放形状本身的面积(绘制的文本仅供参考,我不在乎其大小)

缩放前

enter image description here

缩放后

enter image description here

视图控制器

#import "ViewController.h"
#import "ZoomingView.h"

@interface ViewController ()

@property (strong, nonatomic) IBOutlet UIScrollView *scrollView;

@end

@implementation ViewController
{
    ZoomingView *_zoomingView;
}

- (void)viewDidLayoutSubviews
{
    [self setup];
}

- (void)setup
{
    CGFloat kViewSize = self.scrollView.frame.size.width - 40;

    self.scrollView.minimumZoomScale = 1;
    self.scrollView.maximumZoomScale = 10;
    self.scrollView.delegate = self;
    self.scrollView.contentSize = self.scrollView.bounds.size;

    _zoomingView = [[ZoomingView alloc] initWithFrame:
                    CGRectMake((self.scrollView.frame.size.width - kViewSize) / 2,
                               (self.scrollView.frame.size.height - kViewSize) / 2,
                               kViewSize,
                               kViewSize)];
    [self.scrollView addSubview:_zoomingView];
}

#pragma mark - UIScrollViewDelegate

- (UIView*)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
    return _zoomingView;
}

- (void)scrollViewDidZoom:(UIScrollView *)scrollView
{
    // zooming view position fix

    UIView *zoomView = [scrollView.delegate viewForZoomingInScrollView:scrollView];
    CGRect zvf = zoomView.frame;

    if (zvf.size.width < scrollView.bounds.size.width) {
        zvf.origin.x = (scrollView.bounds.size.width - zvf.size.width) / 2.0f;
    } else {
        zvf.origin.x = 0.0;
    }

    if (zvf.size.height < scrollView.bounds.size.height) {
        zvf.origin.y = (scrollView.bounds.size.height - zvf.size.height) / 2.0f;
    } else {
        zvf.origin.y = 0.0;
    }

    zoomView.frame = zvf;

    [_zoomingView updateWithZoomScale:scrollView.zoomScale];
}

@end

缩放视图

#import "ZoomingView.h"

@implementation ZoomingView
{
    CGFloat _zoomScale;
}

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        [self setup];
    }
    return self;
}

- (void)setup
{
    self.backgroundColor = [UIColor orangeColor];

    _zoomScale = 1;
}

- (void)drawRect:(CGRect)rect
{
    const CGFloat kPointSize = 10;

    NSArray *points = @[[NSValue valueWithCGPoint:CGPointMake(30, 30)],
                        [NSValue valueWithCGPoint:CGPointMake(200, 40)],
                        [NSValue valueWithCGPoint:CGPointMake(180, 200)],
                        [NSValue valueWithCGPoint:CGPointMake(70, 180)]];

    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetLineWidth(context, 1);

    // points

    [[UIColor blueColor] setStroke];

    for (NSValue *value in points) {
        CGPoint point = [value CGPointValue];
        CGContextStrokeRect(context, CGRectMake(point.x - kPointSize / 2,
                                                point.y - kPointSize / 2,
                                                kPointSize,
                                                kPointSize));
    }

    // lines

    [[UIColor redColor] setStroke];

    for (NSUInteger i = 0; i < points.count; i++) {
        CGPoint point = [points[i] CGPointValue];

        if (i == 0) {
            CGContextMoveToPoint(context, point.x, point.y);
        } else {
            CGContextAddLineToPoint(context, point.x, point.y);
        }
    }

    CGContextClosePath(context);
    CGContextStrokePath(context);

    // text

    NSAttributedString *str = [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"%f", _zoomScale] attributes:@{NSFontAttributeName : [UIFont systemFontOfSize:12]}];
    [str drawAtPoint:CGPointMake(5, 5)];
}

- (void)updateWithZoomScale:(CGFloat)zoomScale
{
    _zoomScale = zoomScale;

    [self setNeedsDisplay];
}

@end

编辑

基于提出的解决方案(可以肯定地工作),我很感兴趣是否可以使用我的drawRect例程和Core Graphics方法使其工作。

因此,我以这种方式更改了代码,从this答案开始采用了建议的缩放比例和contentsScale方法。结果,在没有contentsScale的情况下,绘图看起来会非常模糊,并且效果会更好,但是无论如何还是会出现轻微的模糊。

尽管我不知道为什么,但是采用分层的方法可以提供最佳结果。

- (void)drawRect:(CGRect)rect
{
    const CGFloat kPointSize = 10;

    NSArray *points = @[[NSValue valueWithCGPoint:CGPointMake(30, 30)],
                        [NSValue valueWithCGPoint:CGPointMake(200, 40)],
                        [NSValue valueWithCGPoint:CGPointMake(180, 200)],
                        [NSValue valueWithCGPoint:CGPointMake(70, 180)]];

    CGFloat scaledPointSize = kPointSize * (1.0 / _zoomScale);
    CGFloat lineWidth = 1.0 / _zoomScale;

    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetLineWidth(context, lineWidth);

    // points

    [[UIColor blueColor] setStroke];

    for (NSValue *value in points) {
        CGPoint point = [value CGPointValue];
        CGContextStrokeRect(context, CGRectMake(point.x - scaledPointSize / 2,
                                                point.y - scaledPointSize / 2,
                                                scaledPointSize,
                                                scaledPointSize));
    }

    // lines

    [[UIColor redColor] setStroke];

    for (NSUInteger i = 0; i < points.count; i++) {
        CGPoint point = [points[i] CGPointValue];

        if (i == 0) {
            CGContextMoveToPoint(context, point.x, point.y);
        } else {
            CGContextAddLineToPoint(context, point.x, point.y);
        }
    }

    CGContextClosePath(context);
    CGContextStrokePath(context);

    // text

    NSAttributedString *str = [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"%f", _zoomScale] attributes:@{NSFontAttributeName : [UIFont systemFontOfSize:12]}];
    [str drawAtPoint:CGPointMake(5, 5)];
}

- (void)updateWithZoomScale:(CGFloat)zoomScale
{
    _zoomScale = zoomScale;

    [self setNeedsDisplay];

    [CATransaction begin];
    [CATransaction setValue:[NSNumber numberWithBool:YES]
                     forKey:kCATransactionDisableActions];
    self.layer.contentsScale = zoomScale;
    [CATransaction commit];
}

1 个答案:

答案 0 :(得分:1)

最好将框和线形放在CAShapeLayer上,您可以在其中根据缩放比例更新线宽。

您只需要创建和定义一次线形。不过,对于您的盒子,当您更改缩放比例时,您需要重新创建路径(以使盒子的宽度/高度保持恒定的非缩放点大小。

尝试一下。您应该能够简单地替换当前的ZoomingView.m类-无需更改视图控制器。

//
//  ZoomingView.m
//
//  modified by Don Mag
//

#import "ZoomingView.h"

@interface ZoomingView()

@property (strong, nonatomic) CAShapeLayer *shapeLayer;
@property (strong, nonatomic) CAShapeLayer *boxesLayer;

@property (strong, nonatomic) NSArray *points;

@end

@implementation ZoomingView

{
    CGFloat _zoomScale;
    CGFloat _kPointSize;
}

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        [self setup];
    }
    return self;
}

- (void)setup
{
    self.backgroundColor = [UIColor orangeColor];

    _points = @[[NSValue valueWithCGPoint:CGPointMake(30, 30)],
                [NSValue valueWithCGPoint:CGPointMake(200, 40)],
                [NSValue valueWithCGPoint:CGPointMake(180, 200)],
                [NSValue valueWithCGPoint:CGPointMake(70, 180)]];

    _zoomScale = 1;

    _kPointSize = 10.0;

    // create and setup boxes layer
    _boxesLayer = [CAShapeLayer new];
    [self.layer addSublayer:_boxesLayer];
    _boxesLayer.strokeColor = [UIColor redColor].CGColor;
    _boxesLayer.fillColor = [UIColor clearColor].CGColor;
    _boxesLayer.lineWidth = 1.0;
    _boxesLayer.frame = self.bounds;

    // create and setup shape layer
    _shapeLayer = [CAShapeLayer new];
    [self.layer addSublayer:_shapeLayer];
    _shapeLayer.strokeColor = [UIColor greenColor].CGColor;
    _shapeLayer.fillColor = [UIColor clearColor].CGColor;
    _shapeLayer.lineWidth = 1.0;
    _shapeLayer.frame = self.bounds;

    // new path for shape
    UIBezierPath *thePath = [UIBezierPath new];

    for (NSValue *value in _points) {

        CGPoint point = [value CGPointValue];

        if ([value isEqualToValue:_points.firstObject]) {
            [thePath moveToPoint:point];
        } else {
            [thePath addLineToPoint:point];
        }

    }

    [thePath closePath];

    [_shapeLayer setPath:thePath.CGPath];

    // trigger the boxes update
    [self updateWithZoomScale:_zoomScale];
}

- (void)drawRect:(CGRect)rect
{
    // text

    NSAttributedString *str = [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"%f", _zoomScale] attributes:@{NSFontAttributeName : [UIFont systemFontOfSize:12]}];
    [str drawAtPoint:CGPointMake(5, 5)];
}

- (void)updateWithZoomScale:(CGFloat)zoomScale
{
    _zoomScale = zoomScale;

    CGFloat scaledPointSize = _kPointSize * (1.0 / zoomScale);

    // create a path for the boxes
    //  needs to be done here, because the width/height of the boxes
    //  must change with the scale
    UIBezierPath *thePath = [UIBezierPath new];

    for (NSValue *value in _points) {

        CGPoint point = [value CGPointValue];

        CGRect r = CGRectMake(point.x - scaledPointSize / 2.0,
                              point.y - scaledPointSize / 2.0,
                              scaledPointSize,
                              scaledPointSize);

        [thePath appendPath:[UIBezierPath bezierPathWithRect:r]];

    }

    [_boxesLayer setPath:thePath.CGPath];

    _boxesLayer.lineWidth = 1.0 / zoomScale;
    _shapeLayer.lineWidth = 1.0 / zoomScale;

    [self setNeedsDisplay];
}

@end

结果:

enter image description here enter image description here

注意:应该不用多说,但是...这是您开始使用的起点,而不是“生产代码”。