问题描述
我正在尝试计算两个坐标之间的距离并将它们显示在 UITableViewCell 的标签内。
到目前为止一切顺利 - 我现在的问题是,每次我滚动 tableview 时,标签的值都会改变并且距离完全混淆...... 到目前为止我读到的是,这个问题是由于出队和可重用数据造成的
但在我继续说之前,这是我的代码:
class JobTableViewCell: UITableViewCell,CLLocationManagerDelegate {
@IBOutlet weak var distance: UILabel!
let location = CLLocationManager()
static var takenLocation: String?
override func layoutSubviews() {
super.layoutSubviews()
location.delegate = self
self.location.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager,didUpdateLocations locations: [CLLocation]) {
if let lastLocation = locations.last {
let geocoder = CLGeocoder()
//get job coordinates
geocoder.geocodeAddressstring(job.location) { placemarks,error in
let placemarkW = placemarks?.first
if let placemark = placemarkW
{
let lat = placemark.location?.coordinate.latitude
let lon = placemark.location?.coordinate.longitude
let jobLocation = CLLocation(latitude: lat!,longitude: lon!)
//get user coordinates
let myLocation = CLLocation(latitude: lastLocation.coordinate.latitude,longitude: lastLocation.coordinate.longitude)
//get distance between coordinates
let distance = myLocation.distance(from: jobLocation) / 1000
self.distance.text = String(format: "%.01fkm",distance)
self.job.distance = distance
//JobTableViewCell.takenLocation = String(format: "%.01km",distance)
} else {
self.distance.text = "Not Valid"
self.job.distance = 0.0
}
self.reloading?.reloadIt()
}
}
self.location.stopUpdatingLocation()
guard let _: CLLocationCoordinate2D = manager.location?.coordinate else { return }
}
}
我在计算或显示距离方面没有任何问题,我唯一的问题是我不知道如何重用 LabelData(如果这甚至是正确的方法:/)
据我所知,我需要转到 TableViewController 并编写类似 cell.distance.text = idonwkNowwhat 的内容,但这就是我卡住的地方
更新:
func tableView(_ tableView: UITableView,cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = tableView.dequeueReusableCell(withIdentifier: "Jobcell",for: indexPath) as! JobTableViewCell
let job = jobs[indexPath.row]
cell.job = job
cell.jobHeader.text = job.postHeader //just leave this line so the function is not empty
//cell.cellDelegate = self
return cell
}
解决方法
这里有很多问题。
-
这并不是非常重要,但是获取地标、查找位置、获取纬度和经度以及构建新位置的过程是不必要的复杂。从
CLPlaceMark
返回的geocodeAddressString
数组有一个location
,它是CLLocation
,所以直接使用它:geocoder.geocodeAddressString(job.location) { placemarks,error in guard let jobLocation = placemarks?.first(where: { $0.location != nil })?.location else { // handle error where no placemark with a valid location was found return } // get distance between coordinates let distance = myLocation.distance(from: jobLocation) / 1000 // now update model or UI here }
同样,
didUpdateLocations
为您提供一组CLLocation
对象,因此获取最后一个有效位置(即horizontalAccuracy
为非负的位置)是一行代码:func locationManager(_ manager: CLLocationManager,didUpdateLocations locations: [CLLocation]) { guard let myLocation = locations.last(where: { $0.horizontalAccuracy >= 0 }) else { return } // now just use `myLocation` }
-
job.location
的地理编码不属于didUpdateLocations
。如果您当前的位置更新,您真的需要再次对作业的位置进行地理编码吗? (我知道您似乎取消了位置更新,但这表明用户位置的逻辑与地理编码过程的一般性混淆。)
此外,我建议
Job
对象不应具有distance
属性,而应仅具有coordinate
属性,即CLLocationCoordinate2D
。坐标是工作的属性,而不是与您当时碰巧所在位置的距离。应在显示和更新单元格时计算作业到用户当前位置的CLLocationDistance
。将位置更新与地理编码分离的优点在于,您可以轻松地(例如,如果需要)保持位置更新。显然,如果您只显示到最近十分之一公里的距离,您可以添加 100 米的
distanceFilter
,以减少正在执行的不必要的工作量。 -
您已将
CLLocationManager
设为单元格的属性(假设您的委托方法正在更新单元格属性)。经理不应该是单元的属性。对于整个视图(或可能是整个应用),您实际上只需要/想要一个CLLocationManager
。 -
您正在更新异步完成处理程序闭包内的单元格属性。您需要非常小心,因为您不知道在关闭完成时单元格是否已被重用。如果您确实想要异步更新单元格,则应重新查询模型集合以获取更新的行。
所以,把这一切放在一起,我建议:
-
更改
Job
模型以捕获坐标,而不是距离; -
当要显示单元格时,根据需要对地址进行地理编码(将坐标保存在模型中以节省执行冗余地理编码请求的需要);
-
有一个
CLLocationManager
实例,只需在位置更改时重新加载表。
例如参见https://github.com/robertmryan/GeocoderTableView.git。
,几件事:
正如 Sandeep 在他们的评论中所说,在 layoutSubviews 中调用 self.location.startUpdatingLocation()
是一个坏主意。在 viewDidLoad()
或 viewDidAppear()
执行此操作。
下一点:调用 geocodeAddressString(_:completionHandler:)
的完成处理程序获取用户的当前位置并计算新的距离值。然后将其存储到标签视图 distance
的文本中。大概这是一个不属于表格视图单元格的标签。
您应该做的是将距离值(作为双精度值)保存到视图控制器中的实例变量中,然后告诉您的表视图重新加载任何显示距离信息的单元格。如果这是单个单元格,您可以使用单个 indexPath 调用 reloadRows(at:with:)
。如果它是多个(但不是全部)单元格,您可以传入一组 IndexPaths 以供单元格更新。如果您需要更新表格视图中的所有单元格,请调用 reloadData()
。
应该编写您的 tableView(_:cellForRowAt:)
表视图委托方法,以从您的实例变量中获取距离值并将其安装在需要它的单元格中。