当前位置:   article > 正文

SwiftUI 高级之水平滚动并获取滚动位置

swiftui 获取scrollview元素的位置

效果

41085-13869c5769744ed9.jpg
Jietu20200204-213912@2x.jpg

代码

Page代码

  1. //
  2. // PagingScrollView.swift
  3. // SwiftUIPagingScrollView
  4. //
  5. // Created by myf on 27/08/2019.
  6. // Copyright © 2019 Pavel Zak. All rights reserved.
  7. //
  8. import SwiftUI
  9. struct PagingScrollView: View {
  10. let items: [AnyView]
  11. init<A: View>(activePageIndex:Binding<Int>, itemCount: Int, pageWidth:CGFloat, tileWidth:CGFloat, tilePadding: CGFloat, @ViewBuilder content: () -> A) {
  12. let views = content()
  13. self.items = [AnyView(views)]
  14. self._activePageIndex = activePageIndex
  15. self.pageWidth = pageWidth
  16. self.tileWidth = tileWidth
  17. self.tilePadding = tilePadding
  18. self.tileRemain = (pageWidth-tileWidth-2*tilePadding)/2
  19. self.itemCount = itemCount
  20. self.contentWidth = (tileWidth+tilePadding)*CGFloat(self.itemCount)
  21. self.leadingOffset = tileRemain+tilePadding
  22. self.stackOffset = contentWidth/2 - pageWidth/2 - tilePadding/2
  23. }
  24. /// index of current page 0..N-1
  25. @Binding var activePageIndex : Int
  26. /// pageWidth==frameWidth used to properly compute offsets
  27. let pageWidth: CGFloat
  28. /// width of item / tile
  29. let tileWidth : CGFloat
  30. /// padding between items
  31. private let tilePadding : CGFloat
  32. /// how much of surrounding iems is still visible
  33. private let tileRemain : CGFloat
  34. /// total width of conatiner
  35. private let contentWidth : CGFloat
  36. /// offset to scroll on the first item
  37. private let leadingOffset : CGFloat
  38. /// since the hstack is centered by default this offset actualy moves it entirely to the left
  39. private let stackOffset : CGFloat // to fix center alignment
  40. /// number of items; I did not come with the soluion of extracting the right count in initializer
  41. private let itemCount : Int
  42. /// some damping factor to reduce liveness
  43. private let scrollDampingFactor: CGFloat = 0.66
  44. /// current offset of all items
  45. @State var currentScrollOffset: CGFloat = 0
  46. /// drag offset during drag gesture
  47. @State private var dragOffset : CGFloat = 0
  48. func offsetForPageIndex(_ index: Int)->CGFloat {
  49. let activePageOffset = CGFloat(index)*(tileWidth+tilePadding)
  50. return self.leadingOffset - activePageOffset
  51. }
  52. func indexPageForOffset(_ offset : CGFloat) -> Int {
  53. guard self.itemCount>0 else {
  54. return 0
  55. }
  56. let offset = self.logicalScrollOffset(trueOffset: offset)
  57. let floatIndex = (offset)/(tileWidth+tilePadding)
  58. var computedIndex = Int(round(floatIndex))
  59. computedIndex = max(computedIndex, 0)
  60. return min(computedIndex, self.itemCount-1)
  61. }
  62. /// current scroll offset applied on items
  63. func computeCurrentScrollOffset()->CGFloat {
  64. return self.offsetForPageIndex(self.activePageIndex) + self.dragOffset
  65. }
  66. /// logical offset startin at 0 for the first item - this makes computing the page index easier
  67. func logicalScrollOffset(trueOffset: CGFloat)->CGFloat {
  68. return (trueOffset-leadingOffset) * -1.0
  69. }
  70. var body: some View {
  71. GeometryReader { outerGeometry in
  72. HStack(alignment: .center, spacing: self.tilePadding) {
  73. /// building items into HStack
  74. ForEach(0..<self.items.count) { index in
  75. self.items[index]
  76. .offset(x: self.currentScrollOffset, y: 0)
  77. .frame(width: self.tileWidth)
  78. }
  79. }
  80. .onAppear {
  81. self.currentScrollOffset = self.offsetForPageIndex(self.activePageIndex)
  82. }
  83. .offset(x: self.stackOffset, y: 0)
  84. .background(Color.black.opacity(0.00001)) // hack - this allows gesture recognizing even when background is transparent
  85. .frame(width: self.contentWidth)
  86. .simultaneousGesture( DragGesture(minimumDistance: 1, coordinateSpace: .local) // can be changed to simultaneous gesture to work with buttons
  87. .onChanged { value in
  88. self.dragOffset = value.translation.width
  89. self.currentScrollOffset = self.computeCurrentScrollOffset()
  90. }
  91. .onEnded { value in
  92. // compute nearest index
  93. let velocityDiff = (value.predictedEndTranslation.width - self.dragOffset)*self.scrollDampingFactor
  94. let newPageIndex = self.indexPageForOffset(self.currentScrollOffset+velocityDiff)
  95. self.dragOffset = 0
  96. withAnimation(.interpolatingSpring(mass: 0.1, stiffness: 20, damping: 1.5, initialVelocity: 0)){
  97. self.activePageIndex = newPageIndex
  98. self.currentScrollOffset = self.computeCurrentScrollOffset()
  99. }
  100. }
  101. )
  102. }
  103. }
  104. }

