【问题标题】:Zoom UIView inside UIScrollView with drawing用绘图在 UIScrollView 内缩放 UIView
【发布时间】:2019-07-14 05:30:39
【问题描述】:

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

缩放前

缩放后

视图控制器

#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];
}

【问题讨论】:

    标签: ios objective-c uiscrollview drawing


    【解决方案1】:

    您最好将框和线形放在CAShapeLayers 上,在那里您可以根据缩放比例更新线宽。

    您只需要创建和定义一次线形。但是,对于您的框,您需要在更改缩放时重新创建路径(以将框的宽度/高度保持在恒定的非缩放点大小。

    试试这个。您应该能够简单地替换您当前的 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
    

    结果:

    注意:不言而喻,但是...这是您使用的起点,而不是“生产代码”。

    【讨论】:

    • 非常感谢,此解决方案有效,请查看我编辑的答案,也许您可​​以简要说明为什么我会变得模糊(抱歉,如果它看起来有点离题)
    • 我实现了你的更新代码,看看你的意思。我将尝试看一些东西 - 如果我发现任何东西,我会再次发表评论。
    • 只是一个假设,也许使用CGContextScaleCTM 会有所帮助 - 首先缩放(到什么大小?),然后减小到需要的大小,所以也许我们可以避免模糊
    猜你喜欢
    • 1970-01-01
    • 2011-07-03
    • 1970-01-01
    • 1970-01-01
    • 2011-03-19
    • 2012-06-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-30
    相关资源
    最近更新 更多