MapKit 自定义注解视图导致崩溃

MapKit custom annotation view causing crashes

我创建了一个显示数字的自定义 AnnotaionView。

导致问题最多的代码似乎在布局中。

class NumberAnnotationView: MKAnnotationView {
    private var __number: Int
    private var textLayer: CALayer!
    private var shapeLayer: CAShapeLayer!

    var number: Int {
        get {
            return __number
        }
        set {
            __number = newValue
            self.layer.setNeedsDisplay()
        }
    }

    override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
        __number = 0

        super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)

        self.frame = CGRect(x: 0, y: 0, width: 20, height: 20)
        self.isOpaque = false

        shapeLayer = CAShapeLayer()
        shapeLayer.contentsScale = UIScreen.main.scale
        shapeLayer.zPosition = 0

        textLayer = CALayer()
        textLayer.contentsScale = UIScreen.main.scale
        textLayer.zPosition = 0

        self.layer.addSublayer(shapeLayer)
        self.layer.addSublayer(textLayer)

        self.layer.contentsScale = UIScreen.main.scale
        self.layer.delegate = self
        self.layer.setNeedsDisplay()
    }
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    override func draw(_ layer: CALayer, in ctx: CGContext) {
        if textLayer != nil && layer == textLayer {
            UIGraphicsPushContext(ctx)
            let textBounds = "\(number)".nsString.boundingRect(with: textLayer.frame.size, options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: [
                NSFontAttributeName: UIFont.systemFont(ofSize: 14)
                ], context: nil)

            textLayer.frame = CGRect(x: (layer.frame.size.width - textBounds.width) / 2, y: (layer.frame.size.height - textBounds.height) / 2, width: textBounds.width, height: textBounds.height)

            "\(number)".nsString.draw(in: textLayer.frame, withAttributes: [
                NSFontAttributeName: UIFont.systemFont(ofSize: 14)
                ])

            UIGraphicsPopContext()
        } else if shapeLayer != nil && layer == shapeLayer {
            UIGraphicsPushContext(ctx)
            let path = UIBezierPath(roundedRect: layer.frame, cornerRadius: 5.0)
            shapeLayer.path = path.cgPath
            shapeLayer.fillColor = UIColor.white.cgColor
            shapeLayer.strokeColor = UIColor.blue.cgColor
            shapeLayer.lineWidth = 2.0
            UIGraphicsPopContext()
        } else if layer == self.layer {
            shapeLayer.setNeedsDisplay()
            textLayer.setNeedsDisplay()
        }
    }
    override func layoutSublayers(of layer: CALayer) {
        if (layer == self.layer) {
            shapeLayer.frame = layer.bounds
            textLayer.frame = layer.bounds
            shapeLayer.delegate = self
            textLayer.delegate = self
        }
    }
}

它不断崩溃,给了我一个令人费解的堆栈...

#0  0x000000010d3b294e in -[UIGestureEnvironment _queueGestureRecognizersForResetIfFinished:] ()
#1  0x000000010d3b28ed in -[UIGestureEnvironment _cancelGestureRecognizers:] ()
#2  0x000000010ce93803 in -[UIApplication _cancelGestureRecognizersForView:] ()
#3  0x000000010cf2e832 in -[UIView(Hierarchy) _willMoveToWindow:] ()
#4  0x000000010cf2f3d5 in __85-[UIView(Hierarchy) _makeSubtreePerformSelector:withObject:withObject:copySublayers:]_block_invoke ()
#5  0x000000010cf2f2fe in -[UIView(Hierarchy) _makeSubtreePerformSelector:withObject:withObject:copySublayers:] ()
..........
#40240  0x000000010cf2f3f3 in __85-[UIView(Hierarchy) _makeSubtreePerformSelector:withObject:withObject:copySublayers:]_block_invoke ()
#40241  0x000000010cf2f2fe in -[UIView(Hierarchy) _makeSubtreePerformSelector:withObject:withObject:copySublayers:] ()
#40242  0x000000010cf2f3f3 in __85-[UIView(Hierarchy) _makeSubtreePerformSelector:withObject:withObject:copySublayers:]_block_invoke ()
#40243  0x000000010cf2f2fe in -[UIView(Hierarchy) _makeSubtreePerformSelector:withObject:withObject:copySublayers:] ()
#40244  0x000000010cf2d31f in __UIViewWillBeRemovedFromSuperview ()
#40245  0x000000010cf2cf32 in -[UIView(Hierarchy) removeFromSuperview] ()
#40246  0x000000010c9e21e0 in -[MKAnnotationContainerView removeAnnotationView:] ()
#40247  0x000000010c872391 in -[MKMapView removeAnnotationRepresentation:] ()
#40248  0x000000010c930438 in -[MKAnnotationManager _removeRepresentationForAnnotation:fromCull:] ()
#40249  0x000000010c92f519 in -[MKAnnotationManager updateVisibleAnnotations] ()
#40250  0x000000010c862fe5 in -[MKMapView _didChangeRegionMidstream:] ()
#40251  0x000000010c867e24 in -[MKMapView mapLayer:didChangeRegionAnimated:] ()
#40252  0x000000011340ff38 in __58-[VKMapCameraController zoom:withPoint:completionHandler:]_block_invoke.132 ()
#40253  0x00000001133bcd30 in -[VKAnimation stopAnimation:] ()
#40254  0x00000001133bd0f6 in -[VKTimedAnimation stopAnimation:] ()
#40255  0x00000001133bd1d5 in -[VKTimedAnimation onTimerFired:] ()
#40256  0x00000001133e09e9 in -[VKScreenCanvas animateWithTimestamp:] ()
#40257  0x00000001133e0610 in -[VKScreenCanvas updateWithTimestamp:] ()
#40258  0x000000011339b3dc in -[VKMapView onTimerFired:] ()
#40259  0x00000001137b2c69 in -[GGLDisplayLink _displayLinkFired:] ()
#40260  0x000000010cbffbd5 in CA::Display::DisplayLinkItem::dispatch(unsigned long long) ()
#40261  0x000000010cbffa95 in CA::Display::DisplayLink::dispatch_items(unsigned long long, unsigned long long, unsigned long long) ()
#40262  0x000000010f5ed964 in __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ ()
#40263  0x000000010f5ed5f3 in __CFRunLoopDoTimer ()
#40264  0x000000010f5ed17a in __CFRunLoopDoTimers ()
#40265  0x000000010f5e4f01 in __CFRunLoopRun ()
#40266  0x000000010f5e4494 in CFRunLoopRunSpecific ()
#40267  0x0000000112b93a6f in GSEventRunModal ()
#40268  0x000000010ce7ef34 in UIApplicationMain ()
#40269  0x000000010b6af59f in main

虽然结果证明解决方案与 Map Kit 无关。

看到here,似乎虽然它工作了一段时间,但一个对象不应该被委托给多个CALayers。

子类化 CALayer 并在图层本身上实现自定义绘图最终解决了问题。