iOS Revisited: Maps

Hot

Post Top Ad

Showing posts with label Maps. Show all posts
Showing posts with label Maps. Show all posts

16 Dec 2019

Create custom marker and custom info window in iOS using swift - Google maps

12/16/2019 09:29:00 am 10
In this article, we are going to learn about Custom Marker and Custom Info Window in Google maps.

First, create a new Xcode project and name it as Custom Marker.

In this example we are going to use completely auto layouts without a storyboard, every constraint is programmatically.

Download the sample project from the bottom of this tutorial.

Create custom marker and custom info window in iOS using swift - Google maps

Adding Google Map:

The first thing to do is integrate Google Maps SDK either using pod or other alternatives.

Here I am integrating using cocoapods.

Open terminal, go the project folder then type follow commands.

pod init

open -a xcode podfile

Then add pod 'GoogleMaps' below to the # Pods for CustomMarker.

pod install

After successful installation close the project and open workspace file.

To use google maps we need a key from google.

If you don't have one, get one from here

Okay, All set.

open AppDelegate.swift and import GoogleMaps then add the following line inside didFinishLaunchingWithOptions method:

GMSServices.provideAPIKey("YOUR API KEY")

open ViewController.swift and import GoogleMaps.

Add the following closure before viewDidLoad():

var mapView: GMSMapView = {
     let v = GMSMapView()
     v.translatesAutoresizingMaskIntoConstraints = false
     return v
 }()

Add mapView to the view as subview, add the following code inside viewDidLoad():

self.view.addSubview(mapView)
mapView.padding = UIEdgeInsets(top: 72, left: 25, bottom: 0, right: 25)
mapView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
mapView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
mapView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
mapView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
mapView.delegate = self

Don't panic with error, simply add the following extension to remove the error, we will discuss later in this example.

extension ViewController: GMSMapViewDelegate {
 
}

Now run the app, if your API key is valid you will see the map like below

Adding Google Map

Add Custom Marker:

Before creating a custom marker, first create our model, add the following model:

struct SSPlace {
    var name: String?
    var address: String?
    var coordinates: (lat: Double, lng: Double)?
}

Create array of SSPlaces as follow:

var places = [SSPlace]()

Add some sample data to places array, add the following code to the end of viewDidLoad():

places.append(SSPlace(name: "Fremont Troll", address: "N 36th St, Seattle, WA 98103, United States", coordinates: (lat: 47.651105, lng: -122.347347)))
places.append(SSPlace(name: "Grand Canyon National Park", address: "Arizona, United States", coordinates: (lat: 36.099982, lng: -112.123640)))
places.append(SSPlace(name: "Statue of Liberty National Monument", address: "New York, NY 10004, United States", coordinates: (lat: 40.689323, lng: -74.044490)))
places.append(SSPlace(name: "Yellowstone National Park", address: "United States", coordinates: (lat: 44.429311, lng: -110.588112)))
places.append(SSPlace(name: "Walt Disney World Resort", address: "Orlando, FL, United States", coordinates: (lat: 28.385280, lng: -81.563853)))

Now it's time to create a custom marker view.

Create a new swift file of type UIView and name it as CustomMarkerView. Replace the whole class with the following code:

class CustomMarkerView: UIView {
    
    var imageName: String?
    var borderColor: UIColor!
    
    init(frame: CGRect, imageName: String?, borderColor: UIColor, tag: Int) {
        super.init(frame: frame)
        self.imageName=imageName
        self.borderColor=borderColor
        self.tag = tag
        setupViews()
    }
    
