将CGAffineTransformScale应用于UIView会使图层边框也变大

4

我有一个UIView,在它里面放了一个UIImageView。我在UIView上添加了UIPinchGestureRecognizer来处理缩放和放大,并使UIView和UIImageView一起增长。

我的UIView有一个边框。我是这样添加的:

self.layer.borderColor = [UIColor blackColor].CGColor;
self.layer.borderWidth = 1.0f;
self.layer.cornerRadius = 8.0f;

我的问题是我找不到一种方法可以使我的UIView更大,同时保持边框的相同宽度。当用手指捏和缩放时,边框变得更厚。

这是我的UIPinchGestureRecognizer处理程序:

- (void)scale:(UIPanGestureRecognizer *)sender{

    if([(UIPinchGestureRecognizer*)sender state] == UIGestureRecognizerStateBegan) {
        _lastScale = 1.0;
    }

    CGFloat scale = 1.0 - (_lastScale - [(UIPinchGestureRecognizer*)sender scale]);

    CGAffineTransform currentTransform = self.transform;
    CGAffineTransform newTransform = CGAffineTransformScale(currentTransform, scale, scale);

    _lastScale = [(UIPinchGestureRecognizer*)sender scale];

    [self setTransform:newTransform];
}

非常感谢!

我已经搜索了很多,找到了这个:

self.layer.borderWidth = 2.0f / scaleFactor;

很遗憾,对我来说并没有起作用...虽然有道理,但是无法实现。

我也阅读了关于在后面添加另一个视图,并使前视图具有位置偏移量以使后视图显示并看起来像边框的解决方案。但这不是一个选项,因为我需要我的图像透明查看。

我想重新创建Aviary在他们的应用程序中所做的事情。您可以缩放“贴纸”,边框始终保持相同大小。


你尝试在scale:方法结束时重新应用borderWidth了吗? - ChrisH
边框宽度始终保持不变。如果我NSLog(“%f”,self.layer.borderWith),它始终是我第一次设置的值,但边框确实变大了... - Andres
有道理。图层的值并没有改变,你只是对它们应用了一个变换。那么你不能按照相同的比例将边框宽度缩小吗? - ChrisH
我不这么认为,因为边框在我应用变换的视图层中,我不确定我能否仅缩放边框... - Andres
我认为你的想法是正确的,使用self.layer.borderWidth = 1.0f / scaleFactor;(尽管要记住实际上不存在四分之一像素/半个像素)。请记住,在IOS中,当进行变换以提高效率时,会缓存视图。因此,请确保在更改变换之前更改边框。 - Martin O'Shea
这是正确的行为。变换应用于视图的后备存储,因此可以将其视为仅转换视图的位图而不是单个属性。我建议操纵框架而不是变换,如下面LorikMalorik所指出的。如果操纵框架证明困难,您还可以尝试使用中心和边界属性。 - axiixc
5个回答

1
我需要找到一种方法,在变换(还要创建贴纸)期间保持我的边框相同的宽度,并且需要使用CGAffineTransform解决方案,因为我的贴纸会旋转(如果您正在进行旋转,则无法轻松使用基于帧的解决方案)。我的方法是根据变换的改变来改变layer.borderWidth,像这样。对我来说有效。
class MyView : UIView {
    let borderWidth = CGFloat(2)

