使用下面的代码,如何在任务上从右向左滑动才能选择删除它。
我尝试过使用
.SwipeAction
,但没有成功,并且不知道该使用什么
这是我的观点,我正在使用核心数据
import CoreData
import SwiftUI
struct TaskView: View {
@FetchRequest(sortDescriptors: []) var tasks: FetchedResults<Tasks>
@State private var isPresented = false
@EnvironmentObject var taskModel: TaskViewModel
@Environment(\.managedObjectContext) var moc
@State var TaskStatus: [String] = ["Todays Tasks", "Upcomming", "Completed"]
@Namespace var animation
var body: some View {
ScrollView{
VStack{
HStack{
Spacer()
Text(TaskStatus[0])
.font(.system(size: 30))
.fontWeight(.semibold)
Button {
print("")
} label: {
Text("May 2022")
.foregroundColor(.black)
.padding(.horizontal)
.font(.system(size: 25))
}
Spacer()
}
.padding()
.padding(.top)
customBar()
.padding()
ForEach(tasks,id: \.self){item in
Task(name: item.title ?? "Task Name", desc: item.desc ?? "Task Desc", image: item.icon ?? "sportscourt", image1: "clock", Time: "10", Colour: item.colour ?? "70d6ff", deadline: item.deadline ?? Date())
}
}
}
.overlay(alignment: .bottom){
HStack{
Spacer()
Button {
isPresented.toggle()
} label: {
Image(systemName: "plus.square")
.font(.system(size: 50))
.foregroundColor(.black)
}
.fullScreenCover(isPresented: $isPresented, content: AddTask.init)
.padding()
}
.padding(.horizontal)
}
}
func deleteTask(at offsets: IndexSet) {
for offset in offsets {
let tasky = tasks[offset]
moc.delete(tasky)
}
try?
moc.save()
}
@ViewBuilder
func customBar()-> some View{
let tabs = ["Today", "Upcoming"," Completed"]
HStack(spacing:10){
ForEach(tabs,id: \.self){tab in
Text(tab)
.font(.system(size: 20))
.scaleEffect(0.9)
.foregroundColor(taskModel.currentTab == tab ? .white:.black)
.padding(.vertical,6)
.frame(maxWidth:.infinity)
.background{
if taskModel.currentTab == tab{
Capsule()
.fill(Color(hex: "ff006e"))
.matchedGeometryEffect(id: "TAB", in: animation)
}
}
.contentShape(Capsule())
.onTapGesture{
withAnimation{taskModel.currentTab = tab}
}
}
}
}
}
struct TaskView_Previews: PreviewProvider {
static var previews: some View {
TaskView()
.previewDevice(PreviewDevice(rawValue: "iPhone 13"))
TaskView()
.previewDevice(PreviewDevice(rawValue: "iPhone 8"))
}
}
extension Color {
init(hex: String) {
let hex = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
var int: UInt64 = 0
Scanner(string: hex).scanHexInt64(&int)
let a, r, g, b: UInt64
switch hex.count {
case 3: // RGB (12-bit)
(a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17)
case 6: // RGB (24-bit)
(a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF)
case 8: // ARGB (32-bit)
(a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF)
default:
(a, r, g, b) = (1, 1, 1, 0)
}
self.init(
.sRGB,
red: Double(r) / 255,
green: Double(g) / 255,
blue: Double(b) / 255,
opacity: Double(a) / 255
)
}
}
我希望有人能够为我指明我需要做什么的正确方向。
非常感谢
这是我的非列表自定义 SwipeAction 版本:
与原版接近,只是无法在容器外点击取消。欢迎任何改进或建议。
用途:
ItemView(item)
.mySwipeAction { // red + trash icon as default
deleteMyItem(item)
}
ItemView(item)
.mySwipeAction(color: .green, icon: "flag" ) { // custom color + icon
selectMyItem(item)
}
示例:
代码:
extension View {
func mySwipeAction(color: Color = .red,
icon: String = "trash",
action: @escaping () -> ()) -> some View {
return self.modifier(MySwipeModifier(color: color, icon: icon, action: action ))
}
}
struct MySwipeModifier: ViewModifier {
let color: Color
let icon: String
let action: () -> ()
@AppStorage("MySwipeActive") var mySwipeActive = false
@State private var contentWidth: CGFloat = 0
@State private var isDragging: Bool = false
@State private var isDeleting: Bool = false
@State private var isActive: Bool = false
@State private var dragX: CGFloat = 0
@State private var iconOffset: CGFloat = 40
let miniumDistance: CGFloat = 20
func body(content: Content) -> some View {
ZStack(alignment: .trailing) {
content
.overlay( GeometryReader { geo in Color.clear.onAppear { contentWidth = geo.size.width }})
.offset(x: -dragX)
Group {
color
Image(systemName: icon)
.symbolVariant(.fill)
.foregroundColor(.white)
.offset(x: isDeleting ? 40 - dragX/2 : iconOffset)
}
.frame(width: max(dragX, 0))
// tap on red area after being active > action
.onTapGesture {
withAnimation { action() }
}
}
.contentShape(Rectangle())
// tap somewhere else > deactivate
.onTapGesture {
withAnimation {
isActive = false
dragX = 0
iconOffset = 40
mySwipeActive = false
}
}
.gesture(DragGesture(minimumDistance: miniumDistance)
.onChanged { value in
// if dragging started new > reset dragging state for all (others)
if !isDragging && !isActive {
mySwipeActive = false
isDragging = true
}
if value.translation.width < 0 {
dragX = -min(value.translation.width + miniumDistance, 0)
} else if isActive {
dragX = max(80 - value.translation.width + miniumDistance, -30)
}
iconOffset = dragX > 80 ? -40+dragX/2 : 40-dragX/2
withAnimation(.easeOut(duration: 0.3)) { isDeleting = dragX > contentWidth*0.75 }
// full drag > action
if value.translation.width <= -contentWidth {
withAnimation { action() }
mySwipeActive = false
isDragging = false
isActive = false
return
}
}
.onEnded { value in
withAnimation(.easeOut) {
isDragging = false
// half drag > change to active / show icon
if value.translation.width < -60 && !isActive {
isActive = true
mySwipeActive = true
} else {
isActive = false
mySwipeActive = false
}
// in delete mode > action
if isDeleting { action() ; return }
// in active mode > show icon
if isActive {
dragX = 80
iconOffset = 0
return
}
dragX = 0
isDeleting = false
}
}
)
// reset all if swipe in other cell
.onChange(of: mySwipeActive) { newValue in
print("changed", newValue)
if newValue == false && !isDragging {
withAnimation {
dragX = 0
isActive = false
isDeleting = false
iconOffset = 40
}
}
}
}
}