界面

  1. import SwiftUI
  2. struct TileView: View {
  3. let icon: String
  4. var color: Color
  5. var body: some View {
  6. VStack {
  7. ZStack {
  8. Rectangle()
  9. .fill(color)
  10. .cornerRadius(20.0)
  11. Image(systemName: icon)
  12. .imageScale(.large)
  13. .font(.largeTitle)
  14. }
  15. }
  16. }
  17. }
  18. struct ContentView: View {
  19. @State private var scrollEffectValue: Double = 13
  20. @State private var activePageIndex: Int = 0
  21. let tileWidth: CGFloat = 220
  22. let tilePadding: CGFloat = 20
  23. let numberOfTiles: Int = 10
  24. var items = [Color.red, Color.orange, Color.yellow, Color.green, Color.blue, Color.purple,Color.red, Color.orange, Color.yellow, Color.green, Color.blue, Color.purple]
  25. var body: some View {
  26. VStack {
  27. Spacer()
  28. GeometryReader { geometry in
  29. PagingScrollView(activePageIndex: self.$activePageIndex, itemCount:self.numberOfTiles ,pageWidth:geometry.size.width, tileWidth:self.tileWidth, tilePadding: self.tilePadding){
  30. ForEach(0 ..< self.numberOfTiles) { index in
  31. GeometryReader { geometry2 in
  32. TileView(icon: "\(index + 1).circle",color:self.items[index])
  33. .rotation3DEffect(Angle(degrees: Double((geometry2.frame(in: .global).minX - self.tileWidth*0.5) / -10 )), axis: (x: 2, y: 11, z: 1))
  34. .onTapGesture {
  35. print ("tap on index: \(index) current:\(self.$activePageIndex)")
  36. }
  37. }
  38. }
  39. }
  40. }.frame(height:300)
  41. List{
  42. Text("current:\(self.activePageIndex)")
  43. Text("current:\(self.items[self.activePageIndex].description)")
  44. }
  45. Spacer()
  46. }
  47. }
  48. }
  49. struct ContentView_Previews: PreviewProvider {
  50. static var previews: some View {
  51. ContentView()
  52. }
  53. }

源码参考

https://github.com/izakpavel/SwiftUIPagingScrollView

更多SwiftUI教程和代码关注专栏

QQ:3365059189
SwiftUI技术交流QQ群:518696470

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/836267
推荐阅读
相关标签
  

闽ICP备14008679号