Swift: 为什么在 touchesBegan() 中将 UIView 的帧大小引用为 (0.0, 0.0)?

Swift: why has referenced UIView its frame size as (0.0, 0.0) inside touchesBegan()?

我只是想在一个愚蠢的 iOS 应用程序中进行一个简单的实验,我想在其中绘制轴作为参考,而可拖动的 UIImageViewUIView 上移动.问题是,当我可以 touchesBegan() 方法时,在其上绘制轴的引用 UIView 具有其框架大小 (0.0, 0.0),我收到此错误:

Feb 14 08:44:08 Fishmeter[30272] <Error>: CGContextSetRGBStrokeColor: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.

这是我的可拖动代码 UIImageView:

class PinImageView: UIImageView {
  var viewForAxis:AxisView!

  ...
  init(imageIcon: UIImage?, location:CGPoint, father:UIImageView, name:String, axisView:AxisView) {
    ...
    self.viewForAxis = axisView
    print(self.viewForAxis.frame.size)
    ...
  }

   override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
    if let touch: UITouch = touches.first {
        print(self.viewForAxis.frame.size)
        self.viewForAxis.hidden = false
        self.viewForAxis.pin = self
        //self.viewForAxis.setNeedsDisplay()
        self.viewForAxis.drawAxis(CGPointMake(self.center.x, self.viewForAxis.frame.origin.y), to: (CGPointMake(self.center.x, self.viewForAxis.frame.origin.y + self.viewForAxis.frame.height)))

    }
  }
}

在第一个 print() 中我得到 (303.0, 414.0) 而在 touchesBegan() 中它变成 (0.0, 0.0) 然后我得到那个错误。 而 class AxisView 是:

class AxisView: UIView {

var pin:PinImageView?

func drawAxis(from:CGPoint, to:CGPoint) {
    let context = UIGraphicsGetCurrentContext()
    switch (self.pin!.id) {
    case "redPinA","redPinB":
        CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0)
        break
    case "bluePinA, bluePinB":
        CGContextSetRGBStrokeColor(context, 0.0, 0.0, 1.0, 1.0)
        break
    default: break
    }

    /* line width of axes */
    CGContextSetLineWidth(context, 0.5)

    /* draw vertical axis inside magnifier */
    CGContextMoveToPoint(context, from.x, from.y)
    CGContextAddLineToPoint(context, to.x, to.y)

    /* draw horizontal axis inside magnifier */
    //CGContextMoveToPoint(context, self.zoomedPoint!.x - (self.frame.size.width / 2), self.zoomedPoint!.y)
    //CGContextAddLineToPoint(context, self.zoomedPoint!.x + (self.frame.size.width / 2), self.zoomedPoint!.y)
    CGContextStrokePath(context)
}

/*override func drawRect(rect: CGRect) {
    if (pin != nil) {
        self.drawAxis(CGPointMake((self.pin?.center.x)!, self.frame.origin.y), to: CGPointMake(self.pin!.center.x, self.frame.origin.y + self.frame.height))
    }
}*/

}

正确的做法是什么?正如您从评论中看到的那样,我也尝试将所有内容都放在 drawRect() 方法中,但是 setNeedsDisplay() 没有被调用,因为大小是 (0.0, 0.0)...谢谢大家。

这是我在主视图控制器中所做的:

self.view.addSubview(self.axisView)
self.axisView.hidden = true

self.redPinA = PinImageView(imageIcon: UIImage(named: "icons/red_pin.png"), location: CGPointMake(40, 110), father: self.imageView, name: "redPinA", axisView: self.axisView)

self.redPinB = PinImageView(imageIcon: UIImage(named: "icons/red_pin.png"), location: CGPointMake(80, 110), father: self.imageView, name: "redPinB", axisView: self.axisView)

self.bluePinA = PinImageView(imageIcon: UIImage(named: "icons/blue_pin.png"), location: CGPointMake(200, 110), father: self.imageView, name: "bluePinA", axisView: self.axisView)

self.bluePinB = PinImageView(imageIcon: UIImage(named: "icons/blue_pin.png"), location: CGPointMake(240, 110), father: self.imageView, name: "bluePinB", axisView: self.axisView)

