SwiftUI:ListItem 手势

Posted

技术标签:

【中文标题】SwiftUI:ListItem 手势【英文标题】:SwiftUI: ListItem gestures 【发布时间】:2020-06-29 14:25:47 【问题描述】:

目标是具有以下行为的修改:

(但只有 2 个按钮 - 左侧 1 个,右侧 1 个)

行为:

短扫显示按钮并让用户能够点击它。

强大的长滑动按钮。

能够使用两指手势


最小的可重现示例:

import SwiftUI

public extension View 
    func SwiperizeItem(closureL: @escaping () -> (), closureR: @escaping () -> ()) -> some View
    
        self.modifier( SwiperizeItemModifier(closureL: closureL, closureR: closureR) )
    


struct SwiperizeItemModifier: ViewModifier 
    @State var dragOffset = CGSize.zero
    
    @State var offset1Shown = CGSize(width: 100, height: 0)
    @State var offset1Click = CGSize(width: 250, height: 0)
    
    @State var offset2Shown = CGSize(width: -100, height: 0)
    @State var offset2Click = CGSize(width: -250, height: 0)
    
    @State var BackL = Color.green
    @State var BackR = Color.red
    @State var ForeColorL = Color.white
    @State var ForeColorR = Color.white
    
    @State var closureL: () -> Void
    @State var closureR: () -> Void
    
    func body(content: Content) -> some View 
        HStack
            Button(action:  closureL()  ) 
                Text("Left")
                    .foregroundColor(ForeColorL)
                    
            
            .background(BackL)
            .frame(maxWidth: dragOffset.width > 0 ? dragOffset.width : 0)
            .fixedSize()
            
            
            content
                //.padding([.leading, .trailing], 20)
                .animation(.spring())
                .offset(x: self.dragOffset.width)
                .gesture(DragGesture()
                            .onChanged()
                                value in
                                self.dragOffset = value.translation
                            
                            .onEnded()
                                value in
                                if ( dragOffset.width > 0 ) 
                                    if ( dragOffset.width < offset1Shown.width) 
                                        self.dragOffset = .zero
                                    
                                    else if ( dragOffset.width > offset1Shown.width &&  dragOffset.width < offset1Click.width ) 
                                        self.dragOffset = offset1Shown
                                    
                                    else if ( dragOffset.width > offset1Click.width ) 
                                        self.dragOffset = .zero
                                        closureR()
                                    
                                
                                else 
                                    if ( dragOffset.width > offset2Shown.width) 
                                        self.dragOffset = .zero
                                    
                                    else if ( dragOffset.width < offset2Shown.width && dragOffset.width > offset2Click.width ) 
                                        self.dragOffset = offset2Shown
                                    
                                    else if ( dragOffset.width < offset2Click.width ) 
                                        self.dragOffset = .zero
                                        closureL()
                                    
                                
                            
                )
        
        
    




// ____________________

struct GuestureItem_Previews: PreviewProvider 
    static var previews: some View 
        Group 
            Text("Hello")
                .padding(.all, 30)
                .background( Color( NSColor.red ) )
                .SwiperizeItem(closureL:  print("click left") , closureR:  print("click right") )
        
    


所以...我的问题是:

    使用 SwiftUI 绘制像这里这样的按钮:

我认为解决方案可能与 SwiftUI 组件的新版本有关:LazyHGridOutlineGroup。 https://developer.apple.com/videos/play/wwdc2020/10031

.onDelete() 对我来说不是一个解决方案,因为不可能做 2 个侧边按钮,也不可能编辑“删除”文本

    如何使用 swiftUI 让 2 个手指滑动? (不太重要)

【问题讨论】:

【参考方案1】:

遗憾的是,目前还没有任何原生 SwiftUI 解决方案(截至 SwiftUI 2 beta)。

SwiftUI - Still no support for custom swipe actions How is possible configure the List "onDelete" action / Button? SwiftUI - List editing mode - how to change delete button title?

