重新绘制CGContext后,它已经被Swift绘制了?

时间:2017-02-02 07:39:43

标签: ios swift uikit cgcontext redraw

我正在尝试为自己的练习创建一个简单的单一视图应用程序,但我遇到了一个问题,并且在过去的几天里没有解决这个问题。

Screenshot of the current program

基本上,当我按下任何这些样式按钮或调整滑块时,我希望它重绘屏幕底部的CGContext(红色虚线)。例如,如果我按下STYLE 1按钮,它应该调用名为drawStyleOne()的函数并应用这两个不同的变化:

context?.setLineDash(phase: 0, lengths: [2,3])
context?.setStrokeColor(UIColor.green.cgColor)

我的目标是重新绘制虚线(较小的点)并将上下文的颜色更改为绿色。 (上下文是CGContext的一个实例)

我已经在override func draw(_ rect:CGRect)上绘制了所有内容,但我无法按照我想要的方式重新绘制CGContext。

这是我的代码的一部分,以帮助更好地解释这种情况。

import UIKit

class VectorView: UIView {
private var context: CGContext? = nil

override init(frame: CGRect) {
    super.init(frame: frame)
    //contentMode = .redraw //tried this, didn't work.
}

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}
override func draw(_ rect: CGRect) {
    // lets us make calls
    context = UIGraphicsGetCurrentContext()!
    context?.move(to: CGPoint(x: 0.0, y: 10.0))
    context?.setLineDash(phase: 0, lengths: [2,3])
    context?.setLineJoin(CGLineJoin.bevel)
    context?.setLineCap(CGLineCap.square)

    context?.addLine(to: CGPoint(x: 50.0, y: 70.0))
    context?.addLine(to: CGPoint(x: 100.0, y: 20.0))

    context?.setLineWidth(1.0)
    context?.setStrokeColor(UIColor.red.cgColor)
    context?.drawPath(using: CGPathDrawingMode.stroke)        
}

func drawStyleOne () {

    context = UIGraphicsGetCurrentContext()
    context?.move(to: CGPoint(x: 0.0, y: 10.0))
    context?.setLineDash(phase: 0, lengths: [2,3])
    context?.setLineJoin(CGLineJoin.bevel)
    context?.setLineCap(CGLineCap.square)

    context?.addLine(to: CGPoint(x: 50.0, y: 70.0))
    context?.addLine(to: CGPoint(x: 100.0, y: 20.0))

    context?.setStrokeColor(UIColor.green.cgColor)

    context?.drawPath(using: CGPathDrawingMode.stroke)
    self.setNeedsDisplay()
    //contentMode = .redraw //Tried this and didn't work either..
    NSLog("drawStyleOne got called")
}

并且在AppDelegate中调用上面类中的drawStyleOne函数。

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?
    let screenSize = UIScreen.main.bounds

    private var _vectorView: VectorView? = nil
    private var _buttonStylesView: ButtonStyleView? = nil

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

        window = UIWindow()
        window?.rootViewController = ViewController()
        window?.rootViewController?.view.backgroundColor = UIColor.white
        window?.makeKeyAndVisible()

        _vectorView = VectorView()
        _vectorView?.frame = CGRect(x: 0.0, y: 650, width: 414, height: 70)
        _vectorView?.backgroundColor = UIColor.lightGray
        window?.rootViewController?.view.addSubview(_vectorView!)

        _buttonStylesView = ButtonStyleView()
        _buttonStylesView?.frame = CGRect (x: screenSize.width / 10, y: screenSize.height * 6 / 10, width: 414, height: 100)
        _buttonStylesView?.backgroundColor = UIColor.clear

        window?.rootViewController?.view.addSubview(_buttonStylesView!)

        _buttonStylesView?.styleOne?.addTarget(self, action: #selector(styleButtonPressed), for: UIControlEvents.touchDown)     
        return true
    }

    func styleButtonPressed() {
        NSLog("STYLE BUTTON PRESSED")
        _vectorView?.drawStyleOne()
    }
}

