Browse Author: Sagar Rode

I am an iOS Developer working in Nanostuffs Technologies Pvt. Ltd. I have 2 years of experience in Swift language. I like to learn new things and implement.

Move View/ScrollView to keep TextField visible when keyboard appears!

There is a very common but tricky part comes while handling textfields position when keyboard appears. Its sometimes become headache for developers to handle such small but tricky issue. When keyboard appears then some textfields get covered and user can’t see while editing it.

There are some solutions to handle this but many of them works only for the first time. And then doesn’t work.
I have found one best solution and it works great always and I have modified this code to work on Swift 4. I have used XCode 9.1 for this tutorial code.

Lets do it.

You will have to create new project to try this. Design your storyboard like the image from below link.

https://drive.google.com/a/nanostuffs.com/file/d/1KXJV49BP07T2q8nca4vKlz3eMU7xtkNG/view?usp=sharing

And set delegate of all textFields to ViewController.


Now in your ViewController class you can use it like below


 

import UIKit

class ViewController: UIViewController, UITextFieldDelegate {

     @IBOutlet weak var myScrollView: UIScrollView!

     @IBOutlet weak var constraintContentHeight: NSLayoutConstraint!

     override func viewDidLoad() {

          super.viewDidLoad()

}

override func viewWillAppear(_ animated:Bool) {

     super.viewWillAppear(animated)

//1  Add this observers to observe keyboard shown and hidden events
      NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillBeHidden(aNotification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)

     NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(aNotification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)

}

override func viewWillDisappear(_ animated: Bool) {

     super.viewWillDisappear(animated)

//2  Remove the observers added for keyboard from your ViewController

     NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: nil)

     NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: nil)

}

var activeField: UITextField?

// MARK: – UITextField Delegates

//3  ViewController activeField is an object of UITextField which will be used to manage and resign current active textField 

func textFieldShouldReturn(_ textField: UITextField) -> Bool {

     textField.resignFirstResponder()

     return true

}

func textFieldDidBeginEditing(_ textField: UITextField) {

     activeField = textField

}

func textFieldDidEndEditing(_ textField: UITextField) {

     activeField = nil

}

// Called when the UIKeyboardWillHide is sent

//4  This method is called from selector. So it requires @objc keyword and this method will adjust your scrollView (here myScrollView  😉)  and textFields to show as original.

@objc func keyboardWillBeHidden(aNotification: NSNotification) {

     let contentInsets: UIEdgeInsets = .zero

     self.myScrollView.contentInset = contentInsets

     self.myScrollView.scrollIndicatorInsets = contentInsets

}

// Called when the UIKeyboardWillShow is sent

//5
This method will adjust your scrollView
and will show textFields above the keyboard.
@objc
func
keyboardWillShow(aNotification: NSNotification) {

     var info = aNotification.userInfo!

     let kbSize: CGSize = ((info[“UIKeyboardFrameEndUserInfoKey”] as? CGRect)?.size)!

     print(“kbSize = \(kbSize)”)

     let contentInsets: UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0)

     myScrollView.contentInset = contentInsets

     myScrollView.scrollIndicatorInsets = contentInsets

     var aRect: CGRect = self.view.frame

     aRect.size.height -= kbSize.height

     if !aRect.contains(activeField!.frame.origin) {

          self.myScrollView.scrollRectToVisible(activeField!.frame, animated: true)

     }

  }

} // Class End

UserDefaults with NSCoding and Codable to save any kind of Data… Part – 1 😎

 UserDefaults  is one of the good and handy storage you can use. You can use UserDefaults to store any basic data type for as long as the app is installed. For example, you can use basic types as BoolFloatDoubleIntString, or URL, but you can also write more complex types such as arrays, dictionaries and Date – and even Data values.

Advantage: After writing data to UserDefaults, when you run the app the UserDefaults data gets automatically loaded. Its very easy to use it.

Disadvantage:  Its a bad idea to use UserDefaults to save lot of data as it slows down your app loading and increases app load time.

