[Swift通天遁地]二、表格表单-(7)电子邮件Mail:实现单元格左右滑动调出功能按钮
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
➤微信公众号:山青咏芝(shanqingyongzhi)
➤博客园地址:山青咏芝(https://www.cnblogs.com/strengthen/)
➤GitHub地址:https://github.com/strengthen/LeetCode
➤原文地址:https://www.cnblogs.com/strengthen/p/10189864.html
➤如果链接不是山青咏芝的博客园地址,则可能是爬取作者的文章。
➤原文已修改更新!强烈建议点击原文地址阅读!支持作者!支持原创!
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
本文将演示对单元格进行扩展,当手指在单元格左右滑动时,弹出不同的功能菜单。
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 }