    override var transform: CGAffineTransform {
        didSet {
            self.setBorder()
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        self.setBorder()
    }

    fileprivate func setBorder() {
        self.layer.borderWidth = self.borderWidth / transform.scale
    }
}

extension CGAffineTransform {
    var scale: CGFloat {
        return sqrt((a * a + c * c))
    }
}

1
我也尝试在Swift 4.2中使用CGAffine Effects来实现这种效果,但最终未能成功。最终,我不得不更新约束条件,因为我正在使用锚点。以下是我最终开始使用的代码。SelectedView是应该缩放的视图。请记住,这不会破坏其他CGAffine效果。我仍在使用它们进行旋转。
@objc private func scaleSelectedView(_ sender: UIPinchGestureRecognizer) {
    guard let selectedView = selectedView else {
        return
    }

    var heightDifference: CGFloat?
    var widthDifference: CGFloat?

    // increase width and height according to scale
    selectedView.constraints.forEach { constraint in
        guard
            let view = constraint.firstItem as? UIView,
            view == selectedView
            else {
                return
        }
        switch constraint.firstAttribute {
        case .width:
            let previousWidth = constraint.constant
            constraint.constant = constraint.constant * sender.scale
            widthDifference = constraint.constant - previousWidth
        case .height:
            let previousHeight = constraint.constant
            constraint.constant = constraint.constant * sender.scale
            heightDifference = constraint.constant - previousHeight
        default:
            return
        }
    }

    // adjust leading and top anchors to keep view centered
    selectedView.superview?.constraints.forEach { constraint in
        guard
            let view = constraint.firstItem as? UIView,
            view == selectedView
            else {
                return
        }
        switch constraint.firstAttribute {
        case .leading:
            guard let widthDifference = widthDifference else {
                return
            }
            constraint.constant = constraint.constant - widthDifference / 2
        case .top:
            guard let heightDifference = heightDifference else {
                return
            }
            constraint.constant = constraint.constant - heightDifference / 2
        default:
            return
        }
    }

    // reset scale after applying in order to keep scaling linear rather than exponential
    sender.scale = 1.0
}

注:宽度和高度锚点在视图本身上。顶部和前导锚点在父视图上 - 因此有两个forEach块。


0

CGAffineTransform切换到视图的框架。

用恒定边框宽度捏合视图的示例代码:

@interface ViewController ()
@property (nonatomic, weak) IBOutlet UIView *testView;
@end

@implementation ViewController {
    CGFloat _lastScale;
    CGRect _sourceRect;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.testView.backgroundColor = [UIColor yellowColor];
    self.testView.layer.borderColor = [UIColor blackColor].CGColor;
    self.testView.layer.borderWidth = 1.0f;
    self.testView.layer.cornerRadius = 8.0f;
    _sourceRect = self.testView.frame;
    _lastScale = 1.0;
}

- (IBAction)scale:(UIPanGestureRecognizer *)sender{

    if([(UIPinchGestureRecognizer*)sender state] == UIGestureRecognizerStateBegan) {
        _lastScale = 1.0;
    }

    CGFloat scale = 1.0 - (_lastScale - [(UIPinchGestureRecognizer*)sender scale]);

    CGPoint center = self.testView.center;
    CGRect newBounds = CGRectMake(0, 0, _sourceRect.size.width * scale, _sourceRect.size.height * scale);
    self.testView.bounds = newBounds;
    self.testView.layer.transform = CATransform3DMakeRotation(M_PI * (scale - 1), 0, 0, 1);
}

@end

更新: 我已经检查过Aviary应用程序:按帧缩放,按CGAffineTransform旋转。你可能需要实现一些额外的逻辑才能使它正常工作。

更新: 使用边界并进行旋转操作


这个可以工作,但是一旦我旋转和缩放,比例就会丢失。谢谢! - Andres
创建这样的视图层次结构:RotatingView->ScalableView。 旋转视图不会影响其子视图的比例。 - LorikMalorik
是的,但如果我这样做,那么我的RotationView的框架(我的边框所在的位置)就不会增长... - Andres
尝试使用边界而不是框架:更新的代码可供使用。 - LorikMalorik

0

当设置CGAffineTransformScale时,您将无法单独控制边框宽度。 解决方法是在上述视图层次结构中使用另一个视图,如borderView作为子视图,而不是要缩放的视图(其背景颜色为clearcolor),其大小应根据其他视图的比例因子进行更改。
将所需的边框宽度应用于borderView

祝你好运!


0

在不设置变换的情况下,通过更改视图的边界来缩放视图的大小。这个解决方案对我很有效。

您可以在要缩放的视图类中添加一个方法,示例代码:

- (void)scale:(CGFloat)scale {
    self.bounds = CGRectMake(0, 0, CGRectGetWidth(self.bounds) * scale, CGRectGetHeight(self.bounds) * scale);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接