[英]Custom picker switching views in SwiftUI
我正在開發一個帶有自定義停靠欄的應用程序,但我不確定如何在停靠欄中的項目上突出顯示時更改視圖。 當您突出顯示該項目時,我只是找不到切換視圖的方法。 我嘗試了諸如switch
語句之類的方法,但在我的場景中不起作用。 我也嘗試過使用if-else
語句,但這也沒有用。 非常感謝您幫助找到解決此問題的方法。 請在下面查看我的代碼...
struct MathematicallyController: View {
@State var selection: Int = 1
var body: some View {
ZStack {
ZStack {
if selection == 0 {
//view 1
} else if selection == 1 {
//view 2
} else if selection == 2 {
//view 3
} else {
//view 1
}
}
.overlay(
VStack {
Spacer()
ZStack {
BlurView(style: .systemThinMaterialDark)
.frame(maxWidth: .infinity, maxHeight: 65)
.cornerRadius(20)
.padding()
RoundedRectangle(cornerRadius: 20)
.stroke(lineWidth: 0.9)
.frame(maxWidth: .infinity, maxHeight: 65)
.blur(radius: 2)
.padding()
Picker(selection: selection)
.padding(5)
}
.offset(y: 30)
}
)
}
}
} 選擇器
extension View {
func eraseToAnyView() -> AnyView {
AnyView(self)
}
}
struct SizePreferenceKey: PreferenceKey {
typealias Value = CGSize
static var defaultValue: CGSize = .zero
static func reduce(value: inout CGSize, nextValue: () -> CGSize) {
value = nextValue()
}
}
struct BackgroundGeometryReader: View {
var body: some View {
GeometryReader { geometry in
return Color
.clear
.preference(key: SizePreferenceKey.self, value: geometry.size)
}
}
}
struct SizeAwareViewModifier: ViewModifier {
@Binding private var viewSize: CGSize
init(viewSize: Binding<CGSize>) {
self._viewSize = viewSize
}
func body(content: Content) -> some View {
content
.background(BackgroundGeometryReader())
.onPreferenceChange(SizePreferenceKey.self, perform: { if self.viewSize != $0 { self.viewSize = $0 }})
}
}
struct SegmentedPicker: View {
private static let ActiveSegmentColor: Color = Color(.tertiarySystemBackground)
private static let BackgroundColor: Color = Color(.secondarySystemBackground)
private static let ShadowColor: Color = Color.white.opacity(0.2)
private static let TextColor: Color = Color(.secondaryLabel)
private static let SelectedTextColor: Color = Color(.label)
private static let TextFont: Font = .system(size: 12)
private static let SegmentCornerRadius: CGFloat = 12
private static let ShadowRadius: CGFloat = 10
private static let SegmentXPadding: CGFloat = 16
private static let SegmentYPadding: CGFloat = 9
private static let PickerPadding: CGFloat = 7
private static let AnimationDuration: Double = 0.2
// Stores the size of a segment, used to create the active segment rect
@State private var segmentSize: CGSize = .zero
// Rounded rectangle to denote active segment
private var activeSegmentView: AnyView {
// Don't show the active segment until we have initialized the view
// This is required for `.animation()` to display properly, otherwise the animation will fire on init
let isInitialized: Bool = segmentSize != .zero
if !isInitialized { return EmptyView().eraseToAnyView() }
return
RoundedRectangle(cornerRadius: SegmentedPicker.SegmentCornerRadius)
.fill(.regularMaterial)
.shadow(color: SegmentedPicker.ShadowColor, radius: SegmentedPicker.ShadowRadius)
.frame(width: self.segmentSize.width, height: self.segmentSize.height)
.offset(x: self.computeActiveSegmentHorizontalOffset(), y: 0)
.animation(Animation.linear(duration: SegmentedPicker.AnimationDuration))
.overlay(
RoundedRectangle(cornerRadius: SegmentedPicker.SegmentCornerRadius)
.stroke(lineWidth: 1)
.shadow(color: SegmentedPicker.ShadowColor, radius: SegmentedPicker.ShadowRadius)
.frame(width: self.segmentSize.width, height: self.segmentSize.height)
.offset(x: self.computeActiveSegmentHorizontalOffset(), y: 0)
.animation(Animation.linear(duration: SegmentedPicker.AnimationDuration))
)
.eraseToAnyView()
}
@Binding private var selection: Int
private let items: [Image]
init(items: [Image], selection: Binding<Int>) {
self._selection = selection
self.items = items
}
var body: some View {
// Align the ZStack to the leading edge to make calculating offset on activeSegmentView easier
ZStack(alignment: .leading) {
// activeSegmentView indicates the current selection
self.activeSegmentView
HStack {
ForEach(0..<self.items.count, id: \.self) { index in
self.getSegmentView(for: index)
}
}
}
.padding(SegmentedPicker.PickerPadding)
.background(.regularMaterial)
.clipShape(RoundedRectangle(cornerRadius: SegmentedPicker.SegmentCornerRadius))
}
// Helper method to compute the offset based on the selected index
private func computeActiveSegmentHorizontalOffset() -> CGFloat {
CGFloat(self.selection) * (self.segmentSize.width + SegmentedPicker.SegmentXPadding / 2)
}
// Gets text view for the segment
private func getSegmentView(for index: Int) -> some View {
guard index < self.items.count else {
return EmptyView().eraseToAnyView()
}
let isSelected = self.selection == index
return
Text(self.items[index])
// Dark test for selected segment
.foregroundColor(isSelected ? SegmentedPicker.SelectedTextColor: SegmentedPicker.TextColor)
.lineLimit(1)
.padding(.vertical, SegmentedPicker.SegmentYPadding)
.padding(.horizontal, SegmentedPicker.SegmentXPadding)
.frame(minWidth: 0, maxWidth: .infinity)
// Watch for the size of the
.modifier(SizeAwareViewModifier(viewSize: self.$segmentSize))
.onTapGesture { self.onItemTap(index: index) }
.eraseToAnyView()
}
// On tap to change the selection
private func onItemTap(index: Int) {
guard index < self.items.count else {
return
}
self.selection = index
}
}
struct Picker: View {
@State var selection: Int = 1
private let items: [Image] = [Image(systemName: "rectangle.on.rectangle"), Image(systemName: "timelapse"), Image(systemName: "plus")]
var body: some View {
SegmentedPicker(items: self.items, selection: self.$selection)
.padding()
}
}
在您的 Picker struct
中,您將選擇作為值而不是Binding
。
使用Binding
變量的目的是使傳遞變量的父級監聽struct
中所做的更改。 換句話說,它binds
了 2 個視圖/值。
所以你應該做的是像這樣修改Picker
:
struct Picker: View {
@Binding var selection: Int = 1
private let items: [Image] = [Image(systemName: "rectangle.on.rectangle"), Image(systemName: "timelapse"), Image(systemName: "plus")]
var body: some View {
SegmentedPicker(items: self.items, selection: self.$selection)
.padding()
}
}
並在MathematicallyController
中將Picker(selection: selection)
更改為Picker(selection: $selection)
所以你有:
struct MathematicallyController: View {
@State var selection: Int = 1
var body: some View {
ZStack {
ZStack {
if selection == 0 {
//view 1
} else if selection == 1 {
//view 2
} else if selection == 2 {
//view 3
} else {
//view 1
}
}
.overlay(
VStack {
Spacer()
ZStack {
BlurView(style: .systemThinMaterialDark)
.frame(maxWidth: .infinity, maxHeight: 65)
.cornerRadius(20)
.padding()
RoundedRectangle(cornerRadius: 20)
.stroke(lineWidth: 0.9)
.frame(maxWidth: .infinity, maxHeight: 65)
.blur(radius: 2)
.padding()
Picker(selection: $selection)
.padding(5)
}
.offset(y: 30)
}
)
}
}
}
另請注意, switch
語句與if
語句一樣好。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.