Create your own Slider menu (Drawer) in Swift 3 – Xcode 8 – iOS 10

This article is updated with Swift 3 – Xcode 8 – iOS 10

Why to use a library everytime?

Slider Menu (Drawer)
Slider Menu (Drawer)

Let’s create our own Slide Menu (Drawer) in Swift 3.

1. Create New Project in Xcode 8 with Swift Language

2. Design the Menu in UIViewController

Menu UIViewController
Menu UIViewController

Declaration of Variables and Protocols (Delegate) :

protocol SlideMenuDelegate {
    func slideMenuItemSelectedAtIndex(_ index : Int32)
}

class MenuViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    
    /**
    *  Array to display menu options
    */
    @IBOutlet var tblMenuOptions : UITableView!
    
    /**
    *  Transparent button to hide menu
    */
    @IBOutlet var btnCloseMenuOverlay : UIButton!
    
    /**
    *  Array containing menu options
    */
    var arrayMenuOptions = [Dictionary<String,String>]()
    
    /**
    *  Menu button which was tapped to display the menu
    */
    var btnMenu : UIButton!
    
    /**
    *  Delegate of the MenuVC
    */
    var delegate : SlideMenuDelegate?
}

Following method is for updating the Items in the Menu :

    func updateArrayMenuOptions(){
        arrayMenuOptions.append(["title":"Home", "icon":"HomeIcon"])
        arrayMenuOptions.append(["title":"Play", "icon":"PlayIcon"])
        
        tblMenuOptions.reloadData()
    }

Following method is for click event and animation :

@IBAction func onCloseMenuClick(_ button:UIButton!){
    btnMenu.tag = 0
    
    if (self.delegate != nil) {
        var index = Int32(button.tag)
        if(button == self.btnCloseMenuOverlay){
            index = -1
        }
        delegate?.slideMenuItemSelectedAtIndex(index)
    }
    
    UIView.animate(withDuration: 0.3, animations: { () -> Void in
        self.view.frame = CGRect(x: -UIScreen.main.bounds.size.width, y: 0, width: UIScreen.main.bounds.size.width,height: UIScreen.main.bounds.size.height)
        self.view.layoutIfNeeded()
        self.view.backgroundColor = UIColor.clear
        }, completion: { (finished) -> Void in
            self.view.removeFromSuperview()
            self.removeFromParentViewController()
    })
}


3. Now we will create a Base UIViewController to use anywhere in the project which control the delegate of menu.

First we will create this 3 lines Drawer Icon via Code

Preview_Slider_Drawer_ICon

func addSlideMenuButton(){
    let btnShowMenu = UIButton(type: UIButtonType.system)
    btnShowMenu.setImage(self.defaultMenuImage(), for: UIControlState())
    btnShowMenu.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
    btnShowMenu.addTarget(self, action: #selector(BaseViewController.onSlideMenuButtonPressed(_:)), for: UIControlEvents.touchUpInside)
    let customBarItem = UIBarButtonItem(customView: btnShowMenu)
    self.navigationItem.leftBarButtonItem = customBarItem;
}

func defaultMenuImage() -> UIImage {
    var defaultMenuImage = UIImage()
    
    UIGraphicsBeginImageContextWithOptions(CGSize(width: 30, height: 22), false, 0.0)
    
    UIColor.black.setFill()
    UIBezierPath(rect: CGRect(x: 0, y: 3, width: 30, height: 1)).fill()
    UIBezierPath(rect: CGRect(x: 0, y: 10, width: 30, height: 1)).fill()
    UIBezierPath(rect: CGRect(x: 0, y: 17, width: 30, height: 1)).fill()
    
    UIColor.white.setFill()
    UIBezierPath(rect: CGRect(x: 0, y: 4, width: 30, height: 1)).fill()
    UIBezierPath(rect: CGRect(x: 0, y: 11,  width: 30, height: 1)).fill()
    UIBezierPath(rect: CGRect(x: 0, y: 18, width: 30, height: 1)).fill()
    
    defaultMenuImage = UIGraphicsGetImageFromCurrentImageContext()!
    
    UIGraphicsEndImageContext()
   
    return defaultMenuImage;
}

Delegate (Protocol) method call :

func slideMenuItemSelectedAtIndex(_ index: Int32) {
    let topViewController : UIViewController = self.navigationController!.topViewController!
    print("View Controller is : \(topViewController) \n", terminator: "")
    switch(index){
    case 0:
        print("Home\n", terminator: "")

        self.openViewControllerBasedOnIdentifier("Home")
        
        break
    case 1:
        print("Play\n", terminator: "")
        
        self.openViewControllerBasedOnIdentifier("PlayVC")
        
        break
    default:
        print("default\n", terminator: "")
    }
}

To open a view controller by identifier :

Set the Restoration Identifier and Storyboard Identifier. If current view is open then we will not open it once again for that we have to check via Restoration Identifier.

func openViewControllerBasedOnIdentifier(_ strIdentifier:String){
    let destViewController : UIViewController = self.storyboard!.instantiateViewController(withIdentifier: strIdentifier)
    
    let topViewController : UIViewController = self.navigationController!.topViewController!
    
    if (topViewController.restorationIdentifier! == destViewController.restorationIdentifier!){
        print("Same VC")
    } else {
        self.navigationController!.pushViewController(destViewController, animated: true)
    }
}

4. Now We will assign this drawer to any of the UIViewController

We have to use only one method to add drawer (slide menu) self.addSlideMenuButton()

import UIKit

class HomeVC: BaseViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        addSlideMenuButton()
    }
}