You can use it making an instance of the UserDefaults like below:

let defaults = UserDefaults.standard

Now you can save your data like below:

defaults.set(26, forKey: "Age")

defaults.set(true, forKey: “is_iOS_Best”)

defaults.set(CGFloat.pi, forKey: “Pi”)

defaults.set(Date(), forKey: "CurrentDate")
let myArray = ["iOS", "macOS"]
defaults.set(myArray, forKey: "SavedArray")
let myDict = ["Name": "Sagar", "Hobby": "Coding"]
defaults.set(dict, forKey: "SavedDict")

You can get the stored values/data back using below methods:

  • integer(forKey:) returns an integer if the key existed, or 0 if not.
  • bool(forKey:) returns a boolean if the key existed, or false if not.
  • float(forKey:) returns a float if the key existed, or 0.0 if not.
  • double(forKey:) returns a double if the key existed, or 0.0 if not.
  • object(forKey:) returns Any? so you need to conditionally typecast it to your data type.You can save a complex kind of data too in UserDefaults using NSCoding and Codable protocols.
    NSCoding is available for both Objective-C and Swift developers but Codable is only for Swift developers.We will see the usage of first one : NSCoding


     NSCoding

    Your class must confirm to NSCoding before using it, like you use other protocols : UITableViewDataSource and UITableViewDataDelegate.

    Then you can implement it using below method which changes the object graph to Data, so that it becomes able to save in UserDefaults.

    archivedData(withRootObject:) method of NSKeyedArchiver

    There are many Apple’s own classes that support NSCoding like: UIColorUIImageUIViewUILabelUIImageViewUITableViewSKSpriteNode
    but your custom classes don’t support it by default.

    You can also refer HackingWithSwift blog for free and good coding tutorials.

    Let us have a look at below example for its use.

    For example:

    Consider your custom class is MySelf in which you are using name and image properties.


    import UIKit

    class MySelf: NSObject, NSCoding { //Here NSObject is required to use NSCoding.

         func encode(with aCoder: NSCoder)  {    // this method is required and used to encode data

             aCoder.encode(name, forKey: name)

             aCoder.encode(image, forKey: image)

         }

        required init?(coder aDecoder: NSCoder) {   // this method is required and used to decode data

             name = aDecoder.decodeObject(forKey: name) as! String

            image = aDecoder.decodeObject(forKey: image) as! String

       }


    var
    name: String

       var image: String


    init
    (name: String, image: String) {

             self.name = name

            self.image = image

       }

    }

     


    Now in your ViewController class you can use it like below


     

    import UIKit

    class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate, UICollectionViewDataSource {

        var people = [MySelf]()          // array of MySelf type data

        @IBOutlet weak var myCollectionView: UICollectionView!

        override func viewDidLoad() {

              super.viewDidLoad()

              let defaults = UserDefaults.standard

              if let savedPeople = defaults.object(forKey: people) as? Data {

                      // Get saved values using below method
    people
    = NSKeyedUnarchiver.unarchiveObject(with: savedPeople) as! [MySelf]
    }

          }

        @IBAction func barButton_addPersonTapped(_ sender: Any) {

               addPerson()    

        }

        func addPerson() {      // Show ImagePickerController

               let imagePicker = UIImagePickerController()

              imagePicker.allowsEditing = true

             imagePicker.delegate = self

             present(imagePicker, animated: true) {

                 //  completion block

              }

          }

        func getDocumentsDirectory() -> URL {   // // Get DocumentsDirectory

              let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)

              let documentsDirectory = paths[0]

              return documentsDirectory

        }

    // UICollectionViewController Delegates


    func
    collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {

         return people.count

    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

          let cell = collectionView.dequeueReusableCell(withReuseIdentifier: PersonCollectionViewCell, for: indexPath) as! PersonCollectionViewCell

         let aPerson = people[indexPath.item]

         cell.name.text = aPerson.name

         let path = getDocumentsDirectory().appendingPathComponent(aPerson.image)

         cell.imageView.image = UIImage(contentsOfFile: path.path)

         cell.imageView.layer.cornerRadius = 3

         cell.imageView.layer.borderWidth = 2

         cell.imageView.layer.borderColor = UIColor(displayP3Red: 0, green: 0, blue: 0, alpha: 0.3).cgColor

         cell.layer.cornerRadius = 7

        return cell

     }

    // UIImagePickerController Delegates

    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {

         guard let image = info[UIImagePickerControllerEditedImage] as? UIImage else { return }

         let imageName = UUID().uuidString

         let imagePath = getDocumentsDirectory().appendingPathComponent(imageName)

         if let jpegData = UIImageJPEGRepresentation(image, 80) {

               try? jpegData.write(to: imagePath)  // Write your image jpeg data to imagePath

         }

         let aPerson = MySelf(name: Img, image: imageName)

         people.append(aPerson)

         myCollectionView.reloadData()

         dismiss(animated: true) {

               //

         }

         self.save()  // Save newly added Image with name.

      }


    func
    save() {  // Save image and name using archivedData method of  NSKeyedArchiver

            let saveData = NSKeyedArchiver.archivedData(withRootObject: people)

            let defaults = UserDefaults.standard

          defaults.set(saveData, forKey: people)

       }

    }


    Your custom collectionViewCell class: PersonCollectionViewCell 


     

    import UIKit

    class PersonCollectionViewCell: UICollectionViewCell {

           @IBOutlet weak var imageView: UIImageView!

           @IBOutlet weak var name: UILabel!

    }


    Have fun… 🙂

    I will show the usage of Codable in my next post.

    Let me know if you have any queries or problems implementing this feature. Enjoy … 🙂