    func setupViews() {
        let imgView = UIImageView()
        imgView.translatesAutoresizingMaskIntoConstraints = false
        self.addSubview(imgView)
        imgView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
        imgView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
        imgView.topAnchor.constraint(equalTo: topAnchor).isActive = true
        imgView.heightAnchor.constraint(equalToConstant: 50).isActive = true
        imgView.layer.cornerRadius = 25
        imgView.layer.borderColor = borderColor?.cgColor
        imgView.contentMode = .scaleAspectFill
        imgView.layer.borderWidth = 4
        imgView.clipsToBounds = true
        imgView.image = UIImage(named: imageName!)
        
        let triangleImgView = UIImageView()
        self.insertSubview(triangleImgView, belowSubview: imgView)
        triangleImgView.translatesAutoresizingMaskIntoConstraints = false
        triangleImgView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
        triangleImgView.topAnchor.constraint(equalTo: imgView.bottomAnchor, constant: -6).isActive = true
        triangleImgView.widthAnchor.constraint(equalToConstant: 23/2).isActive = true
        triangleImgView.heightAnchor.constraint(equalToConstant: 24/2).isActive = true
        triangleImgView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
        triangleImgView.image = UIImage(named: "markerTriangle")
        triangleImgView.tintColor = borderColor
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
}

Here we are adding UIImageView to show the place image and one more UIImageView to show the bottom triangle.

Create new array to store all markers:

var markers = [GMSMarker]()

To add the places as markers on the map add the following method:

func addMarkers() {
    markers.removeAll()
    for (index, place) in places.enumerated() {
        let marker = GMSMarker()
        let customMarker = CustomMarkerView(frame: CGRect(x: 0, y: 0, width: customMarkerWidth, height: customMarkerHeight), imageName: place.name, borderColor: primaryColor, tag: index)
        marker.iconView = customMarker
        marker.position = CLLocationCoordinate2D(latitude: place.coordinates!.lat, longitude: place.coordinates!.lng)
        marker.infoWindowAnchor = CGPoint(x: 0.5, y: 0)
        marker.map = self.mapView
        marker.zIndex = Int32(index)
        marker.userData = place
        markers.append(marker)
    }
}

Add the following colors before ViewController class:

let primaryColor = UIColor(red:0.00, green:0.19, blue:0.56, alpha:1.0)

let secondaryColor = UIColor(red:0.89, green:0.15, blue:0.21, alpha:1.0)

After adding markers to the map let focus them so that users can look at those places only.

To focus the markers add the following methods:

func focusMapToShowAllMarkers() {
    if let firstLocation = markers.first?.position {
        var bounds =  GMSCoordinateBounds(coordinate: firstLocation, coordinate: firstLocation)
        
        for marker in markers {
            bounds = bounds.includingCoordinate(marker.position)
        }
        let update = GMSCameraUpdate.fit(bounds, withPadding: 20)
        self.mapView.animate(with: update)
    }
}

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    self.focusMapToShowAllMarkers()
}

Finally call the addMarkers() function at the end of viewDidLoad():

