Monday, November 9, 2015

scrollViewDidScroll - Pull up table to refresh content and change the table background color

Update: December 10, 2016 (Xcode 8.1 and Swift 3.0.1)

Original Post: November 9, 2015

This example shows how to:

1. Refresh the table view by adding ten extra rows while pulling the table up to the bottom.
2. Change the table background color for every 10 rows.

Note: This example does not include the activity indicator.

Edit ViewController.swift as below:

Xcode 8.1 and Swift 3.0.1:


import UIKit

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    
    var tableView : UITableView!
    var tableRowNumber : Int = 10
    
    //Colors used in the table
    var colorArray = [UIColor.yellow, UIColor.orange, UIColor.cyan, UIColor.lightGray, UIColor.white]
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        tableView = UITableView(frame: CGRect(x: 0, y: 30, width: view.bounds.width, height: 300))

        tableView.dataSource = self
        tableView.delegate = self
        
        //Disable the default bouncing feature.
        tableView.bounces = false
        
        view.addSubview(tableView)
        
    }
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return tableRowNumber
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "myIdentifier")
        cell.textLabel?.text = "Row \(indexPath.row)"
        
        //Change the table background color for every 10 rows.
        let color = colorArray[(indexPath.row/10)%colorArray.count]
        cell.backgroundColor = color
        
        return cell
    }
    
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        
        //If we reach the end of the table.
        if ((scrollView.contentOffset.y + scrollView.frame.size.height) >= scrollView.contentSize.height)
        {
            //Add ten more rows and reload the table content.
            tableRowNumber += 10
            tableView.reloadData()
        }
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

}

Original Post: November 9, 2015

import UIKit

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    
    var tableView : UITableView!
    var tableRowNumber : Int = 10
    
    //Colors used in the table
    var colorArray = [UIColor.yellowColor(), UIColor.orangeColor(), UIColor.cyanColor(), UIColor.lightGrayColor(), UIColor.whiteColor()]
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        tableView = UITableView(frame: CGRectMake(0, 30, view.bounds.width, 300))
        tableView.dataSource = self
        tableView.delegate = self
        
        //Disable the default bouncing feature.
        tableView.bounces = false
        
        view.addSubview(tableView)
        
    }
    
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return tableRowNumber
    }
    
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "myIdentifier")
        cell.textLabel?.text = "Row \(indexPath.row)"
        
        //Change the table background color for every 10 rows.
        let color = colorArray[(indexPath.row/10)%colorArray.count]
        cell.backgroundColor = color
        
        return cell
    }
    
    func scrollViewDidScroll(scrollView: UIScrollView) {
        
        //If we reach the end of the table.
        if ((scrollView.contentOffset.y + scrollView.frame.size.height) >= scrollView.contentSize.height)
        {
            //Add ten more rows and reload the table content.
            tableRowNumber += 10
            tableView.reloadData()
        }
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

}

Result:


==========================
Related Post:

Sunday, November 8, 2015

iOS Chinese font comparison: PingFang with various weights 以iOS程式顯示並比較六種不同粗細變化的「蘋方」字體

Update: August 3, 2017 (Swift 3.1 + Xcode 8.3.3)

Original Post: November 8, 2015 (Swift 2)

This post shows how to list the PingFang TC(Traditional Chinese) font with 6 different weights in a table for comparison.

蘋果公司於9月正式推出的iOS9及OS X 10.11 El Capitan (酋長巨石)中,新增了「蘋方」中文字型,有正體(TC)、香港(HK)及簡體(SC)三個版本,每個版本皆有6種不同的粗細變化。

以下的Swift程式以表格列出不同粗細的「蘋方」字體:

ViewController.swift

Update: August 3, 2017 (Swift 3.1 + Xcode 8.3.3)


import UIKit

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    
    //font names
    let fontArray : [String] = [".PingFangTC-Ultralight",".PingFangTC-Thin",".PingFangTC-Light",".PingFangTC-Regular",".PingFangTC-Medium",".PingFangTC-Semibold"]
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let tableView = UITableView(frame: CGRect(x: 0, y: 50, width: view.bounds.width, height: view.bounds.height-50))
        tableView.dataSource = self
        tableView.delegate = self
        
        //Do not draw line separators for empty cells.
        tableView.tableFooterView = UIView()
        
        view.addSubview(tableView)
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 6
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "myIdentifier")
        cell.textLabel?.text = "蘋方字型測試 正體"
        cell.textLabel?.font = UIFont(name: fontArray[indexPath.row], size: 28)
        cell.textLabel?.textAlignment = NSTextAlignment.center
        return cell
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

}


