You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift实现UITextField下方Google Places自动补全及经纬度获取问题

解决Google Places自动补全列表未显示在UITextField正下方的问题 + 经纬度获取方案

嘿,我之前也踩过Google Places自动补全列表位置跑偏的坑!结合官方SDK的正确用法,给你一套既能解决位置偏移问题,又能顺利获取经纬度的完整方案:

核心思路

默认的GMSAutocompleteViewController是全屏弹窗,不符合我们要在TextField下方显示列表的需求。所以我们要改用GMSAutocompleteResultsViewController,手动控制它的容器视图位置,精准定位到当前编辑的TextField正下方。

完整代码实现

1. 初始化相关变量与视图

首先在你的ViewController里声明必要的变量,初始化自动补全控制器和承载列表的容器视图:

import GooglePlaces

class DeliveryViewController: UIViewController {
    @IBOutlet weak var pickupTextField: UITextField!
    @IBOutlet weak var dropoffTextField: UITextField!
    
    // 自动补全结果控制器
    private var autocompleteResultsVC: GMSAutocompleteResultsViewController!
    // 承载自动补全列表的容器(用来控制位置和样式)
    private var autocompleteContainer: UIView!
    // 记录当前正在编辑的TextField,用于定位列表
    private var activeTextField: UITextField?
}

2. 配置视图与事件监听

在viewDidLoad里完成初始化、代理设置和事件绑定:

override func viewDidLoad() {
    super.viewDidLoad()
    
    // 初始化自动补全结果控制器并设置代理
    autocompleteResultsVC = GMSAutocompleteResultsViewController()
    autocompleteResultsVC.delegate = self
    
    // 创建列表容器视图,初始隐藏,添加阴影和圆角优化样式
    autocompleteContainer = UIView()
    autocompleteContainer.backgroundColor = .white
    autocompleteContainer.layer.cornerRadius = 8
    autocompleteContainer.layer.shadowColor = UIColor.black.cgColor
    autocompleteContainer.layer.shadowOpacity = 0.15
    autocompleteContainer.layer.shadowOffset = CGSize(width: 0, height: 2)
    autocompleteContainer.layer.shadowRadius = 6
    autocompleteContainer.isHidden = true
    view.addSubview(autocompleteContainer)
    
    // 将自动补全控制器的视图添加到容器中
    addChild(autocompleteResultsVC)
    autocompleteContainer.addSubview(autocompleteResultsVC.view)
    autocompleteResultsVC.didMove(toParent: self)
    
    // 绑定TextField的编辑事件
    pickupTextField.addTarget(self, action: #selector(textFieldDidBeginEditing(_:)), for: .editingDidBegin)
    dropoffTextField.addTarget(self, action: #selector(textFieldDidBeginEditing(_:)), for: .editingDidBegin)
    pickupTextField.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)
    dropoffTextField.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)
    
    // 点击空白处隐藏列表并收起键盘
    let tapGesture = UITapGestureRecognizer(target: self, action: #selector(hideAutocompleteList))
    view.addGestureRecognizer(tapGesture)
}

3. 处理TextField交互与列表位置计算

这部分是解决位置问题的关键,精准计算容器视图的位置,让它刚好显示在当前TextField正下方:

@objc private func textFieldDidBeginEditing(_ textField: UITextField) {
    activeTextField = textField
    // 显示列表并更新位置
    autocompleteContainer.isHidden = false
    updateAutocompleteListFrame()
}

@objc private func textFieldDidChange(_ textField: UITextField) {
    // 将输入内容传给自动补全控制器进行搜索
    autocompleteResultsVC.sourceTextHasChanged(textField.text ?? "")
}

@objc private func hideAutocompleteList() {
    autocompleteContainer.isHidden = true
    activeTextField?.resignFirstResponder()
    activeTextField = nil
}

// 核心方法:计算列表的正确位置
private func updateAutocompleteListFrame() {
    guard let textField = activeTextField else { return }
    // 先刷新TextField布局,避免AutoLayout导致坐标错误
    textField.layoutIfNeeded()
    // 将TextField的坐标转换到当前ViewController的view坐标系下
    let textFieldFrame = textField.convert(textField.bounds, to: view)
    // 设置容器位置:TextField底部下方4px,宽度与TextField一致,高度设为200
    let containerFrame = CGRect(
        x: textFieldFrame.origin.x,
        y: textFieldFrame.origin.y + textFieldFrame.size.height + 4,
        width: textFieldFrame.size.width,
        height: 200
    )
    autocompleteContainer.frame = containerFrame
    // 让自动补全视图铺满容器
    autocompleteResultsVC.view.frame = autocompleteContainer.bounds
}

4. 实现代理方法获取地点与经纬度

通过GMSAutocompleteResultsViewControllerDelegate获取用户选中的地点信息,包括经纬度:

extension DeliveryViewController: GMSAutocompleteResultsViewControllerDelegate {
    func resultsController(_ resultsController: GMSAutocompleteResultsViewController, didAutocompleteWith place: GMSPlace) {
        // 将选中的地点名称填充到当前TextField
        activeTextField?.text = place.name
        // 获取经纬度
        let selectedLatitude = place.coordinate.latitude
        let selectedLongitude = place.coordinate.longitude
        print("选中地点经纬度:\(selectedLatitude), \(selectedLongitude)")
        
        // 隐藏列表并收起键盘
        hideAutocompleteList()
    }
    
    func resultsController(_ resultsController: GMSAutocompleteResultsViewController, didFailAutocompleteWithError error: Error) {
        print("自动补全出错:\(error.localizedDescription)")
    }
    
    func didRequestAutocompletePredictions(_ resultsController: GMSAutocompleteResultsViewController) {
        UIApplication.shared.isNetworkActivityIndicatorVisible = true
    }
    
    func didUpdateAutocompletePredictions(_ resultsController: GMSAutocompleteResultsViewController) {
        UIApplication.shared.isNetworkActivityIndicatorVisible = false
    }
}

注意事项

  • 确保你的Google Cloud项目已经启用了Places API,并且配置的API_KEY拥有正确的权限,否则会无法获取搜索结果。
  • 如果你的TextField使用AutoLayout布局,一定要在计算位置前调用textField.layoutIfNeeded(),避免坐标计算偏差。
  • 可以根据App的设计风格,调整列表容器的圆角、阴影、高度等样式参数,让它更贴合你的界面。

内容的提问来源于stack exchange,提问作者Fahad Baig

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 08:14:45