SwiftUI 在 NavigationView 中添加个人警报视图,后退按钮不适用于 Xcode 12 iOS14

Posted

技术标签:

【中文标题】SwiftUI 在 NavigationView 中添加个人警报视图,后退按钮不适用于 Xcode 12 iOS14【英文标题】:SwiftUI adding personal alert view in NavigationView, the back button doesn't work with Xcode 12 iOS14 【发布时间】:2020-10-03 15:49:08 【问题描述】:

我有一个扩展名 (.alertLinearProgressBar),例如 .alert 视图,用于显示解压缩进度。 在 ios 14 运行良好之前,现在如果我发出这个警报,导航返回按钮不再起作用(如果我向右拖动,它正在工作并返回列表,所以我猜问题是与返回按钮冲突栏,只有那个按钮不起作用,顶栏中的其他按钮都起作用)。

有人知道这个问题吗?

import SwiftUI

var chatData = ["1","2","3","4"]

struct ContentView: View 
    
    @State private var selection = 0
    @State private var isShowingAlert = false
    
    var body: some View 
        TabView (selection: $selection) 
            NavigationView 
                ZStack (alignment: .bottom) 
                
                    MasterView()
                        .navigationBarTitle(Text("Chat"))
                        .navigationBarItems(
                            leading: EditButton(),
                            trailing: Button(
                                action: 
                                //
                            
                        ) 
                            Image(systemName: "plus.circle")
                            .contentShape(Rectangle())
                        )
                        .background(Color.clear)
                
            
            //IF I comment this line below, the navigation is working well
            .alertLinearProgressBar(isShowing: self.$isShowingAlert, progressValue: .constant(0.5), barHeight: 8, loadingText: .constant(""), titleText: .constant(NSLocalizedString("unzipping", comment: "")),isShowingActivityIndicator: .constant(true)).offset(x: 0, y: 1)
        
    


struct MasterView: View 
    
    
    var body: some View 
        ZStack 
            List 
                ForEach(chatData, id: \.self)  chat in
                    NavigationLink(
                        destination:
                        //Test2()
                            DetailView(text: chat)
                    ) 
                        ChatRow(text: chat)//, progressValue: self.$progressValue)
                    
                    
                
            
        
    

struct ChatRow: View 
    
    var text: String
    
    var body: some View 
        Text(text)
    

struct DetailView: View 
    
    var text: String
    
    var body: some View 
        Text(text)
    

struct ContentView_Previews: PreviewProvider 
    static var previews: some View 
        ContentView()
    

struct AlertLinearProgressBar<Presenting>: View where Presenting: View 

    @Binding var isShowing: Bool
    @Binding var progressValue:Float
    @State var barHeight: Int
    @Binding var loadingText: String
    @Binding var titleText: String
    
    
    @Binding var isShowingProgressBar: Bool
    @Binding var isShowingActivityIndicator: Bool
    
    let presenting: () -> Presenting
    
    var body: some View 

        GeometryReader  geometry in

            self.presenting()
                .blur(radius: self.isShowing ? 2 : 0).offset(y:1)
                .disabled(self.isShowing)
            
            ZStack 
                Rectangle()
                    .frame(width: geometry.size.width * 0.8,
                           height: self.titleText == "" ? 70:100)
                .foregroundColor(Color.white)
                .cornerRadius(15)
                .shadow(radius: 20)
                .overlay(
                    GeometryReader  geometry in
                        VStack 
                            if self.titleText != "" 
                                Text(self.titleText)
                                    .bold()
                                    .offset(x: 0, y: 0)
                                    .padding(EdgeInsets(top: 4, leading: 0, bottom: 0, trailing: 0))
                            
                            HStack 
                                Text("\(self.loadingText) " + "\(self.isShowingProgressBar ? self.progressValue.getPercentage(to: 1):"")")
                                    .font(.caption)
                                ActivityIndicator(isAnimating: .constant(true), isShowing: self.$isShowingActivityIndicator, style: .medium)
                            
                            //.font(.system(size: 13))
                            Spacer()
                                .frame(height:6)
                            ZStack(alignment: .leading) 
                                Rectangle()
                                    .frame(width: geometry.size.width, height: CGFloat(self.barHeight))
                                    .opacity(0.3)
                                    .foregroundColor(Color(UIColor.systemTeal))
                                    .cornerRadius(5.0)
                                Rectangle()
                                    .frame(width: min(CGFloat(self.progressValue)*geometry.size.width, geometry.size.width), height: CGFloat(self.barHeight))
                                    .foregroundColor(Color.blue)
                                    .animation(.linear)
                                    .cornerRadius(5.0)
                            .opacity(self.isShowingProgressBar ? 1 : 0)
                        
                        
                    
                    .padding(EdgeInsets(top: 0, leading: 15, bottom: 0, trailing: 15))
                )
                .padding()
            
            .frame(width: self.isShowing ? geometry.size.width:0,
                   height: self.isShowing ? geometry.size.height:0)
            .transition(.slide)
            .opacity(self.isShowing ? 1 : 0)
        
    
    
    

