【问题标题】:Show full screen view overlaying also TabBar也显示覆盖 TabBar 的全屏视图
【发布时间】:2021-11-19 22:22:36
【问题描述】:

我正在尝试全屏显示带有加载程序的视图。我也想覆盖 TabBar,但我不知道该怎么做。让我展示一下我的代码。

这是ProgressViewModifier

// MARK: - View - Extension

extension View {
    
    /// Show a loader binded to `isShowing` parameter.
    /// - Parameters:
    ///   - isShowing: `Bool` value to indicate if the loader is to be shown or not.
    ///   - text: An optional text to show below the spinning circle.
    ///   - color: The color of the spinning circle.
    /// - Returns: The loader view.
    func progressView(
        isShowing: Binding <Bool>,
        backgroundColor: Color = .black,
        dimBackground: Bool = false,
        text : String? = nil,
        loaderColor : Color = .white,
        scale: Float = 1,
        blur: Bool = false) -> some View {
            
        self.modifier(ProgressViewModifier(
            isShowing: isShowing,
            backgroundColor: backgroundColor,
            dimBackground: dimBackground,
            text: text,
            loaderColor: loaderColor,
            scale: scale,
            blur: blur)
        )
    }
}


// MARK: - ProgressViewModifier

struct ProgressViewModifier : ViewModifier {
    @Binding var isShowing : Bool
    
    var backgroundColor: Color
    var dimBackground: Bool
    var text : String?
    var loaderColor : Color
    var scale: Float
    var blur: Bool
    
    func body(content: Content) -> some View {
    
        ZStack { content

            if isShowing {
                withAnimation {
                    showProgressView()
                }
            }
        }
    }
}


// MARK: - Private methods

extension ProgressViewModifier {
    
    private func showProgressView() -> some View {
        ZStack {
            Rectangle()
                .fill(backgroundColor.opacity(0.7))
                .ignoresSafeArea()
                .background(.ultraThinMaterial)
            VStack (spacing : 20) {
                if isShowing {
                    ProgressView()
                        .tint(loaderColor)
                        .scaleEffect(CGFloat(scale))
                    if text != nil {
                        Text(text!)
                        .foregroundColor(.black)
                        .font(.headline)
                    }
                }
            }
            .background(.clear)
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
    }
}

这是RootTabView,包含TabBar。

struct RootTabView: View {
    var body: some View {
        TabView {
            AddEverydayExpense()
                .tabItem {
                    Label("First", systemImage: "1.circle")
                }
            AddInvestment()
                .tabItem {
                    Label("Second", systemImage: "2.circle")
                }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        RootTabView()
    }
}

这是我的看法。

struct AddEverydayExpense: View {
    
    @ObservedObject private var model = AddEverydayExpenseVM()
    @State private var description: String = ""
    @State private var cost: String = ""
    @State private var date: Date = Date()
    @State private var essential: Bool = false
    @State private var month: Month?
    @State private var category: Category?
    private var isButtonDisabled: Bool {
        return description.isEmpty ||
            cost.isEmpty ||
            month == nil ||
            category == nil
    }
    
    var body: some View {
        NavigationView {
            VStack {
                Form {
                    Section {
                        TextField("", text: $description, prompt: Text("Descrizione"))
                        TextField("", text: $cost, prompt: Text("10€"))
                            .keyboardType(.numbersAndPunctuation)
                        DatePicker(date.string(withFormat: "EEEE"), selection: $date)
                        HStack {
                            CheckboxView(checked: $essential)
                            Text("È considerata una spesa essenziale?")
                        }
                        .onTapGesture {
                            essential.toggle()
                        }
                    }
                    Section {
                        Picker(month?.name ?? "Mese di riferimento", selection: $month) {
                            ForEach(model.months) { month in
                                Text(month.name).tag(month as? Month)
                            }
                        }
                        Picker(category?.name ?? "Categoria", selection: $category) {
                            ForEach(model.categories) { category in
                                Text(category.name).tag(category as? Category)
                            }
                        }
                    }
                    Section {
                        Button("Invia".uppercased()) { print("Button") }
                        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
                        .font(.headline)
                        .listRowBackground(isButtonDisabled ? Color.gray.opacity(0.5) : Color.blue)
                        .foregroundColor(Color.white.opacity(isButtonDisabled ? 0.5 : 1))
                        .disabled(!isButtonDisabled)
                    }
                }
                Spacer()
            }
            .navigationTitle("Aggiungi Spesa")
        }
        .progressView(isShowing: $model.isFetching, blur: true)
    }
}

如您所见,.progressView(isShowing: $model.isFetching, blur: true) 行具有魔力。问题是加载器仅显示在当前视图上,而不显示在选项卡上。 .

我怎样才能达到效果?

【问题讨论】:

    标签: ios swift swiftui view


    【解决方案1】:

    如果您希望进度视图覆盖整个视图(包括标签栏),它必须位于视图层次结构中 TabBar 或以上。现在,它位于子视图中的TabBar 下方。

    因为需要将状态传递给父级(TabBar 的所有者),所以您需要某种可以向下传递给子级的状态管理。这可能意味着只需将Binding 传递给@State。我选择展示如何通过使用@EnvironmentObject 向下传递层次结构的ObservableObject 来实现这一点,这样您就不必显式传递依赖关系。

    class ProgressManager : ObservableObject {
        @Published var inProgress = false
    }
    
    struct ContentView : View {
        @StateObject private var progressManager = ProgressManager()
        
        var body: some View {
            TabView {
                AddEverydayExpense()
                    .tabItem {
                        Label("First", systemImage: "1.circle")
                    }
                AddInvestment()
                    .tabItem {
                        Label("Second", systemImage: "2.circle")
                    }
            }
            .environmentObject(progressManager)
            .progressView(isShowing: $progressManager.inProgress) //<-- Note that this is outside of the `TabBar`
        }
    }
    
    struct AddEverydayExpense : View {
        @EnvironmentObject private var progressManager : ProgressManager
        
        var body: some View {
            Button("Progress") {
                progressManager.inProgress = true
            }
        }
    }
    

    【讨论】:

    • 很好的建议,非常有意义。只有一个问题,使用 MVVM 模式,我如何处理 ProgressManager 对象?我应该把它传递给模型。一种方法是将引用传递给模型。你怎么看?
    • 当然——将引用传递给模型或只是绑定到进度变量。另一种方法是使用 onChange 观察视图模型上的属性,然后根据其状态改变进度管理器。
    猜你喜欢
    • 1970-01-01
    • 2019-11-27
    • 2021-06-07
    • 2012-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多