Google Places Autocomplete in UItextField using Swift - swift

I want to add the Google places autocomplete search suggestion just below UITextfield and I want latitude and longitude of the user. There will be two textfields i.e. pickup location and drop off location. Picture is attached for example. I have tried code but it did not show just below the uitextfield.
import UIKit
import GoogleMaps
import GooglePlaces
import GooglePlacePicker
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var pickupLocation: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
self.pickupLocation.delegate = self
}
func textFieldDidBeginEditing(_ textField: UITextField) {
let acController = GMSAutocompleteViewController()
acController.delegate = self
self.present(acController, animated: true, completion: nil)
}
}
extension ViewController: GMSAutocompleteViewControllerDelegate {
// Handle the user's selection.
func viewController(_ viewController: GMSAutocompleteViewController, didAutocompleteWith place: GMSPlace) {
print("Place name: \(place.name)")
print("Place address: \(String(describing: place.formattedAddress))")
print("Place attributions: \(String(describing: place.attributions))")
dismiss(animated: true, completion: nil)
}
func viewController(_ viewController: GMSAutocompleteViewController, didFailAutocompleteWithError error: Error) {
// TODO: handle the error.
print("Error: ", error.localizedDescription)
}
// User canceled the operation.
func wasCancelled(_ viewController: GMSAutocompleteViewController) {
dismiss(animated: true, completion: nil)
}
The sample picture is below attached

Related

What is wrong with my auto complete?

`Hello ☺️ I'm new to swift . I am working on Google map and Goole Place .
I have a search bar which when I click on the button and search the results should show in an auto complete position , But that's not work .
Here is my code :
func viewController(_ viewController: GMSAutocompleteViewController, didAutocompleteWith place: GMSPlace) {
let camera = GMSCameraPosition.camera(withLatitude: place.coordinate.latitude, longitude: place.coordinate.longitude, zoom: 15.0)
self.googleMapsView.camera = camera
self.dismiss(animated: true, completion: nil) //dismiss after select place
}
func viewController(_ viewController: GMSAutocompleteViewController, didFailAutocompleteWithError error: Error) {
print("Erroe Auto Complte \(error)")
}
func wasCancelled(_ viewController: GMSAutocompleteViewController) {
self.dismiss(animated: true, completion: nil)//when cancel search
}
#IBAction func openSearchAddress(_ sender: UIBarButtonItem) {
let autoCompleteController = GMSAutocompleteViewController()
autoCompleteController.delegate = self
self.locationManager.startUpdatingLocation()
self.present(autoCompleteController, animated: true, completion: nil)
}
**Declaration of variable**
var locationManager = CLLocationManager()
var locationSelected = Location.startLocation
var locationStart = CLLocation()
var locationEnd = CLLocation()
**GMSMapViewDelegate Methods**
func mapView(_ mapView: GMSMapView, idleAt position: GMSCameraPosition) {
viewMap.isMyLocationEnabled = true
}
func mapView(_ mapView: GMSMapView, willMove gesture: Bool) {
viewMap.isMyLocationEnabled = true
if (gesture) {
mapView.selectedMarker = nil
}
}
func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool {
viewMap.isMyLocationEnabled = true
return false
}
func mapView(_ mapView: GMSMapView, didTapAt coordinate: CLLocationCoordinate2D) {
print("COORDINATE \(coordinate)") // when you tapped coordinate
}
func didTapMyLocationButton(for mapView: GMSMapView) -> Bool {
viewMap.isMyLocationEnabled = true
viewMap.selectedMarker = nil
return false
}
**StartLocation and DetinationLocation**
#IBAction func btnStartLocationPressed(_ sender: UIButton) {
let autoCompleteController = GMSAutocompleteViewController()
autoCompleteController.delegate = self
// selected location
locationSelected = .startLocation
self.locationManager.stopUpdatingLocation()
self.present(autoCompleteController, animated: true, completion: nil)
}
#IBAction func btnDetinationLocation(_ sender: UIButton) {
let autoCompleteController = GMSAutocompleteViewController()
autoCompleteController.delegate = self
// selected location
locationSelected = .destinationLocation
self.locationManager.stopUpdatingLocation()
self.present(autoCompleteController, animated: true, completion: nil)
}
**This is Extension for GMSAutocompleteViewController put after ViewController class**
extension ViewController: GMSAutocompleteViewControllerDelegate {
func viewController(_ viewController: GMSAutocompleteViewController, didFailAutocompleteWithError error: Error) {
print("Error \(error.localizedDescription)")
}
func viewController(_ viewController: GMSAutocompleteViewController, didAutocompleteWith place: GMSPlace) {
// Change map location
let camera = GMSCameraPosition.camera(withLatitude: place.coordinate.latitude, longitude: place.coordinate.longitude, zoom: 16.0
)
// set coordinate to text
if locationSelected == .startLocation {
btnStart.setTitle("\(place.coordinate.latitude), \(place.coordinate.longitude)", for: .normal)
locationStart = CLLocation(latitude: place.coordinate.latitude, longitude: place.coordinate.longitude)
} else {
btndesti.setTitle("\(place.coordinate.latitude), \(place.coordinate.longitude)", for: .normal)
locationEnd = CLLocation(latitude: place.coordinate.latitude, longitude: place.coordinate.longitude)
}
self.viewMap.camera = camera
self.dismiss(animated: true, completion: nil)
}
func wasCancelled(_ viewController: GMSAutocompleteViewController) {
self.dismiss(animated: true, completion: nil)
}
func didRequestAutocompletePredictions(_ viewController: GMSAutocompleteViewController) {
UIApplication.shared.isNetworkActivityIndicatorVisible = true
}
func didUpdateAutocompletePredictions(_ viewController: GMSAutocompleteViewController) {
UIApplication.shared.isNetworkActivityIndicatorVisible = false
}
}
using locationStart and locationEnd you can draw polyline

A better way to get show a view controller after tapping selection from Google Places

I'm following a tutorial to use Google Places into my app and I noticed that after clicking on a filtered result the Google Places View Controller gets dismissed and shows the previous screen that called Google Places. I am not sure how to tweak it so that after the user taps on a result it goes straight to another controller. Right now I have it where after the results is tapped, the Google Places controller is dismissed and I call a present to bring up the other controller. How do I go to the next controller after the user taps on the result without going back to the controller that called Google Places?
import UIKit
import GooglePlaces
class TravelerViewController: UIViewController {
#IBAction func autocompleteClicked(_ sender: UIButton) {
let autocompleteController = GMSAutocompleteViewController()
autocompleteController.delegate = self
present(autocompleteController, animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
extension TravelerViewController: GMSAutocompleteViewControllerDelegate {
// Handle the user's selection.
func viewController(_ viewController: GMSAutocompleteViewController, didAutocompleteWith place: GMSPlace) {
print("Place name: \(place.name)")
print("Place address: \(place.formattedAddress)")
print("Place attributions: \(place.attributions)")
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "TravelerResults")
dismiss(animated: true, completion: nil)
self.present(controller, animated: true, completion: nil)
}
func viewController(_ viewController: GMSAutocompleteViewController, didFailAutocompleteWithError error: Error) {
// TODO: handle the error.
print("Error: ", error.localizedDescription)
}
// User canceled the operation.
func wasCancelled(_ viewController: GMSAutocompleteViewController) {
dismiss(animated: true, completion: nil)
}
// Turn the network activity indicator on and off again.
func didRequestAutocompletePredictions(_ viewController: GMSAutocompleteViewController) {
UIApplication.shared.isNetworkActivityIndicatorVisible = true
}
func didUpdateAutocompletePredictions(_ viewController: GMSAutocompleteViewController) {
UIApplication.shared.isNetworkActivityIndicatorVisible = false
}
}
Make separate function for open View Controller.
#objc func openVC() {
let controller = self.storyboard?.instantiateViewController(withIdentifier: "TravelerResults")
self.present(controller!, animated: true, completion: nil)
}
Call this function in AutoComplete.
func viewController(_ viewController: GMSAutocompleteViewController, didAutocompleteWith place: GMSPlace) {
print("Place name: \(place.name)")
print("Place address: \(place.formattedAddress)")
print("Place attributions: \(place.attributions)")
dismiss(animated: true, completion: nil)
openVC()
}

Swift Error: EXC BAD INSTRUCTION code=EXCI386 for userid

I am writing an app login page when I countered this error. I do not know how to debug it so I am asking where I coded wrong. I got a problem on the keychain wrapper code:
KeychainWrapper.standard.set((user?.uid)!,forKey: "uid")
I am following a YouTube tutorial on Swift 4, the most updated version. However, it seems this line of code has some problems, therefore I would like to know the problem in the whole code, whether I missed I bracket or failed to import.
import UIKit
import Firebase
import FirebaseAuth
import FirebaseDatabase
import FirebaseCore
import SwiftKeychainWrapper
import FirebaseStorage
class ViewController: UIViewController {
#IBOutlet weak var usernameField: UITextField!
#IBOutlet weak var emailField: UITextField!
#IBOutlet weak var passwordField: UITextField!
#IBOutlet weak var userimageview: UIImageView!
var imagePicker: UIImagePickerController!
var selectedImage = UIImage()
override func viewDidLoad() {
super.viewDidLoad()
imagePicker = UIImagePickerController()
imagePicker.allowsEditing = true
imagePicker.delegate = self
}
override func viewDidAppear(_ animated: Bool) {
if KeychainWrapper.standard.object(forKey: "KEY_UID") != nil {
self.performSegue(withIdentifier: "toFeed", sender: nil)
}
}
func storeuserdata(userId: String) {
if let imageData = UIImageJPEGRepresentation(selectedImage, 0.2) {
Storage.storage().reference().putData(imageData, metadata: nil) { (metadata, error) in
guard let metadata = metadata else {
// Uh-oh, an error occurred!
return
}
// Metadata contains file metadata such as size, content-type, and download URL.
let downloadURL = metadata.downloadURL
Database.database().reference().child("users").child(userId).setValue(["username": self.usernameField.text!,"userImg": downloadURL
])
}
}
}
#IBAction func signinpressed(_ sender: Any) {
if let email = emailField.text, let password = passwordField.text {
Auth.auth().createUser(withEmail: email, password: password) { (user, error) in
if error != nil && (self.usernameField.text?.isEmpty)! && self.userimageview.image != nil {
Auth.auth().createUser(withEmail: email, password: password) { (user, error) in
self.storeuserdata(userId: (user?.uid)!)
KeychainWrapper.standard.set((user?.uid)!,forKey: "uid")
self.performSegue(withIdentifier: "toFeed", sender: nil)
}
}else {
KeychainWrapper.standard.set((user?.uid)!,forKey: "uid")
self.performSegue(withIdentifier: "toFeed", sender: nil)
}
}
}
}
#IBAction func getPhoto (_ sender: AnyObject) {
present(imagePicker, animated: true, completion: nil)
}
}
extension ViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate{
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let image = info[UIImagePickerControllerEditedImage] as? UIImage {
selectedImage = image
}
imagePicker.dismiss(animated: true, completion: nil)
}
}

How I can retrieve the coordinates by searching the place

I'm facing the problem while retrieving the data from google map Place autocomplete because my view did load latitude and longitude [Unity] sare default while showing the map
let camera = GMSCameraPosition.camera(withLatitude: 30.7096, longitude: 76.7016, zoom: 12)
let mapView = GMSMapView.map(withFrame: CGRect(x: 0, y: 58, width: 320, height: 510), camera: camera)
let marker = GMSMarker()
marker.position = camera.target
marker.snippet = "Hello World"
marker.map = mapView
self.mapSize.addSubview(mapView)
}
#IBAction func autocompleteClicked(_ sender: UIButton)
{
let autocompleteController = GMSAutocompleteViewController()
autocompleteController.delegate = self as! GMSAutocompleteViewControllerDelegate
present(autocompleteController, animated: true, completion: nil)
}
#IBOutlet weak var mapSize: UIView!
Did you implement the GMSAutocompleteViewControllerDelegate protocol in the parent view controller?
extension ViewController: GMSAutocompleteViewControllerDelegate {
// Handle the user's selection.
func viewController(_ viewController: GMSAutocompleteViewController, didAutocompleteWith place: GMSPlace) {
print("Place name: \(place.name)")
print("Place address: \(place.formattedAddress)")
print("Place attributions: \(place.attributions)")
dismiss(animated: true, completion: nil)
}
func viewController(_ viewController: GMSAutocompleteViewController, didFailAutocompleteWithError error: Error) {
// TODO: handle the error.
print("Error: ", error.localizedDescription)
}
// User canceled the operation.
func wasCancelled(_ viewController: GMSAutocompleteViewController) {
dismiss(animated: true, completion: nil)
}
// Turn the network activity indicator on and off again.
func didRequestAutocompletePredictions(_ viewController: GMSAutocompleteViewController) {
UIApplication.shared.isNetworkActivityIndicatorVisible = true
}
func didUpdateAutocompletePredictions(_ viewController: GMSAutocompleteViewController) {
UIApplication.shared.isNetworkActivityIndicatorVisible = false
}
}
You are missing delegate as GMSAutocompleteViewControllerDelegate
which need to be add as -
class ProductDetailsViewController: UIViewController,GMSAutocompleteViewControllerDelegate {}
hence after this no more force wrap of
autocompleteController.delegate = self as! GMSAutocompleteViewControllerDelegate
just write
autocompleteController.delegate = self
after that you will get info as
func viewController(viewController: GMSAutocompleteViewController, didAutocompleteWithPlace place: GMSPlace) {
print("latitude is ",place.coordinate.latitude)
print("longitude is ",place.coordinate.longitude)
}

How to use callback to send back data to previous ViewController

I have 2 ViewControllers like this:
I want when I press button in ViewController will present to ViewController2. Then when I input the text in TextField and press Back Button in ViewController2, will send that text to button in ViewController1.
I can solve this with delegate or using reference the class ViewController in ViewController2. (You can see with my comment code)
But now I want to use CallBack to send back data.
How to do this:
Here is ViewController:
import UIKit
class ViewController: UIViewController {
#IBOutlet var button: UIButton!
var myString = ""
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(animated: Bool) {
button.setTitle(myString, forState: .Normal)
}
#IBAction func goToViewController2(sender: AnyObject) {
let vc2 = storyboard?.instantiateViewControllerWithIdentifier("ViewController2") as! ViewController2
// vc2.vc1 = self
presentViewController(vc2, animated: true, completion: nil)
}
}
And ViewController2:
import UIKit
class ViewController2: UIViewController, UITextFieldDelegate {
#IBOutlet var textField: UITextField!
// var vc1: ViewController?
var callback: ((String) -> Void)?
override func viewDidLoad() {
super.viewDidLoad()
textField.delegate = self
}
func textFieldDidEndEditing(textField: UITextField) {
callback!(textField.text!)
}
#IBAction func backToViewController(sender: AnyObject) {
// vc1?.myString = textField.text!
dismissViewControllerAnimated(true, completion: nil)
}
}
And I don't want to use with completion block in dismiss function, I just want to use my own callback method.
In vc1, set vc2's callback property before presenting it:
#IBAction func goToViewController2(sender: AnyObject) {
let vc2 = storyboard?.instantiateViewControllerWithIdentifier("ViewController2") as! ViewController2
vc2.callback = { <Put your callback code here...> }
presentViewController(vc2, animated: true, completion: nil)
}
And then, in vc2 call the callback function before dismissing the view controller:
#IBAction func backToViewController(sender: AnyObject) {
callback?(textfield.text!)
dismissViewControllerAnimated(true, completion: nil)
}