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

该功能包括以下几个方面:
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))
}
}问题:
,
注意:,我确信它的问题比我提到的要多,我只是目前还没有想到它们,无论如何,这些都是主要的问题之一。
如何在SwiftUI中有效地复制这些特性?我希望能够从子视图(就像单独文件中的子视图,而不是根视图)激活它。
注意:与UIKit接口是一个完全可以接受的解决方案,只要它只是一个实现细节,并且在大多数情况下充当一个常规的SwiftUI视图。
谢谢!
发布于 2020-02-17 20:43:33
我试着做一些类似的事情。这是一个半成品的解决方案(没有几何学读取器推上屏幕,也不使用BlurView).
对我来说,关键的绊脚石是在TapGesture上使用口罩,否则我以前使用的onTapGesture就吞下了所有的表单水龙头。
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)
}
}以下是主要的内容视图:
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))
}
}
}
}https://stackoverflow.com/questions/59277974
复制相似问题