extension Float 
    //number of decimal
    func round(to places: Int) -> Float 
        let divisor = pow(10.0, Float(places))
        return (self * divisor).rounded() / divisor
    
    func getPercentage(to digits: Int) -> String 
        if self >= 1 
            return String(Int(self * 100)) + "%"
        
        return String(format: "%.\(digits)f", self * 100) + "%"
    


extension View 
    func alertLinearProgressBar(isShowing: Binding<Bool>,
                        progressValue: Binding<Float>,
                        barHeight: Int, loadingText: Binding<String>, titleText: Binding<String>=Binding.constant(""), isShowingProgressBar: Binding<Bool>=Binding.constant(true), isShowingActivityIndicator:Binding<Bool>=Binding.constant(false)) -> some View 
        AlertLinearProgressBar(isShowing: isShowing, progressValue: progressValue, barHeight: barHeight, loadingText: loadingText, titleText: titleText, isShowingProgressBar: isShowingProgressBar, isShowingActivityIndicator:isShowingActivityIndicator, presenting: self)
    


struct ActivityIndicator: UIViewRepresentable 

    @Binding var isAnimating: Bool
    @Binding var isShowing: Bool
    
    let style: UIActivityIndicatorView.Style
    var color:UIColor?
    
    func makeUIView(context: UIViewRepresentableContext<ActivityIndicator>) -> UIActivityIndicatorView 
        return UIActivityIndicatorView(style: style)
    

    func updateUIView(_ uiView: UIActivityIndicatorView, context: UIViewRepresentableContext<ActivityIndicator>) 
        isAnimating ? uiView.startAnimating() : uiView.stopAnimating()
        uiView.isHidden = isShowing ? false:true
        if color != nil 
            uiView.color = color!
        
    

【问题讨论】:

【参考方案1】:

即使不透明度设置为 0,您的 AlertLinearProgressBar 看起来也会阻止 NavigationBar。

可以看到覆盖层隐藏时的位置在左上角并与导航栏重叠(尝试设置.opacity(self.isShowing ? 1 : 0.5))。

您可以做的是使用hidden 修饰符真正隐藏它。

这是使用if modifier 的可能解决方案:

struct AlertLinearProgressBar<Presenting>: View where Presenting: View 
    // ...

    var body: some View 
        GeometryReader  geometry in
            ZStack 
                // ...
            
            .frame(width: self.isShowing ? geometry.size.width : 0,
                   height: self.isShowing ? geometry.size.height : 0)
            .transition(.slide)
            .opacity(self.isShowing ? 1 : 0.5)
            .if(!isShowing) 
                $0.hidden() // use `hidden` here
            
        
    

extension View 
    @ViewBuilder func `if`<T>(_ condition: Bool, transform: (Self) -> T) -> some View where T: View 
        if condition 
            transform(self)
         else 
            self
        
    

或者,您可以有条件地显示视图:

struct AlertLinearProgressBar<Presenting>: View where Presenting: View 
    //...
    
    var body: some View 
        GeometryReader  geometry in
            self.presenting()
                .blur(radius: self.isShowing ? 2 : 0).offset(y: 1)
                .disabled(self.isShowing)

            if isShowing 
                // ...
            
        
    

【讨论】:

你明白了,不透明度为 0.5,警报在视图的左上角可见并且覆盖了按钮。谢谢大佬

以上是关于SwiftUI 在 NavigationView 中添加个人警报视图,后退按钮不适用于 Xcode 12 iOS14的主要内容,如果未能解决你的问题,请参考以下文章

在 NavigationView 中清除 SwiftUI 列表未正确返回默认值

SwiftUI:检测 tvOS 上 NavigationView 中的选择更改

在 SwiftUI 中向 NavigationView 添加搜索栏

如何在 SwiftUI 中隐藏 NavigationView Bar

如何在 SwiftUI 中将 TabView 与 NavigationView 一起使用?

在 SwiftUI 中以编程方式移动 NavigationView 的最佳实践是啥