Search This Blog

Saturday, November 18, 2017

UIDatePicker as input view for UITextField. And toolbar for it.

Description


We need to create Date Picker as input view for textfield. It is simple - just create UIDatePicker object and assign it as inputView of textfield. Also we need to create toolbar for this Date Picker. Toolbar should contains two buttons: Cancel and Done. By clicking Cancel just close Date Picker and by clicking Done put selected date in textfield. Also hide date picker by clicking anywhere on the view (it can be done by implementing method touchesBegan).

Full Code

import UIKit

class ViewController: UIViewController {

    @IBOutlet var dateLabel: UILabel!
    
    @IBOutlet var dateTextField: UITextField!
    
    let datePicker = UIDatePicker()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.dateTextField.inputView = self.datePicker
        self.dateTextField.inputAccessoryView = self.createToolbar()
    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        self.view.endEditing(true)
    }
    
    func createToolbar() -> UIToolbar {
        let toolbar = UIToolbar()
        let cancelBarButtonItem = UIBarButtonItem(title: "Cancel", style: .plain,
                                                  target: self, action: #selector(hideDatePicker))
        let flexBarButtonItem = UIBarButtonItem(barButtonSystemItem: .flexibleSpace,
                                                target: nil, action: nil)
        let doneBarButtonItem = UIBarButtonItem(title: "Done", style: .done,
                                                target: self, action: #selector(doneDatePicker))
        toolbar.setItems([cancelBarButtonItem, flexBarButtonItem, doneBarButtonItem],
                         animated: false)
        
        toolbar.barStyle = .default
        toolbar.tintColor = UIColor.black
        toolbar.isTranslucent = false
        toolbar.isUserInteractionEnabled = true
        toolbar.sizeToFit()
        
        return toolbar
    }
    
    @objc func hideDatePicker() {
        self.view.endEditing(true)
    }
    
    @objc func doneDatePicker() {
        let dateFormatter = DateFormatter()
        dateFormatter.dateStyle = .short
        let selectedDate = self.datePicker.date
        self.dateTextField.text = dateFormatter.string(from: selectedDate)
        self.view.endEditing(true)
    }
}

Screens





Tuesday, November 14, 2017

Degree of an Array

Task

Given an array of integers. Degree of this array is defined as the maximum frequency of any element in the array. For example, the array [1, 2, 3, 4, 2, 2, 3] has a degree 3 because the number 2 occurs three times (more than any other numbers). Need to define size of the smallest subArray with degree equals to initial array's degree. For current array smallest subArray with degree 3 will be [2, 3, 4, 2, 2] which size is 5. So answer is 5.

Algorithm


  1. Create three dictionary: 
    • first for stored count of each number in array (countTable dictionary), 
    • second for storing first index of each number (left dictionary) 
    • and third for storing last index of each number (right dictionary)
  2. For each number in array
    • If for this number already exists value in countTable then increase this value by 1
    • If for this number do not exist value in countTable then assign it with value of 1 and also we need to store index of first time we see this value in left dictionary
    • For right dictionary update value each iteration of loop
    • Find max of two values: current degree of array and count of current value
  3. For each key in countTable
    1.  Check if count for this value is equal to degree of array then
      1. For current key (which is int number) we need to calculate length of subArray by following formula: [right index] - [left index] + 1
      2. Get min value from length of initial array and length of subArray (this min value is our answer) 

Full Code

func findMinSubArrayWithDegree(_ array: [Int]) -> Int {
    var degree = 0
    let n = array.count
    var countTable = [Int: Int]()
    var left = [Int: Int]()
    var right = [Int: Int]()
    for i in 0..<n {
        let value = array[i]
        if let count = countTable[value] {
            countTable[value] = count + 1
        } else {
            countTable[value] = 1
            left[value] = i
        }
        right[value] = i
        degree = max(degree, countTable[value]!)
    }
    var result = n
    for key in countTable.keys {
        if degree == countTable[key] {
            let subArrayCount = right[key]! - left[key]! + 1
            result = min(result, subArrayCount)
        }
    }
    return result
}

findMinSubArrayWithDegree([1, 2, 1, 3, 1, 2, 2, 1, 2, 2]) // 9
findMinSubArrayWithDegree([1, 2, 2, 3, 1]) // 2
findMinSubArrayWithDegree([1, 2, 3, 4, 2, 2, 3]) // 5

Saturday, November 11, 2017

iOS Swift. How to create background view for status bar

Here I show you how to create custom background view for status bar. For demo example let create App with Navigation Controller and custom background color of Navigation bar. Code for that provided below. We will put it inside didFinishLaunchingWithOptions method.

func application(_ application: UIApplication, 
    didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        
    UINavigationBar.appearance().barTintColor = UIColor(red: CGFloat(68.0/255.0),
                                                        green: CGFloat(151.0/255.0),
                                                        blue: CGFloat(227.0/255.0),
                                                        alpha: 1.0)
    
    self.window = UIWindow(frame: UIScreen.main.bounds)
    self.window?.makeKeyAndVisible()
    let vc = ViewController()
    vc.view.backgroundColor = UIColor.white
    vc.title = "Custom Status Bar"
    self.window?.rootViewController = UINavigationController(rootViewController: vc)
}

This is how it looks.


Now we have blue color of navigation bar. It will be good to change status bar text color to white. We can do it by changing Info.plist file and set property 
View controller-based status bar appearance to NO


Make status bar text color white we can with settings in App Targets.


Or programmatically in didFinishLaunchingWithOptions method.
UIApplication.shared.statusBarStyle = .lightContent

Now we have white color of status bar text.


Let's go further. Now we need custom background color for status bar. For this purpose let's create special view and add it as subview to our window. Also we must provide couple of constraints.
let statusBarBackgroundView = UIView()
statusBarBackgroundView.translatesAutoresizingMaskIntoConstraints = false
statusBarBackgroundView.backgroundColor = UIColor(red: CGFloat(54.0/255.0),
                                                      green: CGFloat(120.0/255.0),
                                                      blue: CGFloat(181.0/255.0),
                                                      alpha: 1.0)
self.window?.addSubview(statusBarBackgroundView)
self.window?.addConstraints(
            NSLayoutConstraint.constraints(withVisualFormat: "V:|[b(20)]",
                                       options: NSLayoutFormatOptions(),
                                       metrics: nil,
                                       views: ["b" : statusBarBackgroundView]))
self.window?.addConstraints(
            NSLayoutConstraint.constraints(withVisualFormat: "H:|[b]|",
                                       options: NSLayoutFormatOptions(),
                                       metrics: nil,
                                       views: ["b" : statusBarBackgroundView]))

Here is result.


Full Code
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?


    func application(_ application: UIApplication, 
        didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        
        UINavigationBar.appearance().barTintColor = UIColor(red: CGFloat(68.0/255.0),
                                                            green: CGFloat(151.0/255.0),
                                                            blue: CGFloat(227.0/255.0),
                                                            alpha: 1.0)
        
        UIApplication.shared.statusBarStyle = .lightContent
    
        self.window = UIWindow(frame: UIScreen.main.bounds)
        self.window?.makeKeyAndVisible()
        let vc = ViewController()
        vc.view.backgroundColor = UIColor.white
        vc.title = "Custom Status Bar"
        self.window?.rootViewController = UINavigationController(rootViewController: vc)
        
        let statusBarBackgroundView = UIView()
        statusBarBackgroundView.translatesAutoresizingMaskIntoConstraints = false
        statusBarBackgroundView.backgroundColor = UIColor(red: CGFloat(54.0/255.0),
                                                          green: CGFloat(120.0/255.0),
                                                          blue: CGFloat(181.0/255.0),
                                                          alpha: 1.0)
        self.window?.addSubview(statusBarBackgroundView)
        self.window?.addConstraints(
            NSLayoutConstraint.constraints(withVisualFormat: "V:|[b(20)]",
                                           options: NSLayoutFormatOptions(),
                                           metrics: nil,
                                           views: ["b" : statusBarBackgroundView]))
        self.window?.addConstraints(
            NSLayoutConstraint.constraints(withVisualFormat: "H:|[b]|",
                                           options: NSLayoutFormatOptions(),
                                           metrics: nil,
                                           views: ["b" : statusBarBackgroundView]))
        
        return true
    }
}

