gpt4 book ai didi

ios - UIScrollView 缩小具有 -ve 原点的 View

转载 作者:技术小花猫 更新时间:2023-10-29 10:50:06 27 4
gpt4 key购买 nike

我有一个 UIScrollView。在这里我有一个 UIView,它有一个负原点的框架 - 我需要限制 ScrollView ,这样你就不能滚动整个 View ..

我已经在此 ScrollView 中实现了缩放。

缩放时, ScrollView 将根据比例调整可缩放 View 的大小。但它不会调整原点。

所以如果我有一个框架为 {0, -500}, {1000, 1000}

的 View

我缩小到 0.5 的比例,这将给我一个新的框架 {0, -500}, {500, 500}

显然这样不好,整个view都被scrollview缩小了。我希望框架为 {0, -250}, {500, 500}

我可以通过正确调整原点来修复 scrollViewDidZoom 方法中的一些问题。这确实有效,但缩放不平滑。在这里更改原点会导致它跳跃。

我在 UIView 的文档中注意到它说(关于 frame 属性):

Warning: If the transform property is not the identity transform, the value of this property is undefined and therefore should be ignored.

不太清楚这是为什么。

我是不是处理这个问题有误?修复它的最佳方法是什么?

谢谢


下面是我正在使用的测试应用程序的一些源代码:

在 View Controller 中..

- (void)viewDidLoad
{
[super viewDidLoad];
self.bigView = [[BigView alloc] initWithFrame: CGRectMake(0, -400, 1000, 1000)];

[self.bigScroll addSubview: bigView];
self.bigScroll.delegate = self;
self.bigScroll.minimumZoomScale = 0.2;
self.bigScroll.maximumZoomScale = 5;
self.bigScroll.contentSize = bigView.bounds.size;
}

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

- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
// bigView.frame = CGRectMake(0, -400 * scrollView.zoomScale,
// bigView.frame.size.width, bigView.frame.size.height);

bigView.center = CGPointMake(500 * scrollView.zoomScale, 100 * scrollView.zoomScale);
}

然后在 View 中...

- (void)drawRect:(CGRect)rect
{
// Drawing code
CGContextRef ctx = UIGraphicsGetCurrentContext();

CGContextSetFillColorWithColor(ctx, [UIColor whiteColor].CGColor);
CGContextSetStrokeColorWithColor(ctx, [UIColor whiteColor].CGColor);
CGContextFillRect(ctx, CGRectMake(100, 500, 10, 10));

for (int i = 0; i < 1000; i += 100) {
CGContextStrokeRect(ctx, CGRectMake(0, i, 1000, 3));
}
}

请注意,这里的跳跃在较大的缩放比例下更为明显。在我的真实应用程序中,有更多的绘图和处理正在进行,跳跃在任何时候都更加明显。

最佳答案

您不必使用 frame 属性 - 鉴于 Apple 非常坚定的警告,您也不应该使用。在这种情况下,您通常可以使用 boundscenter 来实现您的结果。

在您的情况下,您可以忽略所有 subview 的属性。假设您的 subview 是 viewForZoomingInScrollView,您可以使用 scrollView 的 contentOffsetzoomScale 属性

- (void) setMinOffsets:(UIScrollView*)scrollView
{
CGFloat minOffsetX = MIN_OFFSET_X*scrollView.zoomScale;
CGFloat minOffsetY = MIN_OFFSET_Y*scrollView.zoomScale;

if ( scrollView.contentOffset.x < minOffsetX
|| scrollView.contentOffset.y < minOffsetY ) {

CGFloat offsetX = (scrollView.contentOffset.x > minOffsetX)?
scrollView.contentOffset.x : minOffsetX;

CGFloat offsetY = (scrollView.contentOffset.y > minOffsetY)?
scrollView.contentOffset.y : minOffsetY;

scrollView.contentOffset = CGPointMake(offsetX, offsetY);
}
}

从您的 scrollView 委托(delegate)中的 scrollViewDidScrollscrollViewDidZoom 调用它。这应该可以顺利进行,但如果您有疑问,您也可以通过子类化 scrollView 并使用 layoutSubviews 调用它来实现它。在他们的 PhotoScroller 示例中,Apple 通过覆盖 layoutSubviews 使 scrollView 的内容居中 - 尽管他们疯狂地忽略了自己的警告并调整 subview 的 frame 属性来实现这一点。

更新

上述方法消除了 ScrollView 达到其极限时的“弹跳”。如果您想保留反弹,您可以直接更改 View 的中心属性:

- (void) setViewCenter:(UIScrollView*)scrollView
{
UIView* view = [scrollView subviews][0];
CGFloat centerX = view.bounds.size.width/2-MIN_OFFSET_X;
CGFloat centerY = view.bounds.size.height/2-MIN_OFFSET_Y;

centerX *=scrollView.zoomScale;
centerY *=scrollView.zoomScale;

view.center = CGPointMake(centerX, centerY);
}

更新 2