Original Post: November 8, 2015 (Swift 2)

import UIKit

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    
    //font names
    let fontArray : [String] = [".PingFangTC-Ultralight",".PingFangTC-Thin",".PingFangTC-Light",".PingFangTC-Regular",".PingFangTC-Medium",".PingFangTC-Semibold"]
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let tableView = UITableView(frame: CGRectMake(0, 50, view.bounds.width, view.bounds.height-50))
        tableView.dataSource = self
        tableView.delegate = self
        
        //Do not draw line separators for empty cells.
        tableView.tableFooterView = UIView()
        
        view.addSubview(tableView)
        
    }
    
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 6
    }
    
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "myIdentifier")
        cell.textLabel?.text = "蘋方字型測試 正體"
        cell.textLabel?.font = UIFont(name: fontArray[indexPath.row], size: 30)
        
        return cell
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

}


結果如下:



相關資訊:

在樹莓派上安裝中文字型 Install Chinese Fonts with Raspberry Pi
Google 推新版字體網站,共 804 字體供大家免費取用!
Google Fonts
解決字體顯示問題!蘋果、Google、微軟、Adobe 攜手開發字體(inside.com.tw)

Saturday, November 7, 2015

UIRefreshControl - Pull down to refresh for the UITableView

This post shows how to refresh table by pulling down without using UITableViewController. An activity indicator is shown above the table while pulling it down. Tested with Xcode 6.4 and 7.1.

Edit ViewController.swift as below:

import UIKit

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    
    var tableView : UITableView!
    var tableRowNumber : Int = 5
    var refreshControl : UIRefreshControl!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        tableView = UITableView(frame: CGRectMake(0, 30, view.bounds.width, 400))
        tableView.dataSource = self
        tableView.delegate = self
        
        view.addSubview(tableView)
        
        refreshControl = UIRefreshControl()
        refreshControl.addTarget(self, action: Selector("enlargeTable"), forControlEvents: UIControlEvents.ValueChanged)
        tableView.addSubview(refreshControl)
    }
    
    func enlargeTable() {
        tableRowNumber += 5
        tableView.reloadData()
        refreshControl.endRefreshing()
    }
    
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return tableRowNumber
    }
    
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "myIdentifier")
        cell.textLabel?.text = "\(indexPath.row)"
        
        return cell
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    

}

=========================
Related Post:

Tuesday, November 3, 2015

Using completion block to detect dismissViewControllerAnimated of ViewControllerB in ViewControllerA

Update: August 3, 2017 (Swift 3.1 + Xcode 8.3.3)
Original Post: November 3, 2015 (Swift 2)

If I have a default ViewControllerA, which presents ViewControllerB with 'OverCurrentContext' UIModalPresentationStyle, then I face a problem that 'viewWillAppear' in ViewControllerA is not called after ViewControllerB is dismissed. Consequently, it would be difficult for me to run some codes in ViewControllerA since I don't know when ViewControllerB is dismissed. 

Here is my original description to this problem:

iOS: Detect dismissViewControllerAnimated while using UIModalPresentationStyle.OverCurrentContext

In order to run some codes in ViewControllerA right after ViewControllerB is dismissed, a completion block is created in ViewControllerB. Then function myFuncInViewControllerA() in ViewControllerA can be executed at the right moment. The solution is as below:

Update: August 3, 2017 (Swift 3.1 + Xcode 8.3.3)

Remember to select ViewControllerA.swift as a custom class in the identify inspector in the storyboard after renaming ViewController.swift as ViewControllerA.swift.



ViewControllerA.swift:


import UIKit