Ways to create NSLayoutConstraint

There are a lot of ways to work with AutoLayout and Constraints. You can specify constraints using different techniques. Here I enumerate methods that I know:
  1. Using UIBuilder and Storyboard
  2. Using code and constraint method
  3. Using code and NSLayoutConstraint constructor
  4. Create Constraints with Visual Format 
  5. Using Framework - SnapKit
We will make simple task - create view of size: width 240, height 128, and center it vertically and horizontally.

1. Using UIBuilder


Adding width and height constraints


Align center vertically and horizontally

There are 4 constraints 

Result


2. Using code - way 1 - constraint method


Activate each constraint separately.
override func viewDidLoad() {
    super.viewDidLoad()
        
    let customView = UIView()
    customView.backgroundColor = UIColor.lightGray
    // 1. Add your view as subview
    self.view.addSubview(customView)
    // 2. Activate AutoLayout
    customView.translatesAutoresizingMaskIntoConstraints = false
    // 3. Activate Constraints
    customView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
    customView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
    customView.widthAnchor.constraint(equalToConstant: 240).isActive = true
    customView.heightAnchor.constraint(equalToConstant: 128).isActive = true
}

Or it can be done differently - activate all constraints together.
NSLayoutConstraint.activate([
    customView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor),
    customView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
    customView.widthAnchor.constraint(equalToConstant: 240),
    customView.heightAnchor.constraint(equalToConstant: 128)
 ])

