Slide Carousel cards on Cards tap in SwiftUI.

huangapple go评论77阅读模式
英文:

Slide Carousel cards on Cards tap in SwiftUI

问题

我在SwiftUI中创建了一个走马灯卡片,它在DragGesture上正常工作。

我希望在卡片的点击上实现相同的体验,即在.onTapGesture上,无论哪张卡片被点击,它都应该滑动到屏幕中央,就像附加的视频中所示。

Slide Carousel cards on Cards tap in SwiftUI.

我的当前代码 -

import SwiftUI

struct Item: Identifiable {
    var id: Int
    var title: String
    var color: Color
}

class Store: ObservableObject {
    @Published var items: [Item]
    
    let colors: [Color] = [.red, .orange, .blue, .teal, .mint, .green, .gray, .indigo, .black]
    
    // 虚拟数据
    init() {
        items = []
        for i in 0...7 {
            let new = Item(id: i, title: "Item \(i)", color: colors[i])
            items.append(new)
        }
    }
}

struct ContentView: View {
    
    @StateObject var store = Store()
    @State private var snappedItem = 0.0
    @State private var draggingItem = 0.0
    @State var activeIndex: Int = 0
    
    var body: some View {
        
        ZStack {
            ForEach(store.items) { item in
                
                // 文章视图
                ZStack {
                    RoundedRectangle(cornerRadius: 18)
                        .fill(item.color)
                    Text(item.title)
                        .padding()
                }
                .frame(width: 200, height: 200)
                
                .scaleEffect(1.0 - abs(distance(item.id)) * 0.2 )
                .opacity(1.0 - abs(distance(item.id)) * 0.3 )
                .offset(x: myXOffset(item.id), y: 0)
                .zIndex(1.0 - abs(distance(item.id)) * 0.1)
            }
        }
        .gesture(getDragGesture())
        .onTapGesture {
            // 将卡片移动到中心
        }
    }
    
    private func getDragGesture() -> some Gesture {
        
        DragGesture()
            .onChanged { value in
                draggingItem = snappedItem + value.translation.width / 100
            }
            .onEnded { value in
                withAnimation {
                    draggingItem = snappedItem + value.predictedEndTranslation.width / 100
                    draggingItem = round(draggingItem).remainder(dividingBy: Double(store.items.count))
                    snappedItem = draggingItem
                    
                    // 获取活动项目的索引
                    self.activeIndex = store.items.count + Int(draggingItem)
                    if self.activeIndex > store.items.count || Int(draggingItem) >= 0 {
                        self.activeIndex = Int(draggingItem)
                    }
                }
            }
    }
    
    func distance(_ item: Int) -> Double {
        return (draggingItem - Double(item)).remainder(dividingBy: Double(store.items.count))
    }
    
    func myXOffset(_ item: Int) -> Double {
        let angle = Double.pi * 2 / Double(store.items.count) * distance(item)
        return sin(angle) * 200
    }
}

注意:在.onTapGesture中,您需要编写逻辑来将卡片移动到屏幕中央,但此处的代码未提供相关逻辑。

英文:

I have created a carousel cards in SwiftUI, it is working on the DragGesture

I want to achieve same experience on the tap of cards i.e. on .onTapGesture, which ever cards is being tapped it should be slide to centre on the screen like shown in the video attached

Slide Carousel cards on Cards tap in SwiftUI.

My current code -

import SwiftUI
struct Item: Identifiable {
var id: Int
var title: String
var color: Color
}
class Store: ObservableObject {
@Published var items: [Item]
let colors: [Color] = [.red, .orange, .blue, .teal, .mint, .green, .gray, .indigo, .black]
// dummy data
init() {
items = []
for i in 0...7 {
let new = Item(id: i, title: "Item \(i)", color: colors[i])
items.append(new)
}
}
}
struct ContentView: View {
@StateObject var store = Store()
@State private var snappedItem = 0.0
@State private var draggingItem = 0.0
@State var activeIndex: Int = 0
var body: some View {
ZStack {
ForEach(store.items) { item in
// article view
ZStack {
RoundedRectangle(cornerRadius: 18)
.fill(item.color)
Text(item.title)
.padding()
}
.frame(width: 200, height: 200)
.scaleEffect(1.0 - abs(distance(item.id)) * 0.2 )
.opacity(1.0 - abs(distance(item.id)) * 0.3 )
.offset(x: myXOffset(item.id), y: 0)
.zIndex(1.0 - abs(distance(item.id)) * 0.1)
}
}
.gesture(getDragGesture())
.onTapGesture {
//move card to centre
}
}
private func getDragGesture() -> some Gesture {
DragGesture()
.onChanged { value in
draggingItem = snappedItem + value.translation.width / 100
}
.onEnded { value in
withAnimation {
draggingItem = snappedItem + value.predictedEndTranslation.width / 100
draggingItem = round(draggingItem).remainder(dividingBy: Double(store.items.count))
snappedItem = draggingItem
//Get the active Item index
self.activeIndex = store.items.count + Int(draggingItem)
if self.activeIndex > store.items.count || Int(draggingItem) >= 0 {
self.activeIndex = Int(draggingItem)
}
}
}
}
func distance(_ item: Int) -> Double {
return (draggingItem - Double(item)).remainder(dividingBy: Double(store.items.count))
}
func myXOffset(_ item: Int) -> Double {
let angle = Double.pi * 2 / Double(store.items.count) * distance(item)
return sin(angle) * 200
}
}

答案1

得分: 1

你需要将.onTapGesture()修饰符应用于ForEach内部的单个项目,而不是围绕它。

然后,您只需处理不同的情况,将被点击的项目与当前位于前面的项目进行比较,并相应地更改draggingItem的值。

这是视图主体内的代码:

ZStack {
    ForEach(store.items) { item in
        
        // 文章视图
        ZStack {
            RoundedRectangle(cornerRadius: 18)
                .fill(item.color)
            Text(item.title)
                .padding()
        }
        .frame(width: 200, height: 200)
        
        .scaleEffect(1.0 - abs(distance(item.id)) * 0.2 )
        .opacity(1.0 - abs(distance(item.id)) * 0.3 )
        .offset(x: myXOffset(item.id), y: 0)
        .zIndex(1.0 - abs(distance(item.id)) * 0.1)
        
        // 这是修饰符 - 放在项目上,而不是放在 ForEach 上
        .onTapGesture {
            
            // 需要使用 withAnimation
            withAnimation {
                
                draggingItem = Double(item.id)
            }
        }
    }
}
.gesture(getDragGesture())
英文:

You need to apply the .onTapGesture() modifier to the single item inside the ForEach, not around it.

Then, you just need to handle the different cases, comparing the tapped item with the one currently on the front, and change the value of draggingItem accordingly.

Here's the code inside the view's body:

ZStack {
ForEach(store.items) { item in
// article view
ZStack {
RoundedRectangle(cornerRadius: 18)
.fill(item.color)
Text(item.title)
.padding()
}
.frame(width: 200, height: 200)
.scaleEffect(1.0 - abs(distance(item.id)) * 0.2 )
.opacity(1.0 - abs(distance(item.id)) * 0.3 )
.offset(x: myXOffset(item.id), y: 0)
.zIndex(1.0 - abs(distance(item.id)) * 0.1)
// Here is the modifier - on the item, not on the ForEach
.onTapGesture {
// withAnimation is necessary
withAnimation {
draggingItem = Double(item.id)
}
}
}
}
.gesture(getDragGesture())

huangapple
  • 本文由 发表于 2023年1月8日 23:59:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/75049237.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定