DEV Community

GoyesDev
GoyesDev

Posted on

[SUI] Calculando número de página del ScrollView

Un ScrollGeometry tiene las siguientes propiedades:

  • bounds: El CGRect del ScrollView.
  • containerSize: Tamaño (CGSize) del contenedor del ScrollView.
  • contentInsets: Márgenes del contenido del ScrollView.
  • contentOffset: Offset (CGPoint) del contenido del ScrollView.
  • contentSize: Tamaño (CGSize) del contenido del ScrollView.
  • visibleRect: Rectángulo (CGRect) visible del ScrollView
struct ContentView: View {
  let data: [Content] = [
    .init(text: "1"),
    .init(text: "2"),
    .init(text: "3"),
    .init(text: "4"),
    .init(text: "5"),
    .init(text: "6"),
    .init(text: "7"),
    .init(text: "8"),
    .init(text: "9"),
    .init(text: "10"),
    .init(text: "11"),
    .init(text: "12"),
    .init(text: "13"),
    .init(text: "14"),
    .init(text: "15"),
    .init(text: "16"),
    .init(text: "17"),
    .init(text: "18"),
    .init(text: "19"),
    .init(text: "20"),
    .init(text: "21"),
  ]
  @State var selected: Content? = nil
  @State var loaded = false
  @State var pageNumber = 1

  var body: some View {
    VStack {
      Spacer()

      Text("Página actual: \(pageNumber)")

      ScrollViewReader { proxy in
        ScrollView(.horizontal) {
          LazyHStack {
            ForEach(data) { i in
              Text("Item: [\(i.text)]")
                .background(i.id == selected?.id ? .gray : .clear)
                .containerRelativeFrame(.horizontal, count: 8, span: 1, spacing: 0, alignment: .center)
                .scrollTransition(.interactive, axis: .horizontal) { effect, phase in
                  effect
                    .opacity(phase.isIdentity ? 1: 0)
                    .scaleEffect(phase.isIdentity ? 1 : 0.5)
                }
                .if(loaded && (i.id == data.last?.id || i.id == data.first?.id)) {
                  $0
                    .onScrollVisibilityChange(threshold: 0.5) { visible in
                      if visible {
                        print("Debo cargar más entradas")
                      }
                    }
                }
            }
          }
          .scrollTargetLayout()
        }
        .frame(height: 100)
        // ⚠️ La alineación se hace por página
        .scrollTargetBehavior(.paging)
        .onScrollGeometryChange(for: Int.self, of: { geometry in
          let totalWidth = geometry.containerSize.width
          let currentPosition = geometry.contentOffset.x
          let relativePosition = Int((currentPosition / totalWidth).rounded())

          return relativePosition + 1
        }, action: { oldValue, newValue in
          pageNumber = newValue
        })
        .onAppear {
          Task {
            try! await Task.sleep(for: .milliseconds(50))
            selected = data[data.count / 2]
            // ⚠️ Como la alineación es por página, se deben alinear los bordes "leading" si quiero centrar una página
            proxy.scrollTo(selected!.id, anchor: .leading)
            try! await Task.sleep(for: .milliseconds(50))
            loaded = true
          }
        }
      }
      Spacer()
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)