• Skip to main content
  • Skip to primary sidebar

Ravi Shankar

Tweaking Apps

  • Swift
  • Tech Tips

PDFKit – View, Annotate PDF file in Swift

April 10, 2020 By Ravi Shankar 1 Comment

In this demo, we will be using PDFKit to View and Annotate PDF file. Let us start by selecting a Single View App template and name the project as PDFDemo.

For this demo, we will be using a PDF file “TheWakingLights.pdf” and this can downloaded from the github project folder. Now drag and drop this file to Xcode project and in “Choose options for adding these files” select “Copy items if needed”

Displaying PDF

In Xcode’s project navigator, select ViewController.swift and add the following lines after viewDidLoad function.

  
            func openPDFDocument() {   
                guard let path = Bundle.main.url(forResource: "TheWakingLights",                        withExtension: "pdf")
                else { 
                 return     
                }      
                let document = PDFDocument(url: path)   
                let pdfView = PDFView(frame: view.frame)     
                pdfView.document = document       
                view.addSubview(pdfView)  

This function does the following

   guard let path = Bundle.main.url(forResource: "TheWakingLights",
                                         withExtension: "pdf") else { return }

Creates URL instance with the path of “TheWalkingLights.pdf” file.

Loads the pdf file in a PDFDocument object

 let document = PDFDocument(url: path)

Creates an instance of PDFView and sets the frame to ViewController’s view then assigns the PDFDocument to PDFView instance.

        let pdfView = PDFView(frame: view.frame)
        pdfView.document = document
        view.addSubview(pdfView)

You will be seeing couple of unresolved identified errors because of missing import PDFKit statement. Add import statement after import UIKit and now you should be able to run the app on the simulator and select text on the PDF

select text

Adding Annotation to PDF

PDFKit provides option to add annotation to PDF file using PDFAnnoation class. Various annotation can be added like Circle, line, Strikeout, Square, Underline, Highlight etc.. Let us see a code example of adding highlight to PDF page.

Add the following code snippet after openPDFDocument function.

  func highlightAnnotation() -> PDFAnnotation {
        let annotation = PDFAnnotation(bounds: CGRect(x: 30, y: 80, width: 230, height: 50),
                                       forType: .highlight, withProperties: nil)
        annotation.color = .yellow
        return annotation
    }

The above code does the following

  1. Creates a PDFAnnotation instance by specifying coordinates for adding the annotation and setting the type of annotation to highlight.
  2.  Then the annotation colour is set to yellow.

In the openDocument function add the following line after view.addSubView(pdfview)

   pdfView.currentPage?.addAnnotation(highlightAnnotation())

This gets the current page from PDFView and sets the highlight annotation. When you run the app on the simulation you will see the highlight as shown below.

final

Download the source code from here

 

Filed Under: ios, Swift Tagged With: Annotation, PDFDocument, PDFKit, Swift

Tab Bar Controller with WebView

April 9, 2020 By Ravi Shankar

In this article, we will see step by step instruction on working of Tab Bar Controller, UIWebView and Activity Indicator by loading couple of web pages using UIWebView in two different Tabs.

Tab Bar Controller

Tab Bar Controller is used for organising list of View Controllers by having seperate tabs for each View Controller. Check out the Apple Documentation for more information.

UIWebView

When you want to allow users to browse website then you can use UIWebView to load those pages. These WebViews needs to be placed inside a container such as UIViewController. Check out Apple Documentation for more information on UIWebView.

Project Setup

Create a new project by selecting Single View Application as project template. You can also choose Tabbed Application but in this demo we will see how to embed a ViewController inside a Tab Bar Controller.

Enter the project details and select the language as Swift.

Embed View Controller inside Tab Bar Controller

Navigate to Main.storyboard in Project Navigator and select View Controller. Now to embed this View Controller inside a Tab bar Controller, click Editor -> Embed In -> Tab Bar Controller

The Interface builder should now display View Controller and Tab Bar Controller as shown below.

Add Second View Controller

Now let us see how to add another tab by setting relationship between second View Controller and Tab Bar Controller. Drag and drop UIViewController from Object library to Interface builder.

Control + Drag from Tab Bar Controller to UIViewController and select view controllers under Relationship Segue. This should add second tab bar item pointing the second View Controller.

Add UIWebView and Activity Indicator View

We are not using Auto Layout or Size Classes for this demo, so disable these features using the option available under File Inspector.

Drag and drop UIWebView from object libary to both the View Controllers. Make sure it is aligned centrally to View Controller and covers the entire View Controller area. Similarly add Activity Indicator View from object libary to top of UIWebView and align to the Centre both vertically and horizontally.

Edit Tab Bar Items

The Tab Bar Controller display two tab bar items which are linked to each View Controller. Add required images for each Tab bar items to the Images.xcassets (Download the images from GitHub project).
To provide a name and image for Tab Bar items, select the item in the corresponding View Controller and enter the details in Attributes Inspector. For the first View Controller, the name and image are set as shown below.

repeat the same for the second View Controller as well.

Link class with UIViewController

The project comes with a default ViewController.swift file which is mapped with the first View Controller. Let us give a proper name to this controller. Unfortnately Xcode Refactor option still does not work for Swift (Xcode version 6.4). Manually rename the file to WorldViewController.swift and also make the corresponding name change in Class.

class WorldController: UIViewController {

Add a second view controller class by right clicking the project, select New File option.

In the choose template screen, select Cocoa Touch Class and provide name as SportsViewController making it a subclass of UIViewController.

Create IBOutelts for UIWebView and Activity Indicator View

Using Assistant Editor, navigate to Main.storyboard in the second window. In the Identity Inspector, set the class name for both ViewControllers to WorldViewController and SportsViewController.

Drag and drop UIWebView from WorldViewController (Interface builder) to WorldViewController.swift to create IBOutlet. Do the same for Activity Indicator View as well.

@IBOutlet weak var webView: UIWebView!

@IBOutlet weak var activityIndicator: UIActivityIndicatorView!

Repeat the same exercise for SportsViewController and create corresponding IBOutlets.

Code Implementation

Navigate to WorldViewController.swift file and add the following code snippet in ViewDidLoad function.

let url = NSURL(string: "http://edition.cnn.com/world")
let urlRequest = NSURLRequest(URL: url!)
webView.loadRequest(urlRequest)

activityIndicator.startAnimating()
activityIndicator.hidesWhenStopped = true

The first three lines of the code create a NSURLRequest and the object is passed to UIWebView loadRequest function. The last two lines starts the Activity Indicator View and sets the property to hide the indicator when it is stopped animating.

We need to tell the Activity Indicator when the page has finished loading. This can be done by implementing UIWebViewDelegate’s webViewDidFinishLoad function.

Add the following piece of code to make the View Controller as a delegate for webView.

class WorldController: UIViewController, UIWebViewDelegate {

and in the viewDidLoad function add webView.delegate = self. This would ensure that the View Contorller can handle any WebView delegate related calls.

Now implement the webViewDidFinishLoad function and stop the animation of Activity Indicator View when the webview has finished loading the page.

//MARK:- WebView Delegate method

    func webViewDidFinishLoad(webView: UIWebView) {
        activityIndicator.stopAnimating()
    }

Repeat the above code implementation for SportsViewController excepy by chaning the URL string.

class SportsViewController: UIViewController, UIWebViewDelegate {
    @IBOutlet var webView: UIWebView!
    @IBOutlet var activityIndicator: UIActivityIndicatorView!

    override func viewDidLoad() {
        super.viewDidLoad()

        let url = NSURL(string: "http://edition.cnn.com/sport")
        let urlRequest = NSURLRequest(URL: url!)
        webView.loadRequest(urlRequest)

        activityIndicator.startAnimating()
        activityIndicator.hidesWhenStopped = true

        webView.delegate = self
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    //MARK:- UIWebView Delegate methods
    func webViewDidFinishLoad(webView: UIWebView) {
        activityIndicator.stopAnimating()
    }

}

When you finally build and run the project, you should see the follwing on iOS simulator.

Download the source code from here

Filed Under: Apple, ios, Programming, Xcode

CoreData tutorial in Swift 5 using NSFetchedResultsController

December 25, 2019 By Ravi Shankar 38 Comments

Let us see an example TaskManager app using CoreData written in Swift Programming language. This app provides the following functionality

  • Allow users to enter new task
  • Update existing task
  • delete task

TaskManager using CoreData201407121030.jpg

You can download source code for this project from Github. If you are familiar with user interface then move on to the Core Data implementation in Swift section

Create a new File -> New -> Project and select template Single View Application

201407121032.jpg

Enter Product Name, Language as Swift and select “Use Core Data” for the new Project.

201407121037.jpg

User Interface Implementation

 

Add new TableViewController for managing tasks

 

Delete ViewController.swift and Add new view controller which will be used for displaying the list of tasks.

201407121059.jpg

Right click on the Project and select New File

201407121101.jpg

Choose the template as Cocoa Touch under iOS -> Source

201407121103.jpg

Enter name of the file as TaskManagerViewController with Subclass as UITableViewController and Language as Swift.

201407121104.jpg

Add new UITableViewController to the Storyboard

 

Navigate to Main.storyboard, delete the ViewController and add new TableViewController to the Storyboard

201407121108.jpg

Embed this TableViewController inside a navigation controller. You can do this by clicking Editor menu -> Embed In -> Navigation Controller.

201407121110.jpg

Navigate to Storyboard file and select Table View Controller. Click Identity Inspector and set the Class to the TaskManagerViewController.

Add button to the navigation bar

 

Users can navigate to new task screen by tapping the button on the TaskManager. Drag and drop the button bar item to the navigation bar.

201407121116.jpg

In the attributes inspector, set the identifier for button bar item as Add. Also enter title as “Task Manager” in the navigation bar.

Add View Controller for entering task detail

 

Now to enter task detail, let us add new View Controller. From the Objects library, drag and drop View Controller on to storyboard. Then drag and drop a navigation item to this View Controller. Add a Done bar button item for saving the changes, Cancel bar button item for dismissing the screen. Also provide a title for the screen as Task Detail.

201407121138.jpg

Then add a textfield to the View Controller to enter details about the task.

201407121540.jpg

Now to connect the Task Manager screen to Task Detail screen, select the Add button in Task Manager screen, hold the control button and make a connection to the Task Detail screen. Select type of Action Segue as Show.

201407121125.jpg201407121127.jpg

Select each View Controller and Click on the Resolve Auto Layout Issues option and pick Reset to Suggested Constraints. This would ensure that the controls alignment and display remains same in different screen sizes.

 

201407121557.jpg

Add View Controller Class

Right click on the Project, select New File from menu list.

201407130919.jpg

Select Cocoa Touch Class as template.

201407130920.jpg

Enter the Class name as TaskDetailViewController, Subclass of UIViewController and Language as Swift.

201407130921.jpg

Navigate to Storyboard file and select Task Detail View Controller. Click Identity Inspector and set the Class to the TaskDetailViiewController.

201407141329.jpg

Now let us add the function required for dismissing the View Controller. This gets called when user taps done and cancel buttons. Navigate to TaskDetailViewController.swift file and add the following functions.

    
   @IBAction func done(sender: AnyObject) {
        if task != nil {
            editTask()
        } else {
            createTask()
        }
        dismissViewController()
    }
    
    @IBAction func cancel(sender: AnyObject) {
         dismissViewController()
    }
    
    // MARK:- Dismiss ViewControllers
    
    func dismissViewController() {
        navigationController?.popViewController(animated: true)
    }

 

Connect the IBActions with the corresponding done and cancel buttons

201407130930.jpg

 

Try to compile and run the app on the Simulator. You should see the following Task Manager screen and tapping + sign should display the Task Detail screen with TextField, Cancel and Done button.

201407130933.jpg201407130935.jpg

 

When you tap the Cancel or Done button should take you to the Task Manager screen.

Create IBOutlet element for UITextField Element

Add the following piece of code after the Class TaskDetailViewController: UIViewController

@IBOutlet var txtDesc: UITextField!

Now use the connection inspector to connect the IBOutlet variable to the textfield on the User Interface.

Core Data Implementation

 

Click Show Project Navigator and select CoreData model file (ending with extension .xcdatamodelid)

201407121042.jpg

Click Add Entity option available at the bottom of Xcode Editor window.

201407121045.jpg

Then enter the name of the entity as Tasks.

201407121047.jpg

Click Add Attribute then enter name as desc and choose the type as String. This attribute will be used for storing the task detail information.

201407121049.jpg

Now to generate the CoreData mapping class, click Editor and select Create NSManagedObject Subclass.

201407121051.jpg

Select the data models and click Next

201407121052.jpg

Then select entities and in this example it is Tasks

201407121053.jpg

Make sure to select Language for NSManagedObject class as Swift. Click Create to the create new class.

201407121055.jpg

Click Yes to to configure an Objective-C birding header for the Swift class.

201407121056.jpg

This should create a new class called Tasks.swfit with variable for corresponding attribute defined in Entities

Write code to save new task details

Add the following line at the top of TaskDetailViewController class

  let managedObjectContext = (UIApplication.shared.delegate as! AppDelegate).managedObjectContext

The above line defines a variable to store the ManagedObjectContext. In order to save the task information entered in the UITextField, add the following code to TaskDetailViewController.swift. Then call this function in the @IBAction done.

   func createTask() {
        let entityDescripition = NSEntityDescription.entity(forEntityName: "Tasks", in: managedObjectContext)
        let task = Tasks(entity: entityDescripition!, insertInto: managedObjectContext)
        task.desc = txtDesc.text!
        do {
            try managedObjectContext.save()
        } catch _ {
        }
    }
@IBAction func done(sender: AnyObject) {
	createTask()
	dismissViewController()
}

 

The createTask function uses the Tasks Entity class and ManagedObjectContext class to save the information entered in the text field in to SQLite using CoreData.

Now if you try to run this app in Xcode simulator, you should be able to enter the details in textfield and save the task. But there is no way to see the saved tasks.

Write code to retrieve information from SQLite using NSFetchedResultsController

We will be using NSFetchedResultsController to retrieve and manage information returned from Core Data. Write the following code after the class declaration of TaskManagerViewController class

 let managedObjectContext = (UIApplication.shared.delegate as! AppDelegate).managedObjectContext
    
var fetchedResultController: NSFetchedResultsController = NSFetchedResultsController<NSFetchRequestResult>()
    

201407141923.jpg

 

You might see the above error message “Use of undeclared type ’NSFetchedResultsContoller”. This can be fixed by importing CoreData module and making your class a delegate of NSFetchedResultsControllerDelegate.

import CoreData

class TaskManagerViewController: UITableViewController, NSFetchedResultsControllerDelegate {

Then add the following function to populate fetchedResultController.

  
    func getFetchedResultController() -> NSFetchedResultsController<NSFetchRequestResult> {
        fetchedResultController = NSFetchedResultsController(fetchRequest: taskFetchRequest(),  managedObjectContext: managedObjectContext, sectionNameKeyPath: nil, cacheName: nil)
        return fetchedResultController
    }
      

 

Update the ViewDidLoad method of TableViewController to populate fetchedResultController variable and set the delegate to self and call the function to retrieve the results.

override func viewDidLoad() {
        super.viewDidLoad()

       fetchedResultController = getFetchedResultController()
        fetchedResultController.delegate = self
        do {
            try fetchedResultController.performFetch()
        } catch _ {
        }
    }

Implement the TableView related functions to display the data

Navigate to Main.storyoard then to TaskManagerViewController and set Identifier for Prototype Cells as “Cell”

201407142004.jpg

 

Add the following functions required for population of rows in tableView and to refresh the tableView when content is changed.

 

And for refreshing the tableview content add the following piece of code snippet.

  func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {          tableView.reloadData()
    }

Swift uses namespaces for classes, so the downcast of NSManagedObject class to Tasks class will work only when you append the module name with the class for Entity

  let task = fetchedResultController.object(at: indexPath as IndexPath) as! Tasks

Navigate to TaskManager.xcdatamodeld, select Tasks under Entities and using the Data Model Inspector append Module name i.e TaskManager with the class.

201407142038.jpg

 

 

Now you should be able to see the data added via TaskDetailScreen in the TaskManager Screen.

201407142059.jpg

Implementation of deleting rows from tableView and CoreData entity

 

Copy and paste the following code to provide the users with the option to delete the rows.

 override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        let managedObject:NSManagedObject = fetchedResultController.object(at: indexPath as IndexPath) as! NSManagedObject
        managedObjectContext.delete(managedObject)
        do {
            try managedObjectContext.save()
        } catch _ {
        }
    }

201407142116.jpg

Implementation of editing task details

Now let us see how to edit a task in the TaskManager screen. We will re-use the same TaskDetail screen for editing the task information. First let create segue from UITableViewCell to TaskDetailViewController. You can do this by holding the Control key and drag and drop the connection to TaskDetailViewController.

201407142127.jpg

 

Make sure to set the Segue identifier as edit using Attributes Inspector

201407142128.jpg

 

Add the following piece of code in TaskManagerViewController to populate the task detail for the edited row.

      override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "edit" {
            let cell = sender as! UITableViewCell
            let indexPath = tableView.indexPath(for: cell)
            let taskController:TaskDetailViewController = segue.destination as! TaskDetailViewController
            let task:Tasks = fetchedResultController.object(at: indexPath!) as! Tasks
            taskController.task = task
        }
    }
    

 

Navigate to TaskDetailViewController and variable which used to pass the task details across the ViewControllers

var task: Task? = nil

In viewDidLoad fund, populate the textField with the task details for edit operation i.e task is not nil

 override func viewDidLoad() {
        super.viewDidLoad()
     if task != nil {
            txtDesc.text = task?.desc
        }
    }

 

Then add a new function to save the edited task. Also modify the done function to handle create and edit task functionality

func editTask() {
       task?.desc = txtDesc.text!
        do {
            try managedObjectContext.save()
        } catch _ {
        }
    }
@IBAction func done(sender: AnyObject) {
        if task != nil {
            editTask()
        } else {
            createTask()
        }
        dismissViewController()
    }

 

Now when you run the app in Xcode simulator, you should be able to edit the task.

The final piece of code left is to add background colour to the navigation bar for both the controllers. Click AppDelegate.swift file and add the following function. Then call this function from the didFinishLaunchingWithOptions.

    func setupAppearance() {
        let navigationBarAppearance = UINavigationBar.appearance()
        navigationBarAppearance.barTintColor = UIColor(red: 51.0/255.0, green: 104.0/255.0, blue: 121.0/255.0, alpha: 1.0)
        navigationBarAppearance.tintColor = UIColor.white
        navigationBarAppearance.titleTextAttributes = [NSAttributedString.Key.foregroundColor:UIColor.white]
    }

 

Running the app on the iOS simulator should change the appearance for the navigation bar.

201407142233.jpg

 

Hope you found this beginner tutorial useful. Please use the comments section to share your thoughts and suggestion

Download the source code for this tutorial from GitHub

Filed Under: Develop, ios, iPhone, Programming, Swift, Xcode Tagged With: CoreData, delegation, NSFetchedResultContoller, Swift, Tutorials, UITableView, User Interface, Xcode

Storyboard, Scene and Segue

November 22, 2019 By Ravi Shankar Leave a Comment

Storyboard in iOS is helps you to design the user interface of your App. A storyboard can contain one or more Scenes (View Controllers) and the connection or relationship between two scenes are known as Segue. This is how a typical storyboard with Scenes and Segue look.

The above example storyboard contains two scenes, first and second. These scenes are connected to each other by a Show Segue. Now let us see more about the Storyboard and Segues

Initial ViewController

App needs to know the ViewControler that will be displayed as the initial screen. You can set any controller as the initial scene using the option available as part of the Attributes Inspector

 

The storyboard will display an arrow in front of the Initial View Controller as shown in below screenshot. You can also drag and drop this arrow to the change the Initial View Controller.

Types of Segues

A segue is a relationship set between two view controllers. This can be done by pressing Control then drag and drop from one View Controller to anlther. Listed below are the different types of segues

  • Show 
  • Show Detail
  • Present Modally
  • PopOver Presentation
  • Custom

Check out this excellent Stackoverflow answer on types of Segues

Unwind Segues

Only the Show segue has a back button in the navigation bar of the child controller which takes you back to the Parent controller. For rest of the segues, we can use unwind segues to dismiss the child controller. Unwind segue can be created by first adding a IBAction with parameter type as UIStoryboardSegue to the parent Vuew Controller.

   //MARK:- Unwind Segue
    @IBAction func cancel(segue:UIStoryboardSegue {
        // do nothing
    }

Add a button to the child view controller press Control then drag and drop the button to the Exit button available at the top of the ViewController. Now select the Action Segue which was added to the parent view controller.

Transfering data between ViewControllers

One of the common question that gets asked is “how to transfer data between two view controllers“. Create a property in the child view controller and set the value for this property in the prepareForSegue function of the parent view controller.

//MARK:- ViewController Transition

     override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if  segue.identifier  ==   "detail"   {
            let  childViewController  =  segue.destination  as! SecondViewController
            childViewController.studentName  =  nameTextField.text
        }
    }

In the above prepareForSegue function, we check whether segue identifier is equal to detail. The identifier name can be provided under the Storyboard Segue in Attributes Inspector. Then we set the value for studentName property of the SecondViewController. By this way, you can transfer data between the View Controllers,

In the SecondViewController, you should be able access this property value and perform the required action. The below viewDidLoad code snippet access the property value and displays it in a label.

class SecondViewController: UIViewController {

    var studentName:String?
    @IBOutlet var displayName:UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        if let name = studentName {
            displayName.text = name
        }
    }
}

Non Segue Transition

Another alternate way to transition between the View Controllers is to use instantiateViewController withIdentifier function of storyboard

//MARK:- Non Segue
  @IBAction func nonSegueCall(sender: AnyObject) {
        let childViewContoller  =
            storyboard?.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController
        childViewContoller . studentName  =  nameTextField.text
        present( childViewContoller ,  animated: true ,  completion: nil)
    }

The above code snippet provides the same behaviour as prepareForSegue function. This function can be called on tap of a button and this requires an identifier to specified for the View Controller.

Download sample code from here.

Filed Under: ios, Xcode Tagged With: prepareForSegue, Scene, Segue, Storyboard

Get your current address in Swift (Swift 5)

November 7, 2019 By Ravi Shankar 22 Comments

In this Swift tutorial, we will see the steps required for using CoreLocation framework and retrieve the latitude and longitude of the location. Then use the CLGeocoder to reverse geocode the latitude and longitude details. This is a very basic tutorial that retrieves the location details on tap of a button and displays the information on the screen.

Updated :- The source has now been to updated to show the address details in TextFields.

Click File -> New and select Project sub menu list.

201407251656.jpg

In the Choose a template screen, select Single View Application and click Next button.

201407251657.jpg

Enter the product name as WhereAmI and select the language as Swift then click Next. Select a folder and create the new project and that folder.

201407251658.jpg

Navigate to ViewController.swift file and add a new function with name as findMyLocation after viewDidLoad function.

@IBAction func findMyLocation(sender: AnyObject) {

}

Navigate to Main.storyboard file, drag and drop Button from the Object Library on to the View Controller. Using the attributes inspector, change the name of the button to “Where Am I?”. If you need then change the background and text colour for the button. Then centre align the button both vertically and horizontally. Use the Resolve Auto Layout Option and pick Reset to Suggested Constraints.

201407251707.jpg

Navigate to Connections Inspector and connect the button to the findMyLocation action under Received Actions (Touch Up Inside event).

201407251711.jpg

Click the Project under Project Navigator and navigate to Build Phases. Click the + sign under Link Binary With Libraries and pick CoreLocation.framework from the list.

Add CoreLocation framework to Swift project201407251718.jpg

CoreLocation in Swift

Now navigate back to ViewController.swift file, add import CoreLocation after UIKit. Then we need to assign the current class as the delegate for CLLocationManagerDelegate. This is required because we will be using couple of delegate methods from CLLocationManager.

class ViewController: UIViewController, CLLocationManagerDelegate {

Define a constant for CLLocationManager after the class declaration.

let locationManager = CLLocationManager()

Navigate to IBAction function findMyLocation and add the following code

locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()

The above lines sets the class as delegate for locationManager, specifies the location accuracy and starts receiving location updates from CoreLocation. In order to get the location updates we need to implement the delegate functions of CLLocationManager, didUpdateLocations and didFailWithError

func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
}

func locationManager(manager: CLLocationManager!, didFailWithError error: NSError!) {
}

didUpdateLocations function is triggered when new location updates are available. Similarly didFailWithError is called when there is a problem receiving the location updates. Let us first start implementing the simpler one i.e didFailWithError function. When there is an error, we are going to log the message to the console.

func locationManager(manager: CLLocationManager!, didFailWithError error: NSError!) {
	println("Error while updating location " + error.localizedDescription)
}

Then update didUpdateLocations function with the following code, also add a new function for printing the location details.

func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
CLGeocoder().reverseGeocodeLocation(manager.location, completionHandler: {(placemarks, error)->Void in
		if error {
			println("Reverse geocoder failed with error" + error.localizedDescription)
			return
		}

		if placemarks.count > 0 {
			let pm = placemarks[0] as CLPlacemark
			self.displayLocationInfo(pm)
		} else {
			println("Problem with the data received from geocoder")
		}
	})
}

func displayLocationInfo(placemark: CLPlacemark) {
	if placemark != nil {
		//stop updating location to save battery life
		locationManager.stopUpdatingLocation()
		println(placemark.locality ? placemark.locality : "")
		println(placemark.postalCode ? placemark.postalCode : "")
		println(placemark.administrativeArea ? placemark.administrativeArea : "")
		println(placemark.country ? placemark.country : "")
	}
}

in the didUpdateLocations function, we pass the location co-ordinates to

 CLGeocoder().reverseGeocodeLocation

. Then check for error and process the location array (placemarks). Then for displaying the location details, we pass the placemark detail to displayLocationInfo function.

Add key to Info.plist for request permission to User’s location

In order to use the user’s location you need to request permission from the user by adding the keys “NSLocationWhenInUseUsageDescription” or “NSLocationAlwaysUsageDescription” to your Info.plist file, with the value blank or optionally a message included in the prompt to the user. One of those two key is required in iOS 8 to ask for your location.

Testing Location in Simulator

It is not possible to test current location with simulator. So we need to define a custom location or use the location already defined for debug purpose. When an app tries to access the location services on a simulator it displays the following popup.

Current Location Simulator Xcode 6

Here is a workaround to test the app using Xcode 11. Run the app on the a Simulator and tap “Find My Location” button. Nothing will happen as the app has not been given permission to use Location Services on Simulator.

201407251956.jpg

Click Debug menu then Location and select Apple from the sub menu list. Still nothing happens with the app and no messages are written to the console.

201407251957.jpg

Now click Hardware menu and select Home from menu list.

201407251959.jpg

Tap the Settings icon on Simulator -> Privacy -> Location -> Tap , select Always to allow the app to access the location.

201407252003.jpg201407252005.jpg

Now you should see Apple address details in the Console Output.

201407252008.jpg

You can also try out other location using the Simulate Location option.

Simulate location in Xcode 6 simulator

Hopefully the testing should be lot easier after issue with “Allow to use Current Location” is fixed.

Source from GitHub

Filed Under: Apple, Develop, ios, Programming, Xcode Tagged With: Apple, CoreLocation, Swift, Xcode

NSUnknownKeyException – this class is not key value coding-compliant for the key

November 6, 2019 By Ravi Shankar Leave a Comment

NSUnknownKeyException – this class is not key value coding-compliant for the key common error that most of the Swift beginners would face during the learning period.

Terminating app due to uncaught exception ‘NSUnknownKeyException’, reason: ‘[<Handbook.StudentEntryController 0x78f948a0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key nametxt.’
*** First throw call stack:
(
0 CoreFoundation 0x02316a14 __exceptionPreprocess + 180
1 libobjc.A.dylib 0x01751e02 objc_exception_throw + 50
2 CoreFoundation 0x02316631 -[NSException raise] + 17
3 Foundation 0x013e71bc -[NSObject(NSKeyValueCoding) setValue:forUndefinedKey:] + 282
4 Foundation 0x0134183a _NSSetUsingKeyValueSetter + 115
5 Foundation 0x013417bf -[NSObject(NSKeyValueCoding) setValue:forKey:] + 295
6 UIKit 0x02cac06d -[UIViewController setValue:forKey:] + 85
7 Foundation 0x0137601d -[NSObject(NSKeyValueCoding) setValue:forKeyPath:] + 384
8 UIKit 0x02f1fcb4 -[UIRuntimeOutletConnection connect] + 132
9 libobjc.A.dylib 0x0176600c -[NSObject performSelector:] + 62
10 CoreFoundation 0x02246f51 -[NSArray makeObjectsPerformSelector:] + 273
11 UIKit 0x02f1e34e -[UINib instantiateWithOwner:options:] + 2102
12 UIKit 0x02cb3abc -[UIViewController _loadViewFromNibNamed:bundle:] + 429
13 UIKit 0x02cb44f4 -[UIViewController loadView] + 189
14 UIKit 0x02f47b66 -[UITableViewController loadView] + 88
15 UIKit 0x02cb4900 -[UIViewController loadViewIfRequired] + 154
16 UIKit 0x02cbb406 -[UIViewController __viewWillAppear:] + 114
17 UIKit 0x02cde5b9 -[UIViewController(UIContainerViewControllerProtectedMethods) beginAppearanceTransition:animated:] + 202
18 UIKit 0x02cf09cc -[UINavigationController _startCustomTransition:] + 1389
19 UIKit 0x02d02769 -[UINavigationController _startDeferredTransitionIfNeeded:] + 803
20 UIKit 0x02d03ada -[UINavigationController __viewWillLayoutSubviews] + 68
21 UIKit 0x02edfc4a -[UILayoutContainerView layoutSubviews] + 252
22 UIKit 0x02bb5008 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 810
23 libobjc.A.dylib 0x01766059 -[NSObject performSelector:withObject:] + 70
24 QuartzCore 0x029b480a -[CALayer layoutSublayers] + 144
25 QuartzCore 0x029a84ee _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 388
26 QuartzCore 0x029a8352 _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 26
27 QuartzCore 0x0299ae8b _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 317
28 QuartzCore 0x029cee03 _ZN2CA11Transaction6commitEv + 561
29 QuartzCore 0x029d0674 _ZN2CA11Transaction17flush_transactionEv + 50
30 UIKit 0x02ae28bc _UIApplicationHandleEventQueue + 8398
31 CoreFoundation 0x022306ff __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 15
32 CoreFoundation 0x0222638b __CFRunLoopDoSources0 + 523
33 CoreFoundation 0x022257a8 __CFRunLoopRun + 1032
34 CoreFoundation 0x022250e6 CFRunLoopRunSpecific + 470
35 CoreFoundation 0x02224efb CFRunLoopRunInMode + 123
36 GraphicsServices 0x05cf5664 GSEventRunModal + 192
37 GraphicsServices 0x05cf54a1 GSEventRun + 104
38 UIKit 0x02ae8bfa UIApplicationMain + 160
39 Handbook 0x0007513d main + 93
40 libdyld.dylib 0x04b11a21 start + 1
41 ??? 0x00000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException

This issue occurs when a storyboard refers to a deleted or renamed IBOutlet. If you navigate to the connection instepctor of the ViewController, you shoud notice an exclaimation mark as shown below.

Remove the IBOutlet mapping by clicking on X mark and remove the orphaned IBOutlet.

Filed Under: IBOutlet, ios, NSUknownKeyException

Swift – WebView demo

November 6, 2019 By Ravi Shankar 24 Comments

Updated for Swift 5

In this short tutorial, we will see an example in Swift programming language using UIWebView. The WebView will load a webpage and provide option to refresh, stop, go back and go forward. This tutorial should give a brief overview on how to declare IBAction, IBOutlets and use Objective-C classes (NSURL and NSURLRequest) in Swift


Interface Design

Step 1: Create a Single View Application Project and make sure to select the programming language as Swift

Choose Swift Language in Xcode

Step 2: Select Main.Storyboard under Project navigator. then drag and drop WebView and Toolbar from Object Library to the View Controller.

Step 3: Now place four bar button items on Toolbar to provide stop, refresh, go back and go forward functionality. You can also use flexible and fixed separators for aligning the bar button items.

Use Suggested Constraints in Xcode 6

Step 4: Make sure to use the SuggestedConstraints for WebView and Toolbar. You can do this by selecting the controls and using the Reset to Suggested Constraints available as part of Resolve Auto Layout option. This would ensure that the controls gets adjusted automatically depending upon the device’s screen width and height.

Reset to Suggested Constraints

Updated – 28/08/2014

Since some users are facing problems with Reset to Suggested Constraints in Xcode 6 Beta 6, you can use Pin option to define the four constraints using the default values. Then click Add 4 Constraints available at the bottom of the screen.

201408281213.jpg

Write Code

Step 5: Navigate to ViewController.swift file on the Project Navigator. Add the following line after class ViewController: UIViewController which defines the IBOutlet element for WebView.

@IBOutlet var webView: UIWebView!

Then connect this IBOutlet element to the WebView on InterfaceBuilder.

Step 6: In the viewDidLoad function, create URL and NSURLRequest for the webpage and associate this with the WebView by calling loadRequest method.

override func viewDidLoad() 
{   
  super.viewDidLoad() 
  let url = NSURL(string: "https://rshankar.com")
  let request = NSURLRequest(url: url! as URL)       
  webView.delegate = self
  activityIndicator.hidesWhenStopped = true
  activityIndicator.startAnimating()
  webView.loadRequest(request as URLRequest) 
}

Step 7: Now add the corresponding IBAction methods for the four buttons stop, refresh, go back and go forward. And connect these IBActions to the buttons on Interface builder.

func webViewDidFinishLoad(_ webView: UIWebView) 
{ 
      activityIndicator.stopAnimating()   
}                                                                                           

@IBAction func doRefresh(_: AnyObject) {                                                              
  webView.reload()
}

@IBAction func goBack(_: AnyObject) 
{   
   webView.goBack()    
}

@IBAction func goForward(_: AnyObject) {
  webView.goForward()
}

@IBAction func stop(_: AnyObject) 
{
   webView.stopLoading()
}

Step 8: Compile and run the project by selecting a suitable simulator.

Download the souce code from GitHub.

Filed Under: ios, Programming, Xcode Tagged With: Swift, WebView, Xcode

Value for SWIFT_VERSION cannot be empty.

November 6, 2019 By Ravi Shankar Leave a Comment

This error occurs when the Swift version is not specified under Swift Compiler Languge for your project.
All you need to do is, Navigate to Build Settings -> Swift Compiler Language, from the drop down specify the Swift version.

Filed Under: ios, Swift Compiler, Swift Version

  1. Pages:
  2. «
  3. 1
  4. 2
  5. 3
  6. 4
  7. 5
  8. 6
  9. 7
  10. ...
  11. 44
  12. »
« Previous Page
Next Page »

Primary Sidebar

TwitterLinkedin

Recent Posts

  • How to block keywords in Jio broadband
  • How to disable opening an app automatically at login in Mac
  • How to set preferred Wifi network on Mac
  • Attribute Unavailable: Estimated section warning before iOS 11.0
  • How to recover Firefox password from Time Machine backup

Pages

  • About
  • Privacy Policy
  • Terms and Conditions

Copyright 2022 © rshankar.com

Terms and Conditions - Privacy Policy