[Swift通天遁地]二、表格表单,7电子邮件Mail:实现单元格左右滑动调出功能按钮

★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

➤微信公众号:山青咏芝(shanqingyongzhi)

➤博客园地址:山青咏芝(https://www.cnblogs.com/strengthen/

➤GitHub地址:https://github.com/strengthen/LeetCode

➤原文地址:

➤如果链接不是山青咏芝的博客园地址,则可能是爬取作者的文章。

➤原文已修改更新!强烈建议点击原文地址阅读!支持作者!支持原创!

★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

热烈欢迎,请直接点击!!!

进入博主App Store主页,下载使用各个作品!!!

注:博主将坚持每月上线一个新app!!!

目录:[Swift]通天遁地Swift

本文将演示对单元格进行扩展,当手指在单元格左右滑动时,弹出不同的功能菜单。

Github项目:【MGSwipeTableCell】

下载该项目的源代码。文件夹【demo】->文件夹【MailAppDemoSwift】->文件夹【MailAppDemoSwift】

->双击文件【MailAppDemoSwift。xcodeproj】打开示例工程。

选择该项目中的几个文件,拖动到自己的开发项目中。按住【Shift】,选择

【MGSwipeButton.h】

【MGSwipeButton.m】

【MGSwipeTableCell.h】

【MGSwipeTableCell.m】

按下【Command】,以选择其他不相邻的文件。

【MailViewController.swift】

【MailTableCell.swift】

【ObjCBridgingHeader.h】

将上面选择的7个文件拖动到自己的项目中。

->保持默认的设置选项,点击【Finish】

接着对项目进行一些设置,以引入桥接文件。

点击项目名称【DemoApp】->【Buildings】->桥接文件配置区域【Object-C Bridging Header】

->在配置选项中双击,打开配置窗口。

->在配置窗口中,输入刚刚导入的桥接文件的名称:【DemoApp/ObjCBridgingHeader.h】

在项目导航区,打开视图控制器的代码文件【ViewController.swift】

现在开始编写代码,创建一个可通过左右滑动,来调出功能按钮的表格。

  1 import UIKit
  2 
  3 //添加一个邮件数据类,这个类将用来表示表格中的数据
  4 class MailData
  5 {
  6     //给类添加四个属性:
  7     //1.来源
  8     var from: String!
  9     //2.主题
 10     var subject: String!
 11     //3.内容
 12     var message: String!
 13     //4.日期
 14     var date: String!
 15     //添加两个属性
 16     //1.邮件是否已被阅读
 17     var read = false
 18     //2.邮件是否拥有标记
 19     var flag = false
 20 }
 21 
 22 //创建一个别名,表示功能按钮被点击时所执行的方法的类型。
 23 typealias MailActionCallback = (_ cancelled: Bool, _ deleted: Bool, _ actionIndex: Int) -> Void
 24 
 25 //使当前的视图控制器类,遵循:
 26 //1.表格的数据源协议UITableViewDataSource
 27 //2.表格视图代理协议UITableViewDelegate
 28 //3.滑动表格单元格代理协议
 29 //4.动作表单协议
 30 class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, MGSwipeTableCellDelegate, UIActionSheetDelegate
 31 {
 32     //添加一个表格视图类型的变量,作为当前类的一个属性
 33     var tableView: UITableView!
 34     //创建一个邮件数据类型的数组,作为表格的数据源
 35     var demoData = [MailData]()
 36     //使用刚刚创建的别名,创建一个动作类型
 37     var actionCallback: MailActionCallback?
 38     
 39     //添加一个方法,用来设置表格的数据源
 40     func prepareDemoData()
 41     {
 42         //创建第一个字符串数组常量,作为邮件的来源
 43         var from = [
 44             "Vincent",
 45             "Mr Glass",
 46             "Marsellus",
 47             "Ringo",
 48             "Sullivan"
 49         ]
 50         
 51         //创建第二个字符串数组常量,作为邮件的主题
 52         var subjects = [
 53             "You think water moves fast?",
 54             "They called me Mr Glass",
 55             "The path of the righteous man",
 56             "Do you see any Teletubbies in here?",
 57             "Now that we know who you are"
 58         ]
 59         
 60         //创建第三个字符串数组常量,作为邮件的信息
 61         var messages = [
 62             "You should see ice. It moves like it has a mind. ",
 63             "And I will strike down upon thee with great vengeance and furious anger.",
 64             "Look, just because I don't be givin' no man a foot massage don't make it right.",
 65             "No? Well, that's what you see at a toy store.",
 66             "In a comic, you know how you can tell who the arch-villain's going to be?"
 67         ]
 68         
 69         //通过一个循环,遍历信息数组
 70         for i in 0 ..< messages.count
 71         {
 72             //创建一个邮件数据对象
 73             let mail = MailData()
 74             //从数组中加载相应的内容
 75             mail.from = from[i]
 76             //并依次设置邮件数据对象的各个属性
 77             mail.subject = subjects[i]
 78             mail.message = messages[i]
 79             //设置邮件数据对象的时间属性
 80             mail.date = String(format: "11:%d", arguments: [43 - i])
 81             //将邮件数据对象,添加到表格的数据源数组当中
 82             demoData.append(mail)
 83         }
 84     }
 85     
 86     //添加一个方法
 87     //用来从数据源数组中,根据单元格的位置获得相应的数据
 88     func mailForIndexPath(_ path: IndexPath) -> MailData
 89     {
 90         //根据单元格的行号,返回数据源数组中的数据
 91         return demoData[(path as NSIndexPath).row]
 92     }
 93     
 94     //添加一个方法
 95     //用来响应单元格中的删除按钮被点击时的事件
 96     func deleteMail(_ path:IndexPath)
 97     {
 98         //从数据源数组中删除指定位置的数据
 99         demoData.remove(at: (path as NSIndexPath).row)
100         //从表格中删除指定的单元格
101         tableView.deleteRows(at: [path], with: .left)
102     }
103     
104     //添加一个方法
105     //当邮件的状态改变时调用此方法
106     //例如邮件从未读转换为已读
107     func updateCellIndicator(_ mail: MailData, cell: MailTableCell)
108     {
109         //创建两个颜色变量,作为标识邮件状态的图标的颜色
110         var color: UIColor
111         var innerColor : UIColor?
112         
113         //根据邮件不同的状态,设置邮件不同的颜色
114         if !mail.read && mail.flag
115         {
116             //当邮件未读并有标识时
117             //设置标识图标的标识颜色
118             color = UIColor.init(red: 1.0, green: 149/255.0, blue: 0.05, alpha: 1.0)
119             //设置标识图标内部的颜色
120             innerColor = UIColor.init(red: 0.0, green: 122/255.0, blue: 1.0, alpha: 1.0)
121         }
122         else if mail.flag
123         {
124             //当邮件具有标识时,
125             //设置标识图标的标识颜色
126             color = UIColor.init(red: 1.0, green: 149/255.0, blue: 0.05, alpha: 1.0)
127         }
128         else if mail.read
129         {
130             //当邮件处于已读状态时,
131             //设置标识图标的颜色为无色
132             //即在视觉上隐藏该图标
133             color = UIColor.clear
134         }
135         else
136         {
137             //设置邮件在其他状态下的默认颜色
138             color = UIColor.init(red: 0.0, green: 122/255.0, blue: 1.0, alpha: 1.0)
139         }
140         
141         //设置标识图标的颜色
142         cell.indicatorView.indicatorColor = color
143         //设置标识图标内部的颜色
144         cell.indicatorView.innerColor = innerColor
145     }
146     
147     //添加一个方法,用来弹出一个动作表单
148     func showMailActions(_ mail: MailData, callback: @escaping MailActionCallback)
149     {
150         //设置动作属性的值
151         actionCallback = callback
152         
153         //初始化一个动作表单,依次设置相关参数
154         let sheet = UIActionSheet.init(title: "Actions", //标题
155                                        delegate: self,//代理
156                                        cancelButtonTitle: "Cancel",//取消按钮的标题
157                                        destructiveButtonTitle: "Trash")//销毁按钮的标题
158         
159         //往动作表单中依次添加三个按钮,
160         //并设置三个按钮的标题文字
161         sheet.addButton(withTitle: "Mark as unread")
162         sheet.addButton(withTitle: "Mark as read")
163         sheet.addButton(withTitle: "Flag")
164         
165         //在根视图中显示动作表单
166         sheet.show(in: self.view)
167     }
168     
169     //添加一个代理方法,用来监听动作表单中的选项被点击时的事件
170     func actionSheet(_ actionSheet: UIActionSheet, clickedButtonAt index: Int)
171     {
172         //获得当前类的属性的值
173         if let action = actionCallback
174         {
175             //根据点击的不同选项,执行不同的操作
176             action(index == actionSheet.cancelButtonIndex,
177                    index == actionSheet.destructiveButtonIndex,
178                    index)
179             actionCallback = nil
180         }
181     }
182     
183     //添加一个方法,用来根据不同的标识状态,返回不同的文字内容
184     func readButtonText(_ read:Bool) -> String
185     {
186         return read ? "Mark as\nunread" : "Mark as\nread"
187     }
188     
189     
190     override func viewDidLoad()
191     {
192         super.viewDidLoad()
193         
194         //初始化一个矩形区域,作为表格的显示区域
195         let frame = CGRect(x: 0, y: 20, width: 320, height: 548)
196         //创建一个指定显示区域的表格视图
197         tableView = UITableView(frame: frame, style: UITableViewStyle.plain)
198         
199         //设置表格对象的数据源为当前的视图控制器对象
200         tableView.delegate = self
201         //设置表格对象的代理为当前的视图控制器对象
202         tableView.dataSource = self
203         //将表格视图添加到根视图中
204         view.addSubview(tableView)
205         
206         //调用方法,用来初始化表格的数据源
207         prepareDemoData()
208     }
209     
210     //添加一个代理方法,用来设置表格的行数
211     func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
212     {
213         //在此设置表格的行数为数组的长度
214         return demoData.count
215     }
216     
217     //添加一个代理方法,用来初始化或复用表格中的单元格
218     func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
219     {
220         //创建一个字符串常量,作为单元格的复用标识
221         let identifier = "MailCell"
222         //根据复用标识,从表格中获得可以复用的单元格
223         var cell: MailTableCell! = tableView.dequeueReusableCell(withIdentifier: identifier) as? MailTableCell
224 
225         //如果没有可以复用的单元格
226         if cell == nil
227         {
228             //则初始化一个默认样式的单元格,并设置单元格的复用标识
229             cell = MailTableCell(style: UITableViewCellStyle.default, reuseIdentifier: identifier)
230         }
231         //设置单元格的代理对象为当前的视图控制器对象
232         cell.delegate = self
233         
234         //根据当前单元格的行号,获得数组中对应的邮件数据
235         let data: MailData = demoData[(indexPath as NSIndexPath).row]
236         //设置单元格的邮件来源标签的文字内容
237         cell!.mailFrom.text = data.from
238         //依次设置其他标签的相关内容
239         cell!.mailSubject.text = data.subject
240         cell!.mailMessage.text = data.message
241         cell!.mailTime.text = data.date
242         
243         //调用方法,根据邮件的状态,刷新单元格的视觉效果
244         updateCellIndicator(data, cell: cell)
245         
246         //最后返回设置好的单元格对象
247         return cell
248     }
249     
250     //添加一个代理方法,用来设置单元格的高度为110
251     func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
252     {
253         return 110
254     }
255     
256     //添加一个代理方法,设置单元格允许滑动的手势
257     func swipeTableCell(_ cell: MGSwipeTableCell, canSwipe direction: MGSwipeDirection) -> Bool
258     {
259         return true
260     }
261     
262     //添加一个代理方法,设置当单元格上有滑动手势时,所显示的功能按钮,以及功能按钮显示的视觉效果
263     func swipeTableCell(_ cell: MGSwipeTableCell, swipeButtonsFor direction: MGSwipeDirection, swipeSettings: MGSwipeSettings, expansionSettings: MGSwipeExpansionSettings) -> [UIView]?
264     {
265         //设置功能按钮的显示方式为三维旋转效果。
266         //共有:边缘、静态、拖动、中心裁切、三维旋转等五种效果
267         swipeSettings.transition = MGSwipeTransition.rotate3D
268         //设置功能按钮的索引为0
269         expansionSettings.buttonIndex = 0
270         
271         //获得在当前单元格中,需要显示的邮件内容
272         let mail = mailForIndexPath(tableView.indexPath(for: cell)!)
273         
274         //处理当手势为从左到右滑动时的情况
275         if direction == MGSwipeDirection.leftToRight
276         {
277             //设置在该手势下,按钮将以弹性的方式返回原来的位置
278             expansionSettings.fillOnTrigger = false
279             //设置触发显示功能按钮的阈值大小,默认值为1.5
280             expansionSettings.threshold = 2
281             //初始化一个颜色常量,作为功能按钮的背景颜色
282             let color = UIColor.init(red:0.0, green:122/255.0, blue:1.0, alpha:1.0)
283             
284             //返回一个功能按钮,并设置按钮的标题、背景颜色和交互动作
285             return [
286                 MGSwipeButton(title: readButtonText(mail.read),//标题
287                               backgroundColor: color,//背景颜色
288                               callback: { (cell) -> Bool in//交互动作
289                               //当该按钮被点击时,将切换当前邮件的阅读状态
290                               mail.read = !mail.read
291                               //同时刷新当前邮件所在单元格的外观状态
292                               self.updateCellIndicator(mail, cell: cell as! MailTableCell)
293                               //接着刷新单元格的内容视图
294                               cell.refreshContentView()
295                               //根据邮件的阅读状态的变化,刷新被添加按钮的标题文字
296                               (cell.leftButtons[0] as! UIButton).setTitle(self.readButtonText(mail.read), for: UIControlState())
297                     
298                     //最后返回真,使功能按钮自动隐藏,结束按钮的点击事件
299                     return true
300                 })
301             ]
302         }
303         else
304         {
305             //处理当手势为从右到左滑动时的情况
306 
307             //设置在该手势下,按钮将在触发时填充单元格
308             expansionSettings.fillOnTrigger = true
309             //设置触发显示功能按钮的阈值大小1.1,默认值为1.5
310             expansionSettings.threshold = 1.1
311             
312             //设置按钮的内边距为15
313             let padding = 15
314             //初始化三个颜色常量,作为三个按钮的背景颜色
315             let color1 = UIColor.init(red:1.0, green:59/255.0, blue:50/255.0, alpha:1.0)
316             let color2 = UIColor.init(red:1.0, green:149/255.0, blue:0.05, alpha:1.0)
317             let color3 = UIColor.init(red:200/255.0, green:200/255.0, blue:205/255.0, alpha:1.0)
318             
319             //添加第一个功能按钮,依次设置相关参数
320             let trash = MGSwipeButton(title: "Trash",//标题
321                                       backgroundColor: color1, //背景颜色
322                                       padding: padding, //内间距
323                                       callback: { (cell) -> Bool in//交互动作
324                                       //当该按钮被点击时,
325                                       //将从表格中移除按钮所在的单元格,
326                                       //并在数组中移除该单元格的内容。
327                                       self.deleteMail(self.tableView.indexPath(for: cell)!)
328                 return false
329             })
330             
331             //添加第二个功能按钮,依次设置相关参数
332             let flag = MGSwipeButton(title: "Flag",//标题
333                                      backgroundColor: color2,//背景颜色
334                                      padding: padding, //内间距
335                                      callback: { (cell) -> Bool in//交互动作
336                                      //获得在当前单元格中,需要显示的邮件内容
337                                      let mail = self.mailForIndexPath(self.tableView.indexPath(for: cell)!)
338                                      //更改邮件的标识状态
339                                      mail.flag = !mail.flag
340                                      //刷新当前邮件所在单元格的外观状态
341                                      self.updateCellIndicator(mail, cell: cell as! MailTableCell)
342                                      //刷新单元格的内容视图
343                                      cell.refreshContentView()
344                 
345                 return true
346             })
347             
348             //添加第三个功能按钮,依次设置相关参数
349             let more = MGSwipeButton(title: "More", //标题
350                                      backgroundColor: color3,//背景颜色
351                                      padding: padding, //内间距
352                                      callback: { (cell) -> Bool in//交互动作
353                                      //获得当前单元格在表格中的位置
354                                      let path = self.tableView.indexPath(for: cell)!
355                                      //获得在当前单元格中,需要显示的邮件内容
356                                      let mail = self.mailForIndexPath(path)
357                 
358                 //创建一个动作表单,拥有:取消、删除和索引三个选项
359                 self.showMailActions(mail, callback: { (cancelled, deleted, index) in
360                     //取消选项被点击时的情况
361                     if cancelled
362                     {
363                         return
364                     }
365                     //删除选项被点击时的情况
366                     else if deleted
367                     {
368                         //此时删除当前的单元格,以及数组中的数据
369                         self.deleteMail(path)
370                     }
371                     //索引选项被点击时的情况,当索引值为1时
372                     else if index == 1
373                     {
374                         //更改邮件的阅读状态
375                         mail.read = !mail.read
376                         //根据更改后的阅读状态,刷新当前单元格的标识图标
377                         self.updateCellIndicator(mail, cell: cell as! MailTableCell)
378                         //同时刷新单元格的内容视图
379                         cell.refreshContentView()
380                         //根据邮件的阅读状态的变化,刷新被添加按钮的标题文字
381                         (cell.leftButtons[0] as! UIButton).setTitle(self.readButtonText(mail.read), for: UIControlState())
382                         //然后以动画的方式,隐藏功能按钮
383                         cell.hideSwipe(animated: true)
384                     }
385                 })
386                 //最后返回假,以保持功能按钮的显示状态
387                 return false
388             })
389             //在方法的末尾,返回三个功能按钮
390             return [trash, flag, more]
391         }
392     }
393     
394     override func didReceiveMemoryWarning() {
395         super.didReceiveMemoryWarning()
396         // Dispose of any resources that can be recreated.
397     }
398 }