现在,我有了这个代码,它创建了一个spirograph的视图,然后在playground视图控制器中实例化它:
class Spirograph: UIView {
var insideRadius: CGFloat = 1 {
didSet {
updatePath()
}
}
var rotationalSpeed: CGFloat = -31.0 / 3.0 {
didSet {
updatePath()
}
}
private lazy var shapeLayer: CAShapeLayer = {
let sl = CAShapeLayer()
sl.fillColor = UIColor.clear.cgColor
sl.strokeColor = UIColor.purple.cgColor
sl.lineWidth = 2
return sl
}()
override func layoutSubviews() {
super.layoutSubviews()
layer.addSublayer(shapeLayer)
updatePath()
}
private func updatePath() {
let path = UIBezierPath()
let multiplier = frame.width / 5
path.move(to: CGPoint(x: multiplier*(1+insideRadius) + bounds.midX, y: bounds.midY))
for t in 0 ... 2000 {
let angle = CGFloat(t) * .pi / 180.0
let x = cos(angle) + insideRadius * cos(rotationalSpeed * angle)
let y = sin(angle) + insideRadius * sin(rotationalSpeed * angle)
path.addLine(to: CGPoint(x: multiplier * x + bounds.midX, y: multiplier * y + bounds.midY))
}
shapeLayer.path = path.cgPath
}
}
然后是实际观点:
class MyViewController : UIViewController {
let spirograph = Spirograph(frame: CGRect(x: 0, y: 0, width: 300, height: 300))
let slider = UISlider(frame: CGRect(x: 0, y: 300, width: 300, height: 20))
@objc func sliderMoved() {
spirograph.insideRadius = CGFloat(slider.value)
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .blue
spirograph.backgroundColor = .white
slider.minimumValue = 0
slider.maximumValue = 1
slider.isContinuous = true
slider.tintColor = .orange
slider.addTarget(self, action: #selector(sliderMoved), for: .valueChanged)
view.addSubview(slider)
view.addSubview(spirograph)
}
}
每当我移动滑块时,即使是一点点,也需要15秒来映射新的肺活量计,可能是因为我没有在sliderMoved func中正确更改insideRadius变量
更新:我在xcode项目中运行相同的代码,它工作得很好,似乎操场无法处理这种类型的代码。
谢谢你的帮助!
如果您希望在更改某些属性(例如insideRadius
和rotationalSpeed
)时更新视图,则通常会为该属性设置didSet
观察器,以触发重新呈现路径。
现在,您的示例是构建图像。这是一种非常低效的方式(顺便说一下,所有透明图像都是不必要的)。我建议只使用CAShapeLayer
。这样,您可以更新path
的CAShapeLayer
,操作系统将负责为您呈现它。
例如,您可能有一个UIView
子类,它捕获您的路径创建内容并更新CAShapeLayer
。各种didSet
观察者将更新该形状图层的路径:
@IBDesignable
open class SpiralView: UIView {
@IBInspectable open var insideRadius: CGFloat = 1 { didSet { updatePath() }}
@IBInspectable open var rotationalSpeed: CGFloat = -31.0 / 3.0 { didSet { updatePath() }}
@IBInspectable open var strokeColor: UIColor = .blue { didSet { shapeLayer.strokeColor = strokeColor.cgColor }}
@IBInspectable open var multiplier: CGFloat = 100 { didSet { updatePath() }}
@IBInspectable open var lineWidth: CGFloat = 3 { didSet { shapeLayer.lineWidth = lineWidth }}
private lazy var shapeLayer: CAShapeLayer = {
let _shapeLayer = CAShapeLayer()
_shapeLayer.fillColor = UIColor.clear.cgColor
_shapeLayer.strokeColor = strokeColor.cgColor
_shapeLayer.lineWidth = lineWidth
return _shapeLayer
}()
override open func layoutSubviews() {
super.layoutSubviews()
layer.addSublayer(shapeLayer)
updatePath()
}
private func updatePath() {
let path = UIBezierPath()
path.move(to: CGPoint(x: multiplier*(1+insideRadius) + bounds.midX, y: bounds.midY))
for t in 0 ... 2000 {
let angle = CGFloat(t) * .pi / 180.0
let x = cos(angle) + insideRadius * cos(rotationalSpeed * angle)
let y = sin(angle) + insideRadius * sin(rotationalSpeed * angle)
path.addLine(to: CGPoint(x: multiplier * x + bounds.midX, y: multiplier * y + bounds.midY))
}
shapeLayer.path = path.cgPath
}
}
现在,您可以根据需要将此视图添加到UI中(例如,您的游乐场视图控制器可能会以编程方式将其添加为其主要view
的子视图;如果在iOS项目中,您可以将其添加到故事板中)。但是现在,当您更新任何这些属性时,将为您重新呈现视图。
顺便说一下,如果你在带有故事板的iOS项目中使用它,那只需要@IBDesignable
和@IBInspectable
。这只是一个很好的功能,可以让你在IB中看到正好呈现的螺旋线,你可以在故事板中调整这些变量并动态反映它。但如果你在操场上这样做,那些关键字就没有必要了。但是,坦率地说,我发现游乐场太慢而不适合这样的事情。
然后,您可以将其连接到滑块,例如
class ViewController: UIViewController {
@IBOutlet weak var slider: UISlider!
@IBOutlet weak var label: UILabel!
@IBOutlet weak var spiralView: SpiralView!
override func viewDidLoad() {
super.viewDidLoad()
slider.value = Float(spiralView.insideRadius)
slider.addTarget(self, action: #selector(valueDidChange(_:)), for: .valueChanged)
}
@objc func valueDidChange(_ sender: Any) {
label.text = "\(slider.value)"
spiralView.insideRadius = CGFloat(slider.value)
}
}
导致: