Search This Blog

Thursday, November 24, 2016

iOS Swift UITableView. How to scroll to a special cell

For scrolling table view to special cell(row) we need to use method scrollToRow and index of special cell.
let index = self.findIndex()
let indexPath = NSIndexPath(row: index, section: 0)
self.tableView.scrollToRow(at: indexPath as IndexPath, at: .top, animated: true)

Wednesday, November 23, 2016

How to find nearest(closest) date to today

If we have array of dates and we need to find what date is neasrest to today date. There is special method in iOS SDK API - timeIntervalSince
public func timeIntervalSince(_ date: Date) -> TimeInterval

This method will return double value - time interval between two dates - self date object and parameter date. Also this time interval can be positive or negative. So for getting nearest date we need to get absolute positive value of time interval. For that purpose we will use method fabs
public func fabs(_: Double) -> Double

Algorithm - How to define nearest date to today
func findNearestDateToToday(datesArray: [Date]) -> Date {
        let today = Date()
        let firstDate = datesArray[0]
        var min = fabs(today.timeIntervalSince(firstDate))
        var minIndex = 0
        for i in 1..<datesArray.count {
            let currentDate = datesArray[i]
            let currentMin = fabs(today.timeIntervalSince(currentDate))
            if currentMin < min {
                min = currentMin
                minIndex = i
            }
        }
        return datesArray[minIndex]
}

Sunday, November 6, 2016

FizzBuzz task in Swift

Task:
Write a program that prints the numbers from 1 to 100. But for multiples of three print "Fizz" instead of the number and for the multiples of five print "Buzz". For numbers which are multiples of both three and five print "FizzBuzz".


Solutions


Zero case - wrong solution (for better understanding)
In this case you never see "FizzBuzz" in output console, because all items that can be divided by 3 and 5 would be already count with print "Fizz".
// zero - wrong solution
for var i in 1..<101 {
    if i % 3 == 0 {
        print("Fizz")
    } else if i % 5 == 0 {
        print("Buzz")
    } else if i % 3 == 0 && i % 5 == 0 {
        print("FizzBuzz")
    } else {
        print(i)
    }
}


First - simple and easy
// first - for loop
for var i in 1..<101 {
    if i % 3 == 0 && i % 5 == 0 {
        print("FizzBuzz")
    } else if i % 3 == 0 {
        print("Fizz")
    } else if i % 5 == 0 {
        print("Buzz")
    } else {
        print(i)
    }
}

Second - using magic number 15 because 3 * 5 = 15
//second - foor loop and magic number 15 = 5 * 3
for var i in 1..<101 {
    if (i % 15 == 0) {
        print("FizzBuzz")
    } else if i % 3 == 0 {
        print("Fizz")
    } else if i % 5 == 0 {
        print("Buzz")
    } else {
        print(i)
    }
}


Third - using case instead of if..else statements
// third - for loop and case operator instead of switch
for var i in 1..<101 {
    switch i {
    case _ where i % 15 == 0:
        print("FizzBuzz")
    case _ where i % 3 == 0:
        print("Fizz")
    case _ where i % 5 == 0:
        print("Buzz")
    default:
        print(i)
    }
}

Fourth - make result string
//fourth - make a result string
for var i in 1..<101 {
    var str = ""
    if i % 3 == 0 {
        str = str + "Fizz"
    }
    if i % 5 == 0 {
        str = str + "Buzz"
    }
    if str == "" {
        str = String(i)
    }
    print(str)
}

Tuesday, November 1, 2016

ios Swift. How to add SegmentedControl in NavigationBar and keep title

What if we want big navigation bar - with title and segmented control under it. How to create this? There is a trick. We can create view under navigation bar, put segmented control in it, make color of navigation bar and color of container view the same and remove botton line of navigation bar.

Steps of creation:

  1.  Add View under Navigation Bar
  2.  Put Segmented Control in this view
  3.  Set Navigation Bar NOT Translucent
  4.  Set background and tint colors for navigation bar
  5.  Set background color for segmented control
  6.  Remove bottom line of Navigation Bar

We will put the code in didFinishLaunchingWithOptions method and use UIAppearance API for UISegmentedControl and for UINavigationBar.

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions:
        [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        
        // get segmented appearance object
        let segmentedAppearance = UISegmentedControl.appearance()
        
        // set background color for segmented
        segmentedAppearance.tintColor = UIColor.white
        
        // get nav bar appearance object
        let navigationAppearance = UINavigationBar.appearance()
        
        navigationAppearance.isTranslucent = false
        
        // change color of navigation bar background
        navigationAppearance.barTintColor = UIColor.red
        
        // change color of navigation bar items (buttons)
        navigationAppearance.tintColor = UIColor.white
        
        // change color of navigation bar title
        navigationAppearance.titleTextAttributes = [NSForegroundColorAttributeName: UIColor.white];
        
        // remove bottom line for navigation bar
        navigationAppearance.shadowImage = UIImage()
        navigationAppearance.setBackgroundImage(UIImage(), for: .default)
        
        return true
    }


Also do not forget to set color of container view
override func viewDidLoad() {
        super.viewDidLoad()
        
        segmentContainer.backgroundColor = UIColor.red
}

Adding container view under navigation bar

After add views and setup colors - one issue is still here. It is bottom line of Navigation bar.


Finally removing bottom line 



iOS Swift. NotificationCenter How to post and receive local notifications

How to transfer data across the application, for example when loading is finished and you need to update displayed information. For that purpose you can use Notification Center and Local Notifications. There are three thing you need:

1. Add Observer
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.updateCountLabel(_:)), 
    name: Notification.Name("UpdateCountLabelObserver"), object: nil)


2. Add function to handle notification
func updateCountLabel(_ notification: Notification) {
    guard let countStr = notification.userInfo?["count"] as? String else {
        return
    }        
    label4.text = "\(countStr)"
}

3. Post notification
let dict = ["count": String(55)]
NotificationCenter.default.post(
    name: Notification.Name("UpdateCountLabelObserver"), object: nil, userInfo: dict)




iOS Swift. How to work with DateFormatter

How to display date in specific format. For example, 01 August 1989,  or in specific locale (Russian: 1 августа 1989) or in specific timezone(MSK or PST)
let date = Date()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd MMM yyyy HH:mm"
label1.text = "With Format: \(dateFormatter.string(from: date))"
        
let locale = Locale(identifier: "ru")
dateFormatter.locale = locale
label2.text = "With RU Locale:  \(dateFormatter.string(from: date))"
        
let timeZone = TimeZone(abbreviation: "MSK")
dateFormatter.timeZone = timeZone
label3.text = "With MSK TimeZone: \(dateFormatter.string(from: date))"

Result


What if we get string with data in specific format and we need to display it in other format(and in specific timezone and specific locale). 

For example, 
input format: yyyy-MM-dd'T'HH:mm:ss.SSSZ
output format: dd MMMM yyyy HH:mm

Firstly, we set input format to DateFormatter and convert input string to date. After that we set nre format and convert date to new string.
 let dateStr = "2016-10-31T14:51:21.780+08:00"
 label4.text = "Before: \(dateStr)"
 let dateFormatter = DateFormatter()
 dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
        
 let timeZone = TimeZone(abbreviation: "MSK")
 dateFormatter.timeZone = timeZone
        
 let locale = Locale(identifier: "ru")
 dateFormatter.locale = locale
        
 let date = dateFormatter.date(from: dateStr)
        
 dateFormatter.dateFormat = "dd MMMM yyyy HH:mm"
 let formattedDateStr = dateFormatter.string(from: date!)
        
 label5.text = "After: \(formattedDateStr)"

Conversion Result