计算两个坐标之间的距离并将其显示在 UITableViewCell 的标签内
Calculate Distance between two Coordinates and display it inside an Label of an UITableViewCell
我正在尝试计算两个坐标之间的距离并将它们显示在 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
}
几件事:
正如 Sandeep 在他们的评论中所说,在 layoutSubviews 中调用 self.location.startUpdatingLocation()
是个坏主意。在 viewDidLoad()
或 viewDidAppear()
.
中执行此操作
下一点:调用 geocodeAddressString(_:completionHandler:)
的完成处理程序获取用户的当前位置并计算新的距离值。然后将其存储到标签视图的文本中,distance
。大概这是一个不属于 table 视图单元格的标签。
您应该做的是将距离值(作为 Double)保存到视图控制器中的实例变量,然后告诉您的 table 视图重新加载显示距离信息的任何单元格。如果那是单个单元格,您可以使用单个 indexPath 调用 reloadRows(at:with:)
。如果它是多个(但不是全部)单元格,您可以传入一个 IndexPaths 数组以供单元格更新。如果您需要更新 table 视图中的所有单元格,请调用 reloadData()
.
您的 tableView(_:cellForRowAt:)
table 视图委托方法应该编写为从您的实例变量中获取距离值并将其安装在需要它的那些单元格中。
这里有很多问题。
这不是很重要,但是获取地标、查找位置、获取经纬度以及构建新位置的过程不必要地复杂。 geocodeAddressString
返回的 CLPlaceMark
数组有一个 location
也就是 CLLocation
,所以直接使用它:
geocoder.geocodeAddressString(job.location) { placemarks, error in
guard
let jobLocation = placemarks?.first(where: { [=10=].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: { [=11=].horizontalAccuracy >= 0 })
else { return }
// now just use `myLocation`
}
job.location
的地理编码不属于 didUpdateLocations
。
如果您当前的位置更新,您真的需要再次对工作位置进行地理编码吗? (我知道您似乎要取消位置更新,但这表明用户位置逻辑和地理编码过程的一般混淆。)
此外,我建议 Job
对象不应该有一个 distance
属性,而只是一个 coordinate
属性 CLLocationCoordinate2D
。坐标是作业的 属性,而不是与您当时恰好所在位置的距离。 CLLocationDistance
应在显示和更新单元格时计算到用户当前位置的作业。
将位置更新与地理编码分离的优点在于,例如,如果需要,您可以轻松地保持位置更新。显然,如果您只显示最近的十分之一公里的距离,您可以添加 100 米的 distanceFilter
,以减少执行的不必要工作量。
您已将单元格的 CLLocationManager
设为 属性(假设您的委托方法正在更新单元格属性)。经理不应该是单元格的 属性。对于整个视图(或者可能是整个应用程序),您实际上只 need/want 一个 CLLocationManager
。
您正在更新异步完成处理程序闭包内的单元格属性。您需要非常小心,因为您不知道在关闭完成时单元格是否已被重用。如果您想要异步更新单元格,您应该重新查询模型集合以获取更新的行。
所以,综合这些,我建议:
更改Job
模型以捕获坐标,而不是距离;
当要显示单元格时,根据需要对地址进行地理编码(将坐标保存在模型中以节省执行冗余地理编码请求的需要);
只有一个 CLLocationManager
实例,只需在位置更改时重新加载 table。
我正在尝试计算两个坐标之间的距离并将它们显示在 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
}
几件事:
正如 Sandeep 在他们的评论中所说,在 layoutSubviews 中调用 self.location.startUpdatingLocation()
是个坏主意。在 viewDidLoad()
或 viewDidAppear()
.
下一点:调用 geocodeAddressString(_:completionHandler:)
的完成处理程序获取用户的当前位置并计算新的距离值。然后将其存储到标签视图的文本中,distance
。大概这是一个不属于 table 视图单元格的标签。
您应该做的是将距离值(作为 Double)保存到视图控制器中的实例变量,然后告诉您的 table 视图重新加载显示距离信息的任何单元格。如果那是单个单元格,您可以使用单个 indexPath 调用 reloadRows(at:with:)
。如果它是多个(但不是全部)单元格,您可以传入一个 IndexPaths 数组以供单元格更新。如果您需要更新 table 视图中的所有单元格,请调用 reloadData()
.
您的 tableView(_:cellForRowAt:)
table 视图委托方法应该编写为从您的实例变量中获取距离值并将其安装在需要它的那些单元格中。
这里有很多问题。
这不是很重要,但是获取地标、查找位置、获取经纬度以及构建新位置的过程不必要地复杂。
geocodeAddressString
返回的CLPlaceMark
数组有一个location
也就是CLLocation
,所以直接使用它:geocoder.geocodeAddressString(job.location) { placemarks, error in guard let jobLocation = placemarks?.first(where: { [=10=].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: { [=11=].horizontalAccuracy >= 0 }) else { return } // now just use `myLocation` }
job.location
的地理编码不属于didUpdateLocations
。如果您当前的位置更新,您真的需要再次对工作位置进行地理编码吗? (我知道您似乎要取消位置更新,但这表明用户位置逻辑和地理编码过程的一般混淆。)
此外,我建议
Job
对象不应该有一个distance
属性,而只是一个coordinate
属性CLLocationCoordinate2D
。坐标是作业的 属性,而不是与您当时恰好所在位置的距离。CLLocationDistance
应在显示和更新单元格时计算到用户当前位置的作业。将位置更新与地理编码分离的优点在于,例如,如果需要,您可以轻松地保持位置更新。显然,如果您只显示最近的十分之一公里的距离,您可以添加 100 米的
distanceFilter
,以减少执行的不必要工作量。您已将单元格的
CLLocationManager
设为 属性(假设您的委托方法正在更新单元格属性)。经理不应该是单元格的 属性。对于整个视图(或者可能是整个应用程序),您实际上只 need/want 一个CLLocationManager
。您正在更新异步完成处理程序闭包内的单元格属性。您需要非常小心,因为您不知道在关闭完成时单元格是否已被重用。如果您想要异步更新单元格,您应该重新查询模型集合以获取更新的行。
所以,综合这些,我建议:
更改
Job
模型以捕获坐标,而不是距离;当要显示单元格时,根据需要对地址进行地理编码(将坐标保存在模型中以节省执行冗余地理编码请求的需要);
只有一个
CLLocationManager
实例,只需在位置更改时重新加载 table。