self.view.addSubview(self.redPinA)
self.view.addSubview(self.redPinB)
self.view.addSubview(self.bluePinA)
self.view.addSubview(self.bluePinB)

该错误正确地指出 touchesBegan 正在调用 drawAxis,它正在调用 CGContext 函数,但您不在有效上下文中(因此,"invalid context 0x0").

您不应直接从 touchesBegan 调用任何 CGContext 函数。您可以更新状态变量,然后调用 setNeedsDisplay,这将导致 drawRect 被 OS 调用,您可以在其中绘制坐标轴。但是不要试图自己画它们。

但我尝试了以下方法并且效果很好:

class AxisView: UIView {

    var pin:PinImageView?

    func drawAxis(from:CGPoint, to:CGPoint) {
        let context = UIGraphicsGetCurrentContext()
        switch pin!.id {
        case "redPinA", "redPinB":
            CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0)
        case "bluePinA", "bluePinB":
            CGContextSetRGBStrokeColor(context, 0.0, 0.0, 1.0, 1.0)
        default: break
        }

        /* line width of axes */
        CGContextSetLineWidth(context, 0.5)

        /* draw vertical axis inside magnifier */
        CGContextMoveToPoint(context, from.x, from.y)
        CGContextAddLineToPoint(context, to.x, to.y)

        /* draw horizontal axis inside magnifier */
        CGContextStrokePath(context)
    }

    override func drawRect(rect: CGRect) {
        if pin != nil {
            drawAxis(CGPointMake(pin!.center.x, frame.origin.y), to: CGPointMake(pin!.center.x, frame.origin.y + frame.height))
        }
    }

}


class PinImageView: UIImageView {

    var lastLocation:CGPoint!
    var id:String!
    var viewForAxis:AxisView!

    init(imageIcon: UIImage?, location:CGPoint, name:String, axisView:AxisView) {

        super.init(image: imageIcon)
        lastLocation = location
        id = name

        viewForAxis = axisView
        print(viewForAxis.frame.size)
        center = location
        frame = CGRect(x: location.x, y: location.y, width: 40.0, height: 60.0)

        userInteractionEnabled = true
    }

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

    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
        if let _ = touches.first {
            print(viewForAxis.frame.size)
            viewForAxis.hidden = false
            viewForAxis.pin = self
            viewForAxis.setNeedsDisplay()
        }
    }

    override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
        viewForAxis.hidden = true
    }

    override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
        if let touch: UITouch = touches.first {
            center = touch.locationInView(superview)
            viewForAxis.setNeedsDisplay()
        }
    }

}

class ViewController: UIViewController {

    var axisView: AxisView!
    var redPinA: PinImageView!
    var redPinB: PinImageView!
    var bluePinA: PinImageView!
    var bluePinB: PinImageView!

    override func viewDidLoad() {
        super.viewDidLoad()

        axisView = AxisView()
        axisView.frame = view.bounds
        axisView.backgroundColor = UIColor.clearColor()

        view.addSubview(axisView)
        axisView.hidden = true

        redPinA = PinImageView(imageIcon: UIImage(named: "red_pin.png"), location: CGPointMake(40, 110), name: "redPinA", axisView: axisView)

        redPinB = PinImageView(imageIcon: UIImage(named: "red_pin.png"), location: CGPointMake(80, 110), name: "redPinB", axisView: axisView)

        bluePinA = PinImageView(imageIcon: UIImage(named: "blue_pin.png"), location: CGPointMake(200, 110), name: "bluePinA", axisView: axisView)

        bluePinB = PinImageView(imageIcon: UIImage(named: "blue_pin.png"), location: CGPointMake(240, 110), name: "bluePinB", axisView: axisView)

        view.addSubview(redPinA)
        view.addSubview(redPinB)
        view.addSubview(bluePinA)
        view.addSubview(bluePinB)
    }

}

我从您的评论中推断,您曾尝试直接调用此代码来解决 setNeedsDisplay 未导致 drawRect 被调用的问题。这听起来像是一个单独的问题。解决方案不是自己调用 drawRect(或 drawAxis),而是找出为什么 setNeedsDisplay 没有完成这项工作。但是问题不在于您问题中的代码,而显然是其他问题的结果。