【问题标题】:UIView bounds.applying but with rotationUIView bounds.applying 但有旋转
【发布时间】:2018-10-01 10:58:12
【问题描述】:

我想在视图周围创建一个虚线边框,可以移动/旋转/缩放。

这是我的代码:

func addBorder() {        
    let f = selectedObject.bounds.applying(selectedObject.transform)
    borderView.backgroundColor = UIColor(red: 1, green: 0, blue: 0, alpha: 0.5) //just for testing
    borderView.frame = f
    borderView.center = selectedObject.center
    borderView.transform = CGAffineTransform(translationX: selectedObject.transform.tx, y: selectedObject.transform.ty)

    removeBorder() //remove old border

    let f2 = CGRect(x: 0, y: 0, width: borderView.frame.width, height: borderView.frame.height)
    let dashedBorder = CAShapeLayer()
    dashedBorder.strokeColor = UIColor.black.cgColor
    dashedBorder.lineDashPattern = [2, 2]
    dashedBorder.frame = f2
    dashedBorder.fillColor = nil
    dashedBorder.path = UIBezierPath(rect: f2).cgPath
    dashedBorder.name = "border"
    borderView.layer.addSublayer(dashedBorder)
}

它看起来像这样:

这还不错,但我希望边框也可以旋转,因为它可能会误导用户,因为触摸区域仅在图像上。

我尝试对变换应用旋转:

func addBorder() {        
    let f = selectedObject.bounds.applying(selectedObject.transform)
    borderView.backgroundColor = UIColor(red: 1, green: 0, blue: 0, alpha: 0.5) //just for testing
    borderView.frame = f
    borderView.center = selectedObject.center
    let rotation = atan2(selectedObject.transform.b, selectedObject.transform.a)

    borderView.transform = CGAffineTransform(rotationAngle: rotation).translatedBy(x: selectedObject.transform.tx, y: selectedObject.transform.ty)

    removeBorder() //remove old border

    let f2 = CGRect(x: 0, y: 0, width: borderView.frame.width, height: borderView.frame.height)
    let dashedBorder = CAShapeLayer()
    dashedBorder.strokeColor = UIColor.black.cgColor
    dashedBorder.lineDashPattern = [2, 2]
    dashedBorder.frame = f2
    dashedBorder.fillColor = nil
    dashedBorder.path = UIBezierPath(rect: f2).cgPath
    dashedBorder.name = "border"
    borderView.layer.addSublayer(dashedBorder)
}

但旋转后是这样的:

我该如何解决这个问题?

