近来想在App中实现一种效果(如下图),在Fullscreen的MapView上叠一个Navigation Bar,Tap MapView则Navigation Bar出现,再次Tap则消失,类似于iOS自带的照片App的效果。初想应该很简单,但过程中也经历一些曲折,主要还是自己对framework内部机制不熟。记录一下,以供将来参考。
最初的想法就是继承MKMapView,截获touch事件,然后再处理Navigation Bar的显示问题。但实践下来发现根本行不通,MKMapView的子类无法收到touches:Begin等消息。通过截获UIWindow的hittest方法发现,所有的touches事件并不是发给MKMapView,而是MKAnnotationContainerView。这个类在文档中找不到,想上App Store就别打它的主意了。
好吧,找万能的google。看见stackoverflow上有人建议把MapView做为子类放在一个自定义的UIView里,然后通过UIView给MapView转发touches消息。结果再次让我失望,不论是用MKAnnotationContainerView的实例还是MKMapView的实例,调用touches:begin都不会有任何响应。看别人的回复都说这招是可行的,为什么在我这里就不工作呢?和iOS5有关吗?翻了翻Event Handling Guide,关于forwarding touch events,这样一段话让我更迷茫。
The classes of the UIKit framework are not designed to receive touches that are not bound to them; in programmatic terms, this means that the view
property of the UITouch
object must hold a reference to the framework object in order for the touch to be handled.
我的理解是UITouch里的view属性必须与实际的消息接收View一致才可以,通过在UIWindow的sendevent中观察也发现,MapView正常情况下收到的消息中, UITouch.view就是MKAnnotationContainerView。本来以为离成功只有一步了,但悲催的发现UITouch.view是readonly!
只能再想办法。于是打起了GestureRecognizer的主意,low level的touches:begin行不通,说不定high level的GestureRecognizer可行,而且MapView本身并不响应普通的Tap。于是给MapView加入一个UITapGestureRecognizer,果然可行。
剩下的就是怎么样把Navigation Bar加上了,直接把MapView包在一个ViewController里,然后push进navigation controller中。Build, run,嗯,有点像了。但navigation bar没有alpha效果。简单,改一下alpha属性。还是不对,navigation bar只是颜色变浅了一点(说明alpha还是起作用了),但并没有和MapView混合在一起。试了多种方案之后,发现原来只需要将navigation bar的style属性设置为UIBarStyleTranslucent就可以了。
整个过程持续了近一周,走了不少弯路。总结下来,developer center里的文档(特别是Guide,要一字不落的读),sample是解决问题的关键。