Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/16.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
SwiftUI-列表中的自定义滑动操作_Swift_List_Swipe_Swiftui - Fatal编程技术网

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

如何在SwiftUI中使用自定义滑动操作

我尝试使用UIKit框架在SwiftUI中实现这些功能。但这对我不起作用

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")
                }
            }
        }
    }
}