【问题讨论】:

    标签: ios swift uiview cashapelayer cgrect


    【解决方案1】:

    这是一个基于您的代码的示例:

    //initial transforms
    selectedObject.transform = CGAffineTransform.init(rotationAngle: .pi / 4).translatedBy(x: 150, y: 15)
    
    func addBorder() {
        let borderView = UIView.init(frame: selectedObject.bounds)
        self.view.addSubview(borderView)
        borderView.backgroundColor = UIColor(red: 1, green: 0, blue: 0, alpha: 0.5) //just for testing
        borderView.center = selectedObject.center
        borderView.transform = selectedObject.transform
    
        removeBorder() //remove old border
    
        let dashedBorder = CAShapeLayer()
        dashedBorder.strokeColor = UIColor.black.cgColor
        dashedBorder.lineDashPattern = [2, 2]
        dashedBorder.fillColor = nil
        dashedBorder.path = UIBezierPath(rect: borderView.bounds).cgPath
        dashedBorder.name = "border"
        borderView.layer.addSublayer(dashedBorder)
    }
    

    【讨论】:

    • 不起作用。它看起来像这样:imgur.com/a/OSIAaWw 在我的应用程序中你也可以扩展。我不想拉伸边框,我只想调整它的大小并旋转到正确的位置。
    【解决方案2】:

    这里是for问题的解决方案:

    func addBorder() {
    
        borderView.backgroundColor = UIColor(red: 1, green: 0, blue: 0, alpha: 0.5) //just for testing
        let degrees: CGFloat = 20.0 //the value in degrees for rotation
        let radians: CGFloat = degrees * (.pi / 180)
        borderView.transform = CGAffineTransform(rotationAngle: radians)
    
        removeBorder()
    
        let dashedBorder = CAShapeLayer()
        dashedBorder.strokeColor = UIColor.black.cgColor
        dashedBorder.lineDashPattern = [2, 2]
        dashedBorder.frame = borderView.bounds
        dashedBorder.fillColor = nil
        dashedBorder.path = UIBezierPath(roundedRect: borderView.bounds, cornerRadius:0).cgPath
        dashedBorder.name = "border"
        borderView.layer.addSublayer(dashedBorder)
    }
    

    以上代码在 Xcode 10 和 Swift 4.2 中测试

    【讨论】:

    • 但它只使用旋转。尝试应用比例或平移。即使你缩放它也会拉伸 CAShapeLayer 路径。这个想法是计算最终的边界,它将有一个默认的比例并且只旋转。
    • 好的,我很抱歉。我已经进行了认真的测试并成功了。就我而言,我只是更新了同一个borderView,而不是每次都创建一个新的。在第二次变换更改后,它以某种方式弄乱了帧。但是当我每次都再次创建它时。现在,唯一的问题是边框的视觉大小。因为我的只有 0.25 比例边框的“selectObject”统计数据已经很薄而且几乎不可见。
    • 我终于做到了。这样就完成了技巧:dashedBorder.lineWidth = CGFloat(abs(1 / scale(from: t))) 其中 t 是变换,比例方法是从变换中提取比例。
    • 很高兴知道。一切顺利。
    • 您的回答对我有所帮助,但最终代码有点复杂,所以我还是发布了它。 lineWidth 解决方案并不完美。
    【解决方案3】:

    即使我已经接受了答案,因为它帮助我理解了我发布最终答案的问题,因为它更重要。而且我认为它对其他人有帮助,因为我在 Stackoverflow 或其他地方找不到这个解决方案。

    这个想法是创建一个borderView,其边界与selectedObject 相同。这是来自@Incredible_dev 的解决方案,但是有一个问题:当borderView 向任何方向缩放时,线本身会延伸。我想保持线的大小,只是想在selectedObject附近。所以,我将selectedObject 的边界与从selectedObject.transform 提取的比例相乘。然后我从selectedObject 复制平移和旋转。

    这是最终代码:

    var borderView: UIView!
    var selectedObject: UIView?
    
    extension CGAffineTransform { //helper extension
    
        func getScale() -> CGFloat {
            return (self.a * self.a + self.c * self.c).squareRoot()
        }
    
        func getRotation() -> CGFloat {
            return atan2(self.b, self.a)
        }
    
    }
    
    func removeBorder() { //remove the older border
        if borderView != nil {
            borderView.removeFromSuperview()
        }
    }
    
    func addBorder() {
        guard let selectedObject = selectedObject else { return }
    
        removeBorder() //remove old border
    
        let t = selectedObject.transform
        let s = t.getScale()
        let r = t.getRotation()
    
        borderView = UIView(frame: CGRect(x: 0, y: 0, width: selectedObject.bounds.width * s, height: selectedObject.bounds.height * s)) //multiply bounds with selectedObject's scale
        dividerImageView.addSubview(borderView) //add borderView to the "scene"
    
        borderView.transform = CGAffineTransform(translationX: t.tx, y: t.ty).rotated(by: r) //copy translation and rotation, order is important
        borderView.center = selectedObject.center
    
        let dashedBorder = CAShapeLayer() //create 2-point wide dashed line
        dashedBorder.lineWidth = 2
        dashedBorder.strokeColor = UIColor.black.cgColor
        dashedBorder.lineDashPattern = [2, 2]
        dashedBorder.fillColor = nil
        dashedBorder.path = UIBezierPath(rect: borderView.bounds).cgPath
        borderView.layer.addSublayer(dashedBorder)
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-08-12
      • 1970-01-01
      • 1970-01-01
      • 2012-09-04
      • 2010-10-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多