class ViewControllerA: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let button = UIButton(frame: CGRect(x: (view.frame.width-200)/2, y: 50, width: 200, height: 20))
        button.setTitle("Button", for: UIControlState.normal)
        button.setTitleColor(UIColor.blue, for: UIControlState.normal)
        button.setTitleColor(UIColor.cyan, for: UIControlState.highlighted)
        button.contentMode = UIViewContentMode.center
        button.addTarget(self, action: #selector(btnPressed), for: UIControlEvents.touchUpInside)
        view.addSubview(button)
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        print("viewWillAppear")
    }
    
    func btnPressed() {
        let controllerB = ViewControllerB()
        controllerB.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext
        controllerB.modalTransitionStyle = UIModalTransitionStyle.crossDissolve
        
        controllerB.dismissVCCompletion(){ () in
            self.myFuncInViewControllerA()
        }
        
        present(controllerB, animated: true, completion: nil)
    }
    
    func myFuncInViewControllerA() {
        print("Back to ViewControllerA!")
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}

ViewControllerB.swift:


import UIKit

class ViewControllerB: UIViewController {
    
    typealias typeCompletionHandler = () -> ()
    var completion : typeCompletionHandler = {}
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = UIColor(white: 0, alpha: 0.5)
        
        let viewB = UIView(frame: CGRect(x: 0, y: 0, width: view.frame.width*0.6, height: view.frame.height*0.6))
        viewB.center = view.center
        viewB.backgroundColor = UIColor.orange
        
        let buttonBack = UIButton(frame: CGRect(x: (viewB.frame.width-200)/2, y: 100, width: 200, height: 20))
        buttonBack.setTitle("Back", for: UIControlState.normal)
        buttonBack.setTitleColor(UIColor.blue, for: UIControlState.normal)
        buttonBack.setTitleColor(UIColor.cyan, for: UIControlState.highlighted)
        buttonBack.contentMode = UIViewContentMode.center
        buttonBack.addTarget(self, action: #selector(btnBackPressed), for: UIControlEvents.touchUpInside)
        viewB.addSubview(buttonBack)
        
        view.addSubview(viewB)
    }
    
    func btnBackPressed() {
        dismiss(animated: true, completion: {
            self.completion()
        })
    }
    
    func dismissVCCompletion(completionHandler: @escaping typeCompletionHandler) {
        self.completion = completionHandler
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

}

Result:


Original Post: November 3, 2015 (Swift 2)

ViewControllerA.swift:


class ViewControllerA: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let button = UIButton(frame: CGRectMake(50, 100, 200, 20))
        button.setTitle("Button", forState: UIControlState.Normal)
        button.setTitleColor(UIColor.blueColor(), forState: UIControlState.Normal)
        button.setTitleColor(UIColor.cyanColor(), forState: UIControlState.Highlighted)
        button.addTarget(self, action: "btnPressed:", forControlEvents: UIControlEvents.TouchUpInside)
        view.addSubview(button)
        
    }
    
    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)
        print("viewWillAppear")
    }
    
    func btnPressed(sender: UIButton) {
        let controllerB = ViewControllerB()
        controllerB.modalPresentationStyle = UIModalPresentationStyle.OverCurrentContext
        controllerB.modalTransitionStyle = UIModalTransitionStyle.CrossDissolve
        
        controllerB.dismissVCCompletion(){ () in
            self.myFuncInViewControllerA()
        }

        presentViewController(controllerB, animated: true, completion: nil)
    }
    
    func myFuncInViewControllerA() {
        print("Back to ViewControllerA!")
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}


ViewControllerB.swift:

class ViewControllerB: UIViewController {

    typealias typeCompletionHandler = () -> ()
    var completion : typeCompletionHandler = {}
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = UIColor(white: 0, alpha: 0.5)
        
        let viewB = UIView(frame: CGRectMake(0,0,view.frame.width*0.6,view.frame.height*0.6))
        viewB.center = view.center
        viewB.backgroundColor = UIColor.orangeColor()

        let buttonBack = UIButton(frame: CGRectMake(30, 100, 200, 20))
        buttonBack.setTitle("Back", forState: UIControlState.Normal)
        buttonBack.setTitleColor(UIColor.blueColor(), forState: UIControlState.Normal)
        buttonBack.setTitleColor(UIColor.cyanColor(), forState: UIControlState.Highlighted)
        buttonBack.addTarget(self, action: "btnBackPressed:", forControlEvents: UIControlEvents.TouchUpInside)
        viewB.addSubview(buttonBack)
        
        view.addSubview(viewB)
    }
    
    func btnBackPressed(sender: UIButton) {
        dismissViewControllerAnimated(true, completion: {
            self.completion()
        })
    }
    
    func dismissVCCompletion(completionHandler: typeCompletionHandler) {
        self.completion = completionHandler
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

}

References: