我有一个带有白色虚线边框的蓝色视图。从下图中可以看出,旋转应用程序后,视图会改变其尺寸,边框也不会调整到视图的新宽度和高度。
我需要找到一种方法
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)
}
}
答案 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
}
}
请注意,这些替代方案都不需要覆盖viewWillTransitionToSize
或traitCollectionDidChange
。这些是更高级别的UIViewController
概念,可能会在设备轮换期间调用,但如果某些其他代码更改了您的视图大小,则不会发生这种概念。最好使用简单的UIView
级drawRect
或layoutSubviews
方法,因为它们始终有效。
答案 1 :(得分:-1)
视图转换时需要显示调用集。您可以使用此功能检测它。它可以很好地检测方向变化
override func traitCollectionDidChange(previousTraitCollection: UITraitCollection?) {
self.myView.setNeedsDisplay()
}