Files
GitHawk/SwipeCellKit/Example/MailExample/MailViewController.swift
2017-06-26 11:14:21 -04:00

302 lines
12 KiB
Swift
Executable File

//
// MailViewController.swift
//
// Created by Jeremy Koch
// Copyright © 2017 Jeremy Koch. All rights reserved.
//
import UIKit
import SwipeCellKit
class MailViewController: UITableViewController {
var emails: [Email] = []
var defaultOptions = SwipeTableOptions()
var isSwipeRightEnabled = true
var buttonDisplayMode: ButtonDisplayMode = .titleAndImage
var buttonStyle: ButtonStyle = .backgroundColor
// MARK: - Lifecycle
override func viewDidLoad() {
tableView.allowsSelection = true
tableView.allowsMultipleSelectionDuringEditing = true
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 100
navigationItem.rightBarButtonItem = editButtonItem
view.layoutMargins.left = 32
resetData()
}
// MARK: - Table view data source
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return emails.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MailCell") as! MailCell
cell.delegate = self
cell.selectedBackgroundView = createSelectedBackgroundView()
let email = emails[indexPath.row]
cell.fromLabel.text = email.from
cell.dateLabel.text = email.relativeDateString
cell.subjectLabel.text = email.subject
cell.bodyLabel.text = email.body
cell.unread = email.unread
return cell
}
// MARK: - Actions
@IBAction func moreTapped(_ sender: Any) {
let controller = UIAlertController(title: "Swipe Transition Style", message: nil, preferredStyle: .actionSheet)
controller.addAction(UIAlertAction(title: "Border", style: .default, handler: { _ in self.defaultOptions.transitionStyle = .border }))
controller.addAction(UIAlertAction(title: "Drag", style: .default, handler: { _ in self.defaultOptions.transitionStyle = .drag }))
controller.addAction(UIAlertAction(title: "Reveal", style: .default, handler: { _ in self.defaultOptions.transitionStyle = .reveal }))
controller.addAction(UIAlertAction(title: "\(isSwipeRightEnabled ? "Disable" : "Enable") Swipe Right", style: .default, handler: { _ in self.isSwipeRightEnabled = !self.isSwipeRightEnabled }))
controller.addAction(UIAlertAction(title: "Button Display Mode", style: .default, handler: { _ in self.buttonDisplayModeTapped() }))
controller.addAction(UIAlertAction(title: "Button Style", style: .default, handler: { _ in self.buttonStyleTapped() }))
controller.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
controller.addAction(UIAlertAction(title: "Reset", style: .destructive, handler: { _ in self.resetData() }))
present(controller, animated: true, completion: nil)
}
func buttonDisplayModeTapped() {
let controller = UIAlertController(title: "Button Display Mode", message: nil, preferredStyle: .actionSheet)
controller.addAction(UIAlertAction(title: "Image + Title", style: .default, handler: { _ in self.buttonDisplayMode = .titleAndImage }))
controller.addAction(UIAlertAction(title: "Image Only", style: .default, handler: { _ in self.buttonDisplayMode = .imageOnly }))
controller.addAction(UIAlertAction(title: "Title Only", style: .default, handler: { _ in self.buttonDisplayMode = .titleOnly }))
controller.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
present(controller, animated: true, completion: nil)
}
func buttonStyleTapped() {
let controller = UIAlertController(title: "Button Style", message: nil, preferredStyle: .actionSheet)
controller.addAction(UIAlertAction(title: "Background Color", style: .default, handler: { _ in
self.buttonStyle = .backgroundColor
self.defaultOptions.transitionStyle = .border
}))
controller.addAction(UIAlertAction(title: "Circular", style: .default, handler: { _ in
self.buttonStyle = .circular
self.defaultOptions.transitionStyle = .reveal
}))
controller.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
present(controller, animated: true, completion: nil)
}
// MARK: - Helpers
func createSelectedBackgroundView() -> UIView {
let view = UIView()
view.backgroundColor = UIColor.lightGray.withAlphaComponent(0.2)
return view
}
func resetData() {
emails = mockEmails
emails.forEach { $0.unread = false }
tableView.reloadData()
}
}
extension MailViewController: SwipeTableViewCellDelegate {
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath, for orientation: SwipeActionsOrientation) -> [SwipeAction]? {
let email = emails[indexPath.row]
if orientation == .left {
guard isSwipeRightEnabled else { return nil }
let read = SwipeAction(style: .default, title: nil) { action, indexPath in
let updatedStatus = !email.unread
email.unread = updatedStatus
let cell = tableView.cellForRow(at: indexPath) as! MailCell
cell.setUnread(updatedStatus, animated: true)
}
read.hidesWhenSelected = true
read.accessibilityLabel = email.unread ? "Mark as Read" : "Mark as Unread"
let descriptor: ActionDescriptor = email.unread ? .read : .unread
configure(action: read, with: descriptor)
return [read]
} else {
let flag = SwipeAction(style: .default, title: nil, handler: nil)
flag.hidesWhenSelected = true
configure(action: flag, with: .flag)
let delete = SwipeAction(style: .destructive, title: nil) { action, indexPath in
self.emails.remove(at: indexPath.row)
}
configure(action: delete, with: .trash)
let cell = tableView.cellForRow(at: indexPath) as! MailCell
let closure: (UIAlertAction) -> Void = { _ in cell.hideSwipe(animated: true) }
let more = SwipeAction(style: .default, title: nil) { action, indexPath in
let controller = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
controller.addAction(UIAlertAction(title: "Reply", style: .default, handler: closure))
controller.addAction(UIAlertAction(title: "Forward", style: .default, handler: closure))
controller.addAction(UIAlertAction(title: "Mark...", style: .default, handler: closure))
controller.addAction(UIAlertAction(title: "Notify Me...", style: .default, handler: closure))
controller.addAction(UIAlertAction(title: "Move Message...", style: .default, handler: closure))
controller.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: closure))
self.present(controller, animated: true, completion: nil)
}
configure(action: more, with: .more)
return [delete, flag, more]
}
}
func tableView(_ tableView: UITableView, editActionsOptionsForRowAt indexPath: IndexPath, for orientation: SwipeActionsOrientation) -> SwipeTableOptions {
var options = SwipeTableOptions()
options.expansionStyle = orientation == .left ? .selection : .destructive
options.transitionStyle = defaultOptions.transitionStyle
switch buttonStyle {
case .backgroundColor:
options.buttonSpacing = 11
case .circular:
options.buttonSpacing = 4
options.backgroundColor = #colorLiteral(red: 0.9467939734, green: 0.9468161464, blue: 0.9468042254, alpha: 1)
}
return options
}
func configure(action: SwipeAction, with descriptor: ActionDescriptor) {
action.title = descriptor.title(forDisplayMode: buttonDisplayMode)
action.image = descriptor.image(forStyle: buttonStyle, displayMode: buttonDisplayMode)
switch buttonStyle {
case .backgroundColor:
action.backgroundColor = descriptor.color
case .circular:
action.backgroundColor = .clear
action.textColor = descriptor.color
action.font = .systemFont(ofSize: 13)
action.transitionDelegate = ScaleTransition.default
}
}
}
class MailCell: SwipeTableViewCell {
@IBOutlet var fromLabel: UILabel!
@IBOutlet var dateLabel: UILabel!
@IBOutlet var subjectLabel: UILabel!
@IBOutlet var bodyLabel: UILabel!
var animator: Any?
var indicatorView = IndicatorView(frame: .zero)
var unread = false {
didSet {
indicatorView.transform = unread ? CGAffineTransform.identity : CGAffineTransform.init(scaleX: 0.001, y: 0.001)
}
}
override func awakeFromNib() {
setupIndicatorView()
}
func setupIndicatorView() {
indicatorView.translatesAutoresizingMaskIntoConstraints = false
indicatorView.color = tintColor
indicatorView.backgroundColor = .clear
contentView.addSubview(indicatorView)
let size: CGFloat = 12
indicatorView.widthAnchor.constraint(equalToConstant: size).isActive = true
indicatorView.heightAnchor.constraint(equalTo: indicatorView.widthAnchor).isActive = true
indicatorView.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 12).isActive = true
indicatorView.centerYAnchor.constraint(equalTo: fromLabel.centerYAnchor).isActive = true
}
func setUnread(_ unread: Bool, animated: Bool) {
let closure = {
self.unread = unread
}
if #available(iOS 10, *), animated {
var localAnimator = self.animator as? UIViewPropertyAnimator
localAnimator?.stopAnimation(true)
localAnimator = unread ? UIViewPropertyAnimator(duration: 1.0, dampingRatio: 0.4) : UIViewPropertyAnimator(duration: 0.3, dampingRatio: 1.0)
localAnimator?.addAnimations(closure)
localAnimator?.startAnimation()
self.animator = localAnimator
} else {
closure()
}
}
}
class IndicatorView: UIView {
var color = UIColor.clear {
didSet { setNeedsDisplay() }
}
override func draw(_ rect: CGRect) {
color.set()
UIBezierPath(ovalIn: rect).fill()
}
}
enum ActionDescriptor {
case read, unread, more, flag, trash
func title(forDisplayMode displayMode: ButtonDisplayMode) -> String? {
guard displayMode != .imageOnly else { return nil }
switch self {
case .read: return "Read"
case .unread: return "Unread"
case .more: return "More"
case .flag: return "Flag"
case .trash: return "Trash"
}
}
func image(forStyle style: ButtonStyle, displayMode: ButtonDisplayMode) -> UIImage? {
guard displayMode != .titleOnly else { return nil }
let name: String
switch self {
case .read: name = "Read"
case .unread: name = "Unread"
case .more: name = "More"
case .flag: name = "Flag"
case .trash: name = "Trash"
}
return UIImage(named: style == .backgroundColor ? name : name + "-circle")
}
var color: UIColor {
switch self {
case .read, .unread: return #colorLiteral(red: 0, green: 0.4577052593, blue: 1, alpha: 1)
case .more: return #colorLiteral(red: 0.7803494334, green: 0.7761332393, blue: 0.7967314124, alpha: 1)
case .flag: return #colorLiteral(red: 1, green: 0.5803921569, blue: 0, alpha: 1)
case .trash: return #colorLiteral(red: 1, green: 0.2352941176, blue: 0.1882352941, alpha: 1)
}
}
}
enum ButtonDisplayMode {
case titleAndImage, titleOnly, imageOnly
}
enum ButtonStyle {
case backgroundColor, circular
}