How to achieve the Rotary type Carousel present in the iCarousel Framework of swift. The following is what I wanted to achieve with the SwiftUI
I checked many tutorials and the framework present but I could not able to achieve as shown above image
How to achieve the Rotary type Carousel present in the iCarousel Framework of swift. The following is what I wanted to achieve with the SwiftUI
I checked many tutorials and the framework present but I could not able to achieve as shown above image
Here is a general approach: All items are drawn above each other in a ZStack
, then their position and opacity is changed based on the "distance" to the foremost element.
The demo has a fixed size for the item, but can easily be adapted. Change the values in opacity
and scaleEffect
to your wishes.
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
}
}
draggingItem
AND snappedItem
. –
Vinegarish .scaleEffect
and adapt the radius in myXOffset
e.g. from 200 to 300. Of course this "carousel" still keeps items in a circle. It looks like you want it fully linear ... –
Vinegarish Thank you @ChrisR this is a great way to achieve Carousel experience.
Added active index in the @ChrisR's answer, that might be useful for someone.
@ChrisR once you add active index in your answer, I can remove my post.
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
}
}
© 2022 - 2024 — McMap. All rights reserved.