开发者社区> 问答> 正文

PickerView键盘&点击关闭SwiftUI

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

https://i.stack.imgur.com/ByQOk.png

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

PickerView有点像敲击键盘。

PickerView在外面被解雇了。

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

我目前的尝试

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视图。

谢谢!

展开
收起
游客5akardh5cojhg 2019-12-12 10:34:28 578 0
0 条回答
写回答
取消 提交回答
问答地址:
问答排行榜
最热
最新

相关电子书

更多
低代码开发师(初级)实战教程 立即下载
冬季实战营第三期:MySQL数据库进阶实战 立即下载
阿里巴巴DevOps 最佳实践手册 立即下载