当app更改大小类时,删除UIView的边框

时间:2016-04-30 20:47:12

标签: ios uiview

我有一个带有白色虚线边框的蓝色视图。从下图中可以看出,旋转应用程序后,视图会改变其尺寸,边框也不会调整到视图的新宽度和高度。

enter image description here

我需要找到一种方法

  • 了解视图控制器何时更改其大小 - 可能使用viewWillTransitionToSize
  • 删除以前绘制的边框(如果有)。
  • 在视图的drawRect方法中为视图添加新边框。

如何删除先前在视图上绘制的边框?

@IBOutlet weak var myView: UIView!

override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
    coordinator.animateAlongsideTransition(nil, completion: {
    _ in
        self.myView.setNeedsDisplay()
    })
}

class RenderView: UIView {
    override func drawRect(rect: CGRect) {
        self.addDashedBorder()
    }
}

extension UIView {
    func addDashedBorder() {
        let color = UIColor.whiteColor().CGColor
        let shapeLayer:CAShapeLayer = CAShapeLayer()
        let frameSize = self.frame.size
        let shapeRect = CGRect(x: 0, y: 0, width: frameSize.width, height: frameSize.height)
        shapeLayer.bounds = shapeRect
        shapeLayer.position = CGPoint(x: frameSize.width/2, y: frameSize.height/2)
        shapeLayer.fillColor = UIColor.clearColor().CGColor
        shapeLayer.strokeColor = color
        shapeLayer.lineWidth = 6
        shapeLayer.lineJoin = kCALineJoinRound
        shapeLayer.lineDashPattern = [6,3]
        shapeLayer.path = UIBezierPath(roundedRect: shapeRect, cornerRadius: 5).CGPath
        self.layer.addSublayer(shapeLayer)
    }
}

Download sample project here

2 个答案:

答案 0 :(得分:1)

这不是正确的做事方式。每次重新绘制RenderView时,系统都会调用drawRect方法,addDashedBorder向视图中添加新图层

drawRect用于使用CoreGraphics或UIKit绘制视图内部,而不是CoreAnimation。在那个方法里面,你应该画画,没有别的。如果您想要使用图层,layoutSubviews是一个更好的添加位置,并更新它以匹配视图。

以下是解决问题的两种方法。两者都正确更新边框,并在旋转设备时平滑地为边框设置动画。

备选方案1:只需在drawRect中绘制边框,而不是使用单独的形状图层。另外,设置您的视图contentMode,以便在尺寸更改时自动重绘。

class ViewController: UIViewController {

    @IBOutlet weak var myView: UIView!

    override func viewDidLoad() {
        super.viewDidLoad()
        self.myView.contentMode = .Redraw
    }
}

class RenderView: UIView {
    override func drawRect(rect: CGRect) {
        let path = UIBezierPath(roundedRect: self.bounds, cornerRadius: 5)

        path.lineWidth = 6

        let pattern: [CGFloat] = [6.0, 3.0]
        path.setLineDash(pattern, count: 2, phase: 0)

        UIColor.whiteColor().setStroke()
        path.stroke()
    }
}

备选方案2:继续使用CAShapeLayer,但只使用lazy stored property创建一个。以覆盖layoutSubviews的方式更新图层,并在必要时将其与视图边界中的任何更改一起设置动画。

class ViewController: UIViewController {
    @IBOutlet weak var myView: UIView!
}

class RenderView: UIView {
    // Create the borderLayer, and add it to our view's layer, on demand, only once.
    lazy var borderLayer: CAShapeLayer = {
        let shapeLayer = CAShapeLayer()
        shapeLayer.fillColor = nil
        shapeLayer.strokeColor = UIColor.whiteColor().CGColor
        shapeLayer.lineWidth = 6
        shapeLayer.lineDashPattern = [6,3]

        self.layer.addSublayer(shapeLayer)

        return shapeLayer
    }()

    override func layoutSubviews() {
        super.layoutSubviews()

        // We will update the borderLayer's path to match the view's current bounds.
        let newPath = UIBezierPath(roundedRect: self.bounds, cornerRadius: 5).CGPath

        // We may be animating from the old bounds to the new bounds.
        // If so, we want the borderLayer to animate alongside that.
        // (UIView does not do this automatically, since it does not know
        //  anything about our borderLayer; it's at the the CoreAnimation level,
        //  below UIKit.)
        //
        // We want an animation that uses the same properties as the existing
        // animation, but applies to a different value: the borderLayer's path.
        // We'll find the existing animation on the view's bounds.size,
        // and if it exists, add our own animation based on it that will
        // apply the path change.

        if let viewBoundsAnimation = self.layer.animationForKey("bounds.size") {
            let pathAnimation = CABasicAnimation()
            pathAnimation.beginTime = viewBoundsAnimation.beginTime
            pathAnimation.duration = viewBoundsAnimation.duration
            pathAnimation.speed = viewBoundsAnimation.speed
            pathAnimation.timeOffset = viewBoundsAnimation.timeOffset
            pathAnimation.timingFunction = viewBoundsAnimation.timingFunction
            pathAnimation.keyPath = "path"
            pathAnimation.fromValue = borderLayer.path
            pathAnimation.toValue = newPath
            borderLayer.addAnimation(pathAnimation, forKey: "path")
        }

        // Finally, whether we are animating or not, make the border layer show the new path.
        // If we are animating, this will appear when the animation is finished.
        // If we are not animating, this will appear immediately.
        self.borderLayer.path = newPath
    }
}

请注意,这些替代方案都不需要覆盖viewWillTransitionToSizetraitCollectionDidChange。这些是更高级别的UIViewController概念,可能会在设备轮换期间调用,但如果某些其他代码更改了您的视图大小,则不会发生这种概念。最好使用简单的UIViewdrawRectlayoutSubviews方法,因为它们始终有效。

答案 1 :(得分:-1)

视图转换时需要显示调用集。您可以使用此功能检测它。它可以很好地检测方向变化

 override func traitCollectionDidChange(previousTraitCollection: UITraitCollection?) {
self.myView.setNeedsDisplay()
}