Масштаб UIView внутри UIScrollView с рисунком - PullRequest
0 голосов
/ 20 февраля 2019

У меня есть вид прокрутки (серый) с увеличенным изображением внутри (оранжевый).Проблема в том, что если я увеличу масштаб этого вида, красная фигура, нарисованная на нем, тоже будет увеличена, включая ширину линий и размер синих квадратов.Я хочу сохранить постоянную ширину линий и размер синих квадратов (как на первом изображении), масштабируя только область самой фигуры в соответствии с уровнем масштабирования (нарисованный текст только для справки, мне не важен его размер)

до увеличения

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.

Таким образом, я изменил свой код таким образом, применив предложенное масштабирование и 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 голосов
/ 20 февраля 2019

Возможно, вам лучше поставить поля и форму линии на CAShapeLayer s, где вы можете обновить ширину линии на основе масштаба увеличения.

Вам нужно только создать и определить свою линию- однаждыДля ваших ящиков, однако, вам нужно будет заново создать путь при изменении масштаба (чтобы сохранить ширину / высоту ячеек с постоянным размером без увеличения.

ПопробуйтеВы должны иметь возможность просто заменить текущий класс 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

Примечание: Должно быть само собой разумеется, но ... Это предназначено для того, чтобы начать работу с вами, а не с "рабочим кодом".«

...