从您更新的问题(带有代码)中,我可以看出这些解决方案都不能解决您的问题。似乎正在发生的事情是,您的偏移量越大,变焦运动变得越不稳定。偏移量为 100 点时, Action 仍然相当流畅,但偏移量为 500 点时,则粗糙得令人无法接受。这部分与您的 drawRect 例程有关,部分与 scrollView 中正在进行的(太多)重新计算有关以显示正确的内容。所以我有另一个解决方案......

在您的 viewController 中,将您的 customView 的边界/框架原点设置为正常 (0,0)。我们将使用图层来偏移内容。您需要将 QuartzCore 框架添加到您的项目中,并将其#import 到您的自定义 View 中。

在自定义 View 中初始化两个 CAShapeLayers - 一个用于框,另一个用于线。如果它们共享相同的填充和描边,你将只需要一个 CAShapeLayer(对于这个例子,我改变了你的填充和描边颜色)。每个 CAShapeLayer 都有它自己的 CGContext,你可以用颜色、线宽等每层初始化一次。然后要让 CAShapelayer 完成它的绘制,你所要做的就是用 CGPath 设置它的 path 属性。

#import "CustomView.h"
#import <QuartzCore/QuartzCore.h>

@interface CustomView()
@property (nonatomic, strong) CAShapeLayer* shapeLayer1;
@property (nonatomic, strong) CAShapeLayer* shapeLayer2;
@end

@implementation CustomView

#define MIN_OFFSET_X 100
#define MIN_OFFSET_Y 500

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


- (void) initialiseLayers
{
CGRect layerBounds = CGRectMake( MIN_OFFSET_X,MIN_OFFSET_Y
, self.bounds.size.width + MIN_OFFSET_X
, self.bounds.size.height+ MIN_OFFSET_Y);

self.shapeLayer1 = [[CAShapeLayer alloc] init];
[self.shapeLayer1 setFillColor:[UIColor clearColor].CGColor];
[self.shapeLayer1 setStrokeColor:[UIColor yellowColor].CGColor];
[self.shapeLayer1 setLineWidth:1.0f];
[self.shapeLayer1 setOpacity:1.0f];

self.shapeLayer1.anchorPoint = CGPointMake(0, 0);
self.shapeLayer1.bounds = layerBounds;
[self.layer addSublayer:self.shapeLayer1];

设置界限是关键。与裁剪其 subview 的 View 不同,CALayers 将绘制超出其超层的边界。您将开始在 View 顶部上方绘制 MIN_OFFSET_Y 点,在左侧绘制 MIN_OFFSET_X 点。这允许您在 ScrollView 的内容 View 之外绘制内容,而无需 ScrollView 做任何额外的工作。

Unlike views, a superlayer does not automatically clip the contents of sublayers that lie outside its bounds rectangle. Instead, the superlayer allows its sublayers to be displayed in their entirety by default.
(Apple Docs, Building a Layer Hierarchy)

    self.shapeLayer2 = [[CAShapeLayer alloc] init];

[self.shapeLayer2 setFillColor:[UIColor blueColor].CGColor];
[self.shapeLayer2 setStrokeColor:[UIColor clearColor].CGColor];
[self.shapeLayer2 setLineWidth:0.0f];
[self.shapeLayer2 setOpacity:1.0f];

self.shapeLayer2.anchorPoint = CGPointMake(0, 0);
self.shapeLayer2.bounds = layerBounds;
[self.layer addSublayer:self.shapeLayer2];

[self drawIntoLayer1];
[self drawIntoLayer2];
}

为每个形状层设置一条贝塞尔曲线路径,然后传入:

- (void) drawIntoLayer1 {

UIBezierPath* path = [[UIBezierPath alloc] init];
[path moveToPoint:CGPointMake(0,0)];

for (int i = 0; i < self.bounds.size.height+MIN_OFFSET_Y; i += 100) {
[path moveToPoint:
CGPointMake(0,i)];
[path addLineToPoint:
CGPointMake(self.bounds.size.width+MIN_OFFSET_X, i)];
[path addLineToPoint:
CGPointMake(self.bounds.size.width+MIN_OFFSET_X, i+3)];
[path addLineToPoint:
CGPointMake(0, i+3)];
[path closePath];
}

[self.shapeLayer1 setPath:path.CGPath];
}

- (void) drawIntoLayer2 {
UIBezierPath* path = [UIBezierPath bezierPathWithRect:
CGRectMake(100+MIN_OFFSET_X, MIN_OFFSET_Y, 10, 10)];
[self.shapeLayer2 setPath:path.CGPath];
}

这避免了对 drawRect 的需要 - 如果您更改路径属性,您只需要重新绘制图层。即使您确实像调用 drawRect 一样频繁地更改路径属性,绘图现在也应该明显更有效。由于 path 是一个动画属性,如果需要,您还可以免费获得动画。

在你的情况下,我们只需要设置一次路径,所以所有的工作都在初始化时完成一次。

现在您可以从 scrollView 委托(delegate)方法中删除任何居中代码,不再需要它了。

关于ios - UIScrollView 缩小具有 -ve 原点的 View ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16361810/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com