簡體   English   中英

SwiftUI 中的自定義選擇器切換視圖

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM