Как передать значение смещения прокрутки из представления через контроллер просмотра страницы Swiftui

Я пытаюсь показать контроллер страницы, как второй снимок экрана, когда идет вниз и скрывает вид сверху.

Первый скриншот

Снимок экрана

Второй скриншот

Снимок экрана

Код:-

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
          }
        }
    }
 }

Может кто-нибудь объяснить мне, как показать контроллер страницы вверху при прокрутке вниз, у меня есть смещение прокрутки, но я не знаю, как перейти на главный экран. Я пытался реализовать выше, но пока нет результатов.

Любая помощь будет принята с благодарностью.

Заранее спасибо.


person Sham Dhiman    schedule 26.04.2021    source источник
comment
Итак, когда пользователь прокручивает вниз, вместо черных кнопок с текстом (например, ПН, ВТ), видимых сверху, вы хотите вместо этого показывать индикаторы страниц (маленькие черные кнопки), я прав?   -  person Sardorbek Ruzmatov    schedule 01.05.2021
comment
Да @Nomadic... Когда пользователь прокручивает вниз, показывает индикаторы страниц, а когда пользователь прокручивает вверх, показывает те же черные кнопки   -  person Sham Dhiman    schedule 01.05.2021


Ответы (1)


Вы должны инвертировать роли между дочерними и родительскими. Вместо того, чтобы дочернее представление считывало обновленное значение (CenterHomeView), это должно делать родительское представление (ScrollViewSetOffsetView).

struct ViewOffsetKey: PreferenceKey {
 typealias Value = [CGFloat]
 static var defaultValue: [CGFloat] = []

 static func reduce(value: inout [CGFloat], nextValue: () -> [CGFloat]) {
   value.append(contentsOf: nextValue())
 }
}

// child view setting the vertical offset
struct CenterHomeView: View {
   @Binding var dayIndex: Int
   
   var body: some View {
      ZStack {
         if dayIndex == 0 {
            ScrollView {
               VStack {
                  ForEach(0..<100){ index in
                     Text("\(index)")
                        .padding(5)
                        .border(Color.black, width: 1)
                  }.frame(width: 100)
               }
               .background(GeometryReader { geometry in
                  Color.clear.preference(
                     key: ViewOffsetKey.self,
                     value:
                        [geometry.frame(in: CoordinateSpace.named("scroll")).origin.y]
                  )
               })
            }.coordinateSpace(name: "scroll")
         }
         else if dayIndex == 1 {
            Color.blue
         }else if dayIndex == 2 {
            Color.gray
         }else if dayIndex == 3 {
            Color.green
         }else if dayIndex == 4 {
            Color.red
         }else if dayIndex == 5 {
            Color.yellow
         }else if dayIndex == 6 {
            Color.pink
         }else if dayIndex == 7 {
            Color.blue
         }
         Spacer()
      }
   }
}


// Parent view reading the vertical offset
struct ScrollViewSetOffsetView: View {
   @State private var verticalOffset:CGFloat = 0
   @Binding var dayIndex: Int
   @Binding var views: [HostingController<CenterHomeView>]
   
   var body: some View {
      NavigationView {
         ZStack {
            VStack {
               if verticalOffset <= 80  {
                  DayTopView(currentDayIndex: $dayIndex)
               } else {
                  PageControl(numberOfPages: 7, currentPage: self.$dayIndex)
                     .frame(width: CGFloat(7 * 18))
                     .padding(.trailing)
               }
               
               PageViewController(pages: views, currentPage: $dayIndex)
            }
            .onPreferenceChange(ViewOffsetKey.self) { vals in
               verticalOffset = -(vals.last ?? 0)
            }
         }
      }
   }
}
person Sardorbek Ruzmatov    schedule 02.05.2021
comment
@ Шам Дхиман, это решает твою проблему? - person Sardorbek Ruzmatov; 05.05.2021