将 CGAffineTransform Scale 应用于 UIView 使图层边框也变大

Posted

技术标签:

【中文标题】将 CGAffineTransform Scale 应用于 UIView 使图层边框也变大【英文标题】:Apply CGAffineTransformScale to UIView makes the layer border go bigger too 【发布时间】:2014-07-10 17:23:30 【问题描述】:

我有一个 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 borderWidth 始终相同。它不会增加,如果我 NSLog("%f",self.layer.borderWith) 它总是我第一次设置的值,但边框确实变大了...... 有道理。图层值没有改变,您只是对它们应用变换。那么你不能按照你放大图层的相同比例缩小边框宽度吗? 我不这么认为,因为边框在我正在应用变换的视图层中,我不确定我是否可以只缩放边框... 我认为你的想法是正确的 self.layer.borderWidth = 1.0f / scaleFactor; (尽管记住实际上并没有像四分之一点/半像素这样的东西)记住 ios 在转换为高效时会兑现视图。因此,请确保在更改转换之前更改边界。 【参考方案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

注意:宽度和高度锚点位于视图本身。 Top 和Leading 锚点位于superview 上——因此有两个forEach 块。

【讨论】:

【参考方案2】:

我必须想办法在变换过程中保持边框宽度相同(也创建贴纸),并且需要使用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))
    

【讨论】:

【参考方案3】:

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

祝你好运!

【讨论】:

【参考方案4】:

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 旋转。您可能需要实现一些额外的逻辑才能使其工作。

统一更新: 使用边界和旋转播放

【讨论】:

这行得通,但是一旦我旋转和缩放比例就会丢失。谢谢! 创建这样一个视图层次结构:RotatingView->ScalableView。旋转视图不会影响它的子视图比例 是的,但是如果我这样做,那么我的 RotationView 的框架(我的边框所在的位置)将不会增长... 尝试使用边界而不是框架:更新代码来玩【参考方案5】:

不设置变换,通过改变它的边界来缩放视图的大小。这个解决方案对我有用。

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

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

【讨论】:

以上是关于将 CGAffineTransform Scale 应用于 UIView 使图层边框也变大的主要内容,如果未能解决你的问题,请参考以下文章

如何让 CGAffineTransform Scale 围绕其中心缩小视图?

iOS:CGAffineTransform Scale 移动我的对象

CGAffineTransform:如何计算乘法 CGAffineTransform?

将 UIView.transform 设置为任意翻译 CGAffineTransform 啥都不做

CGAffineTransform 旋转 90 度

CGAffineTransform 操作顺序不一致