diff --git a/Sources/ACarousel/ACarousel.swift b/Sources/ACarousel/ACarousel.swift index 1cc1664..81f45df 100644 --- a/Sources/ACarousel/ACarousel.swift +++ b/Sources/ACarousel/ACarousel.swift @@ -36,19 +36,26 @@ public struct ACarousel : 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) } } @@ -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, index: Binding = .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, index: Binding = .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 } @@ -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 = .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 = .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 } diff --git a/Sources/ACarousel/ACarouselViewModel.swift b/Sources/ACarousel/ACarouselViewModel.swift index 79199ef..5ebdbe8 100644 --- a/Sources/ACarousel/ACarouselViewModel.swift +++ b/Sources/ACarousel/ACarouselViewModel.swift @@ -36,8 +36,9 @@ class ACarouselViewModel: ObservableObject where Data : RandomAccessCo private let _sidesScaling: CGFloat private let _autoScroll: ACarouselAutoScroll private let _canMove: Bool - - init(_ data: Data, id: KeyPath, index: Binding, spacing: CGFloat, headspace: CGFloat, sidesScaling: CGFloat, isWrap: Bool, autoScroll: ACarouselAutoScroll, canMove: Bool) { + let showPageControl: Bool + + init(_ data: Data, id: KeyPath, index: Binding, 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 ") @@ -51,6 +52,7 @@ class ACarouselViewModel: 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 @@ -105,8 +107,8 @@ class ACarouselViewModel: ObservableObject where Data : RandomAccessCo extension ACarouselViewModel where ID == Data.Element.ID, Data.Element : Identifiable { - convenience init(_ data: Data, index: Binding, 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, 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) } } diff --git a/Sources/ACarousel/PageControl.swift b/Sources/ACarousel/PageControl.swift new file mode 100644 index 0000000..69659f5 --- /dev/null +++ b/Sources/ACarousel/PageControl.swift @@ -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..