override func viewDidLoad() {
       super.viewDidLoad()
       /***
       self.addMarkers()
}

That's it run the app, you can see markers as follows:

Create custom marke in iOS using swift - Google maps

Add Custom Info Window:

Create a new swift file of type UIView and name it as CustomMarkerInfoWindow. Replace the whole class with the following code:

class CustomMarkerInfoWindow: UIView {
    
    var txtLabel: UILabel = {
        let v = UILabel()
        v.translatesAutoresizingMaskIntoConstraints = false
        return v
    }()
    
    var subtitleLabel: UILabel = {
        let v = UILabel()
        v.translatesAutoresizingMaskIntoConstraints = false
        return v
    }()
    
    var chevronButton: UIButton = {
        let v = UIButton()
        v.translatesAutoresizingMaskIntoConstraints = false
        return v
    }()
    
    var imgView: UIImageView = {
        let v = UIImageView()
        v.translatesAutoresizingMaskIntoConstraints = false
        return v
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        backgroundColor = primaryColor
        self.addSubview(imgView)
        imgView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
        imgView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
        imgView.topAnchor.constraint(equalTo: topAnchor).isActive = true
        imgView.widthAnchor.constraint(equalTo: imgView.heightAnchor, multiplier: 1).isActive = true
        imgView.heightAnchor.constraint(equalToConstant: 60).isActive = true
        imgView.clipsToBounds = true
        imgView.contentMode = .scaleAspectFill
        imgView.layer.cornerRadius = 5
        imgView.layer.masksToBounds = true
        imgView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMinXMaxYCorner]
        
        self.addSubview(chevronButton)
        chevronButton.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
        chevronButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -8).isActive = true
        chevronButton.widthAnchor.constraint(equalToConstant: 16).isActive = true
        chevronButton.heightAnchor.constraint(equalToConstant: 16).isActive = true
        chevronButton.setImage(UIImage(named: "chevron"), for: .normal)
        chevronButton.tintColor = UIColor.white
        chevronButton.isUserInteractionEnabled = false
        
        self.addSubview(txtLabel)
        txtLabel.topAnchor.constraint(greaterThanOrEqualTo: topAnchor, constant: 4).isActive = true
        txtLabel.leadingAnchor.constraint(equalTo: imgView.trailingAnchor, constant: 8).isActive = true
        txtLabel.trailingAnchor.constraint(equalTo: chevronButton.leadingAnchor, constant: -8).isActive = true
        txtLabel.bottomAnchor.constraint(greaterThanOrEqualTo: centerYAnchor, constant: 2).isActive = true
        txtLabel.font = UIFont.boldSystemFont(ofSize: 16)
        txtLabel.numberOfLines = 2
        txtLabel.textColor = UIColor.white
        txtLabel.text = "dfsdfd"
        
        self.addSubview(subtitleLabel)
        subtitleLabel.topAnchor.constraint(equalTo: txtLabel.bottomAnchor, constant: 0).isActive = true
        subtitleLabel.leadingAnchor.constraint(equalTo: txtLabel.leadingAnchor).isActive = true
        subtitleLabel.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
        subtitleLabel.font = UIFont.systemFont(ofSize: 14, weight: .light)
        subtitleLabel.textColor = UIColor.white
        subtitleLabel.text = "55656556"
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        layer.masksToBounds = true
        layer.cornerRadius = 5
    }   
}

Code looks big but that's a simple auto layout.

Okay, now whenever user taps on the marker we need to change the border color to red and show info window.

First to change the selected marker color to red add the following delegate method to the extension:

func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool {
    guard let customMarkerView = marker.iconView as? CustomMarkerView else { return false }
    let imgName = customMarkerView.imageName
    let customMarker = CustomMarkerView(frame: CGRect(x: 0, y: 0, width: customMarkerWidth, height: customMarkerHeight), imageName: imgName, borderColor: secondaryColor, tag: customMarkerView.tag)
    marker.iconView = customMarker
    return false
}

Then to show the Marker info window add the following delegate method:

func mapView(_ mapView: GMSMapView, markerInfoWindow marker: GMSMarker) -> UIView? {
     if let place = marker.userData as? SSPlace {
         marker.tracksInfoWindowChanges = true
         let infoWindow = CustomMarkerInfoWindow()
         infoWindow.tag = 5555
         let height: CGFloat = 65
         let paddingWith = height + 16 + 32
         infoWindow.frame = CGRect(x: 0, y: 0, width: getEstimatedWidthForMarker(place, padding: paddingWith) + paddingWith, height: height)
         infoWindow.imgView.image = UIImage(named: place.name!)
         infoWindow.txtLabel.text = place.name
         infoWindow.subtitleLabel.text = place.address
         return infoWindow
     }
     return nil
 }
 
 func getEstimatedWidthForMarker(_ place: SSPlace, padding: CGFloat) -> CGFloat {
     var estimatedWidth: CGFloat = 0
     let infoWindow = CustomMarkerInfoWindow()
     let maxWidth = (UIDevice.current.userInterfaceIdiom == .pad ? UIScreen.main.bounds.width * 0.7 : UIScreen.main.bounds.width * 0.8) - padding
     let titleWidth = (place.name ?? "").width(withConstrainedHeight: infoWindow.txtLabel.frame.height, font: infoWindow.txtLabel.font)
     let subtitleWidth = (place.address ?? "").width(withConstrainedHeight: infoWindow.subtitleLabel.frame.height, font: infoWindow.subtitleLabel.font)
     estimatedWidth = min(maxWidth, max(titleWidth, subtitleWidth))
     return estimatedWidth
 }
 
 extension String {
     func width(withConstrainedHeight height: CGFloat, font: UIFont) -> CGFloat {
         let constraintRect = CGSize(width: .greatestFiniteMagnitude, height: height)
         let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: font], context: nil)
         
         return ceil(boundingBox.width)
     }
 }

The getEstimatedWidthForMarker method is to calculate the window size based on the content.

Next after closing the info window you should change marker color is default blue, for that add the following delegate method:

func mapView(_ mapView: GMSMapView, didCloseInfoWindowOf marker: GMSMarker) {
    guard let customMarkerView = marker.iconView as? CustomMarkerView else { return }
    let imgName = customMarkerView.imageName
    let customMarker = CustomMarkerView(frame: CGRect(x: 0, y: 0, width: customMarkerWidth, height: customMarkerHeight), imageName: imgName, borderColor: primaryColor, tag: customMarkerView.tag)
    marker.iconView = customMarker
}

That's it all done, run the code you will see a map with markers then tap any one of the markers you will see the info window opened with marker color changed.

Create custom info window in iOS using swift - Google maps

This tutorial looks long, but you can directly download code or check in the Github.


Read More

20 Sept 2017

iOS 11 Officially Released By Apple. What's new in iOS 11 & Features.

9/20/2017 01:00:00 am 0
For Download Settings -> General -> Software Update -> Download and Install.

iOS 11 is performance optimised for 64-bit apps. 32-bit apps will need
to be updated by the app developer to work with this version of iOS.


iOS 11 Ofiicially Released By Apple. What's new in iOS 11 & Features.


Supported Devices :


iPhone
  • iPhone 7 Plus
  • iPhone 7
  • iPhone 6s
  • iPhone 6s Plus
  • iPhone 6
  • iPhone 6 Plus
  • iPhone SE
  • iPhone 5s
iPad
  • 12.9-inch iPad Pro (first-generation)
  • 12.9-inch iPad Pro (second-generation)
  • 9.7-inch iPad Pro
  • 10.5-inch iPad Pro
  • iPad (fifth-generation)
  • iPad Air 2
  • iPad Air
  • iPad mini 4
  • iPad mini 3
  • iPad mini 2
iPod touch
  • iPod touch (6th generation)


iOS 11 brings hundreds of new features to iPhone and iPad including following :

App Store:

All-new App Store designed for discovering great apps and games every day.

New Games tab to find new games and see what’s most popular with top game charts.

New Today tab helps you discover new apps and games with stories, how-to guides and more.

All-new App Store designed for discovering great apps and games every day.

 

Siri :

New Siri voice is more natural and expressive.

Translate English words and phrases into Chinese, French, German, Italian or Spanish (beta)

Siri suggestions based on your usage of Safari, News, Mail and Messages.

Works with banking apps for account transfer and balances.

Works with apps that display QR codes.

Hindi and Shanghainese dictation.


New Siri voice is more natural and expressive

 

Camera :

Portrait mode now supports optical image stabilisation, HDR and True Tone flash.

Photos and videos will take up half the space with the new HEIF and HEVC image and video formats.

Redesigned set of nine filters optimised for natural skin tones.

Automatically identify and scan QR codes.
 

Photos :

Loop, Bounce and Long Exposure Live Photo effects.

Mute, trim or choose a new key photo for Live Photos.

Memory Movies automatically adapt content for portrait and landscape orientation.

More than a dozen new memory types including pets, babies, weddings and sporting events.

People album is more accurate and stays up to date across devices with iCIoud Photo Library.

Animated GIF support.


Maps :

Indoor maps for major airports and shopping centres.

Lane guidance and speed limit information with Turn-by-turn directions.

One-handed zoom with double tap and swipe.

Interact with Flyover by moving your device.

Do Not Disturb While Driving :

Automatically silences notifications while driving and keeps iPhone.

Optional Message auto-reply to alert selected contacts that you're driving.


New features designed for iPad :

An all-new Dock provides quick access to your favourite and recently used apps and can even be shown on top of active apps.

    Dock resizes so you can add all your favourite apps
    Recently used and Continuity apps are available on the right

Enhanced Slide Over and Split View

    Apps can be easily started in Slide Over and Split View from the Dock
    Slide Over and background apps now run simultaneously
    Apps in Slide Over and Split View can now be placed on the left side of the screen.

Drag and drop

    Move text, images and files between apps on iPad
    MuIti-Touch to move multiple items at the same time
    Spring-loading to move content between apps

Markup

    Markup works across documents, PDFs, web pages, photos and more
    Instantly mark up anything in iOS - just place Apple Pencil on what you want to mark up.

Notes

    Instantly create a new note by tapping the Lock screen with Apple Pencil.
    Inline drawing available by simply placing Apple Pencil in the body of a note.
    Search handwritten text.
    Document scanner auto-corrects for skewing and uses image filters to remove shadows.

Files

    AII-new Files app to browse, search and organise files.
    Works with iCIoud Drive and third-party cloud file providers.
    Recents view for quick access to recently used files across all apps and cloud services.

QuickType :

Flick down on letter keys to enter numbers, symbols and punctuation marks on iPad.

One-handed keyboard support on iPhone.

New keyboards for Armenian, Azerbaijani, Belarusian, Georgian, Irish, Kannada, Malayalam, Maori, Odia, Swahili and Welsh
.

Augmented Reality :

Augmented reality technologies that apps from the App Store can use to deliver content on top of real-world scenes for interactive gaming, immersive shopping experiences, industrial design and more.

Augmented reality technologies that apps from the App Store


sample projects on Augmented reality. 

Machine Learning :

Core machine learning technologies that apps from the App Store can use to deliver intelligent features with machine learning data processed on device for high performance and user privacy.

Core machine learning technologies that apps from the App Store


 sample project on Machine Learning.

Accessibility :

VoiceOver description support for images.

VoiceOver table and list support in PDFs.

Type to Siri support for basic search queries.

Spoken and braille caption support for videos.

Dynamic Type increases text and app UI to larger sizes.

Redesigned Invert Colours makes media content easier to view.


Some features may not be available in all countries or all areas
.
Read More

Post Top Ad