我甚至将NSLog(" drawStyleOne被调用")放在最后只是为了验证函数是否被调用(并且确实如此),但无论我尝试什么,它都不会重绘行。

非常感谢任何帮助/建议。

2 个答案:

答案 0 :(得分:3)

您需要在override func draw(_ rect: CGRect)内(或从那里调用的例程内)执行所有的绘图。所以你的draw需要在外面设置一个标志(通过你的动作)来告诉它你要绘制哪些样式。您使用setNeedsDisplay()触发重绘 - 但应该从您的操作调用,而不是从绘图代码调用。您当前的func drawStyleOne() 尝试绘制(但可能是无效的currentContext),然后调用setNeedsDisplay(),它只调度您要运行的实际draw例程,总是画出相同的“风格”。

UIView类在幕后工作做了很多 - 如果确保draw没有被不必要地调用,并在必要时安排它,它会确定哪些部分是视图需要重新绘制,如果它们被遮挡(这就是为什么它会传递给你rect),它会设置draw的当前图形上下文,等等。

请参阅此Ray Wenderlich教程或Apple docs。正如Apple文档所说:

  

对于自定义视图,您必须覆盖drawRect:方法并执行   全部你的画作。

(我的重点)

所以你的代码应该是这样的:

class VectorView: UIView {
    public var whichStyle: SomeEnumOfYours = // your default value

    override func draw(_ rect: CGRect) {
        switch self.whichStyle {
        case .style1:
            self.drawStyleOne()
        case .style2:
            self.drawStyleTwo()
        //...
        }
    }
}

//...
func styleButtonPressed() {
    NSLog("STYLE BUTTON PRESSED")
    _vectorView?.whichStyle = // whichever style required
    _vectorView?.setNeedsDisplay()
}

答案 1 :(得分:0)

我知道这不是你问题的答案,但你可以用另一种方式看问题。您并不需要使用上下文或覆盖drawRect以便能够绘制线条。您可以直接使用图层。将它们添加到视图中,并在要更改样式并添加另一个样式时将其删除。你可以这样使用它:

import UIKit

class VectorView: UIView {

   required init?(coder aDecoder: NSCoder) {
      fatalError("init(coder:) has not been implemented")
   }

   var myColor = UIColor.red
   var myPhase = 0
   var myLength = [2,3]

   override func layoutSubviews() {
      super.layoutSubviews()
      guard let sublayers = self.layer.sublayers else { return }
      // remove sublayer if any exists
      for layer in sublayers {
          layer.removeFromSuperlayer()
      }
      // draws your lines according to the style you have defined with your variables above
      styledDrawing()
   }

   func styledDrawing() {
       // creates a path and styles it
       let path = UIBezierPath()
       path.move(to: CGPoint(x: 0.0, y: 10.0))
       path.setLineDash(myLength, count: myLength.count, phase: myPhase)
       path.lineJoinStyle = .bevel
       path.lineCapStyle = .square

       // add lines to the path
       path.addLine(to: CGPoint(x: 50.0, y: 70.0))
       path.addLine(to: CGPoint(x: 100.0, y: 20.0))

       // creates a layer,adds your path to it and adds the layer to your view's layer
       let layer = CAShapeLayer()
       layer.path = path.cgPath
       layer.lineWidth = 1.0
       layer.strokeColor = myColor.cgColor
       self.layer.addSublayer(layer) 
   }
}

然后在你appDelegate:

func styleButtonPressed() {
    NSLog("STYLE BUTTON PRESSED")
    _vectorView?.myColor = UIColor.green
    _vectorView?.myLength = [4,1]
    _vectorView?.setNeedsLayout()
}

每当您想要更改样式时,都会更改变量myPhase,myLength和myColor,并在appDelegate中调用yourView.setNeedsLayout()。这样你只需要一种方法来绘制所有样式。