geocodeAddressString()
UITextField
's and how I believe they are affecting my MKMapView
code (based somewhat on comments I saw here)UITextField
's are utilizing a slightly modified form of an extension (originally by 'nhgrif') which I found here where the aim is to be able to setup a daisy-chain of textfields such that hitting the Next (or Return) button on the pop-up keyboard will automatically proceed to the desired next (or in some cases, previous) textfield.
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) }
}
}
nextField
to Address:
previousField
to Name and the nextField
to City:
previousField
to Address and the nextField
to State:
previousField
to City and nextField
to Zipcode:
previousField
to State - intentionally leaving the nextField
unset:
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)
}
And yes, I verified that I have no breakpoints set in the code
geocodeAddressString()
code is correct (I used it in another app for Swift-2), but I'm very suspicious of the way the State and Zipcode Outlets get renamed when I attempt to chain them with the other fields.I'd suggest getting rid of those various casts:
func geocodeAddress() {
//...
geoCoder.geocodeAddressString(addr) { placemarks, error in
//...
}
}
It's easiest to let it infer the correct types for you.
Regarding the naming of outlets, IB is trying to make life easier for you, naming them for you in the absence of any specified name. But with these additional outlets, its default algorithm is falling short. You can probably get around this by naming the outlets yourself in the "Document" section of the "Identity Inspector".