Source Code is available at the Github AKSwiftSlideMenu

GitHub AKSwiftSlideMenu Releases
GitHub AKSwiftSlideMenu Releases

You can download for both versions Swift 2 or Swift 3. From Releases Tab at GitHub AKSwiftSlideMenu .

Check other blog posts about Swift

Happy Coding 🙂

Google Place Autocomplete View With Swift Language through Alamofire networking library

Google Place Autocomplete
Google Place Autocomplete

Add the Alamofire CocoaPods in your swift project.

alamofire
alamofire

Find your Google place API key on Google APIs Console.

GOOGLE PLACE API KEY
GOOGLE PLACE API KEY

ViewConroller.swift

import UIKit

class ViewController: UIViewController {
  override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)

    let gpaViewController = GooglePlacesAutocomplete(
      apiKey: "YOUR API KEY",
      placeType: .Address
    )

    gpaViewController.placeDelegate = self

    presentViewController(gpaViewController, animated: true, completion: nil)
  }
}

extension ViewController: GooglePlacesAutocompleteDelegate {
  func placeSelected(place: Place) {
    println(place.description)
  }

  func placeViewClosed() {
    dismissViewControllerAnimated(true, completion: nil)
  }
}

GooglePlacesAutocomplete.xib

GooglePlacesAutocomplete.xib
GooglePlacesAutocomplete.xib

GooglePlacesAutocomplete.swift

import UIKit
import Alamofire

enum PlaceType: Printable {
  case All
  case Geocode
  case Address
  case Establishment
  case Regions
  case Cities

  var description : String {
    switch self {
    case .All: return ""
    case .Geocode: return "geocode"
    case .Address: return "address"
    case .Establishment: return "establishment"
    case .Regions: return "regions"
    case .Cities: return "cities"
    }
  }
}

struct Place {
  let id: String
  let description: String
}

protocol GooglePlacesAutocompleteDelegate {
  func placeSelected(place: Place)
  func placeViewClosed()
}

// MARK: - GooglePlacesAutocomplete
class GooglePlacesAutocomplete: UINavigationController {
  var gpaViewController: GooglePlacesAutocompleteContainer?

  var placeDelegate: GooglePlacesAutocompleteDelegate? {
    get { return gpaViewController?.delegate }
    set { gpaViewController?.delegate = newValue }
  }

  convenience init(apiKey: String, placeType: PlaceType = .All) {
    let gpaViewController = GooglePlacesAutocompleteContainer(
      apiKey: apiKey,
      placeType: placeType
    )

    self.init(rootViewController: gpaViewController)
    self.gpaViewController = gpaViewController

    let closeButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Stop, target: self, action: "close")

    gpaViewController.navigationItem.leftBarButtonItem = closeButton
    gpaViewController.navigationItem.title = "Enter Address"
  }

  func close() {
    placeDelegate?.placeViewClosed()
  }
}

