Swift中Google Map点击Marker无法显示自定义Xib视图问题
我帮你排查了代码里的几个关键问题,咱们一步步来修复:
1. 修复Xib加载的错误
你的MapMarkerWindow类里的instanceFromNib方法存在问题:作为class方法,这里的self指向的是类本身而非实例,导致Xib无法正确关联IBOutlet。同时返回类型应该直接指定为MapMarkerWindow,避免后续强制转换的风险。
修改后的MapMarkerWindow代码:
class MapMarkerWindow: UIView { @IBOutlet weak var saloonImage: UIImageView! @IBOutlet weak var nameLbl: UILabel! @IBOutlet weak var addressLbl: UILabel! @IBOutlet weak var descriptionLbl: UILabel! var spotData: NSDictionary? class func instanceFromNib() -> MapMarkerWindow { // 这里owner传nil,因为Xib的根视图已设置为MapMarkerWindow类 return UINib(nibName: "MapMarkerWindowView", bundle: nil).instantiate(withOwner: nil, options: nil).first as! MapMarkerWindow } }
注意:要确保你的Xib文件中,根视图的Class设置为
MapMarkerWindow,并且所有IBOutlet都已正确关联。
2. 给Marker绑定UserData
你在创建Marker时没有把state数据赋值给marker.userData,导致点击Marker时marker.userData为nil,后续无法给InfoWindow的控件赋值,甚至会触发崩溃。
修改创建Marker的循环代码:
for state in states { let state_marker = GMSMarker() state_marker.position = CLLocationCoordinate2D(latitude: state.lat, longitude: state.long) state_marker.title = state.name state_marker.snippet = "Hey, this is \(state.snippets)" // 把state数据转成NSDictionary绑定到userData(如果State是自定义模型,也可直接绑定模型) let markerData = [ "name": state.name, "address": state.snippets, // 可根据你的需求替换为State的对应字段 "description": "自定义描述内容", "image": UIImage(named: "saloon_default") // 替换为你的实际图片资源 ] as NSDictionary state_marker.userData = markerData state_marker.map = mapView }
3. 修复视图层级问题
你把infoWindow添加到了self.view,但之前设置了view = mapView,也就是说mapView是当前ViewController的根视图,会覆盖整个屏幕,导致infoWindow被mapView挡住。应该把infoWindow添加到mapView上:
修改didTap marker方法里的添加视图代码:
// 替换 self.view.addSubview(infoWindow) 为: mapView.addSubview(infoWindow)
4. 修正坐标计算逻辑
mapView.projection.point(for: location)返回的是mapView坐标系内的点,所以设置infoWindow的位置时,要基于mapView的坐标系调整:
// 替换原来的位置计算代码 let markerPoint = mapView.projection.point(for: location) var infoWindowFrame = infoWindow.frame infoWindowFrame.origin.x = markerPoint.x - infoWindowFrame.size.width / 2 infoWindowFrame.origin.y = markerPoint.y - infoWindowFrame.size.height - 10 // 向上偏移避免挡住Marker infoWindow.frame = infoWindowFrame
5. 优化InfoWindow的复用逻辑
你每次点击Marker都重新加载Xib创建新的infoWindow,可以改成复用已有的,避免重复创建:
在viewDidLoad里初始化一次infoWindow:
override func viewDidLoad() { super.viewDidLoad() // ...其他代码 self.infoWindow = MapMarkerWindow.instanceFromNib() self.infoWindow.alpha = 0.9 self.infoWindow.layer.cornerRadius = 12 self.infoWindow.layer.borderWidth = 2 // 先把infoWindow添加到mapView,初始隐藏 self.infoWindow.isHidden = true mapView?.addSubview(self.infoWindow) }
然后在didTap marker方法里只更新数据和位置:
func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool { guard let markerData = marker.userData as? NSDictionary else { infoWindow.isHidden = true return false } locationMarker = marker infoWindow.isHidden = false // 赋值数据 infoWindow.nameLbl.text = markerData["name"] as? String infoWindow.addressLbl.text = markerData["address"] as? String infoWindow.descriptionLbl.text = markerData["description"] as? String infoWindow.saloonImage.image = markerData["image"] as? UIImage // 计算位置 let markerPoint = mapView.projection.point(for: marker.position) var infoWindowFrame = infoWindow.frame infoWindowFrame.origin.x = markerPoint.x - infoWindowFrame.size.width / 2 infoWindowFrame.origin.y = markerPoint.y - infoWindowFrame.size.height - 10 infoWindow.frame = infoWindowFrame return false }
最后,在didTapAt coordinate方法里隐藏infoWindow:
func mapView(_ mapView: GMSMapView, didTapAt coordinate: CLLocationCoordinate2D) { infoWindow.isHidden = true locationMarker = nil }
按照这些步骤修改后,你的自定义InfoWindow应该就能正常显示了。
内容的提问来源于stack exchange,提问作者raheem

