网点重命名自己?破坏 geocodeAddressString()?
outlets renaming themselves? breaking geocodeAddressString()?
环境:
- Xcode-8
- iOS- 10
- Swift-3
概览:
我有一个关于 Outlets 的奇怪问题,它似乎在设置时更改了目标的名称,我相信,这是geocodeAddressString()
我遇到的问题
一些背景故事:
- 我的观点有很多要素,但就此 posting 而言,我主要关心的是
UITextField
以及我认为它们如何影响我的 MKMapView
代码(基于我在这里看到的评论)
- 我的
UITextField
正在使用稍微修改过的 扩展 (最初由 'nhgrif' 开发),我在这里找到了它的目的是能够设置文本字段的菊花链,以便点击弹出键盘上的 Next(或 Return)按钮将自动转到所需的下一个(或在某些情况下为上一个)文本字段。
private var kAssociationKeyNextField: UInt8 = 0
private var kAssociationKeyPreviousField: UInt8 = 1 // I added this
extension UITextField {
@IBOutlet var nextField: UITextField? {
get { return objc_getAssociatedObject(self, &kAssociationKeyNextField) as? UITextField }
set(newField) { objc_setAssociatedObject(self, &kAssociationKeyNextField, newField, .OBJC_ASSOCIATION_RETAIN) }
}
// I added the following
@IBOutlet var previousField: UITextField? {
get { return objc_getAssociatedObject(self, &kAssociationKeyPreviousField) as? UITextField }
set(newField) { objc_setAssociatedObject(self, &kAssociationKeyPreviousField, newField, .OBJC_ASSOCIATION_RETAIN) }
}
}
- 从 Xcode / 故事板的角度来看,这提供了以下 UI 用于设置菊花链中的下一个(and/or 上一个)字段:
向下钻取
除了截屏视频之外,我不确定如何真正解释我所看到的问题,但由于我无法弄清楚如何在此处 post 这样,所以必须提供大量屏幕截图.. .
- 从 Name 字段开始,然后将
nextField
设置为 地址:
- 然后select地址字段并将
previousField
设置为姓名和nextField
到城市:
到目前为止,一切似乎都运行良好...
- 现在 select City 字段并将
previousField
设置为 地址和nextField
到状态:
哎呀! 请注意,与 State 字段关联的名称现在是“下一个字段"
- 继续 State 字段,将
previousField
设置为 City 和 nextField
到 邮政编码 :
State 字段仍然显示为“Next Field " - 现在 Zipcode 字段 ALSO 显示为 "下一个字段"
- 完成 Zipcode 字段,将
previousField
设置为 State - 故意不设置 nextField
:
更多代码
这是此特定视图 class 的其余代码大部分
class NewLocationViewController: UIViewController, CLLocationManagerDelegate, UITextFieldDelegate {
@IBOutlet weak var doGeoLocate: UISwitch!
@IBOutlet weak var name: UITextField!
@IBOutlet weak var address: UITextField!
@IBOutlet weak var city: UITextField!
@IBOutlet weak var state: UITextField!
@IBOutlet weak var zipcode: UITextField!
@IBOutlet weak var done: UIBarButtonItem!
@IBOutlet weak var map: MKMapView!
var coords: CLLocationCoordinate2D?
var locationManager: CLLocationManager = CLLocationManager()
var currentLocation: CLLocation!
override func viewDidLoad() {
super.viewDidLoad()
name.delegate = self
address.delegate = self
city.delegate = self
state.delegate = self
zipcode.delegate = self
locationManager.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled() {
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.delegate = self
locationManager.startUpdatingLocation()
}
currentLocation = nil
doGeoLocate.isOn = false
map.isHidden = true
done.isEnabled = false
navigationController?.isNavigationBarHidden = false
navigationController?.isToolbarHidden = false
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if doGeoLocate.isOn == true {
textField.resignFirstResponder()
}
else if textField.nextField == nil {
if (!checkFields()) {
// walk back up chain to find last non-filled text-field...
var tmpField = textField
while ((tmpField.previousField != nil) && (tmpField.previousField?.hasText)!) {
tmpField = tmpField.previousField!
}
tmpField.previousField?.becomeFirstResponder()
}
else {
textField.resignFirstResponder()
}
}
else {
textField.nextField?.becomeFirstResponder()
}
return checkFields()
}
func checkFields() -> Bool {
//... if doGeoLocate switch is on - return true
//... if ALL fields are populated, call geocodeAddress() and return true
//... otherwise return false
}
func geocodeAddress() {
print("GA") //#=#
let geoCoder = CLGeocoder()
let addr = "\(address.text) \(city.text) \(state.text) \(zipcode.text)"
print("ADDR: `\(addr)'")//#=#
geoCoder.geocodeAddressString(addr, completionHandler: {
(placemarks: [CLPlacemark]?, error: NSError?) -> Void in
print("IN geocodeAddressString")//#=#
//if error.localizedDescription.isEmpty == false {
// print("Geocode failed with error: \(error.localizedDescription)")
//}
//else if placemarks!.count > 0 {
let placemark = placemarks![0]
let location = placemark.location
self.coords = location!.coordinate
self.map.isHidden = false
//}
} as! CLGeocodeCompletionHandler) //<<<=== NOTE THIS LINE
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
//...
}
@IBAction func toggleGeoLocate(_ sender: AnyObject) {
//...
}
@IBAction func useNewLocation(_ sender: AnyObject) {
//...
}
}
在 运行 应用程序中,填写所有字段,当我单击与邮政编码字段关联的数字键盘中的 'Done' 按钮时 - 我得到一个例外。调试日志如下所示:
TFSR: (TFSR Optional("Name") => Optional("Address"))
Returning false
TFSR: (TFSR Optional("Address") => Optional("City"))
Returning false
TFSR: (TFSR Optional("City") => Optional("State"))
Returning false
TFSR: (TFSR Optional("State") => Optional("Zipcode"))
Returning false
GA
ADDR: `Optional("2112 Murray Avenue ") Optional("Pittsburgh ") Optional("PA") Optional("15217")'
(lldb)
异常显示为:
func geocodeAddress() {
//...
geoCoder.geocodeAddressString(addr, completionHandler: {
(placemarks: [CLPlacemark]?, error: NSError?) -> Void in
//...
} as! CLGeocodeCompletionHandler) //<<< Thread 1: EXC_BREAKPOINT (code=1, subcode=0x10006c518)
}
是的,我确认我没有在代码中设置断点
求和
我有理由相信 geocodeAddressString()
代码是正确的(我在 Swift-2 的另一个应用程序中使用了它),但我我非常怀疑州和邮政编码 Outlets 在我尝试将它们与其他字段链接时重命名的方式。
有人有任何想法吗?
我建议摆脱那些各种各样的演员表:
func geocodeAddress() {
//...
geoCoder.geocodeAddressString(addr) { placemarks, error in
//...
}
}
让它为您推断正确的类型是最简单的方法。
关于网点的命名,IB为了方便您,在没有指定名称的情况下为您命名。但是有了这些额外的渠道,它的默认算法就不够用了。您可以通过在 "Identity Inspector".
的 "Document" 部分自己命名插座来解决这个问题
环境:
- Xcode-8
- iOS- 10
- Swift-3
概览:
-
我有一个关于 Outlets 的奇怪问题,它似乎在设置时更改了目标的名称,我相信,这是
geocodeAddressString()
我遇到的问题
一些背景故事:
- 我的观点有很多要素,但就此 posting 而言,我主要关心的是
UITextField
以及我认为它们如何影响我的MKMapView
代码(基于我在这里看到的评论) - 我的
UITextField
正在使用稍微修改过的 扩展 (最初由 'nhgrif' 开发),我在这里找到了它的目的是能够设置文本字段的菊花链,以便点击弹出键盘上的 Next(或 Return)按钮将自动转到所需的下一个(或在某些情况下为上一个)文本字段。private var kAssociationKeyNextField: UInt8 = 0 private var kAssociationKeyPreviousField: UInt8 = 1 // I added this extension UITextField { @IBOutlet var nextField: UITextField? { get { return objc_getAssociatedObject(self, &kAssociationKeyNextField) as? UITextField } set(newField) { objc_setAssociatedObject(self, &kAssociationKeyNextField, newField, .OBJC_ASSOCIATION_RETAIN) } } // I added the following @IBOutlet var previousField: UITextField? { get { return objc_getAssociatedObject(self, &kAssociationKeyPreviousField) as? UITextField } set(newField) { objc_setAssociatedObject(self, &kAssociationKeyPreviousField, newField, .OBJC_ASSOCIATION_RETAIN) } } }
- 从 Xcode / 故事板的角度来看,这提供了以下 UI 用于设置菊花链中的下一个(and/or 上一个)字段:
向下钻取
-
除了截屏视频之外,我不确定如何真正解释我所看到的问题,但由于我无法弄清楚如何在此处 post 这样,所以必须提供大量屏幕截图.. .
- 从 Name 字段开始,然后将
nextField
设置为 地址: - 然后select地址字段并将
previousField
设置为姓名和nextField
到城市: - 现在 select City 字段并将
previousField
设置为 地址和nextField
到状态: - 继续 State 字段,将
previousField
设置为 City 和nextField
到 邮政编码 : - 完成 Zipcode 字段,将
previousField
设置为 State - 故意不设置nextField
:
更多代码
-
这是此特定视图 class 的其余代码大部分
class NewLocationViewController: UIViewController, CLLocationManagerDelegate, UITextFieldDelegate {
@IBOutlet weak var doGeoLocate: UISwitch!
@IBOutlet weak var name: UITextField!
@IBOutlet weak var address: UITextField!
@IBOutlet weak var city: UITextField!
@IBOutlet weak var state: UITextField!
@IBOutlet weak var zipcode: UITextField!
@IBOutlet weak var done: UIBarButtonItem!
@IBOutlet weak var map: MKMapView!
var coords: CLLocationCoordinate2D?
var locationManager: CLLocationManager = CLLocationManager()
var currentLocation: CLLocation!
override func viewDidLoad() {
super.viewDidLoad()
name.delegate = self
address.delegate = self
city.delegate = self
state.delegate = self
zipcode.delegate = self
locationManager.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled() {
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.delegate = self
locationManager.startUpdatingLocation()
}
currentLocation = nil
doGeoLocate.isOn = false
map.isHidden = true
done.isEnabled = false
navigationController?.isNavigationBarHidden = false
navigationController?.isToolbarHidden = false
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if doGeoLocate.isOn == true {
textField.resignFirstResponder()
}
else if textField.nextField == nil {
if (!checkFields()) {
// walk back up chain to find last non-filled text-field...
var tmpField = textField
while ((tmpField.previousField != nil) && (tmpField.previousField?.hasText)!) {
tmpField = tmpField.previousField!
}
tmpField.previousField?.becomeFirstResponder()
}
else {
textField.resignFirstResponder()
}
}
else {
textField.nextField?.becomeFirstResponder()
}
return checkFields()
}
func checkFields() -> Bool {
//... if doGeoLocate switch is on - return true
//... if ALL fields are populated, call geocodeAddress() and return true
//... otherwise return false
}
func geocodeAddress() {
print("GA") //#=#
let geoCoder = CLGeocoder()
let addr = "\(address.text) \(city.text) \(state.text) \(zipcode.text)"
print("ADDR: `\(addr)'")//#=#
geoCoder.geocodeAddressString(addr, completionHandler: {
(placemarks: [CLPlacemark]?, error: NSError?) -> Void in
print("IN geocodeAddressString")//#=#
//if error.localizedDescription.isEmpty == false {
// print("Geocode failed with error: \(error.localizedDescription)")
//}
//else if placemarks!.count > 0 {
let placemark = placemarks![0]
let location = placemark.location
self.coords = location!.coordinate
self.map.isHidden = false
//}
} as! CLGeocodeCompletionHandler) //<<<=== NOTE THIS LINE
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
//...
}
@IBAction func toggleGeoLocate(_ sender: AnyObject) {
//...
}
@IBAction func useNewLocation(_ sender: AnyObject) {
//...
}
}
TFSR: (TFSR Optional("Name") => Optional("Address"))
Returning false
TFSR: (TFSR Optional("Address") => Optional("City"))
Returning false
TFSR: (TFSR Optional("City") => Optional("State"))
Returning false
TFSR: (TFSR Optional("State") => Optional("Zipcode"))
Returning false
GA
ADDR: `Optional("2112 Murray Avenue ") Optional("Pittsburgh ") Optional("PA") Optional("15217")'
(lldb)
func geocodeAddress() {
//...
geoCoder.geocodeAddressString(addr, completionHandler: {
(placemarks: [CLPlacemark]?, error: NSError?) -> Void in
//...
} as! CLGeocodeCompletionHandler) //<<< Thread 1: EXC_BREAKPOINT (code=1, subcode=0x10006c518)
}
是的,我确认我没有在代码中设置断点
求和
我有理由相信geocodeAddressString()
代码是正确的(我在 Swift-2 的另一个应用程序中使用了它),但我我非常怀疑州和邮政编码 Outlets 在我尝试将它们与其他字段链接时重命名的方式。有人有任何想法吗?
我建议摆脱那些各种各样的演员表:
func geocodeAddress() {
//...
geoCoder.geocodeAddressString(addr) { placemarks, error in
//...
}
}
让它为您推断正确的类型是最简单的方法。
关于网点的命名,IB为了方便您,在没有指定名称的情况下为您命名。但是有了这些额外的渠道,它的默认算法就不够用了。您可以通过在 "Identity Inspector".
的 "Document" 部分自己命名插座来解决这个问题