SwiftUI 在 NavigationView 中添加个人警报视图,后退按钮不适用于 Xcode 12 iOS14
SwiftUI adding personal alert view in NavigationView, the back button doesn't work with Xcode 12 iOS14
我有一个类似于 .alert 视图的扩展 (.alertLinearProgressBar) 来显示解压缩进度。
在 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!
}
}
}
看起来您的 AlertLinearProgressBar
即使将不透明度设置为 0
也会阻塞 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) {
[=10=].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 {
// ...
}
}
}
}
我有一个类似于 .alert 视图的扩展 (.alertLinearProgressBar) 来显示解压缩进度。 在 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!
}
}
}
看起来您的 AlertLinearProgressBar
即使将不透明度设置为 0
也会阻塞 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) {
[=10=].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 {
// ...
}
}
}
}