在视图SwiftUI中带有按钮的选取框

9lowa7mx  于 2022-10-31  发布在  Swift
关注(0)|答案(2)|浏览(221)

我试图有一个字幕水平滚动的效果,但我的按钮是不可点击的。视图渲染良好,但当我点击按钮,它应该打印出'tapped user1',例如,但没有效果。
编辑:如果我按照建议在ScrollView上添加marquee修饰符,它会导致极其错误的滚动行为。理想情况下,这只是一个带有marquee的HStack,里面有一堆可点击的按钮,没有内置的滚动行为,但是如果没有ScrollView的 Package ,模块似乎就不能工作了。
我使用此链接创建了一个Marquee视图修改器:https://swiftuirecipes.com/blog/swiftui-marquee
我的视图代码如下:

struct MyView: View {
    var body: some View {
        let users = ["user1", "user2", "user3", "user4", "user5", "user6"]
        return ScrollView(.horizontal, showsIndicators: false) {
             HStack {
                 ForEach(users, id: \.self) { user in
                     if user == users.first {
                         Button(action: {
                             print("tapped \(user)")
                         }, label: {
                             Text(user)
                         })
                     } else {
                         Text("•")
                         Button(action: {
                             print("tapped \(user)")
                         }, label: {
                             Text(user)
                         })
                     }
                 }
             }.frame(height: 20)
              .marquee(duration: 10)
         }
     }
 }

选取框教程中的代码如下所示:

struct Marquee: ViewModifier {
  let duration: TimeInterval
  let direction: Direction
  let autoreverse: Bool

  @State private var offset = CGFloat.zero
  @State private var parentSize = CGSize.zero
  @State private var contentSize = CGSize.zero

  func body(content: Content) -> some View {
    // measures parent view width
    Color.clear
      .frame(height: 0)
      // measureSize from https://swiftuirecipes.com/blog/getting-size-of-a-view-in-swiftui
      .measureSize { size in
        parentSize = size
        updateAnimation(sizeChanged: true)
      }

    content
      .measureSize { size in
        contentSize = size
        updateAnimation(sizeChanged: true)
      }
      .offset(x: offset)
      // animationObserver from https://swiftuirecipes.com/blog/swiftui-animation-observer
      .animationObserver(for: offset, onComplete: {
        updateAnimation(sizeChanged: false)
      })
  }

  private func updateAnimation(sizeChanged: Bool) {
    if sizeChanged || !autoreverse {
      offset = max(parentSize.width, contentSize.width) * ((direction == .leftToRight) ? -1 : 1)
    }
    withAnimation(.linear(duration: duration)) {
      offset = -offset
    }
  }

  enum Direction {
    case leftToRight, rightToLeft
  }
}

extension View {
  func marquee(duration: TimeInterval,
               direction: Marquee.Direction = .rightToLeft,
               autoreverse: Bool = false) -> some View {
    self.modifier(Marquee(duration: duration,
                          direction: direction,
                          autoreverse: autoreverse))
  }
}

struct SizePreferenceKey: PreferenceKey {
  static var defaultValue: CGSize = .zero

  static func reduce(value: inout CGSize, nextValue: () -> CGSize) {
    value = nextValue()
  }
}

struct MeasureSizeModifier: ViewModifier {
  func body(content: Content) -> some View {
    content.background(GeometryReader { geometry in
      Color.clear.preference(key: SizePreferenceKey.self,
                             value: geometry.size)
    })
  }
}

extension View {
  func measureSize(perform action: @escaping (CGSize) -> Void) -> some View {
    self.modifier(MeasureSizeModifier())
      .onPreferenceChange(SizePreferenceKey.self, perform: action)
  }
}

public struct AnimationObserverModifier<Value: VectorArithmetic>: AnimatableModifier {
  // this is the view property that drives the animation - offset, opacity, etc.
  private let observedValue: Value
  private let onChange: ((Value) -> Void)?
  private let onComplete: (() -> Void)?

  // SwiftUI implicity sets this value as the animation progresses
  public var animatableData: Value {
    didSet {
      notifyProgress()
    }
  }

  public init(for observedValue: Value,
              onChange: ((Value) -> Void)?,
              onComplete: (() -> Void)?) {
    self.observedValue = observedValue
    self.onChange = onChange
    self.onComplete = onComplete
    animatableData = observedValue
  }

  public func body(content: Content) -> some View {
    content
  }

  private func notifyProgress() {
    DispatchQueue.main.async {
      onChange?(animatableData)
      if animatableData == observedValue {
        onComplete?()
      }
    }
  }
}

public extension View {
    func animationObserver<Value: VectorArithmetic>(for value: Value,
                                                    onChange: ((Value) -> Void)? = nil,
                                                    onComplete: (() -> Void)? = nil) -> some View {
      self.modifier(AnimationObserverModifier(for: value,
                                                 onChange: onChange,
                                                 onComplete: onComplete))
    }
}
xn1cxnb4

xn1cxnb41#

把修改器放在scrollview上,它会解决你的问题
就像这样。

import SwiftUI
struct MyView: View {
    var body: some View {
        let users = ["user1", "user2", "user3", "user4", "user5", "user6"]
        return ScrollView(.horizontal, showsIndicators: false) {
             HStack {
                 ForEach(users, id: \.self) { user in
                     if user == users.first {
                         Button(action: {
                             print("tapped \(user)")
                         }, label: {
                             Text(user)
                         })
                     } else {
                         Text("•")
                         Button(action: {
                             print("tapped \(user)")
                         }, label: {
                             Text(user)
                         })
                     }
                 }
             }.frame(height: 20)

         }
        .marquee(duration: 10)
     }
 }
gpnt7bae

gpnt7bae2#

尝试在按钮.buttonStyle(PlainButtonStyle())或其他类似按钮样式上添加修饰符。如果不起作用,则尝试只使用您想要的按钮标签并添加修饰符.onTapGesture {your code}

相关问题