Возьмём за основу статью Знакомьтесь, Swift!, где показано как сделать простое приложение на Swift, и добавим туда такие известные и полезные штуки как pull to refresh и infinite scrolling используя встроенные возможности языка. Чтобы было еще интереснее, добавим немного асинхронности, иначе приложение будет каждый раз замирать на время обновления.
За основу мы берём вышеозначенный пример, поэтому будем просто дополнять его. Для начала в класс контроллера добавим 2 переменные, которые будут отвечать за количество ячеек и за текст, отображаемый в ячейках
И модифицируем код генерации ячеек, используя эти переменные
Теперь сделаем привязку TableView к контроллеру, чтобы можно было производить над ней нужные нам манипуляции. В interface builder выбираем TableView, нажимаем cmd+alt+enter и правой кнопкой тянем в появившееся окно
Вбиваем имя по которому будем обращаться
Так же добавим в TableView компонент View, на котором разместим 2 элемента, чтобы получилось следующее
Эта View нужна для того, чтобы отображать уведомление о том, что идет обновление, и нам нужно, чтобы она была видна только тогда, когда идет подгрузка новых данных (для этого будем использовать свойство tableFooterView.hidden), поэтому нужно ее скрыть вначале, и показать только потом. Так же нужно будет вручную запускать анимацию UIActivityIndicatorView, для этого, аналогично как и выше, добавим привязку
Для предварительной подготовки этих действий будет достаточно.
Теперь можно перейти непосредственно к реализации pull to refresh. В класс контролера добавим новую переменную специального класса UIRefreshControl
В viewDidLoad добавим код, инициализирующую эту переменную и привязывающую ее к tableView
Теперь нам нужно определить функцию refresh, которая и будет вызываться каждый раз, при выполнении действия pull to refresh. Чтобы обновление происходило в асинхронном режиме, используем следующую схему (не буду вдаваться в описание подробностей, разобраться коде не трудно самостоятельно)
В итоге, получаем
UPD: Если вы используете UITableViewController (а лучше использовать его в данном и аналогичных случаях), то код будет даже проще. В UITableViewController уже есть свойства tableView и refreshControl, поэтому не нужно делать привязку UITableView вручную и не надо в классе объявлять refreshControl. Достаточно написать в viewDidLoad следующий код и все будет работать
С бесконечной прокруткой немного по сложнее, но не на много. В класс контролера добавим новую переменную loadMoreStatus, которая будет отвечать за защиту от повторного обновления, если оно уже запущено
В viewDidLoad добавим код, который первично скроет View с информацией о подгрузке новых данных
Добавим определение специальной функции scrollViewDidScroll, которая вызывается каждый раз, когда происходит любая прокрутка. Если мы доматываем до конца списка, то вызывается функция loadMore, которая реализует асинхронную подгрузку новых данных
В результате все работает, приложение не замирает, а данные добавляются успешно
Вот таким нехитрым способом можно реализовать pull to refresh и infinite scrolling, и само собой, из-за асинхронного обновления, можно, например, делать запросы JSON к серверу простым синхронным способом и это не помешает работе приложения.
Подготовка
За основу мы берём вышеозначенный пример, поэтому будем просто дополнять его. Для начала в класс контроллера добавим 2 переменные, которые будут отвечать за количество ячеек и за текст, отображаемый в ячейках
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var countRow = 20
var text = "Habrapost"
И модифицируем код генерации ячеек, используя эти переменные
func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int
{
return countRow
}
func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell!
{
let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: "MyTestSwiftCell")
cell.text = "\(text) \(indexPath.row)"
cell.detailTextLabel.text = "Hi, \(indexPath.row)"
cell.detailTextLabel.textColor = UIColor.purpleColor()
return cell
}
Теперь сделаем привязку TableView к контроллеру, чтобы можно было производить над ней нужные нам манипуляции. В interface builder выбираем TableView, нажимаем cmd+alt+enter и правой кнопкой тянем в появившееся окно
Вбиваем имя по которому будем обращаться
Так же добавим в TableView компонент View, на котором разместим 2 элемента, чтобы получилось следующее
Эта View нужна для того, чтобы отображать уведомление о том, что идет обновление, и нам нужно, чтобы она была видна только тогда, когда идет подгрузка новых данных (для этого будем использовать свойство tableFooterView.hidden), поэтому нужно ее скрыть вначале, и показать только потом. Так же нужно будет вручную запускать анимацию UIActivityIndicatorView, для этого, аналогично как и выше, добавим привязку
Для предварительной подготовки этих действий будет достаточно.
Pull to refresh
Теперь можно перейти непосредственно к реализации pull to refresh. В класс контролера добавим новую переменную специального класса UIRefreshControl
var refreshControl:UIRefreshControl!
В viewDidLoad добавим код, инициализирующую эту переменную и привязывающую ее к tableView
override func viewDidLoad() {
super.viewDidLoad()
refreshControl = UIRefreshControl()
refreshControl.attributedTitle = NSAttributedString(string: "Идет обновление...")
refreshControl.addTarget(self, action: "refresh:", forControlEvents: UIControlEvents.ValueChanged)
tableView.addSubview(refreshControl)
}
Теперь нам нужно определить функцию refresh, которая и будет вызываться каждый раз, при выполнении действия pull to refresh. Чтобы обновление происходило в асинхронном режиме, используем следующую схему (не буду вдаваться в описание подробностей, разобраться коде не трудно самостоятельно)
func refresh(sender:AnyObject) {
refreshBegin("Refresh",
refreshEnd: {(x:Int) -> () in
self.tableView.reloadData()
self.refreshControl.endRefreshing()
})
}
func refreshBegin(newtext:String, refreshEnd:(Int) -> ()) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
println("refreshing")
self.text = newtext
sleep(2)
dispatch_async(dispatch_get_main_queue()) {
refreshEnd(0)
}
}
}
В итоге, получаем
UPD: Если вы используете UITableViewController (а лучше использовать его в данном и аналогичных случаях), то код будет даже проще. В UITableViewController уже есть свойства tableView и refreshControl, поэтому не нужно делать привязку UITableView вручную и не надо в классе объявлять refreshControl. Достаточно написать в viewDidLoad следующий код и все будет работать
override func viewDidLoad() {
super.viewDidLoad()
refreshControl = UIRefreshControl()
refreshControl.attributedTitle = NSAttributedString(string: "Идет обновление...")
refreshControl.addTarget(self, action: "refresh:", forControlEvents: UIControlEvents.ValueChanged)
}
Infinite scrolling
С бесконечной прокруткой немного по сложнее, но не на много. В класс контролера добавим новую переменную loadMoreStatus, которая будет отвечать за защиту от повторного обновления, если оно уже запущено
var loadMoreStatus = false
В viewDidLoad добавим код, который первично скроет View с информацией о подгрузке новых данных
override func viewDidLoad() {
super.viewDidLoad()
refreshControl = UIRefreshControl()
refreshControl.attributedTitle = NSAttributedString(string: "Идет обновление...")
refreshControl.addTarget(self, action: "refresh:", forControlEvents: UIControlEvents.ValueChanged)
tableView.addSubview(refreshControl)
self.tableView.tableFooterView.hidden = true
}
Добавим определение специальной функции scrollViewDidScroll, которая вызывается каждый раз, когда происходит любая прокрутка. Если мы доматываем до конца списка, то вызывается функция loadMore, которая реализует асинхронную подгрузку новых данных
func scrollViewDidScroll(scrollView: UIScrollView!) {
let currentOffset = scrollView.contentOffset.y
let maximumOffset = scrollView.contentSize.height - scrollView.frame.size.height
let deltaOffset = maximumOffset - currentOffset
if deltaOffset <= 0 {
loadMore()
}
}
func loadMore() {
if ( !loadMoreStatus ) {
self.loadMoreStatus = true
self.activityIndicator.startAnimating()
self.tableView.tableFooterView.hidden = false
loadMoreBegin("Load more",
loadMoreEnd: {(x:Int) -> () in
self.tableView.reloadData()
self.loadMoreStatus = false
self.activityIndicator.stopAnimating()
self.tableView.tableFooterView.hidden = true
})
}
}
func loadMoreBegin(newtext:String, loadMoreEnd:(Int) -> ()) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
println("loadmore")
self.text = newtext
self.countRow += 20
sleep(2)
dispatch_async(dispatch_get_main_queue()) {
loadMoreEnd(0)
}
}
}
В результате все работает, приложение не замирает, а данные добавляются успешно
Вот таким нехитрым способом можно реализовать pull to refresh и infinite scrolling, и само собой, из-за асинхронного обновления, можно, например, делать запросы JSON к серверу простым синхронным способом и это не помешает работе приложения.