SwiftUI-列表中的自定义滑动操作
如何在SwiftUI中使用自定义滑动操作 我尝试使用UIKit框架在SwiftUI中实现这些功能。但这对我不起作用SwiftUI-列表中的自定义滑动操作,swift,list,swipe,swiftui,Swift,List,Swipe,Swiftui,如何在SwiftUI中使用自定义滑动操作 我尝试使用UIKit框架在SwiftUI中实现这些功能。但这对我不起作用 import SwiftUI import UIKit init() { override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfig
import SwiftUI
import UIKit
init() {
override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let important = importantAction(at: indexPath)
return UISwipeActionsConfiguration(actions: [important])
}
func importantAction(at indexPath: IndexPath) -> UIContextualAction {
let action = UIContextualAction(style: .normal, title: "Important") { (action, view, completion) in
print("HI")
}
action.backgroundColor = UIColor(hue: 0.0861, saturation: 0.76, brightness: 0.94, alpha: 1.0) /* #f19938 */
action.image = UIImage(named: "pencil")
return action
}
}
struct TestView: View {
NavigationView {
List {
ForEach(appointmentsViewModel.appointments.identified(by: \.id)) { appointment in Row_Appointments(appointment: appointment)
}.onDelete(perform: delete)
}
}
}
}
更新
据我所知,从Xcode 12 beta 1(在WWDC 2020发布)起,情况并没有改善
起初的
从Xcode 11.3.1开始,SwiftUI不支持列表
项的自定义滑动操作。基于苹果SDK的发展历史,我们不太可能在下一个主要SDK版本(WWDC 2020)或更高版本之前看到支持
您最好实现不同的用户界面,例如将切换按钮添加为列表项的子视图或列表项。可以通过以下方式完成:
List {
ForEach(items) { (item) in
Text("\(item.title)")
}
.onDelete(perform: self.delete)
}.swipeActions()
然后需要添加这个swipeActions()修饰符
struct ListSwipeActions: ViewModifier {
@ObservedObject var coordinator = Coordinator()
func body(content: Content) -> some View {
return content
.background(TableViewConfigurator(configure: { tableView in
delay {
tableView.delegate = self.coordinator
}
}))
}
class Coordinator: NSObject, ObservableObject, UITableViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
print("Scrolling ....!!!")
}
func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle {
return .delete
}
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let isArchived = false
let title = isArchived ? NSLocalizedString("Unarchive", comment: "Unarchive") : NSLocalizedString("Archive", comment: "Archive")
let archiveAction = UIContextualAction(style: .normal, title: title, handler: {
(action, view, completionHandler) in
// update data source
completionHandler(true)
})
archiveAction.title = title
archiveAction.image = UIImage(systemName: "archivebox")!
archiveAction.backgroundColor = .systemYellow
let configuration = UISwipeActionsConfiguration(actions: [archiveAction])
return configuration
}
}
}
extension List {
func swipeActions() -> some View {
return self.modifier(ListSwipeActions())
}
}
并具有TableViewConfigurator
,可在列表后面搜索表视图
struct TableViewConfigurator: UIViewControllerRepresentable {
var configure: (UITableView) -> Void = { _ in }
func makeUIViewController(context: Context) -> UIViewController {
UIViewController()
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
let tableViews = UIApplication.nonModalTopViewController()?.navigationController?.topViewController?.view.subviews(ofType: UITableView.self) ?? [UITableView]()
for tableView in tableViews {
self.configure(tableView)
}
}
}
基于用于简化表视图委托设置的
请注意,这将覆盖表视图委托,并可能会破坏现有的一些表视图行为。虽然可以通过自己将方法添加到自定义委托来修复诸如header hight之类的问题,但其他问题可能无法修复
struct ListSwipeActions:ViewModifier{
@ObservedObject变量协调器=协调器()
func正文(内容:内容)->某些视图{
返回内容
.introspectTableView{tableView中的
tableView.delegate=self.coordinator
}
}
类协调器:NSObject、ObserveObject、UITableViewDelegate{
func tableView(tableView:UITableView,editingStyleForRowAt indexPath:indexPath)->UITableViewCell.EditingStyle{
返回。删除
}
func tableView(u tableView:UITableView,trailingswipeactionscoconfigurationforrowat indexPath:indexPath)->uiswipeactionscoconfiguration{
让archiveAction=UIContextualAction(样式:.normal,标题:“title”){action,view,completionHandler在
//更新数据源
completionHandler(true)
}
archiveAction.image=UIImage(系统名:“archivebox”)!
archiveAction.backgroundColor=.systemYellow
let configuration=UISwipeActionsConfiguration(操作:[archiveAction])
返回配置
}
}
}
扩展列表{
func swipeActions()->一些视图{
返回self.modifier(ListSwipeActions())
}
}
我也希望如此,现在有了以下实现
SwipeController检查何时执行刷卡操作并执行刷卡操作,因为现在您可以在executeAction函数的打印行下添加刷卡操作。但最好从中生成一个抽象类
然后在SwipeLeftRightContainer结构中,我们在DragGesture中拥有大部分逻辑。当你拖动它时,它会改变偏移量,然后调用SwipeController查看是否达到向左或向右滑动的阈值。然后,当您完成拖动时,它将进入DragGesture的oneded回调。在这里,我们将重置偏移量,并让SwipeController决定执行一个操作
请记住,视图中的许多变量对于iPhoneX是静态的,所以您应该将它们更改为最适合的
这也可以为左右滑动创建一个动作,但您可以根据自己的使用情况进行调整
import SwiftUI
/** executeRight: checks if it should execute the swipeRight action
execute Left: checks if it should execute the swipeLeft action
submitThreshold: the threshold of the x offset when it should start executing the action
*/
class SwipeController {
var executeRight = false
var executeLeft = false
let submitThreshold: CGFloat = 200
func checkExecutionRight(offsetX: CGFloat) {
if offsetX > submitThreshold && self.executeRight == false {
Utils.HapticSuccess()
self.executeRight = true
} else if offsetX < submitThreshold {
self.executeRight = false
}
}
func checkExecutionLeft(offsetX: CGFloat) {
if offsetX < -submitThreshold && self.executeLeft == false {
Utils.HapticSuccess()
self.executeLeft = true
} else if offsetX > -submitThreshold {
self.executeLeft = false
}
}
func excuteAction() {
if executeRight {
print("executed right")
} else if executeLeft {
print("executed left")
}
self.executeLeft = false
self.executeRight = false
}
}
struct SwipeLeftRightContainer: View {
var swipeController: SwipeController = SwipeController()
@State var offsetX: CGFloat = 0
let maxWidth: CGFloat = 335
let maxHeight: CGFloat = 125
let swipeObjectsOffset: CGFloat = 350
let swipeObjectsWidth: CGFloat = 400
@State var rowAnimationOpacity: Double = 0
var body: some View {
ZStack {
Group {
HStack {
Text("Sample row")
Spacer()
}
}.padding(10)
.zIndex(1.0)
.frame(width: maxWidth, height: maxHeight)
.cornerRadius(5)
.background(RoundedRectangle(cornerRadius: 10).fill(Color.gray))
.padding(10)
.offset(x: offsetX)
.gesture(DragGesture(minimumDistance: 5).onChanged { gesture in
withAnimation(Animation.linear(duration: 0.1)) {
offsetX = gesture.translation.width
}
swipeController.checkExecutionLeft(offsetX: offsetX)
swipeController.checkExecutionRight(offsetX: offsetX)
}.onEnded { _ in
withAnimation(Animation.linear(duration: 0.1)) {
offsetX = 0
swipeController.prevLocX = 0
swipeController.prevLocXDiff = 0
self.swipeController.excuteAction()
}
})
Group {
ZStack {
Rectangle().fill(Color.red).frame(width: swipeObjectsWidth, height: maxHeight).opacity(opacityDelete)
Image(systemName: "multiply").font(Font.system(size: 34)).foregroundColor(Color.white).padding(.trailing, 150)
}
}.zIndex(0.9).offset(x: swipeObjectsOffset + offsetX)
Group {
ZStack {
Rectangle().fill(Color.green).frame(width: swipeObjectsWidth, height: maxHeight).opacity(opacityLike)
Image(systemName: "heart").font(Font.system(size: 34)).foregroundColor(Color.white).padding(.leading, 150)
}
}.zIndex(0.9).offset(x: -swipeObjectsOffset + offsetX)
}
}
var opacityDelete: Double {
if offsetX < 0 {
return Double(abs(offsetX) / 50)
}
return 0
}
var opacityLike: Double {
if offsetX > 0 {
return Double(offsetX / 50)
}
return 0
}
}
struct SwipeListView: View {
var body: some View {
ScrollView {
ForEach(0..<10) { index in
SwipeLeftRightContainer().listRowInsets(EdgeInsets(top: 0, leading: 10, bottom: 0, trailing: 10))
}
}
}
}
struct SwipeLeftRight_Previews: PreviewProvider {
static var previews: some View {
SwipeListView()
}
}
导入快捷界面
/**executeRight:检查是否应执行SwiperRight操作
execute Left:检查是否应该执行swipleft操作
submitThreshold:应开始执行操作时x偏移量的阈值
*/
类开关控制器{
var executeRight=false
var executeLeft=false
让submitThreshold:CGFloat=200
func checkExecutionRight(offsetX:CGFloat){
如果offsetX>submitThreshold&&self.executeRight==false{
Utils.hapticsucture()
self.executeRight=true
}否则,如果offsetX<提交阈值{
self.executeRight=false
}
}
func checkExecutionLeft(偏移量x:CGFloat){
如果offsetX<-submitThreshold&&self.executeLeft==false{
Utils.hapticsucture()
self.executeLeft=true
}否则,如果offsetX>-提交阈值{
self.executeLeft=false
}
}
函数执行(){
如果执行权{
打印(“执行权”)
}否则,如果执行{
打印(“左执行”)
}
self.executeLeft=false
self.executeRight=false
}
}
结构SwipeLeftRightContainer:视图{
var swipcontroller:swipcontroller=swipcontroller()
@状态var offsetX:CGFloat=0
设maxWidth:CGFloat=335
设maxHeight:CGFloat=125
设swipeObjectsOffset:CGFloat=350
让swipeObjectsWidth:CGFloat=400
@状态变量RowAnimationCapacity:Double=0
var body:一些观点{
ZStack{
团体{
HStack{
文本(“样本行”)
垫片()
}
}.填充(10)
.zIndex(1.0)
.框架(宽度:maxWidth,高度:maxHeight)
.转弯半径(5)
.背景(圆角反射角(拐角半径:10).填充(颜色.灰色))
.填充(10)
.偏移量(x:偏移量x)
.手势(DragGesture(最小距离:5)。在
withAnimation(动画.线性(持续时间:0.1)){
offsetX=手势.translation.width
}
swipeController.checkExecutionLeft(offsetX:offsetX)
swipeController.checkExecutionRight(offsetX:offsetX)
}.onEnded{uuin
withAnimation(动画.线性(持续时间:0.1))
func swipeActions<T>(edge: HorizontalEdge = .trailing, allowsFullSwipe: Bool = true, content: () -> T) -> some View where T : View
List {
ForEach(appointmentsViewModel.appointments.identified(by: \.id)) { appointment in
Row_Appointments(appointment: appointment)
}
.swipeActions(edge: .trailing) {
Button {
print("Hi")
} label: {
Label("Important", systemImage: "pencil")
}
}
}
struct ContentView: View {
@State private var total = 0
var body: some View {
NavigationView {
List {
ForEach(1..<100) { i in
Text("\(i)")
.swipeActions(edge: .leading) {
Button {
total += i
} label: {
Label("Add \(i)", systemImage: "plus.circle")
}
.tint(.indigo)
}
.swipeActions(edge: .trailing) {
Button {
total -= i
} label: {
Label("Subtract \(i)", systemImage: "minus.circle")
}
}
}
}
.navigationTitle("Total: \(total)")
}
}
}
List {
ForEach(store.messages) { message in
MessageCell(message: message)
.swipeActions(edge: .leading) {
Button { store.toggleUnread(message) } label: {
if message.isUnread {
Label("Read", systemImage: "envelope.open")
} else {
Label("Unread", systemImage: "envelope.badge")
}
}
}
.swipeActions(edge: .trailing) {
Button(role: .destructive) {
store.delete(message)
} label: {
Label("Delete", systemImage: "trash")
}
Button { store.flag(message) } label: {
Label("Flag", systemImage: "flag")
}
}
}
}
}