iOS – Tap to zoom and share image using Apple’s QuickLook framework

QuickLook framework is one of the best framework by Apple. It can be used to preview files such as iWork, MS Office, RTF, CSV documents, pdf, text files, Images.

You can use this framework to implement image zooming and sharing feature. We don’t need to write this zooming and sharing code ourself. QuickLook framework provides this all awesome functionality.

Let’s start implementing this powerful and easy framework.
(I have used Swift 3 and XCode 8.3.2)

*  Use Single View application template and give a name to your project(App). Select Swift language.

* On Storyboard make your view controller UI like this :  Check this image.
Take 2 UIImageViews  and take 1 UIButton on each UIImageView. Create necessary IBOutlets and IBActions.

* Your final ViewController Code should look like below.


import UIKit
import QuickLook   // Import QuickLook to use it’s functionalities.

let imageURLs =  [“https://static2.hdwallpapers.net/wallpapers/2015/03/24/574/thumb_steve-jobs-typographic-portrait.jpg”, “http://www.planetware.com/photos-large/F/france-paris-eiffel-tower.jpg”]

class Downloader  {    // This is Image Downloader helper class
        class func downloadImageWithURL(_ url:String) -> UIImage? {
               let data = try? Data(contentsOf: URL(string: url)!)

if
let imgData = data {
                   return UIImage(data: imgData)
               }

              else {
                  print(“\n\nThis image url may be wrong : \n \(url)”)
                  return nil
               }

    }

}


class iOSConcurrencyViewController: UIViewController, QLPreviewControllerDataSource

    // Connect this 2 imageView outlets with your imageViews.
    @IBOutlet weak var imageView1: UIImageView!  
    @IBOutlet weak var imageView2: UIImageView!

   // Connect this outlet with Bar button named – Start
    @IBOutlet
weak var barButton_Start: UIBarButtonItem!

   let quickLookController = QLPreviewController()
   var myQLimageURL: URL?

    override func viewDidLoad() {

        super.viewDidLoad()

       // Add QuickLook dataSource to  self
       quickLookController.dataSource =
self

    }

@IBAction func didClickOnStart(_ sender: AnyObject) {  // Connect this IBAction with Bar button item – Start

        // Here we will start downloading images and then disable the start button.
        barButton_Start.isEnabled = false

        //   ##################  Dispatch Queues ##################

         // Default(System provided) global Dispatch queue

        // It can download any image in any order at any time once started.

        let imgDownloader_DefaultConcurrentQueue = DispatchQueue.global(qos: .default)

        //img1

        downloadUsingGCD_AndShowImage(in: imageView1, index: 0, with: imgDownloader_DefaultConcurrentQueue)

        //img2

        downloadUsingGCD_AndShowImage(in: imageView2, index: 1, with: imgDownloader_DefaultConcurrentQueue)

}

 

// For GCD – Dispatch Queues

    func downloadUsingGCD_AndShowImage(in imageView: UIImageView, index imageIndex: Int, with Queue: DispatchQueue) {

            Queue.async {  // To avoid blocking UI, download images in background.

            let img = Downloader.downloadImageWithURL(imageURLs[imageIndex])

            let fileParts = imageURLs[imageIndex].components(separatedBy: “/”)

            DispatchQueue.main.async { // UI update code should be on main thread.

                imageView.image = img

                self.saveImageToDirectory(imageName: fileParts.last!, img: img!)

            }

        }

    }


// You have used a button on each imageView. Give 0 tag for button1 and 1 for button2. Then connect both buttons IBAction to this.
@IBAction func button_ZoomImage_Tapped(_ sender: UIButton) {

        var index = Int()

        switch sender.tag {

        case 0:

            index = 0

        case 1:

            index = 1

        default:

            break

        }

        // Get image name with extension from image url
        let fileParts = imageURLs[index].components(separatedBy: “/”)

        myQLimageURL = getImageFromDirectory(imageName: fileParts.last!) 

        if let QLImgURL = myQLimageURL {

            if QLPreviewController.canPreview(QLImgURL as QLPreviewItem) { 

                // This line refreshes current preview otherwise you will see only 1 image for both image views.
                quickLookController.refreshCurrentPreviewItem() 

                //navigationController?.pushViewController(quickLookController, animated: true)

                            present(quickLookController, animated: true, completion: {

                                print(“completion block”)

                            })

            }

        }

    }

 

    // Get Documents Directory helper function

    func getDocumentsDirectory() -> URL {

        let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)

        let documentsDirectory = paths[0]

        return documentsDirectory

    }

    

    // Save Image to Directory

    func saveImageToDirectory(imageName: String, img: UIImage) {

        //if let image = UIImage(named: imageName) {

            if let data = UIImageJPEGRepresentation(img, 0.8) {

                let filename = getDocumentsDirectory().appendingPathComponent(imageName)

                try? data.write(to: filename)

            }

        //}

    }

    

    // Get Image from Directory

    func getImageFromDirectory(imageName: String) -> URL? {

        let imageURL = getDocumentsDirectory().appendingPathComponent(imageName)

        let imagePath = (getDocumentsDirectory().appendingPathComponent(imageName)).path

        if FileManager.default.fileExists(atPath: imagePath) {

            return imageURL

        }

        else {

            return nil

        }

    }

    

    // QuickLook Datasource
    func numberOfPreviewItems(in controller: QLPreviewController) -> Int {

        return 1    // This will return only 1 image at a time

    }

    func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {

        return myQLimageURL! as QLPreviewItem

    }

 

Have fun… 🙂

Let me know if you have any queries or problems implementing this feature. Enjoy … 🙂

 

iOS – Image Downloader helper class (Swift 3)

Image Downloader Class written in Swift 3 to download images.

class Downloader {

        class func downloadImageWithURL(_ url:String) -> UIImage? {

           let data = try? Data(contentsOf: URL(string: url)!)

         if let imgData = data {

            return UIImage(data: imgData)

        }

        else {

            print(“\n\nThis image url may be wrong : \n \(url)”)

            return nil

        }

    }

} // class end

You can then use this class to download and show image in your UIImage  as below:

        let image  =  Downloader.downloadImageWithURL(“http://images.freeimages.com/images/premium/small-comps/3352/33528960-palouse-waterfall-washington.jpg”)

yourImageView.image = image

Then you are done. Enjoy… 🙂