轮播视图 SwiftUI

4
如何实现Swift框架iCarousel中的旋转式走马灯。以下是我想在SwiftUI中实现的内容。

enter image description here

我查看了许多教程和现有的框架,但无法像上面的图片所示那样实现。

这个有帮助吗?https://www.appcoda.com/learnswiftui/swiftui-carousel.html - user18457910
@MrDeveloper 感谢您的回复。我也检查过了,但不符合我的要求。 - Chetan
2个回答

28

以下是一般的方法:所有项目都在ZStack中相互叠放,然后根据到最前面元素的“距离”更改它们的位置和不透明度。

该演示对项目有固定大小,但可以轻松适应。根据您的需要更改opacityscaleEffect的值。

输入图像描述

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
    
    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(
            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
                    }
                }
        )
    }
    
    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
这段代码中是否可能检测当前索引? - Daniel Wijono
@SadmanSamee 在我的情况下,我将draggingItem = snappedItem + value.predictedEndTranslation.width / 100更改为draggingItem = snappedItem + val.translation.width / 100 - Daniel Wijono
非常好。@ChrisR,你做的另一件事是帮助我更好地理解为什么要使用StateObject。通过尝试,我发现我想要在View本身控制对象的生命周期时使用StateObject。在我的情况下,其他视图需要那些信息,所以我可能会使用Observable Obj...但这非常有帮助。 - hulkinggrunt
@ChrisR 在加载时有没有办法设置活动项目? - umair151
@AbdelrahmanMohamed 只需去掉 .scaleEffect 并调整 myXOffset 中的半径,例如从 200 调整到 300。当然,这个 "carousel" 仍然会将项目保持在一个圆圈中。看起来你想要完全线性的效果... - undefined
显示剩余5条评论

5

谢谢@ChrisR,这是实现Carousel体验的好方法。

@ChrisR的答案中增加了活动索引,这可能对某些人有用。

@ChrisR,一旦您在答案中添加了活动索引,我就可以删除我的帖子了。

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(
            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)
                        }
                        print(self.activeIndex)
                    }
                }
        )
    }
    
    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
    }
}

在加载时,我们可以设置activeIndex来显示不同的项目吗? - umair151
嘿@Anupam Mishra,首先,感谢您提供的额外信息。现在我有一个问题:如果我想在路由内以线性方式进行操作怎么办?就像这里一样https://camo.githubusercontent.com/74615f988a4a2be5f43db6d04e69b23c0f79d7ec28a7cf0095a79681160d365e/687474703a2f2f672e7265636f726469742e636f2f78743836694b334137662e676966 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接