From ec4e3e75f90d1623ebfa29799899526d9b485fa3 Mon Sep 17 00:00:00 2001 From: Jeffrey Blayney Date: Sun, 15 Sep 2019 15:36:38 -0600 Subject: [PATCH 1/3] adding context menu example table view --- Example/ContextMenuViewController.swift | 117 ++++++++++++++++++++++++ Example/RootViewController.swift | 5 + 2 files changed, 122 insertions(+) create mode 100644 Example/ContextMenuViewController.swift diff --git a/Example/ContextMenuViewController.swift b/Example/ContextMenuViewController.swift new file mode 100644 index 0000000..fe6ff68 --- /dev/null +++ b/Example/ContextMenuViewController.swift @@ -0,0 +1,117 @@ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import UIKit +import SwiftReorder + +class ContextMenuViewController: UITableViewController, UIContextMenuInteractionDelegate { + + var items = (1...10).map { "Item \($0)" } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + init() { + super.init(style: .plain) + } + + override func viewDidLoad() { + super.viewDidLoad() + + title = "Basic" + + tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell") + tableView.allowsSelection = false + tableView.reorder.delegate = self + } + + @available(iOS 13.0, *) + func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? { + + return UIContextMenuConfiguration(identifier: nil, previewProvider: nil, actionProvider: { suggestedActions in + return self.makeContextMenu() + }) + } + + @available(iOS 13.0, *) + private func makeContextMenu() -> UIMenu { + + // More extensive than it needs to be, but helpful for showing options. + + let rename = UIAction(title: "Rename", image: UIImage(systemName: "square.and.pencil")) { action in + // Show rename UI + } + + // Here we specify the "destructive" attribute to show that it’s destructive in nature + let delete = UIAction(title: "Delete", image: UIImage(systemName: "trash"), attributes: .destructive) { action in + // Delete this photo 😢 + } + + // The "title" will show up as an action for opening this menu + let edit = UIMenu(title: "Edit...", children: [rename, delete]) + + // Create a UIAction for sharing + let share = UIAction(title: "Share", image: UIImage(systemName: "square.and.arrow.up")) { action in + // Show system share sheet + } + + // Create our menu with both the edit menu and the share action + return UIMenu(title: "Main Menu", children: [edit, share]) + } + +} + +extension ContextMenuViewController { + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return items.count + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + if let spacer = tableView.reorder.spacerCell(for: indexPath) { + return spacer + } + + let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) + cell.textLabel?.text = items[indexPath.row] + + + if #available(iOS 13.0, *) { + let interaction = UIContextMenuInteraction(delegate: self) + cell.addInteraction(interaction) + } else { + // Fallback on earlier versions -- Do Nothing + } + + return cell + } + +} + +extension ContextMenuViewController: TableViewReorderDelegate { + + func tableView(_ tableView: UITableView, reorderRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) { + let item = items[sourceIndexPath.row] + items.remove(at: sourceIndexPath.row) + items.insert(item, at: destinationIndexPath.row) + } + +} diff --git a/Example/RootViewController.swift b/Example/RootViewController.swift index 4b2b49f..9c32785 100644 --- a/Example/RootViewController.swift +++ b/Example/RootViewController.swift @@ -31,6 +31,7 @@ class RootViewController: UITableViewController { case nonMovable case effects case customCells + case contextMenu case count } @@ -75,6 +76,8 @@ extension RootViewController { cell.textLabel?.text = "Effects" case .customCells: cell.textLabel?.text = "Custom Cells" + case .contextMenu: + cell.textLabel?.text = "Context Menu" case .count: break } @@ -97,6 +100,8 @@ extension RootViewController { navigationController?.pushViewController(EffectsViewController(), animated: true) case .customCells: navigationController?.pushViewController(CustomCellsViewController(), animated: true) + case .contextMenu: + navigationController?.pushViewController(ContextMenuViewController(), animated: true) case .count: break } From ced75ed92647f546d29d070576ae8cd1594d5f67 Mon Sep 17 00:00:00 2001 From: thejeff77 Date: Fri, 20 Sep 2019 15:06:48 -0600 Subject: [PATCH 2/3] Cleanup, better long press duration and a tap haptic feedback recognizer --- Example/ContextMenuViewController.swift | 38 +++++++++++++++---------- SwiftReorder.xcodeproj/project.pbxproj | 5 ++++ 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/Example/ContextMenuViewController.swift b/Example/ContextMenuViewController.swift index fe6ff68..9109c81 100644 --- a/Example/ContextMenuViewController.swift +++ b/Example/ContextMenuViewController.swift @@ -25,6 +25,13 @@ class ContextMenuViewController: UITableViewController, UIContextMenuInteraction var items = (1...10).map { "Item \($0)" } + private var impactFeedbackgenerator : AnyObject? { + if #available(iOS 10, *) { + return UIImpactFeedbackGenerator(style: .light) + } + return nil + } + required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -41,6 +48,11 @@ class ContextMenuViewController: UITableViewController, UIContextMenuInteraction tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell") tableView.allowsSelection = false tableView.reorder.delegate = self + + if #available(iOS 13.0, *) { + tableView.reorder.longPressDuration = 0.09 + tableView.reorder.animationDuration = 0.1 + } } @available(iOS 13.0, *) @@ -53,28 +65,14 @@ class ContextMenuViewController: UITableViewController, UIContextMenuInteraction @available(iOS 13.0, *) private func makeContextMenu() -> UIMenu { - - // More extensive than it needs to be, but helpful for showing options. - let rename = UIAction(title: "Rename", image: UIImage(systemName: "square.and.pencil")) { action in - // Show rename UI - } - - // Here we specify the "destructive" attribute to show that it’s destructive in nature let delete = UIAction(title: "Delete", image: UIImage(systemName: "trash"), attributes: .destructive) { action in - // Delete this photo 😢 } - // The "title" will show up as an action for opening this menu - let edit = UIMenu(title: "Edit...", children: [rename, delete]) - - // Create a UIAction for sharing let share = UIAction(title: "Share", image: UIImage(systemName: "square.and.arrow.up")) { action in - // Show system share sheet } - // Create our menu with both the edit menu and the share action - return UIMenu(title: "Main Menu", children: [edit, share]) + return UIMenu(title: "Main Menu", children: [delete, share]) } } @@ -112,6 +110,16 @@ extension ContextMenuViewController: TableViewReorderDelegate { let item = items[sourceIndexPath.row] items.remove(at: sourceIndexPath.row) items.insert(item, at: destinationIndexPath.row) + if #available(iOS 10, *) { + impactFeedbackgenerator!.impactOccurred() + } + } + func tableView(_ tableView: UITableView, canReorderRowAt indexPath: IndexPath) -> Bool { + if #available(iOS 10, *) { + impactFeedbackgenerator!.impactOccurred() + } + return true + } } diff --git a/SwiftReorder.xcodeproj/project.pbxproj b/SwiftReorder.xcodeproj/project.pbxproj index e2b52af..fb8f999 100644 --- a/SwiftReorder.xcodeproj/project.pbxproj +++ b/SwiftReorder.xcodeproj/project.pbxproj @@ -14,6 +14,7 @@ 66FC50F71D5EE49D00CFCCCE /* LongListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66FC50EE1D5EE49D00CFCCCE /* LongListViewController.swift */; }; 66FC50F81D5EE49D00CFCCCE /* NonMovableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66FC50EF1D5EE49D00CFCCCE /* NonMovableViewController.swift */; }; 66FC50F91D5EE49D00CFCCCE /* RootViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66FC50F01D5EE49D00CFCCCE /* RootViewController.swift */; }; + 954884B2232EE07600668FCC /* ContextMenuViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 954884B1232EE07600668FCC /* ContextMenuViewController.swift */; }; AB6E455C21A82AAD00D47CFC /* CustomCellsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB6E455B21A82AAD00D47CFC /* CustomCellsViewController.swift */; }; D9DAD5E12134663E00484E71 /* SwiftReorder.h in Headers */ = {isa = PBXBuildFile; fileRef = D9DAD5DF2134663E00484E71 /* SwiftReorder.h */; settings = {ATTRIBUTES = (Public, ); }; }; D9DAD5E42134663E00484E71 /* SwiftReorder.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D9DAD5DD2134663E00484E71 /* SwiftReorder.framework */; }; @@ -66,6 +67,7 @@ 66FC50F01D5EE49D00CFCCCE /* RootViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = RootViewController.swift; path = Example/RootViewController.swift; sourceTree = SOURCE_ROOT; }; 66FC50FA1D5EE4CA00CFCCCE /* UITableView+Reorder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UITableView+Reorder.swift"; path = "Source/UITableView+Reorder.swift"; sourceTree = ""; }; 66FC50FC1D5EE4D600CFCCCE /* ReorderController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ReorderController.swift; path = Source/ReorderController.swift; sourceTree = ""; }; + 954884B1232EE07600668FCC /* ContextMenuViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextMenuViewController.swift; sourceTree = ""; }; AB6E455B21A82AAD00D47CFC /* CustomCellsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomCellsViewController.swift; sourceTree = ""; }; D9DAD5DD2134663E00484E71 /* SwiftReorder.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftReorder.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D9DAD5DF2134663E00484E71 /* SwiftReorder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SwiftReorder.h; sourceTree = ""; }; @@ -121,6 +123,7 @@ 66FC50EF1D5EE49D00CFCCCE /* NonMovableViewController.swift */, 66FC50EB1D5EE49D00CFCCCE /* EffectsViewController.swift */, AB6E455B21A82AAD00D47CFC /* CustomCellsViewController.swift */, + 954884B1232EE07600668FCC /* ContextMenuViewController.swift */, 66FC50ED1D5EE49D00CFCCCE /* Info.plist */, ); path = Example; @@ -265,6 +268,7 @@ 66FC50F91D5EE49D00CFCCCE /* RootViewController.swift in Sources */, AB6E455C21A82AAD00D47CFC /* CustomCellsViewController.swift in Sources */, 66FC50F81D5EE49D00CFCCCE /* NonMovableViewController.swift in Sources */, + 954884B2232EE07600668FCC /* ContextMenuViewController.swift in Sources */, 66FC50F11D5EE49D00CFCCCE /* AppDelegate.swift in Sources */, 66FC50F71D5EE49D00CFCCCE /* LongListViewController.swift in Sources */, 66FC50F51D5EE49D00CFCCCE /* GroupedViewController.swift in Sources */, @@ -410,6 +414,7 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; + DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = Example/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = adamshin.SwiftReorder; From c511251c3bd6241ed0f5e18ecad7eb3994e9d21f Mon Sep 17 00:00:00 2001 From: thejeff77 Date: Fri, 20 Sep 2019 15:32:11 -0600 Subject: [PATCH 3/3] modifying touch durations --- Example/ContextMenuViewController.swift | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Example/ContextMenuViewController.swift b/Example/ContextMenuViewController.swift index 9109c81..5e129a6 100644 --- a/Example/ContextMenuViewController.swift +++ b/Example/ContextMenuViewController.swift @@ -21,7 +21,7 @@ import UIKit import SwiftReorder -class ContextMenuViewController: UITableViewController, UIContextMenuInteractionDelegate { +class ContextMenuViewController: UITableViewController { var items = (1...10).map { "Item \($0)" } @@ -43,7 +43,7 @@ class ContextMenuViewController: UITableViewController, UIContextMenuInteraction override func viewDidLoad() { super.viewDidLoad() - title = "Basic" + title = "Context Menu" tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell") tableView.allowsSelection = false @@ -54,7 +54,9 @@ class ContextMenuViewController: UITableViewController, UIContextMenuInteraction tableView.reorder.animationDuration = 0.1 } } - +} + +extension ContextMenuViewController: UIContextMenuInteractionDelegate { @available(iOS 13.0, *) func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? { @@ -74,7 +76,6 @@ class ContextMenuViewController: UITableViewController, UIContextMenuInteraction return UIMenu(title: "Main Menu", children: [delete, share]) } - } extension ContextMenuViewController {