Search This Blog

Tuesday, March 14, 2017

Create custom UITextField and custom clear button for it


Goals


  1. Create custom UITextField
    1. It should be transparent
    2. It should have border
    3. It should have rounded corners
    4. Rounded corners and border width should be setting via Xcode Interface Builder (IBInspectable / IBDesignable)
  2. Create custom clear button for custom UITextField
    1. Use Extensions for creating custom clear button
    2. Use Google Material Design Icon as image for button
    3. Set for button image custom color
    4. Make button always visible even if textfield is not in focus
    5. Set for button padding from right side of textfield
  3. Make textfield not editable but still available for touches
    1. Do not show keyboard
    2. Add default parameter values
    3. Clear button should be available for tap
    4. Fill textfield by clicking on special button
    5. Send Local Notification when custom clear button is tapped
  4. Use completely different approach for the same goal - Create custom clear button without adding Right View in UITextField

Implementation


Custom TextField

import UIKit
import UIKit

@IBDesignable
class CustomTextField: UITextField {
    
    @IBInspectable
    var borderWidth: CGFloat {
        get {
            return self.layer.borderWidth
        }
        set {
            self.layer.borderWidth = newValue
        }
    }
    
    @IBInspectable
    var borderColor: UIColor? {
        get {
            return UIColor(cgColor: self.layer.borderColor!)
        }
        set {
            self.layer.borderColor = newValue?.cgColor
        }
    }
    
    @IBInspectable
    var cornerRadius: CGFloat {
        set {
            self.layer.cornerRadius = newValue
        }
        get {
            return self.layer.cornerRadius
        }
    }

}






Add Custom Clear Button with Extensions


import Foundation
import UIKit

extension UITextField {
    
    func clearButtonWithImage(_ image: UIImage) {
        let clearButton = UIButton()
        clearButton.setImage(image, for: .normal)
        clearButton.frame = CGRect(x: 0, y: 0, width: 15, height: 15)
        clearButton.contentMode = .scaleAspectFit
        clearButton.addTarget(self, action: #selector(self.clear(sender:)), for: .touchUpInside)
        self.rightView = clearButton
        self.rightViewMode = .always
    }
    
    func clear(sender: AnyObject) {
        self.text = ""
    }
    
}

override func viewDidLoad() {
        super.viewDidLoad()
        
        let clearImage = UIImage(named: "ic_close_white_48pt")!
        self.firstWordTextField.clearButtonWithImage(clearImage)
}





Moving Clear Button

override func rightViewRect(forBounds bounds: CGRect) -> CGRect {
        var rightViewRect = super.rightViewRect(forBounds: bounds)
        rightViewRect.origin.x -= 10;
        return rightViewRect
}

import UIKit
import UIKit

@IBDesignable
class CustomTextField: UITextField {
    
    @IBInspectable
    var borderWidth: CGFloat {
        get {
            return self.layer.borderWidth
        }
        set {
            self.layer.borderWidth = newValue
        }
    }
    
    @IBInspectable
    var borderColor: UIColor? {
        get {
            return UIColor(cgColor: self.layer.borderColor!)
        }
        set {
            self.layer.borderColor = newValue?.cgColor
        }
    }
    
    @IBInspectable
    var cornerRadius: CGFloat {
        set {
            self.layer.cornerRadius = newValue
        }
        get {
            return self.layer.cornerRadius
        }
    }
    
    override func rightViewRect(forBounds bounds: CGRect) -> CGRect {
        var rightViewRect = super.rightViewRect(forBounds: bounds)
        rightViewRect.origin.x -= 10;
        return rightViewRect
    }

}





Add Color to Clear Button

import Foundation
import UIKit

extension UITextField {
    
    func clearButtonWithImage(_ image: UIImage,
                              _ imageColor: UIColor = UIColor.white,
                              _ isAlwaysVisible: Bool = true) {
        let clearButton = UIButton()
        clearButton.tintColor = imageColor
        let coloredImage = image.withRenderingMode(.alwaysTemplate)
        clearButton.setImage(coloredImage, for: .normal)
        clearButton.frame = CGRect(x: 0, y: 0, width: 15, height: 15)
        clearButton.contentMode = .scaleAspectFit
        clearButton.addTarget(self, action: #selector(self.clear(sender:)), for: .touchUpInside)
        self.rightView = clearButton
        self.rightViewMode = isAlwaysVisible ? .always : .never
    }
    
    func clear(sender: AnyObject) {
        self.text = ""
    }
    
}


Create two clear buttons with default color and with custom color

override func viewDidLoad() {
        super.viewDidLoad()
        
        let clearImage = UIImage(named: "ic_close_white_48pt")!
        
        self.firstWordTextField.clearButtonWithImage(clearImage)
        self.secondWordTextField.clearButtonWithImage(clearImage, UIColor.red)
}




Send Local Notification when tapped on clear button

import Foundation
import UIKit

extension UITextField {
    
