代码之家  ›  专栏  ›  技术社区  ›  schmidt9

使用绘图在uiscrollview内缩放uiview

  •  0
  • schmidt9  · 技术社区  · 6 年前

    我有一个滚动视图(灰色),里面有一个缩放视图(橙色)。问题是,如果我缩放这个视图,在它上面绘制的红色图形也会被缩放,包括线条宽度和蓝色正方形大小。我想要的是保持恒定的线条宽度和蓝色正方形的大小(就像在第一张图片上一样),根据缩放级别缩放形状本身的区域(绘制的文本只是为了参考,我不关心它的大小)

    变焦前

    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 常规和核心图形方法。

    所以我用这种方式修改了代码,应用了建议的缩放和 contentsScale 接近 this 回答。作为结果,没有 内容刻度 绘画看起来很模糊,用它好多了,但光模糊仍然存在。

    所以用分层的方法会得到最好的结果,尽管我不知道为什么。

    - (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];
    }
    
    0 回复  |  直到 6 年前
        1
  •  1
  •   DonMag    6 年前

    你最好把你的盒子和线条穿上 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

    注: 不用说,但是…这是您使用的起点,而不是“生产代码”。