我有一个滚动视图(灰色),里面有一个缩放视图(橙色)。问题是如果我缩放此视图,则绘制在其上的红色形状也会被缩放,包括线宽和蓝色正方形大小。我想要的是保持恒定的线宽和蓝色正方形的大小(如第一张图片所示),以根据缩放级别缩放形状本身的面积(绘制的文本仅供参考,我不在乎其大小)>
缩放前
缩放后
视图控制器
#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];
}
答案 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
结果:
注意:应该不用多说,但是...这是您开始使用的起点,而不是“生产代码”。