// MARK: - GooglePlaceSearchDisplayController
class GooglePlaceSearchDisplayController: UISearchDisplayController {
  override func setActive(visible: Bool, animated: Bool) {
    if active == visible { return }

    searchContentsController.navigationController?.navigationBarHidden = true
    super.setActive(visible, animated: animated)

    searchContentsController.navigationController?.navigationBarHidden = false

    if visible {
      searchBar.becomeFirstResponder()
    } else {
      searchBar.resignFirstResponder()
    }
  }
}

// MARK: - GooglePlacesAutocompleteContainer
class GooglePlacesAutocompleteContainer: UIViewController {
  var delegate: GooglePlacesAutocompleteDelegate?
  var apiKey: String?
  var places = [Place]()
  var placeType: PlaceType = .All

  convenience init(apiKey: String, placeType: PlaceType = .All) {
    self.init(nibName: "GooglePlacesAutocomplete", bundle: nil)
    self.apiKey = apiKey
    self.placeType = placeType
  }

  override func viewDidLoad() {
    super.viewDidLoad()

    let tv: UITableView? = searchDisplayController?.searchResultsTableView
    tv?.registerClass(UITableViewCell.self, forCellReuseIdentifier: "Cell")
  }
}

// MARK: - GooglePlacesAutocompleteContainer (UITableViewDataSource / UITableViewDelegate)
extension GooglePlacesAutocompleteContainer: UITableViewDataSource, UITableViewDelegate {
  func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return places.count
  }

  func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = self.searchDisplayController?.searchResultsTableView?.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell

    // Get the corresponding candy from our candies array
    let place = self.places[indexPath.row]

    // Configure the cell
    cell.textLabel.text = place.description
    cell.accessoryType = UITableViewCellAccessoryType.DisclosureIndicator

    return cell
  }

  func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    delegate?.placeSelected(self.places[indexPath.row])
  }
}

// MARK: - GooglePlacesAutocompleteContainer (UISearchDisplayDelegate)
extension GooglePlacesAutocompleteContainer: UISearchDisplayDelegate {
  func searchDisplayController(controller: UISearchDisplayController, shouldReloadTableForSearchString searchString: String!) -> Bool {
    getPlaces(searchString)
    return false
  }

  private func getPlaces(searchString: String) {
    Alamofire.request(.GET,
      "https://maps.googleapis.com/maps/api/place/autocomplete/json",
      parameters: [
        "input": searchString,
        "type": "(\(placeType.description))",
        "key": apiKey ?? ""
      ]).responseJSON { request, response, json, error in
        if let response = json as? NSDictionary {
          if let predictions = response["predictions"] as? Array<AnyObject> {
            self.places = predictions.map { (prediction: AnyObject) -> Place in
              return Place(
                id: prediction["id"] as String,
                description: prediction["description"] as String
              )
            }
          }
        }

        self.searchDisplayController?.searchResultsTableView?.reloadData()
    }
  }
}

I have learned this thing from Howard Wilson’s(watsonbox) Github Repository : watsonbox/ios_google_places_autocomplete

Happy coding 🙂

Requesting Access to the Address Book – Swift Language – iOS 8

Request to Access Address Book
Request to Access Address Book

Here is simple steps to requesting access to the Address Book in Swift Language iOS 8

Import the framework of Address Book:

      import AddressBook

Create an object of Address Book:

      var addressBook: ABAddressBookRef?

Create a method for assigning the value to addressBook:

      func createAddressBook(){
          var error: Unmanaged<CFError>?
          addressBook = ABAddressBookCreateWithOptions(nil, &error).takeRetainedValue()
      }

Post following code to requesting access:

      switch ABAddressBookGetAuthorizationStatus(){
      case .Authorized:
        println("Already authorized")
        createAddressBook()
        /* Access the address book */
      case .Denied:
        println("Denied access to address book")

      case .NotDetermined:
        createAddressBook()
        if let theBook: ABAddressBookRef = addressBook{
          ABAddressBookRequestAccessWithCompletion(theBook,
            {(granted: Bool, error: CFError!) in

              if granted{
                println("Access granted")
              } else {
                println("Access not granted")
              }

            })
        }

      case .Restricted:
        println("Access restricted")

      default:
        println("Other Problem")
      }

