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,似乎虽然它工作了一段时间,但一个对象不应该被委托给多个CALayer
s。
子类化 CALayer
并在图层本身上实现自定义绘图最终解决了问题。
我创建了一个显示数字的自定义 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,似乎虽然它工作了一段时间,但一个对象不应该被委托给多个CALayer
s。
子类化 CALayer
并在图层本身上实现自定义绘图最终解决了问题。