    func clearButtonWithImage(_ image: UIImage,
                              _ imageColor: UIColor = UIColor.white,
                              _ isAlwaysVisible: Bool = true) {
        let clearButton = UIButton()
        clearButton.tintColor = imageColor
        let coloredImage = image.withRenderingMode(.alwaysTemplate)
        clearButton.setImage(coloredImage, for: .normal)
        clearButton.frame = CGRect(x: 0, y: 0, width: 15, height: 15)
        clearButton.contentMode = .scaleAspectFit
        clearButton.addTarget(self, action: #selector(self.clear(sender:)), for: .touchUpInside)
        self.rightView = clearButton
        self.rightViewMode = isAlwaysVisible ? .always : .never
    }
    
    func clear(sender: AnyObject) {
        self.text = ""
        
        NotificationCenter.default.post(
            name: Notification.Name("TextFieldCleared"), object: nil, userInfo: nil)
    }
    
}

override func viewDidLoad() {
        super.viewDidLoad()
        
        let clearImage = UIImage(named: "ic_close_white_48pt")!
        
        self.firstWordTextField.clearButtonWithImage(clearImage)
        self.secondWordTextField.clearButtonWithImage(clearImage, UIColor.red)
        
        NotificationCenter.default.addObserver(self,
                                               selector: #selector(ViewController.clearSelection),
                                               name: Notification.Name("TextFieldCleared"),
                                               object: nil)
}




Full Code of ViewController

import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak var firstWordTextField: CustomTextField!
    
    @IBOutlet weak var secondWordTextField: CustomTextField!
    
    @IBOutlet weak var firstButton: UIButton!
    
    @IBOutlet weak var secondButton: UIButton!
    
    var isFirstSelected: Bool = false
    
    var isSecondSelected: Bool = false
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let clearImage = UIImage(named: "ic_close_white_48pt")!
        
        self.firstWordTextField.clearButtonWithImage(clearImage)
        self.secondWordTextField.clearButtonWithImage(clearImage, UIColor.red)
        
        NotificationCenter.default.addObserver(self,
                                               selector: #selector(ViewController.clearSelection),
                                               name: Notification.Name("TextFieldCleared"),
                                               object: nil)
    }
    
    @IBAction func firstButtonTapped(_ sender: Any) {
        if !self.isFirstSelected {
            self.firstWordTextField.text = "Fill First TextField"
            self.setFirstButtonSelected(selected: !self.firstButton.isSelected)
        }
    }
    
    @IBAction func secondButtonTapped(_ sender: Any) {
        if !self.isSecondSelected {
            self.secondWordTextField.text = "A long time ago"
            self.setSecondButtonSelected(selected: !self.secondButton.isSelected)
        }
    }
    
    
    func clearSelection() {
        if self.isFirstSelected {
            if let text = self.firstWordTextField.text {
                if text.isEmpty {
                    self.setFirstButtonSelected(selected: false)
                }
            }
        }
        
        if self.isSecondSelected {
            if let text = self.secondWordTextField.text {
                if text.isEmpty {
                    self.setSecondButtonSelected(selected: false)
                }
            }
        }
    }
    
    func setFirstButtonSelected(selected: Bool) {
        self.isFirstSelected = selected
        self.firstButton.isSelected = selected
    }
    
    func setSecondButtonSelected(selected: Bool) {
        self.isSecondSelected = selected
        self.secondButton.isSelected = selected
    }

}


Sources




Monday, March 6, 2017

Firebase create user with email, password, display name and photo url


Problem

In Firebase you can create user account with email and password. But what if you want to set user displayName and photo?  For that purpose after creating user with email and password you can use FIRUserProfileChangeRequest.

Algorithm


  1. Create new user with email and password with completion handler with method FIRAuth.auth()?.createUser
  2. In success of completion handler use method of user  user.profileChangeRequest() and set user display name changeRequest.displayName and commit changes changeRequest.commitChanges

Source Code

     /* 
     1. Create new user account with email and password.
     2. Update create account and set user display name.
     */
    func registerNewUser(email: String,
                  password: String,
                  displayName: String,
                  successHandler: ((_ uid:String) -> Void)?,
                  errorHandler: ((_ errorString: String) -> Void)?) {
        FIRAuth.auth()?.createUser(
            withEmail: email,
            password: password,
            completion: { (user, error) in
                if let error = error {
                    if let errorHandler = errorHandler {
                        errorHandler(error.localizedDescription)
                    }
                } else if let user = user {
                    let changeRequest = user.profileChangeRequest()
                    changeRequest.displayName = displayName
                    changeRequest.commitChanges(completion: { error in
                        if let error = error {
                            if let errorHandler = errorHandler {
                                errorHandler(error.localizedDescription)
                            }
                        } else if let successHandler = successHandler {
                            successHandler(user.uid)
                        }
                    })
                }
        })
    }

Link to Firebase Documentation



Sunday, March 5, 2017

UIImageView Content Modes

For new people in ios development slightly unclear how work content mode for UIImage View. What is the difference between scale modes for UIImageView?
There are three main scales modes
  • scale to fill (FILL image in UIIMageView frame - image will be changed)
  • scale aspect fit (scale image to FULLY FIT in UIIMageView frame - image will be the same but may change size)
  • scale aspect fill (scale image until smallest side fits UIImageView frame - some part of image may be clipped)
I wrote a demo program to show how this modes work.

import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak var switcher: UISwitch!
    
    @IBOutlet weak var imageView: UIImageView!
    
    @IBOutlet weak var segmentedControl: UISegmentedControl!
    
    @IBAction func segmentSelected(_ sender: Any) {
        let index = self.segmentedControl.selectedSegmentIndex
        switch index {
        case 0:
            self.imageView.contentMode = .scaleToFill
            break
        case 1:
            self.imageView.contentMode = .scaleAspectFit
            break
        case 2:
            self.imageView.contentMode = .scaleAspectFill
            break
        default:
            self.imageView.contentMode = .scaleAspectFit
            break
        }
    }
    
    @IBAction func switcherChanged(_ sender: Any) {
        self.imageView.clipsToBounds = self.switcher.isOn
    }
    
}

Original Image




Scale To Fill



Scale Aspect Fit




Scale Aspect Fill (NO Clip To Bounds)



Scale Aspect Fill (Clip To Bounds)