3. Using code - way 2 - NSLayoutConstraint constructor


Another way to create NSLayoutConstraint
let centerXConstraint = NSLayoutConstraint(item: customView,
                                                   attribute: .centerX,
                                                   relatedBy: .equal,
                                                   toItem: self.view,
                                                   attribute: .centerX,
                                                   multiplier: 1,
                                                   constant: 0)
let centerYConstraint = NSLayoutConstraint(item: customView,
                                                   attribute: .centerY,
                                                   relatedBy: .equal,
                                                   toItem: self.view,
                                                   attribute: .centerY,
                                                   multiplier: 1,
                                                   constant: 0)
let widthConstraint = NSLayoutConstraint(item: customView,
                                                   attribute: .width,
                                                   relatedBy: .equal,
                                                   toItem: nil,
                                                   attribute: .notAnAttribute,
                                                   multiplier: 1,
                                                   constant: 240)
let heightConstraint = NSLayoutConstraint(item: customView,
                                                   attribute: .height,
                                                   relatedBy: .equal,
                                                   toItem: nil,
                                                   attribute: .notAnAttribute,
                                                   multiplier: 1,
                                                   constant: 128)

NSLayoutConstraint.activate([centerXConstraint, centerYConstraint, widthConstraint, heightConstraint])

Or, for activation of constraints, instead of NSLayoutConstraint.activate() we can use method of parent view addConstraints
self.view.addConstraints([centerXConstraint, centerYConstraint, widthConstraint, heightConstraint])

4. Create Constraints with Visual Format 

There is one more way to create constraints. It is called Visual Format. For creating constraint you must provide description of it with special syntax. V for Vertical constraints, H for Horizontal.
self.view.addConstraints(NSLayoutConstraint.constraints(
            withVisualFormat: "V:[customView(128)]",
            options: NSLayoutFormatOptions(),
            metrics: nil,
            views: ["customView": customView]))
self.view.addConstraints(NSLayoutConstraint.constraints(
            withVisualFormat: "H:[customView(240)]",
            options: NSLayoutFormatOptions(),
            metrics: nil,
            views: ["customView": customView]))
self.view.addConstraints(NSLayoutConstraint.constraints(
            withVisualFormat: "V:[superView]-(<=1)-[customView]",
            options: NSLayoutFormatOptions.alignAllCenterX,
            metrics: nil,
            views: ["customView": customView, "superView": self.view]))
self.view.addConstraints(NSLayoutConstraint.constraints(
            withVisualFormat: "H:[superView]-(<=1)-[customView]",
            options: NSLayoutFormatOptions.alignAllCenterY,
            metrics: nil,
            views: ["customView": customView, "superView": self.view]))

5. Using Framework - SnapKit

NSLayoutConstraints as Apple provide it is not most handy thing. So there is a framework which simplify creation of constraints.

You can get it here - SnapKit Framework

import SnapKit

let customView = UIView()
customView.backgroundColor = UIColor.lightGray
self.view.addSubview(customView)
customView.snp.makeConstraints { (maker) in
    maker.width.equalTo(240)
    maker.height.equalTo(128)
    maker.centerX.equalTo(self.view)
    maker.centerY.equalTo(self.view)
}

Friday, November 10, 2017

iOS Swift. How to create app without storyboard

There is a question: Is it possible to create iOS application . without using Storyboard?. Answer is Yes, it is possible. For implementing this we must create our custom UIWindow and set root controller for it. In code below I show how to do this.

AppDelegate class and didFinishLaunchingWithOptions method

In AppDelegate class method didFinishLaunchingWithOptions we create UIWindow and set root controller for it.
import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?


    func application(_ application: UIApplication, 
        didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        
        self.window = UIWindow(frame: UIScreen.main.bounds)
        self.window?.makeKeyAndVisible()
        
        let viewController = ViewController()
        let navigationController = UINavigationController(rootViewController: viewController)
        self.window?.rootViewController = navigationController
        
        return true
    }
}

ViewController

Our ViewController. We set title and add button to center of it using constraints.
import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.title = "No Storyboard"
        self.view.backgroundColor = UIColor.white
        
        let button =  UIButton(type: .system)
        button.setTitle("Success", for: .normal)
        self.view.addSubview(button)
        
        button.translatesAutoresizingMaskIntoConstraints = false
        button.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
        button.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
        button.widthAnchor.constraint(equalToConstant: 200).isActive = true
        button.heightAnchor.constraint(equalToConstant: 50).isActive = true
    }
}

Result