-
Notifications
You must be signed in to change notification settings - Fork 6
/
CollectionSwipableCellExtension.swift
199 lines (159 loc) · 4.91 KB
/
CollectionSwipableCellExtension.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
//
// CollectionSwipableCellExtension.swift
// AviasalesComponents
//
// Created by Dmitry Ryumin on 22/09/2017.
// Copyright © 2017 Aviasales. All rights reserved.
//
import Foundation
/**
Layout of swipable buttons
**/
@objc
public protocol CollectionSwipableCellLayout: AnyObject {
/**
Container view for action buttons
**/
var actionsView: UIView { get }
/**
Width of opened buttons
**/
func swipingAreaWidth() -> CGFloat
/**
Swipable buttons inset for case when swipable area has some margin from cell's content
**/
func swipingAreaInset() -> CGFloat
/**
Initialization of actionsView and its subviews
**/
func setupActionsView()
/**
Method for set frames of action buttons
**/
func layoutActionsView()
/**
Call after long swipe when swipable area is fully opened
**/
func cellDidFullOpen()
/**
Use or not haptic feedback on full open
**/
func hapticFeedbackIsEnabled() -> Bool
}
/**
Swipable extension delegate
**/
@objc
public protocol CollectionSwipableCellExtensionDelegate: AnyObject {
/**
Is needed show swipable buttons in cell on indexPath
**/
func isSwipable(itemAt indexPath: IndexPath) -> Bool
/**
Return swipable buttons layout for cell on indexPath
**/
func swipableActionsLayout(forItemAt indexPath: IndexPath) -> CollectionSwipableCellLayout?
}
@objcMembers
public class CollectionSwipableCellExtension: NSObject {
/**
Swipable extension delegate
**/
public weak var delegate: CollectionSwipableCellExtensionDelegate? {
didSet {
handler?.delegate = delegate
}
}
/**
Enable/disable swipable functionality
**/
public var isEnabled: Bool = false {
didSet {
if isEnabled {
let direction: UIUserInterfaceLayoutDirection = isRtlLayoutDirection(of: collection.view) ? .rightToLeft : .leftToRight
handler = CollectionSwipableCellHandler(collection: collection, direction: direction)
handler?.delegate = delegate
handler?.applyToCollection()
} else {
handler?.removeFromCollection()
handler = nil
}
}
}
private let collection: SwipableActionsCollection
private var handler: CollectionSwipableCellHandler?
/**
Initialization with UICollectionView
**/
@objc(initWithCollectionView:)
public init(with collectionView: UICollectionView) {
self.collection = SwipableUICollectionView(collectionView: collectionView)
super.init()
startHandlingViewWindow()
}
/**
Initialization with UITableView
**/
@objc(initWithTableView:)
public init(with tableView: UITableView) {
self.collection = SwipableUITableView(tableView: tableView)
super.init()
startHandlingViewWindow()
}
// MARK: Handle move out of window
private class AnchorView: UIView {
var emptyWindowHandler: (() -> (Void))?
override func willMove(toWindow newWindow: UIWindow?) {
super.willMove(toWindow: newWindow)
if let emptyWindowHandler = emptyWindowHandler, newWindow == nil {
emptyWindowHandler()
}
}
}
private func startHandlingViewWindow() {
let anchorView = AnchorView(frame: .zero)
anchorView.alpha = 0
anchorView.emptyWindowHandler = { [weak self] in
self?.closeAllActions()
}
collection.view.addSubview(anchorView)
}
}
// MARK: Public methods for programmatic control
public extension CollectionSwipableCellExtension {
/**
Open actions for cell at specified index path
**/
@objc
func openActionsForCell(at indexPath: IndexPath, animated: Bool = true) {
handler?.openActionsForCell(at: indexPath, animated: animated)
}
/**
Open actions for cell at specified index path with specified width
**/
@objc
func openActionsForCell(at indexPath: IndexPath, visibleWidth: CGFloat, animated: Bool = true) {
handler?.openActionsForCell(at: indexPath, customVisibleWidth: visibleWidth, animated: animated)
}
/**
Close opened actions
**/
@objc
func closeAllActions(animated: Bool = true) {
handler?.closeCellInProgress(animated: animated)
}
}
internal class SwipableHandlerWrapper {
private(set) weak var handler: CollectionSwipableCellHandler?
init(handler: CollectionSwipableCellHandler) {
self.handler = handler
}
}
private func isRtlLayoutDirection(of view: UIView) -> Bool {
if #available(iOS 9.0, *) {
return UIView.userInterfaceLayoutDirection(for: view.semanticContentAttribute) == .rightToLeft
} else {
let lang = Locale.current.languageCode
return NSLocale.characterDirection(forLanguage: lang!) == .rightToLeft
}
}