From f90ceaafabd14a44230cbc524743b1071ae93ff9 Mon Sep 17 00:00:00 2001 From: Marlo Kessler Date: Mon, 20 Jul 2020 18:14:42 +0200 Subject: [PATCH] Initial commit --- .gitignore | 5 + .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + Package.swift | 24 +++ README.md | 3 + Sources/PageView/IndicatorPosition.swift | 17 +++ Sources/PageView/PageIndicator.swift | 49 +++++++ Sources/PageView/PageView.swift | 137 ++++++++++++++++++ 8 files changed, 250 insertions(+) create mode 100644 .gitignore create mode 100644 .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata create mode 100644 .swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 Package.swift create mode 100644 README.md create mode 100644 Sources/PageView/IndicatorPosition.swift create mode 100644 Sources/PageView/PageIndicator.swift create mode 100644 Sources/PageView/PageView.swift diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..95c4320 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.DS_Store +/.build +/Packages +/*.xcodeproj +xcuserdata/ diff --git a/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..706eede --- /dev/null +++ b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000..83fad38 --- /dev/null +++ b/Package.swift @@ -0,0 +1,24 @@ +// swift-tools-version:5.2 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "PageView", + platforms: [ + .iOS(.v13), + .tvOS(.v13), + .watchOS(.v6), + .macOS(.v10_15) + ], + products: [ + .library( + name: "PageView", + targets: ["PageView"]), + ], + targets: [ + .target( + name: "PageView", + dependencies: []) + ] +) diff --git a/README.md b/README.md new file mode 100644 index 0000000..56bb86c --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# PageView + +This package contains a PageView purely written in SwiftUI. diff --git a/Sources/PageView/IndicatorPosition.swift b/Sources/PageView/IndicatorPosition.swift new file mode 100644 index 0000000..dc3a3e5 --- /dev/null +++ b/Sources/PageView/IndicatorPosition.swift @@ -0,0 +1,17 @@ +// +// IndicatorPosition.swift +// ViewCTestApp +// +// Created by Marlo Kessler on 20.07.20. +// Copyright © 2020 Marlo Kessler. All rights reserved. +// + +import Foundation + +@available(iOS 13, *) +public enum IndicatorPosition { + case topOutBounds + case topInBounds + case bottomOutBounds + case bottomInBounds +} diff --git a/Sources/PageView/PageIndicator.swift b/Sources/PageView/PageIndicator.swift new file mode 100644 index 0000000..5f125d6 --- /dev/null +++ b/Sources/PageView/PageIndicator.swift @@ -0,0 +1,49 @@ +// +// PageIndicator.swift +// ViewCTestApp +// +// Created by Marlo Kessler on 20.07.20. +// Copyright © 2020 Marlo Kessler. All rights reserved. +// + +import SwiftUI + +@available(iOS 13, *) +struct PageIndicator: UIViewRepresentable { + + @Binding var currentIndex: Int + var pageNumber: Int + var pageIndicatorColor: UIColor? + var currentPageIndicatorColor: UIColor? + + private let pageControl = UIPageControl() + + func makeUIView(context: Context) -> UIPageControl { + pageControl.numberOfPages = pageNumber + if let color = pageIndicatorColor{ pageControl.pageIndicatorTintColor = color } + if let color = currentPageIndicatorColor{ pageControl.currentPageIndicatorTintColor = color } + pageControl.addTarget(context.coordinator, action: #selector(context.coordinator.change), for: .valueChanged) + return pageControl + } + + func updateUIView(_ uiView: UIPageControl, context: Context) { + uiView.currentPage = currentIndex + } + + func makeCoordinator() -> Coordinator { + return Coordinator(parent: self) + } + + class Coordinator { + + init(parent: PageIndicator) { + self.parent = parent + } + + private let parent: PageIndicator + + @objc func change() { + parent.currentIndex = parent.pageControl.currentPage + } + } +} diff --git a/Sources/PageView/PageView.swift b/Sources/PageView/PageView.swift new file mode 100644 index 0000000..e16fdd0 --- /dev/null +++ b/Sources/PageView/PageView.swift @@ -0,0 +1,137 @@ +// +// PageView.swift +// ViewCTestApp +// +// Created by Marlo Kessler on 18.07.20. +// Copyright © 2020 Marlo Kessler. All rights reserved. +// + +import SwiftUI + +@available(iOS 13, *) +public struct PageView: View { + + public init(pageCount: Int, + currentIndex: Binding = Binding(get: {return 0}, set: {_ in}), + @ViewBuilder content: @escaping () -> Content) + { + self.pageCount = pageCount + self._currentIndex = currentIndex + self.content = content + } + + private init(_ pageCount: Int, + _ currentIndex: Binding, + _ hideIndicator: Bool, + _ indicatorPosition: IndicatorPosition, + _ currentPageIndicatorColor: UIColor?, + _ indicatorColor: UIColor?, + _ indicatorBackgroundColor:Color?, + @ViewBuilder _ content: @escaping () -> Content) + { + self.pageCount = pageCount + self._currentIndex = currentIndex + + self.hideIndicator = hideIndicator + self.indicatorPosition = indicatorPosition + self.currentPageIndicatorColor = currentPageIndicatorColor + self.indicatorColor = indicatorColor + self.indicatorBackgroundColor = indicatorBackgroundColor + + self.content = content + } + + + + // MARK: - Variables + private let pageCount: Int + @Binding private var currentIndex: Int + private let content: () -> Content + + @GestureState private var translation: CGFloat = 0 + + private var hideIndicator: Bool = false + private var indicatorPosition: IndicatorPosition = .bottomInBounds + private var indicatorColor: UIColor? + private var currentPageIndicatorColor: UIColor? + private var indicatorBackgroundColor: Color? + + + + // MARK: - Methods + public func indicator(position: IndicatorPosition = .bottomInBounds, current: UIColor? = nil, other: UIColor? = nil, background: Color? = nil) -> PageView { + return PageView(pageCount, $currentIndex, hideIndicator, position, current, other, background, content) + } + + public func hideIndicator(_ hide: Bool = true) -> PageView { + return PageView(pageCount, $currentIndex, hide, indicatorPosition, currentPageIndicatorColor, indicatorColor, indicatorBackgroundColor, content) + } + + + + // MARK: - View + private var indicator: some View { + PageIndicator(currentIndex: self.$currentIndex, + pageNumber: self.pageCount, + pageIndicatorColor: self.indicatorColor, + currentPageIndicatorColor: self.currentPageIndicatorColor) + .frame(height: 15) + .padding(.horizontal, 8) + .background(self.indicatorBackgroundColor ?? Color.clear.opacity(0.5)) + .clipped() + .cornerRadius(7.5) + } + + public var body: some View { + GeometryReader { geometry in + + ZStack { + + if self.indicatorPosition == .topInBounds { + VStack { + self.indicator + .offset(y: 16) + Spacer() + } + .zIndex(1) + } + + VStack { + + if self.indicatorPosition == .topOutBounds { self.indicator } + + HStack(spacing: 0) { + self.content().frame(width: geometry.size.width) + } + .frame(width: geometry.size.width, alignment: .leading) + .offset(x: -CGFloat(self.currentIndex) * geometry.size.width) + .offset(x: self.translation) + .clipped() + .animation(.interactiveSpring()) + .gesture( + DragGesture() + .updating(self.$translation) { value, state, _ in + state = value.translation.width + } + .onEnded { value in + let offset = value.translation.width / geometry.size.width + let newIndex = (CGFloat(self.currentIndex) - offset).rounded() + self.currentIndex = min(max(Int(newIndex), 0), self.pageCount - 1) + } + ) + + if self.indicatorPosition == .bottomOutBounds { self.indicator } + } + + if self.indicatorPosition == .bottomInBounds { + VStack { + Spacer() + self.indicator + .offset(y: -16) + } + } + + } + } + } +}