Happy Coding 🙂

iOS 8 Map Kit Obj-C : Get Users Location

Map View | User Location
Map View | User Location

iOS 8 Map Kit Obj-C : Get Users Location

In your .plist Add a new row with the key name:

NSLocationWhenInUseUsageDescription

Or

NSLocationAlwaysUsageDescription

Define the header:

#define IS_OS_8_OR_LATER ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)

Update your files with following code:

ViewController.h

#import <MapKit/MapKit.h>
#import <MapKit/MKAnnotation.h>

@interface YourViewController : UIViewController <MKMapViewDelegate,  CLLocationManagerDelegate> {

}

@property(nonatomic, retain) IBOutlet MKMapView *mapView;
@property(nonatomic, retain) CLLocationManager *locationManager;

ViewController.m

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.


    mapView.delegate = self;
    self.locationManager = [[CLLocationManager alloc] init];
    self.locationManager.delegate = self;
    #ifdef __IPHONE_8_0
    if(IS_OS_8_OR_LATER) {
         // Use one or the other, not both. Depending on what you put in info.plist
        [self.locationManager requestWhenInUseAuthorization];
        [self.locationManager requestAlwaysAuthorization];
    }
    #endif
    [self.locationManager startUpdatingLocation];

    mapView.showsUserLocation = YES;
    [mapView setMapType:MKMapTypeStandard];
    [mapView setZoomEnabled:YES];
    [mapView setScrollEnabled:YES];
}

-(void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:YES];

    self.locationManager.distanceFilter = kCLDistanceFilterNone;
    self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
    [self.locationManager startUpdatingLocation];
    NSLog(@"%@", [self deviceLocation]);

    //View Area
    MKCoordinateRegion region = { { 0.0, 0.0 }, { 0.0, 0.0 } };
    region.center.latitude = self.locationManager.location.coordinate.latitude;
    region.center.longitude = self.locationManager.location.coordinate.longitude;
    region.span.longitudeDelta = 0.005f;
    region.span.longitudeDelta = 0.005f;
    [mapView setRegion:region animated:YES];

}

- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
    MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(userLocation.coordinate, 800, 800);
    [self.mapView setRegion:[self.mapView regionThatFits:region] animated:YES];
}

- (NSString *)deviceLocation {
    return [NSString stringWithFormat:@"latitude: %f longitude: %f", self.locationManager.location.coordinate.latitude, self.locationManager.location.coordinate.longitude];
}

Helping, Learning, Coding 🙂

How to add an Objective-C file in your Swift Project? or How to set Objective-C bridging header?

Bridging header
Bridging header

To import a set of Objective-C files in the same app target as your Swift code, you rely on an Objective-C bridging header to expose those files to Swift. Xcode offers to create this header file when you add an Objective-C file to an existing Swift app.

If you accept, Xcode creates the header file along with the file you were creating, and names it by your product module name followed by adding “-Bridging-Header.h”.

Alternatively, you can create a bridging header yourself by choosing File > New > File > (iOS or OS X) > Source > Header File.

You’ll need to edit the bridging header file to expose your Objective-C code to your Swift code.

To import Objective-C code into Swift from the same target

  1. In your Objective-C bridging header file, import every Objective-C header you want to expose to Swift.

    For example:

        #import "XYZCustomCell.h"
        #import "XYZCustomView.h"
        #import "XYZCustomViewController.h"
    
  2. Under Build Settings, make sure the Objective-C Bridging Header build setting under Swift Compiler – Code Generation has a path to the header.

    The path should be relative to your project, similar to the way your Info.plist path is specified in Build Settings. In most cases, you should not need to modify this setting.

Any public Objective-C headers listed in this bridging header file will be visible to Swift. The Objective-C functionality will be available in any Swift file within that target automatically, without any import statements. Use your custom Objective-C code with the same Swift syntax you use with system classes.

For Example:

    let myCell = XYZCustomCell()
    myCell.subtitle = "A custom cell"

Helping, Learning, Coding 🙂

Source : Apple Documents