In my project i have a mapView with a lot of annotations & i would like to add a search functionality to the map so i can search those annotations and quickly find the annotation i want.
I followed a tutorial i found on the web but it searches globally (MKLocalSearch) and not the annotations.
I tried looking for a tutorial \ Help for my problem but i couldn't get any help for a long time now.
Here is my search code :
I've made these annotations :
let LitzmanLocation = CLLocationCoordinate2DMake(32.100668,34.775192)
// Drop a pin
let Litzman = MKPointAnnotation()
Litzman.coordinate = LitzmanLocation
Litzman.title = "Litzman Bar"
Litzman.subtitle = "נמל תל אביב 18,תל אביב"
mapView.addAnnotation(Litzman)
let ShalvataLocation = CLLocationCoordinate2DMake(32.101145,34.775163)
// Drop a pin
let Shalvata = MKPointAnnotation()
Shalvata.coordinate = ShalvataLocation
Shalvata.title = "Shalvata"
Shalvata.subtitle = "האנגר 28,נמל תל אביב"
mapView.addAnnotation(Shalvata)
let MarkidLocation = CLLocationCoordinate2DMake(32.074961,34.781679)
// Drop a pin
let Markid = MKPointAnnotation()
Markid.coordinate = MarkidLocation
Markid.title = "Markid"
Markid.subtitle = "אבן גבירול 30,תל אביב"
mapView.addAnnotation(Markid)
Currently the search i have:
MapViewController:
//All my Map code is here
}
}
}
extension MapViewController: HandleMapSearch {
func dropPinZoomIn(placemark:MKPlacemark){
// cache the pin
selectedPin = placemark
// clear existing pins
let annotation = MKPointAnnotation()
annotation.coordinate = placemark.coordinate
annotation.title = placemark.name
if let _ = placemark.locality,
let _ = placemark.administrativeArea {
annotation.subtitle = ""
}
mapView.addAnnotation(annotation)
let span = MKCoordinateSpanMake(0.01, 0.01)
let region = MKCoordinateRegionMake(placemark.coordinate, span)
mapView.setRegion(region, animated: true)
}
}
SearchTable:
import UIKit
import MapKit
class LocationSearchTable : UITableViewController {
var matchingItems = [CustomAnnotations]()
var mapView: MKMapView? = nil
var handleMapSearchDelegate:HandleMapSearch? = nil
func parseAddress(selectedItem:MKPlacemark) -> String {
// put a space between "4" and "Melrose Place"
let firstSpace = (selectedItem.subThoroughfare != nil && selectedItem.thoroughfare != nil) ? " " : ""
// put a comma between street and city/state
let comma = (selectedItem.subThoroughfare != nil || selectedItem.thoroughfare != nil) && (selectedItem.subAdministrativeArea != nil || selectedItem.administrativeArea != nil) ? ", " : ""
// put a space between "Washington" and "DC"
let secondSpace = (selectedItem.subAdministrativeArea != nil && selectedItem.administrativeArea != nil) ? " " : ""
let addressLine = String(
format:"%#%#%#%#%#%#%#",
// street number
selectedItem.subThoroughfare ?? "",
firstSpace,
// street name
selectedItem.thoroughfare ?? "",
comma,
// city
selectedItem.locality ?? "",
secondSpace,
// state
selectedItem.administrativeArea ?? ""
)
return addressLine
}
func search(keywords:String) {
self.matchingItems.removeAll()
for annotation in self.mapView!.annotations {
if annotation.isKindOfClass(CustomAnnotations) {
//Just an example here for searching annotation by title, you could add other filtering actions else.
if (annotation.title??.rangeOfString(keywords) != nil) {
self.matchingItems.append(annotation as! CustomAnnotations)
}
}
}
self.tableView.reloadData()
}
}
extension LocationSearchTable : UISearchResultsUpdating {
func updateSearchResultsForSearchController(searchController: UISearchController) {
guard let mapView = mapView,
let searchBarText = searchController.searchBar.text else { return }
let request = MKLocalSearchRequest()
request.naturalLanguageQuery = searchBarText
request.region = mapView.region
let search = MKLocalSearch(request: request)
search.startWithCompletionHandler { response, _ in
guard let response = response else {
return
}
self.matchingItems = response.mapItems
self.tableView.reloadData()
}
}
}
extension LocationSearchTable {
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return matchingItems.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("MapSearchCell", forIndexPath: indexPath)
let selectedItem = matchingItems[indexPath.row]
cell.textLabel?.text = selectedItem.title
cell.detailTextLabel?.text = selectedItem.subtitle
return cell
}
}
extension LocationSearchTable {
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let selectedItem = matchingItems[indexPath.row]//.placemark
handleMapSearchDelegate?.dropPinZoomIn(selectedItem)
dismissViewControllerAnimated(true, completion: nil)
}
}
My question is how i can turn this to only search my annotations and not search all over the world with MKLocalSearch.
using swift 3 and Xcode 8
Thanks you for helping.
If I’m reading this correctly, you just want to search annotations rather than all MapKit items. You could update the code in updateSearchResults to search the mapView.annotations array and filter your results (matchingItems) based on what’s in the search controller’s searchbar.
func updateSearchResults(for searchController: UISearchController) {
matchingItems = []
guard let mapView = mapView,
let searchBarText = searchController.searchBar.text else { return }
for item in self.mapView!.annotations {
let title = item.title! as! String
if title.hasPrefix(searchBarText) && searchBarText != "" {
matchingItems.append(item)
}
}
self.tableView.reloadData()
}
I wrote a quick test app and posted it https://github.com/hellzkitchen/stackoverflow-041917. In my example, I passed the selected annotation to handleMapSearch delegate method and just updated the mapView to center on it. Hope this helps.
Related
I don't quite understand what I am doing wrong since I am very new to MVVM. It worked in MVC architecture. I've setup my VM and am able to get the first set of results and even then that's not working properly. I get 4 results instead of 10 which is what LOADLIMIT is set as. I was able to get it to work in an MVC architecture without any issues. The VM function which triggers the query is called multiple (3) times instead of just once i.e. even prior to scrolling.
Here is my VM:
enum FetchRestaurant {
case success
case error
case location
case end
}
class ListViewModel {
let restaurant: [Restaurant]?
let db = Firestore.firestore()
var restaurantArray = [Restaurant]()
var lastDocument: DocumentSnapshot?
var currentLocation: CLLocation?
typealias fetchRestaurantCallback = (_ restaurants: [Restaurant]?, _ message: String?, _ status: FetchRestaurant) -> Void
var restaurantFetched: fetchRestaurantCallback?
var fetchRestaurant: FetchRestaurant?
init(restaurant: [Restaurant]) {
self.restaurant = restaurant
}
func fetchRestaurantCallback (callback: #escaping fetchRestaurantCallback) {
self.restaurantFetched = callback
}
func fetchRestaurants(address: String) {
print("address received: \(address)")
getLocation(from: address) { location in
if let location = location {
self.currentLocation = location
self.queryGenerator(at: location)
} else {
self.restaurantFetched?(nil, nil, .location)
}
}
}
func queryGenerator(at location: CLLocation) {
var query: Query!
if restaurantArray.isEmpty {
query = db.collection("Restaurant_Data").whereField("distributionType", isLessThanOrEqualTo: 2).limit(to: Constants.Mealplan.LOADLIMIT)
} else {
print("last document:\(String(describing: lastDocument?.documentID))")
query = db.collection("Restaurant_Data").whereField("distributionType", isLessThanOrEqualTo: 2).start(afterDocument: lastDocument!).limit(to: Constants.Mealplan.LOADLIMIT)
}
batchFetch(query: query)
}
func batchFetch(query: Query) {
query.getDocuments { (querySnapshot, error) in
if let error = error {
self.restaurantFetched?(nil, error.localizedDescription, .error)
} else if querySnapshot!.isEmpty {
self.restaurantFetched?(nil, nil, .end)
} else if !querySnapshot!.isEmpty {
let queriedRestaurants = querySnapshot?.documents.compactMap { querySnapshot -> Restaurant? in
return try? querySnapshot.data(as: Restaurant.self)
}
guard let restaurants = queriedRestaurants,
let currentLocation = self.currentLocation else {
self.restaurantFetched?(nil, nil, .end)
return }
self.restaurantArray.append(contentsOf: self.applicableRestaurants(allQueriedRestaurants: restaurants, location: currentLocation))
DispatchQueue.main.asyncAfter(deadline: .now(), execute: {
self.restaurantFetched?(self.restaurantArray, nil, .success)
})
self.lastDocument = querySnapshot!.documents.last
}
}
}
func getLocation(from address: String, completionHandler: #escaping (_ location: CLLocation?) -> Void) {
let geocoder = CLGeocoder()
geocoder.geocodeAddressString(address) { (placemarks, error) in
guard let placemarks = placemarks,
let location = placemarks.first?.location else {
completionHandler(nil)
return
}
completionHandler(location)
}
}
}
And in the VC viewDidLoad:
var fetchMore = false
var reachedEnd = false
let leadingScreensForBatching: CGFloat = 5.0
var searchController = UISearchController(searchResultsController: nil)
var currentAddress : String?
var listViewModel = ListViewModel(restaurant: [Restaurant]())
override func viewDidLoad() {
super.viewDidLoad()
listViewModel.fetchRestaurantCallback { (restaurants, error, result) in
switch result {
case .success :
self.loadingShimmer.stopShimmering()
self.loadingShimmer.removeFromSuperview()
guard let fetchedRestaurants = restaurants else { return }
self.restaurantArray.append(contentsOf: fetchedRestaurants)
self.tableView.reloadData()
self.fetchMore = false
case .location :
self.showAlert(alertTitle: "No businesses nearby", message: "Try going back and changing the address")
case .error :
guard let error = error else { return }
self.showAlert(alertTitle: "Error", message: error)
case .end :
self.fetchMore = false
self.reachedEnd = true
}
}
if let currentAddress = currentAddress {
listViewModel.fetchRestaurants(address: currentAddress)
}
}
I would really appreciate links or resources for implementing MVVM in Swift for a Firestore back-end. I'm coming up short on searches here and on Google. Even tried medium.
EDIT
class ListViewController: UITableViewController {
lazy var loadingShimmer: UIImageView = {
let image = UIImage(named: "shimmer_background")
let imageview = UIImageView(image: image)
imageview.contentMode = .top
imageview.translatesAutoresizingMaskIntoConstraints = false
return imageview
}()
var restaurantArray = [Restaurant]()
var planDictionary = [String: Any]()
var fetchMore = false
var reachedEnd = false
let leadingScreensForBatching: CGFloat = 5.0
var searchController = UISearchController(searchResultsController: nil)
var currentAddress : String?
var listViewModel = ListViewModel(restaurant: [Restaurant]())
override func viewDidLoad() {
super.viewDidLoad()
setupTable()
}
override func viewWillAppear(_ animated: Bool) {
clearsSelectionOnViewWillAppear = false
}
func setupTable() {
navigationItem.backBarButtonItem = UIBarButtonItem(title: "Restaurant", style: .plain, target: nil, action: nil)
tableView.register(RestaurantCell.self, forCellReuseIdentifier: "Cell")
tableView.delegate = self
tableView.dataSource = self
let navigationBarHeight: CGFloat = self.navigationController!.navigationBar.frame.height
tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: -navigationBarHeight, right: 0)
tableView.separatorStyle = .none
tableView.showsVerticalScrollIndicator = false
tableView.addSubview(loadingShimmer)
loadingShimmer.topAnchor.constraint(equalTo: tableView.safeAreaLayoutGuide.topAnchor).isActive = true
loadingShimmer.leadingAnchor.constraint(equalTo: tableView.leadingAnchor).isActive = true
loadingShimmer.trailingAnchor.constraint(equalTo: tableView.trailingAnchor).isActive = true
loadingShimmer.startShimmering()
initialSetup()
}
func initialSetup() {
let addressOne = planDictionary["addressOne"] as! String + ", "
let city = planDictionary["city"] as! String + ", "
let postalCode = planDictionary["postalCode"] as! String
currentAddress = addressOne + city + postalCode
setupSearch()
listViewModel.fetchRestaurantCallback { (restaurants, error, result) in
switch result {
case .success :
self.loadingShimmer.stopShimmering()
self.loadingShimmer.removeFromSuperview()
guard let fetchedRestaurants = restaurants else { return }
self.restaurantArray.append(contentsOf: fetchedRestaurants)
self.tableView.reloadData()
self.fetchMore = false
case .location :
self.showAlert(alertTitle: "No businesses nearby", message: "Try going back and changing the address")
case .error :
guard let error = error else { return }
self.showAlert(alertTitle: "Error", message: error)
case .end :
self.fetchMore = false
self.reachedEnd = true
}
}
if let currentAddress = currentAddress {
listViewModel.fetchRestaurants(address: currentAddress)
}
}
override func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
let off = scrollView.contentOffset.y
let off1 = scrollView.contentSize.height
if off > off1 - scrollView.frame.height * leadingScreensForBatching {
print("\(fetchMore), \(reachedEnd)")
if !fetchMore && !reachedEnd {
if let address = self.currentAddress {
print("address sent: \(address)")
listViewModel.fetchRestaurants(address: address)
}
}
}
}
}
That you're only getting back 4 results instead of 10 is not due to a faulty query or get-document request—those are coded properly. You're either losing documents when you parse them (some are failing Restaurant initialization), Constants.Mealplan.LOADLIMIT is wrong, or there aren't more than 4 documents in the collection itself that satisfy the query.
That the query is executed 3 times instead of once is also not due to anything in this code—viewDidLoad is only called once and geocodeAddressString only returns once. You're making a fetch request elsewhere that we can't see.
In the batchFetch method, you have a guard that returns out of the function without ever calling its completion handler. This will leave the UI in a state of limbo. I'd recommend always calling the completion handler no matter why the function returns.
You never manage the document cursor. If the get-document return has less documents than the load limit, then nil the last-document cursor. This way, when you attempt to get the next page of documents, guard against a nil cursor and see if there is even more to fetch.
There's no need to pass in an empty array and have your function fill it; simply construct and return an array of results within ListViewModel itself.
We can't see how you trigger pagination. Is it through a scroll delegate when the user reaches the bottom or through a button tap, for example? If it's through a scroll delegate, then I'd disable that for now and see how many returns you get—I suspect one, instead of 3.
What is the particular reason you've ditched MVC for MVVM here? With MVC, you can get pagination up with just a few lines of code. I think MVVM is overkill for iOS applications and would advise against using it unless you have a compelling reason.
I tried to update TableView cell when click cell from CollectionView with key word of class.
For example, when I click "all", it should perform all data.
When I click "a", it should perform class with "a".
It looks like all perfect until I scroll down TableView.
All data display which is not my intension, it should only show specify data even scroll up/down TableView.
Is it related to dequeueReusableCell issue?
I use FMDB to query data when click CollectionView cell.
I attach gif
and hope it could be better to understand my problem.
Can someone help me out with this please? Thanks.
add cellForRow function here.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellID, for: indexPath) as! ListTableViewCell
if navigationItem.searchController?.isActive == true{
cell.itemImage.image = searchArray[indexPath.row].thumbnailImage()
cell.nameLBL.text = "name:\(searchArray[indexPath.row].name ?? "")"
cell.quantityLBL.text = "Qty:\(searchArray[indexPath.row].quantity ?? 0)pcs"
cell.amountLBL.text = "amount:\(searchArray[indexPath.row].amount ?? 0)"
cell.dateLBL.text = "date:\(searchArray[indexPath.row].date ?? "")"
cell.storeLBL.text = "store:\(searchArray[indexPath.row].store ?? "")"
cell.classLBL.text = "class:\(searchArray[indexPath.row].itemClass ?? "")"
if let reminder = searchArray[indexPath.row].reminder {
let reminderText = ""
cell.reminderLBL.text = "\(reminder)" + reminderText
reminderStatus = true
}else{
cell.reminderLBL.text = "reminder:NO"
if self.array[indexPath.row].reminder == nil{
cell.reminderLBL.text = "reminder:NO"
}
reminderStatus = false
}
}else{
cell.itemImage.image = array[indexPath.row].thumbnailImage()
cell.nameLBL.text = "name:\(array[indexPath.row].name ?? "")"
cell.quantityLBL.text = "Qty:\(array[indexPath.row].quantity ?? 0)pcs"
cell.amountLBL.text = "amount:\(array[indexPath.row].amount ?? 0)"
cell.dateLBL.text = "date:\(array[indexPath.row].date ?? "")"
cell.storeLBL.text = "store:\(array[indexPath.row].store ?? "")"
cell.classLBL.text = "class:\(array[indexPath.row].itemClass ?? "")"
if let reminder = array[indexPath.row].reminder {
let reminderText = ""
cell.reminderLBL.text = "\(reminder)" + reminderText
reminderStatus = true
}else{
cell.reminderLBL.text = "reminder:NO"
if self.array[indexPath.row].reminder == nil{
cell.reminderLBL.text = "reminder:NO"
}
reminderStatus = false
}
print(array.count)
}
return cell
}
add didSelectItemAt function here.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
switch indexPath.row {
case 0:
reloadTableViewAndDatabase()
case 1:
reloadTableViewAndDatabase(itemClass: searchStringOfDaily)
case 2:
reloadTableViewAndDatabase(itemClass: searchStringOfCare)
case 3:
reloadTableViewAndDatabase(itemClass: searchStringOfCosmetic)
default:
reloadTableViewAndDatabase()
}
}
And function which may be factor.
let searchStringOfDaily = "SELECT * FROM Data where itemClass = 'a';"
let searchStringOfCare = "SELECT * FROM Data where itemClass = 'b';"
let searchStringOfCosmetic = "SELECT * FROM Data where itemClass = 'c';"
func reloadTableViewAndDatabase()
{
self.dbHelper.searchAllDataFromDatabase { (dataArray) in
self.array = dataArray
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
func reloadTableViewAndDatabase(itemClass:String)
{
self.dbHelper.searchDataFromDatabase(completionHandler: { (dataArray) in
self.array = dataArray
DispatchQueue.main.async {
self.tableView.reloadData()
}
}, search: itemClass)
}
public func searchDataFromDatabase(completionHandler:([Data]) -> (),search:String) {
var searchResultArray:[Data] = []
if self.database == nil{
self.database = FMDatabase(path: self.databasePath)
}
if self.database.open(){
let searchString = search
do{
let resultNext = try self.database.executeQuery(searchString, values: nil)
while resultNext.next(){
let dataName = resultNext.string(forColumn: "name")
let dataQuantity = resultNext.int(forColumn: "quantity")
let dataAmount = resultNext.int(forColumn: "amount")
let dataDate = resultNext.string(forColumn: "date")
let dataStore = resultNext.string(forColumn: "store")
let dataImageName = resultNext.string(forColumn: "imageName")
let dataItemClass = resultNext.string(forColumn: "itemClass")
let dataReminder = resultNext.string(forColumn: "reminder")
let data = Data(name: dataName!, quantity: Int(dataQuantity), amount: Int(dataAmount), date: dataDate!, store: dataStore!, imageName: dataImageName ?? "", reminder: Int(dataReminder!), itemClass: dataItemClass!)
searchResultArray.append(data)
}
print("query success")
}catch{
print(error.localizedDescription)
}
self.database.close()
}
completionHandler(searchResultArray)
}
I'm trying to change the map annotation to a custom image that i made in a static cell. The map's annotation works but then when i try to put my image in the map zooms to the location but doesn't display the image.
import UIKit
import MapKit
import CoreLocation
class ContactsTableViewController: UITableViewController, MKMapViewDelegate, CLLocationManagerDelegate {
}
override func viewDidLoad() {
super.viewDidLoad()
self.addressMap.delegate = self
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ContactCell", for: indexPath) as! customCell
if let _ = currentAnnotation {
addressMap.removeAnnotation(currentAnnotation!)
}
let contact = contacts.contactWithIndex((indexPath as NSIndexPath).row)
print(contact)
cell.nameLabel.text = contact.name
cell.addressLabel.text = contact.address
let country = "USA"
let city = "Cicero"
let street = contact.address
// Create Address String
let address = "\(country), \(city), \(street)"
// Geocode Address String
geocoder.geocodeAddressString(address) { (placemarks, error) in
// Process Response
self.processResponse(withPlacemarks: placemarks, error: error)
var location: CLLocation?
if let placemarks = placemarks, placemarks.count > 0 {
location = placemarks.first?.location
}
if let location = location {
let coordinate = location.coordinate
print("Custom cell: \(coordinate.latitude), \(coordinate.longitude)")
//Drops pin to current MKPointAnnotationis logged in
self.currentAnnotation = MKPointAnnotation()
self.currentAnnotation?.coordinate = CLLocationCoordinate2D(latitude: coordinate.latitude, longitude: coordinate.longitude)
let image1 = CustomPointAnnotation()
image1.imageName = "mapPin.png"
self.addressMap.addAnnotation(image1)
//Pins the location on the mapview
//cell.addressMap.addAnnotation(self.currentAnnotation!)
cell.addressMap.addAnnotation(image1)
//Auto zoom into user location
let span = MKCoordinateSpanMake(0.02, 0.02)
let region = MKCoordinateRegion(center: (self.currentAnnotation?.coordinate)!, span: span)
cell.addressMap.setRegion(region, animated: true)
} else {
print("No Matching Location Found")
}
}
}
func addressMap(_ addressMap: MKMapView, viewFor annotation: MKAnnotation!) -> MKAnnotationView? {
if !(annotation is CustomPointAnnotation) {
return nil
}
let reuseId = "test"
var anView = addressMap.dequeueReusableAnnotationView(withIdentifier: reuseId)
if anView == nil {
anView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
anView?.canShowCallout = true
} else {
anView?.annotation = annotation
}
//Set annotation-specific properties **AFTER**
//the view is dequeued or created...
let cpa = annotation as! CustomPointAnnotation
anView?.image = UIImage(named:cpa.imageName)
return anView
}
Here's the class for the annotation
import UIKit
import MapKit
class CustomPointAnnotation: MKPointAnnotation {
var imageName: String!
}
If anyone can tell me what I'm doing wrong that's causing my image to not display as the annotation that would be awesome.
A couple of issues:
You don't appear to ever set the coordinate of the CustomPointAnnotation.
The signature for the mapView(_:viewFor:) is incorrect. If you add a breakpoint in your method, you'll see it is not called. Your map outlet may be called addressMap, but the delegate method name is mapView(_:viewFor:), regardless of the name of your MKMapView outlet reference.
I'm trying to make an app, where you get buses positions from web API. When I got the data, I put annotations on mapview for all of the coordinates i got from API. Then, when annotation is tapped on, I try to hide all the annotations which aren't on the same route as the one clicked. It works fine, but when I zoom out the annotations that weren't in the original region aren't hidden. Is there a way to refresh what is displayed when user zooms on map view?
import UIKit
import MapKit
import MBProgressHUD
import Alamofire
class MapViewController: UIViewController {
//MARK: Properties and Outlets
private var timer: NSTimer?
private lazy var dateFormatter = NSDateFormatter()
private var vehicles = [String: Vehicle]()
private var points = [CLLocationCoordinate2D]()
private let locationManager = CLLocationManager()
private var currentLocation: CLLocation?
#IBOutlet weak var mapView: MKMapView!
//MARK: Actions
#IBAction func getMyLocation(sender: UIBarButtonItem) {
getLocation()
}
//MARK: Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
dateFormatter.locale = NSLocale.currentLocale()
dateFormatter.dateFormat = "HH:mm:ss"
timer = NSTimer.scheduledTimerWithTimeInterval(30, target: self, selector: "fetchVehiclesLocations", userInfo: nil, repeats: true)
fetchVehiclesLocations()
getLocation()
}
//MARK: Get Location
private func getLocation() {
let authStatus = CLLocationManager.authorizationStatus()
if authStatus == .NotDetermined {
locationManager.requestWhenInUseAuthorization()
return
}
if authStatus == .Denied || authStatus == .Restricted {
showLocationServicesDeniedAlert()
return
}
startLocationManager()
}
//MARK: Helper methods
private func showLocationServicesDeniedAlert() {
let alert = UIAlertController(title: "Location Services Disabled", message: "Please enable location services for this app in Settings.", preferredStyle: .Alert)
let okAction = UIAlertAction(title: "OK", style: .Default, handler: nil)
alert.addAction(okAction)
presentViewController(alert, animated: true, completion: nil)
}
private func startLocationManager () {
if CLLocationManager.locationServicesEnabled() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.startUpdatingLocation()
showLoadingHUD()
}
}
private func stopLocationManager() {
locationManager.delegate = nil
locationManager.stopUpdatingLocation()
hideLoadingHUD()
}
private func rotateBus() {
UIView.animateWithDuration(1.0, delay: 0, options: .CurveLinear, animations: {
for (id, _) in self.vehicles {
if self.vehicles[id]!.lastPosition != nil {
let annotationView = self.mapView.viewForAnnotation(self.vehicles[id]!.annotation!)
let currentLocationPoint = self.mapView.convertCoordinate(self.vehicles[id]!.lastPosition!, toPointToView: self.mapView)
let destinationPoint = self.mapView.convertCoordinate(self.vehicles[id]!.coordinates, toPointToView: self.mapView)
let yDiff = currentLocationPoint.y - destinationPoint.y
let xDiff = destinationPoint.x - currentLocationPoint.x
let arcTan = atan(yDiff / xDiff)
var angle = CGFloat(M_PI / 2) - arcTan
if xDiff < 0 {
angle = angle + CGFloat(M_PI)
}
if angle.isNaN || angle == 0.0 {
continue
}
else {
annotationView?.transform = CGAffineTransformMakeRotation(CGFloat(angle))
}
}
}
}, completion: nil)
moveBus()
}
private func moveBus() {
UIView.animateWithDuration(28.0, delay: 0, options: .CurveLinear, animations: {
for(id, _) in self.vehicles {
if self.vehicles[id]!.lastPosition != nil {
self.vehicles[id]!.annotation?.coordinate = self.vehicles[id]!.coordinates
}
}
}, completion: nil)
}
private func createVehicles() {
print(vehicles.count)
for (id, _) in vehicles {
if vehicles[id]?.annotation == nil {
let annotation = BusAnnotation()
annotation.imageName = "bus"
annotation.coordinate = vehicles[id]!.coordinates
annotation.title = id
vehicles[id]?.annotation = annotation
mapView.addAnnotation(annotation)
}
}
print(mapView.annotations.count)
rotateBus()
}
private func drawPolyline() {
let myPolyline = MKPolyline(coordinates: &points, count: points.count)
mapView.addOverlay(myPolyline)
}
//MARK: HUD display methods
private func showLoadingHUD() {
let hud = MBProgressHUD.showHUDAddedTo(mapView, animated: true)
hud.labelText = "Getting Your Location..."
}
private func hideLoadingHUD() {
MBProgressHUD.hideAllHUDsForView(mapView, animated: true)
}
//MARK: Networking
func fetchVehiclesLocations() {
let URL = urlVehiclesLocations + apiKey
if !vehicles.isEmpty {
for (id , _) in vehicles {
vehicles[id]?.lastPosition = vehicles[id]?.coordinates
}
}
Alamofire.request(.GET, URL)
.validate()
.responseJSON {
(request, response, result) in
guard result.isSuccess else {
print("Error while fetching \(result.error!)")
return
}
let value = result.value as? [String: AnyObject]
let responseDict = value?["response"] as? [String: AnyObject]
let array = responseDict?["entity"] as? [[String: AnyObject]]
for var i = 0; i < array?.count; i++ {
let item = array?[i]["vehicle"] as? [String: AnyObject]
let position = item?["position"] as? [String: AnyObject]
let trip = item?["trip"] as? [String: AnyObject]
let vehicle = Vehicle()
vehicle.latitude = position?["latitude"] as! Double
vehicle.longitude = position?["longitude"] as! Double
vehicle.tripId = trip?["trip_id"] as! String
let startTime = trip?["start_time"] as! String
vehicle.startTime = self.dateFormatter.dateFromString(startTime)
let vehicleId = item?["vehicle"] as? [String: AnyObject]
let id = vehicleId?["id"] as! String
if self.vehicles[id] == nil {
self.vehicles[id] = vehicle
}
else {
self.vehicles[id]?.latitude = vehicle.latitude
self.vehicles[id]?.longitude = vehicle.longitude
}
}
self.createVehicles()
}
}
func fetchPointsForTripPolyline() {
let URL = urlTripPolyline + apiKey
Alamofire.request(.GET, URL)
.validate()
.responseJSON {
(request, response, result) in
guard result.isSuccess else {
print("Error while fetching \(result.error!)")
return
}
let value = result.value as! [String: AnyObject]
let responseArray = value["response"] as! [[String: AnyObject]]
for var i = 0; i < responseArray.count; i++ {
let longitude = responseArray[i]["shape_pt_lon"] as! CLLocationDegrees
let latitude = responseArray[i]["shape_pt_lat"] as! CLLocationDegrees
let coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
self.points.append(coordinate)
}
self.drawPolyline()
}
}
}
extension MapViewController: CLLocationManagerDelegate {
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let newLocation = locations.last!
if newLocation.timestamp.timeIntervalSinceNow < -5 {
return
}
if newLocation.horizontalAccuracy < 0 {
return
}
if newLocation.horizontalAccuracy <= locationManager.desiredAccuracy {
stopLocationManager()
currentLocation = newLocation
let annotation = MKPointAnnotation()
annotation.coordinate = newLocation.coordinate
mapView.addAnnotation(annotation)
}
let center = CLLocationCoordinate2D(latitude: newLocation.coordinate.latitude, longitude: newLocation.coordinate.longitude)
let region = MKCoordinateRegionMakeWithDistance(center, 1000, 1000)
self.mapView.setRegion(region, animated: true)
}
}
extension MapViewController: MKMapViewDelegate {
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
if !(annotation is BusAnnotation) {
return nil
}
let reuseId = annotation.title!
var busAnnotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId!)
if busAnnotationView == nil {
busAnnotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
busAnnotationView?.canShowCallout = true
}
else {
busAnnotationView?.annotation = annotation
}
let busAnnotation = annotation as! BusAnnotation
busAnnotationView?.image = UIImage(named: busAnnotation.imageName)
return busAnnotationView
}
func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer {
if overlay is MKPolyline {
let lineView = MKPolylineRenderer(overlay: overlay)
lineView.lineWidth = 3.0
lineView.strokeColor = UIColor.greenColor()
return lineView
}
return MKOverlayRenderer()
}
func hideBuses(tripId: String) {
for (_, vehicle) in vehicles {
if vehicle.tripId != tripId {
let annotationView = mapView.viewForAnnotation(vehicle.annotation!)
annotationView?.hidden = true
}
}
}
func mapView(mapView: MKMapView, didSelectAnnotationView view: MKAnnotationView) {
let vehicleId = view.annotation?.title!
let tripId = vehicles[vehicleId!]?.tripId
hideBuses(tripId!)
}
}
I am trying to use someone else's code as a basis for what I want to do. However it appears to have been written for an older version of Swift and so I get lots of errors. I have been able to fix quite a few but now I am stumped.
Code I am correcting is:
import UIKit
class ViewController: UIViewController {
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
var array_data: NSMutableArray = []
#IBOutlet var table_data : UITableView?
override func viewDidLoad() {
super.viewDidLoad()
selectFunc()
self.navigationItem.title="Empolyee List"
var addBtn = UIBarButtonItem(title: "Add", style: UIBarButtonItemStyle.Plain, target: self, action: Selector("insertFunc"))
self.navigationItem.rightBarButtonItem = addBtn
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
selectFunc()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// I did some work on the block below to get rid of the use of Cstring (Deprecated) that has caused issues below
func selectFunc(){
var selectQuery="select * from EmployeInfo"
var result:CInt=0
var stmt:COpaquePointer = nil
result = sqlite3_prepare_v2(appDelegate.database, selectQuery, -1, &stmt, nil);
if result != SQLITE_OK
{
println("failed \(sqlite3_errmsg(appDelegate.database))")
}
else
{
result = sqlite3_step(stmt)
array_data.removeAllObjects()
while result == SQLITE_ROW {
var Dictionary = NSMutableDictionary()
let name = sqlite3_column_text(stmt, 0)
let dep = sqlite3_column_text(stmt, 1)
let sal = sqlite3_column_text(stmt, 2)
// This is where the errors start - next three lines
Dictionary.setObject(String.fromCString(CString(name)), forKey:"name")
Dictionary.setObject(String.fromCString(CString(dep)), forKey: "department")
Dictionary.setObject(String.fromCString(CString(sal)), forKey: "salary")
// The error (not surprisingly) is "Use of Unresolved identifier 'Cstring'"
//OK for those who run into this issue...
//use... Dictionary.setObject(String.fromCString(UnsafePointer<CChar>(name))!, forKey: "name")
Dictionary.setObject(String.fromCString(UnsafePointer<CChar>(dep))!, forKey: "department")
Dictionary.setObject(String.fromCString(UnsafePointer<CChar>(sal))!, forKey: "salary")
array_data .addObject(Dictionary)
result = sqlite3_step(stmt)
}
}
self.table_data!.reloadData()
}
func insertFunc(){
let vc : UIViewController = storyboard!.instantiateViewControllerWithIdentifier("AddViewController") as! UIViewController;
self.navigationController!.pushViewController(vc, animated: true)
}
func numberOfSectionsInTableView(tableView: UITableView!) -> Int {
return 1
}
func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int {
return self.array_data.count
}
func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
//simple cell
var cell = tableView.dequeueReusableCellWithIdentifier("CELL") as? UITableViewCell
if !(cell != nil) {
cell = UITableViewCell(style: UITableViewCellStyle.Value1, reuseIdentifier: "CELL")
}
var dic=array_data.objectAtIndex(indexPath.row) as! NSDictionary
cell!.textLabel!.text = dic.valueForKey("name") as! String
return cell
}
func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!){
let vc = storyboard!.instantiateViewControllerWithIdentifier("update_deleteViewController") as update_deleteViewController;
vc.dictionary=array_data.objectAtIndex(indexPath.row) as NSDictionary
self.navigationController.pushViewController(vc, animated: true)
}
}
There are other issues in the project but this will give me a good start.
Retrieving the data from sqlite database using swift programming language
func retrivingDataFromSqliteDb(){
var database:COpaquePointer = nil
// This is for getting document directory path or database path
let dbpath = appDelegate.getting_SandBox_Path().cStringUsingEncoding(NSUTF8StringEncoding)
// open the database and checking open or not
let error = sqlite3_open(dbpath, &database)
if error != SQLITE_OK {
println("Error while opening");
}
else{
println("already database open");
var selectQuery = "select * from emptab"
var result:CInt = 0
var stmt:COpaquePointer = nil
result = sqlite3_prepare_v2(database, selectQuery, -1, &stmt, nil);
if result != SQLITE_OK {
println("failed : \(sqlite3_errmsg(database))")
}
else {
result = sqlite3_step(stmt)
array_data.removeAllObjects()
while result == SQLITE_ROW {
var Dictionary = NSMutableDictionary()
let fname = sqlite3_column_text(stmt, 0)
let lname = sqlite3_column_text(stmt, 1)
let phone = sqlite3_column_text(stmt, 2)
let email = sqlite3_column_text(stmt, 3)
let address = sqlite3_column_text(stmt, 4)
// adding to retrived objects to dictionary
Dictionary.setObject(String.fromCString(UnsafePointer<CChar>(fname))!, forKey: "firstName")
Dictionary.setObject(String.fromCString(UnsafePointer<CChar>(lname))!, forKey: "LastName")
Dictionary.setObject(String.fromCString(UnsafePointer<CChar>(phone))!, forKey: "MobileNum")
Dictionary.setObject(String.fromCString(UnsafePointer<CChar>(email))!, forKey: "mail")
Dictionary.setObject(String.fromCString(UnsafePointer<CChar>(address))!, forKey: "address")
// dictionary of data adding to the Array
array_data .addObject(Dictionary)
result = sqlite3_step(stmt)
}
println(array_data)
}
}
}