Reputation: 1566
I am trying to show page controller like second screenshot when goes to bottom and hide top view.
First Screenshot
Second Screenshot
Code:-
struct MainHomePage: View {
@State private var day = 0
@State var days: [HostingController<CenterHomeView>] = [HostingController(rootView: CenterHomeView(day: .constant(0))),HostingController(rootView: CenterHomeView(day: .constant(1))),HostingController(rootView: CenterHomeView(day: .constant(2))),HostingController(rootView: CenterHomeView(day: .constant(3))),HostingController(rootView: CenterHomeView(day: .constant(4))),HostingController(rootView: CenterHomeView(day: .constant(5))),HostingController(rootView: CenterHomeView(day: .constant(6)))]
init() {
var pages = [HostingController<CenterHomeView>] ()
for index in 0..<7 {
pages.append(HostingController(rootView: CenterHomeView(day: .constant(index))))
}
self.days = pages
}
var body: some View {
TabView {
ScrolViewSetOffsetView(day: $day, views: $days)
.tabItem {
Image(systemName: "person")
Text("Profile")
}
}
}
}
struct ScrolViewSetOffsetView: View {
@Binding var day: Int
@State private var offSet = 0.0
@Binding var views: [HostingController<CenterHomeView>]
var body: some View {
NavigationView {
ZStack {
VStack {
if offSet <= 80 {
DayTopView(currentDay: $day)
} else {
PageControl(numberOfPages: 7, currentPage: self.$day)
.frame(width: CGFloat(7 * 18))
.padding(.trailing)
}
PageViewController(pages: views, currentPage: $day)
}
}
}
}
}
struct CenterHomeView: View {
@Binding var day: Int
var body: some View {
ZStack {
if day == 0 {
ScrollView {
VStack {
ForEach(0..<100){ index in
Text("\(index)")
}.frame(width: 100)
}.background(GeometryReader {
Color.clear.preference(key: ViewOffsetKey.self,
value: -$0.frame(in: .named("scroll")).origin.y)
}).onPreferenceChange(ViewOffsetKey.self) {
print("offset >> \($0)")
}
}.coordinateSpace(name: "scroll")
}else if day == 1 {
Color.blue
}else if day == 2 {
Color.gray
}else if day == 3 {
Color.green
}else if day == 4 {
Color.red
}else if day == 5 {
Color.yellow
}else if day == 6 {
Color.pink
}else if day == 7 {
Color.blue
}
Spacer()
}.hiddenNavigationBarStyle()
}
}
struct DayTopView: View {
@Binding var currentDay : Int
var active = false
var body: some View {
Group {
VStack {
HStack(alignment: .center, spacing: 6) {
ForEach(0..<7) { offset in
DayButton(offset: offset, active: offset == currentDay) { offset in
currentDay = offset
}
}
}
.padding(.top)
.padding(.bottom)
Spacer().frame(height: 5)
}
}
}
}
final class HostingController<T: View>: UIHostingController<T> {
override var preferredStatusBarStyle: UIStatusBarStyle {
.lightContent
}
}
struct PageControl: UIViewRepresentable {
var numberOfPages: Int
@Binding var currentPage: Int
func makeUIView(context: Context) -> UIPageControl {
let control = UIPageControl()
control.numberOfPages = numberOfPages
control.currentPageIndicatorTintColor = UIColor(displayP3Red: 190/255, green: 251/255, blue: 0/255, alpha: 1.0)
control.pageIndicatorTintColor = UIColor(displayP3Red: 142/255, green: 144/255, blue: 144/255, alpha: 1.0)
return control
}
func updateUIView(_ uiView: UIPageControl, context: Context) {
uiView.currentPage = currentPage
}
}
struct PageViewController: UIViewControllerRepresentable {
var pages: [UIViewController]
@Binding var currentPage: Int
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIViewController(context: Context) -> UIPageViewController {
let pageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal)
pageViewController.dataSource = context.coordinator
pageViewController.delegate = context.coordinator
return pageViewController
}
func updateUIViewController(_ pageViewController: UIPageViewController, context: Context) {
context.coordinator.parent = self
pageViewController.setViewControllers([pages[currentPage]], direction: .forward, animated: false)
}
class Coordinator: NSObject, UIPageViewControllerDataSource, UIPageViewControllerDelegate,UIScrollViewDelegate {
var parent: PageViewController
init(_ pageViewController: PageViewController) {
self.parent = pageViewController
}
func pageViewController(
_ pageViewController: UIPageViewController,
viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let index = parent.pages.firstIndex(of: viewController) else {
return nil
}
if index == 0 {
return nil
}
return parent.pages[index - 1]
}
func pageViewController(
_ pageViewController: UIPageViewController,
viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let index = parent.pages.firstIndex(of: viewController) else {
return nil
}
if index + 1 == parent.pages.count {
return nil
}
return parent.pages[index + 1]
}
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
if completed,
let visibleViewController = pageViewController.viewControllers?.first,
let index = parent.pages.firstIndex(of: visibleViewController) {
parent.currentPage = index
}
}
}
}
Can someone please explain to me how to show page controller at top when scroll to bottom, I've got the scrollview offset but i don't know how to pass in main screen. I've tried to implement by above but no results yet.
Any help would be greatly appreciated.
Thanks in advance.
Upvotes: 1
Views: 288
Reputation: 614
I simplified it because it was complicated to use your code as it is.
The ObservableScrollView
detects whenever a scroll changes and branches views when and when the scroll is at the top and when the scroll is at the top.
https://i.sstatic.net/guwrh.jpg
import SwiftUI
struct ContentView: View {
@State var scrollIsTop: Bool = true
var body: some View {
VStack {
ScrollView(.horizontal) {
if scrollIsTop { // <- your first screenshot view is in here
HStack {
ForEach(1..<100) {
Text("top \($0)")
.frame(height: 55)
.background(.red)
}
}
} else { // <- your second screenshot view is in here
HStack {
ForEach(1..<100) {
Text("top \($0)")
.frame(height: 30)
.background(.blue)
}
}
}
}
ObservableScrollView() { proxy in
ForEach(1..<100) {
Text("num \($0)")
.frame(width: UIScreen.main.bounds.width, height: 55)
}
}
.onPreferenceChange(ScrollViewOffsetPreferenceKey.self) { newValue in
scrollIsTop = newValue > 0 ? false : true
}
}
.frame(width: UIScreen.main.bounds.width)
}
struct ScrollViewOffsetPreferenceKey: PreferenceKey {
static var defaultValue = CGFloat.zero
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
value += nextValue()
}
}
struct ObservableScrollView<Content>: View where Content : View {
@Namespace var scrollSpace
let content: (ScrollViewProxy) -> Content
init(
@ViewBuilder content: @escaping (ScrollViewProxy) -> Content) {
self.content = content
}
var body: some View {
ScrollView {
ScrollViewReader { proxy in
content(proxy)
.background(GeometryReader { geo in
let offset = -geo.frame(in: .named(scrollSpace)).minY
Color.clear
.preference(key: ScrollViewOffsetPreferenceKey.self,
value: offset)
})
}
}
.coordinateSpace(name: scrollSpace)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Upvotes: 0