首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >PickerView键盘&轻触SwiftUI

PickerView键盘&轻触SwiftUI
EN

Stack Overflow用户
提问于 2019-12-11 02:13:37
回答 1查看 3.7K关注 0票数 5

Xcode 11.2.1,Swift 5.0

我想尝试复制苹果在健康应用程序中的功能:

该功能包括以下几个方面:

  1. PickerView的出现有点像敲击键盘.

  1. PickerView在外面被解雇了。--

到目前为止,我已经尝试让事情非常类似的事情启动和运行。例如,我试图用一个自定义主体(可以填充整个屏幕)发出警报,您可以从任何视图中调用它,但我发现它具有很多原因,包括但不受限制:仅限于在自己的本地坐标系中定位视图,无法在SwiftUI布局系统中的所有其他视图之上添加一个视图,等等。键盘--就像选择器的显示样式一样--使得将其放置在其他所有东西上非常困难,类似于警报。至于在外间撤职的特点,我亦曾尝试以某种身份执行,但没有成功。实现这一目的的理想方法是在除子视图之外的所有东西前面添加一个手势识别器(在我的实验中,我试图为TextField添加此选项)。事实证明,这是一项具有挑战性的任务,因为,正如前面提到的,在全局坐标系中列出事情是非常棘手的,而不管您是从哪个孩子那里部署的。我认为,从子视图导航视图层次结构的某些方法可能会奏效,但我不知道如何做到这一点(可能是偏好键或环境变量,我不确定),不知道这样做是否有效,也不知道在SwiftUI中是否存在符合此规范的有意义级别的东西。我怀疑与UIKit的接口可能是唯一的解决方案,但我不知道如何正确地实现这一功能。

我目前的尝试

代码语言:javascript
复制
struct ContentView: View {
    @State var present = false
    @State var date = Date()

    var formattedDate: String {
        let formatter = DateFormatter()
        formatter.dateStyle = .long
        return formatter.string(from: date)
    }
    
    var body: some View {
        VStack {
            
            HStack {
                Rectangle()
                    .fill(Color.green)
                    .cornerRadius(15)
                    .frame(width: 100, height: 100)
                
                Rectangle()
                    .fill(Color.red)
                    .cornerRadius(15)
                    .frame(height: 100)
                    .overlay(Text(formattedDate).foregroundColor(.white))
                    .shadow(radius: 10)
                    .onTapGesture { self.present.toggle() }
                    .padding(20)
                    .alert(isPresented: $present, contents: {
                        
                        VStack {
                            Spacer()
                            HStack {
                                Spacer()
                                DatePicker(selection: self.$date, displayedComponents: .date) { EmptyView() }
                                    .labelsHidden()
                                Spacer()
                            }
                            .padding(.bottom, 20)
                            .background(BlurView(style: .light).blendMode(.plusLighter))
                            
                        }
                    }) {
                        withAnimation(.easeInOut) {
                            self.present = false
                        }
                }
            }
        }
        
    }
}

struct BlurView: UIViewRepresentable {
    
    typealias UIViewType = UIVisualEffectView
    
    let style: UIBlurEffect.Style
    
    func makeUIView(context: Context) -> UIViewType {
        let visualEffectView = UIVisualEffectView(effect: UIBlurEffect(style: style))
        return visualEffectView
    }
    
    func updateUIView(_ uiView: UIViewType, context: Context) { }
}

extension View {

func alert<Contents: View>(isPresented: Binding<Bool>, contents: @escaping () -> Contents, onBackgroundTapped: @escaping () -> Void = { }) -> some View {

    self.modifier(AlertView(isPresented: isPresented, contents: contents, onBackgroundTapped: onBackgroundTapped))
    }
}


struct AlertView<Contents: View>: ViewModifier {
    @Binding var isPresented: Bool
    var contents: () -> Contents
    var onBackgroundTapped: () -> Void = { }

    @State private var rect: CGRect = .zero


    func body(content: Content) -> some View {
        if rect == .zero {
            return AnyView(
                content.bindingFrame(to: $rect))
        } else {
            return AnyView(content
                .frame(width: rect.width, height: rect.height)
                .overlay(
                    ZStack(alignment: .center) {
                        Color
                            .black
                            .opacity(isPresented ? 0.7 : 0)
                            .edgesIgnoringSafeArea(.all)
                            .frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
                            .position(x: UIScreen.main.bounds.midX, y: UIScreen.main.bounds.midY, in: .global)
                            .onTapGesture {
                                self.onBackgroundTapped()
                            }
                            .animation(.easeInOut)


                        contents()
                            .position(x: UIScreen.main.bounds.midX, y: isPresented ? UIScreen.main.bounds.midY : (UIScreen.main.bounds.midY + UIScreen.main.bounds.height), in: .global)
                            .animation(.spring())
                    }
                    .frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height),
                    alignment: .center))
        }

    }
}