您可以使用UIKit 进行自定义滑动操作并将它们包装在UIViewRepresentable 中。

一些解决方案(您可能已经看过了):

SwiftUI - Custom Swipe Actions In List Create gesture to edit list item using SwiftUI

或者您可以只使用库来代替(至少在开发原生解决方案之前)。

一些库:

SwipeCell (SwiftUI) (可能正是你需要的) SwipeCellKit (UIKit)

如果你想实现同时滑动手势,你需要再次使用UIViewRepresentable

How to detect a tap gesture location in SwiftUI?(这仅适用于点击手势,但有很好的解释) SwiftUI: Multitouch gesture / Multiple Gestures(这是对上述内容的改编,但带有滑动手势)

总结

第一个问题的答案:SwipeCell 第二个问题的答案:SwiftUI: Multitouch gesture / Multiple Gestures

【讨论】:

【参考方案2】:

“如何实现滑动删除?”

只要您不想为删除按钮创建自定义 UI,您就可以利用 SwiftUI 并使用所有内置功能。

ForEach 有一个名为.onDelete 的修饰符,它为您提供IndexSet。这表示用户滑动时应删除的行。现在,如果我们实现必要的逻辑并将其包装在动画块中,一切都会按需要进行。

struct ContentView: View 
    @State var cars = ["Tesla Model 3", "BMW i3", "Roadster", "Cybertruck", "Agera Koenigsegg", "Rimac Concept One"]

    var body: some View 
        NavigationView 
            List 
                ForEach(cars, id: \.self)  car in
                    Text(car)
                
                .onDelete  indexSet in
                    withAnimation 
                        cars.remove(atOffsets: indexSet)
                    
                
            
            .navigationTitle("My Cars")
        
    

注意.onDelete 修饰符可用于List,只能应用于ForEach

“如何在 SwiftUI 中做出两指滑动手势?”

截至目前,SwiftUI 不支持为多个手指创建手势。唯一的解决方案是将UIViewRepresentableUIPanGestureRecognizer 结合使用。然后你可以将minimumNumberOfTouches设置为2个手指。

来自 Apple 开发者论坛的 post 展示了如何通过简单的 2 指轻击手势实现类似的效果,但滑动的想法和概念非常相似,上面已经解释过。

希望这会有所帮助! ?

【讨论】:

如果要自定义删除ui怎么办? 不幸的是,SwiftUI 2 / Xcode 12 beta 1 不支持自定义 UI 以滑动删除操作。你最好使用 UIKit,而不是在 SwiftUI 中实现一些 hacky 修饰符。 创建这个问题是因为.onDelete() 不是我的解决方案。请再次阅读问题:我需要 2 个带有自定义文本的按钮——一个在右侧,一个在左侧。所以这不是我的问题的答案。 很抱歉我对这个问题的理解很差。作为建议,更好的问题格式可能会吸引更多人回答。关于这个问题,我的建议是只使用 UIKit 和 UIRepresentable,因为 SwiftUI 对您的要求没有任何支持。【参考方案3】:

ios 15

在 iOS 15 中我们终于可以使用原生的Swipe Actions:

func swipeActions<T>(edge: HorizontalEdge = .trailing, allowsFullSwipe: Bool = true, content: () -> T) -> some View where T : View

它们可以像onMoveonDelete 一样附加到ForEach 容器:

ForEach 
    // ...

.swipeActions(edge: .trailing) 
    Button 
        print("Editing...")
     label: 
        Label("Edit", systemImage: "pencil")
    

【讨论】:

以上是关于SwiftUI:ListItem 手势的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI - navigationBarBackButtonHidden - 向后滑动手势?

SwiftUI 放大手势()延迟

Google AdMob 干扰 SwiftUI 中的摇动手势

SwiftUI - 有没有办法在不覆盖子视图的任何手势的情况下向视图添加手势?

SwiftUI 视图 onAppear 事件调用回扫手势动作

如何在 SwiftUI 中拥有 2 个手势?