實現 UITableViewCell 側滑操作列表

Harley-xk發表於2017-12-19

曾經

我們都知道 UITableView 支援實現側滑操作,一般用來實現刪除一個專案,實現起來也很簡單,只需要實現 UITableView 的三個代理方法

  • 首先告訴UITableView我們需要實現的操作型別,比如返回一個.delete
public func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCellEditingStyle {
	return .delete
}
複製程式碼
  • 然後告訴 UITableView 側滑時刪除按鈕上顯示的文字
func tableView(_ tableView: UITableView, titleForDeleteConfirmationButtonForRowAt indexPath: IndexPath) -> String? {
	return "Delete"
}
複製程式碼
  • 最後實現按鈕觸發後執行的操作
public func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
	// Do somthing
}
複製程式碼

幾個專案下來,我們對這個三部曲早就是滾瓜爛熟了。不過有時候會想,一個簡單的操作按鈕,需要實現三個 Delegate 方法來達到效果,會不會太繁瑣了。。。

更何況還有一件更坑的事情:editingStyleForRowtitleForDeleteConfirmationButton這兩個方法屬於UITableViewDelegate協議,而commit editingStyle這個方法屬於UITableViewDataSource協議。

這意味著,如果你為UITableView實現了通用的DataSource協議,那麼要實現側滑操作就不可避免要破壞程式碼結構了。。。

另外,側滑支援多項操作的需求越來越旺盛,而此時我們的經典三部曲已經無法勝任工作了。

這時候要麼選擇第三方的實現方案,做好隨時被坑的準備;要麼,自己去實現一個更大的。。。坑?

iOS 8 之後

估計蘋果的工程師也為自己這個天才的設計折服了吧,於是在 iOS 8 引入了一個新的 API:UITableViewRowAction,先來看一看定義壓壓驚:

@available(iOS 8.0, *)
open class UITableViewRowAction : NSObject, NSCopying {
    
    public convenience init(style: UITableViewRowActionStyle, title: String?, handler: @escaping (UITableViewRowAction, IndexPath) -> Swift.Void)
    
    open var style: UITableViewRowActionStyle { get }

    open var title: String?

    @NSCopying open var backgroundColor: UIColor? // default background color is dependent on style

    @NSCopying open var backgroundEffect: UIVisualEffect?
}
複製程式碼

先來看構造器,便利構造器接受三個屬性:styletitlehandler

title這個不用說了,肯定就是按鈕顯示的標題了。

style通過定義可以看到,是一個UITableViewRowActionStyle型別的列舉值,通過構造器傳入之後便不可更改了,想來是決定操作按鈕的顯示樣式的吧,destructive這個單詞是不是很熟悉?

@available(iOS 8.0, *)
public enum UITableViewRowActionStyle : Int {
    case `default`
    case destructive
    case normal
}
複製程式碼

繼續往下看到handler,這是一個閉包,顯然是用來響應按鈕點選事件的了,終於不用另外實現一個Delegate方法去響應操作事件了麼?自從 iOS 4 之後引入了 block,蘋果已經一發不可收拾了,API 改造大軍正在路上。。。

class 定義裡面還有兩個屬性:

首先看到backgroundColor,字面意思很好理解,就是背景顏色了,後面註釋了一行小字:default background color is dependent on style

這證實了我們的猜想:style屬性決定了按鈕的樣式,也就是背景顏色,當然,我們也可以通過backgroundColor自己另外指定背景色。

最後一個屬性是backgroundEffect,是UIVisualEffect型別的。UIVisualEffect?!這是啥?有經驗的同學都知道,這是 iOS 7 之後引入的毛玻璃特效啊,不過研究了半天發現並沒有卵用?。也可能是我開啟的方式不對?有知道的同學可以指點下。。。

###實踐 好了,看完了類定義,趕緊來看看怎麼使用吧。先看一下UITableViewDelegate的定義,關於UITableViewRowAction只定義了一個方法:

func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]?
複製程式碼

終於決定拋棄三部曲了麼?,返回值是個陣列,這是支援多個操作項的節奏啊,廢話少說上程式碼:

func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
	let defaultAction = UITableViewRowAction(style: .default, title: "Default") 	{ (action, indexPath) in
		self.alertTitle("Default action at \(indexPath)")
	}
	let normalAction = UITableViewRowAction(style: .normal, title: "Normal") { (action, indexPath) in
		self.alertTitle("Normal action at \(indexPath)")
	}
	let destructiveAction = UITableViewRowAction(style: .destructive, title: "Delete") { (action, indexPath) in
		self.alertTitle("Delete action at \(indexPath)")
	}
	return [defaultAction, normalAction, destructiveAction]
}

func alertTitle(_ title: String) {
	let alert = UIAlertController(title: title, message: nil, preferredStyle: .alert)
	alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
	present(alert, animated: true)
}
複製程式碼

這裡定義了三個UITableViewRowAction,分別對應不同的style,執行之後可以發現,defaultdestructive預設都是紅色,normal預設是灰色。這裡只是簡單定義了一個alertTitle方法用來響應點選反饋,實際專案中需要替換成特定的業務邏輯。

###結語 至此終於水完了UITableViewRowAction相關的內容,雖然這是個 iOS 8 就出來的特性了,不過最近才被我注意到,而且還沒有把backgroundEffect屬性的特性摸清,實在是慚愧。

好訊息是現在大多數 App 都是至少支援 iOS 8 + 了吧?可以再也不用寫繁瑣的三部曲了。

相關文章