public extension View {
    func position(_ position: CGPoint, in coordinateSpace: CoordinateSpace) -> some View {
        return self.position(x: position.x, y: position.y, in: coordinateSpace)
    }
    func position(x: CGFloat, y: CGFloat, in coordinateSpace: CoordinateSpace) -> some View {
        GeometryReader { geometry in
            self.position(x: x - geometry.frame(in: coordinateSpace).origin.x, y: y - geometry.frame(in: coordinateSpace).origin.y)
        }
    }
}

struct GeometryGetter: View {
    @Binding var rect: CGRect
    
    var body: some View {
        return GeometryReader { geometry in
            self.makeView(geometry: geometry)
        }
    }
    
    func makeView(geometry: GeometryProxy) -> some View {
        DispatchQueue.main.async {
            self.rect = geometry.frame(in: .global)
        }
        
        return Rectangle().fill(Color.clear)
    }
}

extension View {
    func bindingFrame(to binding: Binding<CGRect>) -> some View {
        self.background(GeometryGetter(rect: binding))
    }
}

问题:

  • 因为动画不能真正知道视图只占据屏幕的底部,所以它的构建速度太快,因为最终的距离更远。

  • --我不能使用设备的安全区域来填充我的视图,所以我只能猜测将其放置在底部20,但这不是一个非常可伸缩的解决方案。

  • --我不知道--这会很好地放大。坦率地说,这感觉有点麻烦的解决方案,因为我们真的不想把视图放在孩子的边界上,但是我们不得不这样做。此外,如果正在调用的子视图不在屏幕上的最高z索引位置,则会立即遇到问题,因为其他项目会阻碍它和警报(选择器弹出)。

注意:,我确信它的问题比我提到的要多,我只是目前还没有想到它们,无论如何,这些都是主要的问题之一。

如何在SwiftUI中有效地复制这些特性?我希望能够从子视图(就像单独文件中的子视图,而不是根视图)激活它。

注意:与UIKit接口是一个完全可以接受的解决方案,只要它只是一个实现细节,并且在大多数情况下充当一个常规的SwiftUI视图。

谢谢!

EN

回答 1

Stack Overflow用户

发布于 2020-02-17 20:43:33

我试着做一些类似的事情。这是一个半成品的解决方案(没有几何学读取器推上屏幕,也不使用BlurView).

对我来说,关键的绊脚石是在TapGesture上使用口罩,否则我以前使用的onTapGesture就吞下了所有的表单水龙头。

代码语言:javascript
复制
struct BottomSheetPicker: View {
    @Binding var selection: Double

    var min: Double = 0.0
    let max: Double
    var by: Double = 1.0
    var format: String = "%d"

    var body: some View {
        Group {
            Picker("", selection: $selection) {
                ForEach(Array(stride(from: min, through: max, by: by)), id: \.self) { item in
                    Text(String(format: self.format, item))
                }
            }
            .pickerStyle(WheelPickerStyle())
            .foregroundColor(.white)
            .frame(height: 180)
            .padding()
        }.background(Color.gray)
    }
}

以下是主要的内容视图:

代码语言:javascript
复制
struct MyContentView: View {
    @State private var heightCm = 0.0
    @State private var weightKg = 0.0

    @State private var pickerType = 0 // TODO: Update to Enum
    private var isShowingOverlay: Bool {
        get {
            return self.pickerType != 0
        }
    }

    private let heightFormat = "%.0f cm"
    private let weightFormat = "%.1f kg"

    private var heightPicker: some View {
        BottomSheetPicker(selection: $heightCm, max: 300, format: heightFormat)
    }

    private var weightPicker: some View {
        BottomSheetPicker(selection: $weightKg, max: 200, by: 0.5, format: weightFormat)
    }

    // There are a few nicer ways to do this, just wanted to whip up a quick demo
    private var pickerOverlay: some View {
        Group {
            if pickerType == 1 {
                heightPicker
            } else if pickerType == 2 {
                weightPicker
            } else {
                EmptyView()
            }
        }
    }

    var body: some View {
        ZStack {
            Form {
                Section(header: Text("Personal Information")) {
                    HStack {
                        Text("Height")
                        Spacer()
                        Text(String(format: heightFormat, self.heightCm))
                    }
                    .contentShape(Rectangle())
                    .onTapGesture {
                        self.pickerType = 1
                    }

                    HStack {
                        Text("Weight")
                        Spacer()
                        Text(String(format: weightFormat, self.weightKg))
                    }
                    .contentShape(Rectangle())
                    .onTapGesture {
                        self.pickerType = 2
                    }
                }
            }
            .gesture(TapGesture().onEnded({
                self.pickerType = 0
            }), including: isShowingOverlay ? .all : .none)

            if isShowingOverlay {
                pickerOverlay
                    .frame(alignment: .bottom)
                    .transition(AnyTransition.move(edge: .bottom))
                    .animation(.easeInOut(duration: 0.5))
            }
        }
    }
}
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/59277974

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档