ios - 在 Swift 中绘制 CGContext 后重绘它?

标签 ios swift uikit cgcontext redraw

我正在尝试为自己的实践制作一个简单的单 View 应用程序,但是我遇到了一个问题,并且在过去的几天里还没有解决这个问题。

Screenshot of the current program

基本上,当我按下任何这些样式按钮或调整 slider 时,我希望它重新绘制屏幕底部的 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")
}

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

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 Got Call") 放在最后只是为了验证函数是否被调用(确实如此),但是无论我如何尝试,它都不会重绘线条。

任何帮助/建议将不胜感激。

最佳答案

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

UIView类在幕后工作,做了很多事情 - 如果确保 draw不必要时不会被调用,并在必要时安排它,它会找出 View 的哪些部分被遮挡时需要重新绘制(这就是它向您传递 rect 的原因),并设置当前图形draw 的上下文,除其他外。

查看此Ray Wenderlich教程,或Apple docs 。正如苹果文档所说:

For custom views, you must override the drawRect: method and perform all your drawing inside it.

(我的重点)

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

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()
}

关于ios - 在 Swift 中绘制 CGContext 后重绘它?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41996599/

相关文章:

ios - Swift:遍历 plist

ios - 处理两个时区

3d - 围绕自身中心旋转 SCNText 节点(Swift - Scenekit)

ios - UITableView 拉动刷新导致闪烁。如何预防?

iOS swift : Crash on deleteRowsAtIndexPaths

ios - 根据所选的 MKAnnotationView 动态更改 leftCalloutAccessoryView

android - 我如何托管可以在浏览器、Android 和 iOS 上由共享云上相同数据库的用户使用的 Web 服务?

swift - 如何在 Swift 中捕获 NSKeyedUnarchiver "NSInvalidUnarchiveOperationException"?

ios - 获取标签栏高度的错误值?

ios - 动画 View 约束而不动画其他 View