Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 22 additions & 15 deletions Sources/ACarousel/ACarousel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,26 @@ public struct ACarousel<Data, ID, Content> : View where Data : RandomAccessColle
}

private func generateContent(proxy: GeometryProxy) -> some View {
HStack(spacing: viewModel.spacing) {
ForEach(viewModel.data, id: viewModel.dataId) {
content($0)
.frame(width: viewModel.itemWidth)
.scaleEffect(x: 1, y: viewModel.itemScaling($0), anchor: .center)
VStack {
let height = viewModel.showPageControl ? proxy.size.height - 16 : proxy.size.height
HStack(spacing: viewModel.spacing) {
ForEach(viewModel.data, id: viewModel.dataId) {
content($0)
.frame(width: viewModel.itemWidth)
.scaleEffect(x: 1, y: viewModel.itemScaling($0), anchor: .center)
}
}
.frame(width: proxy.size.width, height: height, alignment: .leading)
.offset(x: viewModel.offset)
.gesture(viewModel.dragGesture)
.animation(viewModel.offsetAnimation, value: viewModel.offset)
.onReceive(timer: viewModel.timer, perform: viewModel.receiveTimer)
.onReceiveAppLifeCycle(perform: viewModel.setTimerActive)

if viewModel.showPageControl {
PageControl(numberOfPages: viewModel.data.count, currentPage: $viewModel.activeIndex)
}
}
.frame(width: proxy.size.width, height: proxy.size.height, alignment: .leading)
.offset(x: viewModel.offset)
.gesture(viewModel.dragGesture)
.animation(viewModel.offsetAnimation, value: viewModel.offset)
.onReceive(timer: viewModel.timer, perform: viewModel.receiveTimer)
.onReceiveAppLifeCycle(perform: viewModel.setTimerActive)
}
}

Expand All @@ -74,9 +81,9 @@ extension ACarousel {
/// - autoScroll: A enum that define view to scroll automatically. See
/// ``ACarouselAutoScroll``. default is `inactive`.
/// - content: The view builder that creates views dynamically.
public init(_ data: Data, id: KeyPath<Data.Element, ID>, index: Binding<Int> = .constant(0), spacing: CGFloat = 10, headspace: CGFloat = 10, sidesScaling: CGFloat = 0.8, isWrap: Bool = false, autoScroll: ACarouselAutoScroll = .inactive, canMove: Bool = true, @ViewBuilder content: @escaping (Data.Element) -> Content) {
public init(_ data: Data, id: KeyPath<Data.Element, ID>, index: Binding<Int> = .constant(0), spacing: CGFloat = 10, headspace: CGFloat = 10, sidesScaling: CGFloat = 0.8, isWrap: Bool = false, autoScroll: ACarouselAutoScroll = .inactive, canMove: Bool = true, showPageControl: Bool = false, @ViewBuilder content: @escaping (Data.Element) -> Content) {

self.viewModel = ACarouselViewModel(data, id: id, index: index, spacing: spacing, headspace: headspace, sidesScaling: sidesScaling, isWrap: isWrap, autoScroll: autoScroll, canMove: canMove)
self.viewModel = ACarouselViewModel(data, id: id, index: index, spacing: spacing, headspace: headspace, sidesScaling: sidesScaling, isWrap: isWrap, autoScroll: autoScroll, canMove: canMove, showPageControl: showPageControl)
self.content = content
}

Expand All @@ -100,9 +107,9 @@ extension ACarousel where ID == Data.Element.ID, Data.Element : Identifiable {
/// - autoScroll: A enum that define view to scroll automatically. See
/// ``ACarouselAutoScroll``. default is `inactive`.
/// - content: The view builder that creates views dynamically.
public init(_ data: Data, index: Binding<Int> = .constant(0), spacing: CGFloat = 10, headspace: CGFloat = 10, sidesScaling: CGFloat = 0.8, isWrap: Bool = false, autoScroll: ACarouselAutoScroll = .inactive, canMove: Bool = true, @ViewBuilder content: @escaping (Data.Element) -> Content) {
public init(_ data: Data, index: Binding<Int> = .constant(0), spacing: CGFloat = 10, headspace: CGFloat = 10, sidesScaling: CGFloat = 0.8, isWrap: Bool = false, autoScroll: ACarouselAutoScroll = .inactive, canMove: Bool = true, showPageControl: Bool = false, @ViewBuilder content: @escaping (Data.Element) -> Content) {

self.viewModel = ACarouselViewModel(data, index: index, spacing: spacing, headspace: headspace, sidesScaling: sidesScaling, isWrap: isWrap, autoScroll: autoScroll, canMove: canMove)
self.viewModel = ACarouselViewModel(data, index: index, spacing: spacing, headspace: headspace, sidesScaling: sidesScaling, isWrap: isWrap, autoScroll: autoScroll, canMove: canMove, showPageControl: showPageControl)
self.content = content
}

Expand Down
10 changes: 6 additions & 4 deletions Sources/ACarousel/ACarouselViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@ class ACarouselViewModel<Data, ID>: ObservableObject where Data : RandomAccessCo
private let _sidesScaling: CGFloat
private let _autoScroll: ACarouselAutoScroll
private let _canMove: Bool

init(_ data: Data, id: KeyPath<Data.Element, ID>, index: Binding<Int>, spacing: CGFloat, headspace: CGFloat, sidesScaling: CGFloat, isWrap: Bool, autoScroll: ACarouselAutoScroll, canMove: Bool) {
let showPageControl: Bool

init(_ data: Data, id: KeyPath<Data.Element, ID>, index: Binding<Int>, spacing: CGFloat, headspace: CGFloat, sidesScaling: CGFloat, isWrap: Bool, autoScroll: ACarouselAutoScroll, canMove: Bool, showPageControl: Bool) {

guard index.wrappedValue < data.count else {
fatalError("The index should be less than the count of data ")
Expand All @@ -51,6 +52,7 @@ class ACarouselViewModel<Data, ID>: ObservableObject where Data : RandomAccessCo
self._sidesScaling = sidesScaling
self._autoScroll = autoScroll
self._canMove = canMove
self.showPageControl = showPageControl

if data.count > 1 && isWrap {
activeIndex = index.wrappedValue + 1
Expand Down Expand Up @@ -105,8 +107,8 @@ class ACarouselViewModel<Data, ID>: ObservableObject where Data : RandomAccessCo

extension ACarouselViewModel where ID == Data.Element.ID, Data.Element : Identifiable {

convenience init(_ data: Data, index: Binding<Int>, spacing: CGFloat, headspace: CGFloat, sidesScaling: CGFloat, isWrap: Bool, autoScroll: ACarouselAutoScroll, canMove: Bool) {
self.init(data, id: \.id, index: index, spacing: spacing, headspace: headspace, sidesScaling: sidesScaling, isWrap: isWrap, autoScroll: autoScroll, canMove: canMove)
convenience init(_ data: Data, index: Binding<Int>, spacing: CGFloat, headspace: CGFloat, sidesScaling: CGFloat, isWrap: Bool, autoScroll: ACarouselAutoScroll, canMove: Bool, showPageControl: Bool) {
self.init(data, id: \.id, index: index, spacing: spacing, headspace: headspace, sidesScaling: sidesScaling, isWrap: isWrap, autoScroll: autoScroll, canMove: canMove, showPageControl: showPageControl)
}
}

Expand Down
33 changes: 33 additions & 0 deletions Sources/ACarousel/PageControl.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//
// SwiftUIView.swift
//
//
// Created by csms on 17/04/2023.
//

import SwiftUI

struct PageControl: View {
var numberOfPages: Int

@Binding var currentPage: Int

var body: some View {
HStack {
ForEach(0..<numberOfPages) { index in
Circle()
.frame(width: 8, height: 8)
.foregroundColor(index == self.currentPage ? .accentColor : .primary.opacity(0.2))
.onTapGesture(perform: { self.currentPage = index })
}
}
}
}


struct PageControl_Previews: PreviewProvider {
static var previews: some View {
@State var currentPage: Int = 1
return PageControl(numberOfPages: 5, currentPage: $currentPage)
}
}