I've found many SwiftUI side menu tutorials on the internet, but none of them shows how to switch between content views. So I tried to create one, which seems to be work, my only problem is when I select any of the menu items, there's no transition animation between the contents (except when you select the active one in the menu).
I'd like to know, if this is a good approach and I need some help to complete the animation...
Thank you in advance!
import SwiftUI
enum PageSelector {
case home
case services
case testimonials
case contact
}
struct TestView: View {
#State private var show: Bool = false
#State private var pageSelector: PageSelector = .home
var body: some View {
ZStack {
// Background of the menu
Color.blue.edgesIgnoringSafeArea(.all)
// Menu items and content selection
VStack(alignment: .leading, spacing: 10) {
Spacer()
MenuItem(action: {
self.show.toggle()
self.pageSelector = .home
}, title: "Home", image: "chevron.right")
MenuItem(action: {
self.show.toggle()
self.pageSelector = .services
}, title: "Services", image: "chevron.right")
MenuItem(action: {
self.show.toggle()
self.pageSelector = .testimonials
}, title: "Testimonials", image: "chevron.right")
MenuItem(action: {
self.show.toggle()
self.pageSelector = .contact
}, title: "Contact", image: "chevron.right")
Spacer()
}
// Content
switchContent(show: show)
.disabled(show ? true : false)
.offset(x: show ? 300 : 0)
.rotationEffect(Angle(degrees: show ? -10 : 0))
.rotation3DEffect(Angle(degrees: show ? 40: 0), axis: (x: show ? 120 : 0, y: show ? 234 : 0, z: show ? 0 : 0))
.animation(.spring())
.edgesIgnoringSafeArea(.all)
// Menu button
VStack {
HStack {
Button(action: { self.show.toggle() }) {
MenuButton(show: $show)
}
Spacer()
}
Spacer()
}
}
}
// Switching pages (views)
func switchContent(show: Bool) -> AnyView {
switch pageSelector {
case .home:
return AnyView(HomeView())
case .services:
return AnyView(ServicesView())
case .testimonials:
return AnyView(TestiMonialsView())
case .contact:
return AnyView(HomeView())
}
}
}
struct TestView_Previews: PreviewProvider {
static var previews: some View {
TestView()
}
}
struct MenuItem: View {
let action: ()->Void
let title: String
let image: String
var body: some View {
HStack {
Button(action: action) {
Image(systemName: image)
.foregroundColor(.white)
.padding(.horizontal)
.font(.footnote)
Text(title)
.foregroundColor(.white)
.font(.title)
}
Spacer()
}
}
}
struct MenuButton: View {
#Binding var show: Bool
var body: some View {
HStack {
Image(systemName: self.show ? "xmark" : "list.dash")
.frame(width: 50, height: 50)
.foregroundColor(self.show ? .blue : .white)
.background(self.show ? Color.white : Color.blue)
.cornerRadius(25)
.scaleEffect(self.show ? 0.6 : 1)
.shadow(radius: 20)
.padding()
.opacity(0.8)
.animation(.spring())
}
}
}
A sample view:
import SwiftUI
struct HomeView: View {
var body: some View {
VStack {
Text("This is Home")
}
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
.background(Color.white)
}
}
struct HomeView_Previews: PreviewProvider {
static var previews: some View {
HomeView()
}
}
You need to separate the layout animation from content animations, like the following:
func switchContent(show: Bool) -> AnyView {
return AnyView( HomeSwitchView(pageSelector: self.$pageSelector))
}
}
struct HomeSwitchView: View {
#Binding var pageSelector : PageSelector
var body: some View {
var text : String?
switch pageSelector {
case .home:
text = "home"
case .services:
text = "services"
case .testimonials:
text = "testimonials"
case .contact:
text = "contact"
}
return VStack {
Text("\(text!)")
}
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
.background(Color.blue)
}
}
First, remove all self.show.toggle() from MenuItems, like this:
MenuItem(action: {
self.pageSelector = .home
}, title: "Home", image: "chevron.right")
And then add onAppear modifier on switchContent(), like this:
switchContent(show: show)
.disabled(show ? true : false)
.offset(x: show ? 300 : 0)
.rotationEffect(Angle(degrees: show ? -10 : 0))
.rotation3DEffect(Angle(degrees: show ? 40: 0), axis: (x: show ? 120 : 0, y: show ? 234 : 0, z: show ? 0 : 0))
.animation(.spring())
.onAppear {
self.show = false
}
.edgesIgnoringSafeArea(.all)
Tested, this is working for me.
Related
Well, I'm working on a little app where I have a TextEditor element to type whatever we want. The case is, I want to keep the text on the TextEditor while switching other views, but I can't.
TextEditor before switching the view :
TextEditor after switching the view :
The code is the next one:
struct VistaDatos: View {
#State private var opinion: String = ""
var progreso : Double {
Double(opinion.count)
}
var body: some View {
VStack{
//SOME CODE HERE ...
HStack{
Text("Mi opinión...")
.font(.headline)
Image(systemName: "pencil")
.foregroundColor(.white)
.font(.headline)
}
VStack{
TextEditor(text: $opinion)
.background(.green)
.frame(width: 350, height: 250)
.background().colorMultiply(.green)
.overlay(Rectangle().stroke(Color.black, lineWidth:2))
.disableAutocorrection(true)
.onChange(of: self.opinion) { value in
if Int(opinion.count) > 150 {
self.opinion = String(value.prefix(150))
}
}
Text("Número de palabras: \(Int(progreso))/150").foregroundColor(Int(progreso) >= 100 ? .red : .white)
ProgressView(,value: progreso, total: 150) {
}.frame(width: 350, alignment: .center)
}
}.background(Color.green)
Spacer()
}
}
I have to use .onDisappear event to make it work (it seems to be on the first level stack ), but it isn't working...
How can I make it work?
Thanks in advance.
Since you say you have multiple views, that I assume may need the opinion text,
try this example code. It keeps your text in a ObservableObject,
that you can use throughout your app.
For you to do, is to code the save and retrieve
from wherever you want. In this example it is using the UserDefaults.
class StoreService: ObservableObject {
// your text
#Published var opinion = ""
// ... todo code to store your data when you are finished
func save() {
// save your data
UserDefaults.standard.set(opinion, forKey: "opinion")
}
// ... todo code to retrieve your data when the app starts again
init() {
// get your data
opinion = UserDefaults.standard.string(forKey: "opinion") ?? ""
}
}
struct ContentView: View {
#StateObject var store = StoreService() // <-- here
var body: some View {
NavigationStack {
VStack (spacing: 50) {
Text("\(store.opinion.count) characters typed")
NavigationLink("go to VistaDatos", value: "editor")
.navigationDestination(for: String.self) { str in
VistaDatos()
}
}
}.environmentObject(store) // <-- here
}
}
struct VistaDatos: View {
#EnvironmentObject var store: StoreService // <-- here
var progreso : Double {
Double(store.opinion.count)
}
var body: some View {
VStack{
//SOME CODE HERE ...
HStack{
Text("Mi opinión...").font(.headline)
Image(systemName: "pencil")
.foregroundColor(.white)
.font(.headline)
}
VStack{
TextEditor(text: $store.opinion) // <-- here
.background(.green)
.frame(width: 350, height: 250)
.background().colorMultiply(.green)
.overlay(Rectangle().stroke(Color.black, lineWidth:2))
.disableAutocorrection(true)
.onChange(of: store.opinion) { value in
if store.opinion.count > 150 {
store.opinion = String(value.prefix(150))
}
}
Text("Número de palabras: \(Int(progreso))/150").foregroundColor(Int(progreso) >= 100 ? .red : .white)
ProgressView(value: progreso, total: 150).frame(width: 350, alignment: .center)
Button("Save me") { // <-- here
store.save()
}
}
}.background(Color.green)
Spacer()
}
}
Alternatively, you could use the simple #AppStorage, like this:
struct ContentView: View {
#AppStorage("opinion") var opinion = "" // <-- here
var body: some View {
NavigationStack {
VStack (spacing: 50) {
Text("\(opinion.count) characters typed")
NavigationLink("go to VistaDatos", value: "editor")
.navigationDestination(for: String.self) { str in
VistaDatos()
}
}
}
}
}
struct VistaDatos: View {
#AppStorage("opinion") var opinion = "" // <-- here
var progreso : Double {
Double(opinion.count)
}
var body: some View {
VStack{
//SOME CODE HERE ...
HStack{
Text("Mi opinión...").font(.headline)
Image(systemName: "pencil")
.foregroundColor(.white)
.font(.headline)
}
VStack{
TextEditor(text: $opinion) // <-- here
.background(.green)
.frame(width: 350, height: 250)
.background().colorMultiply(.green)
.overlay(Rectangle().stroke(Color.black, lineWidth:2))
.disableAutocorrection(true)
.onChange(of: opinion) { value in
if opinion.count > 150 {
opinion = String(value.prefix(150))
}
}
Text("Número de palabras: \(Int(progreso))/150").foregroundColor(Int(progreso) >= 100 ? .red : .white)
ProgressView(value: progreso, total: 150).frame(width: 350, alignment: .center)
}
}.background(Color.green)
Spacer()
}
}
Is there a way to remove the cornerRadius of a sheet? I tried it like this:
.sheet(isPresented: $showModal) {
Modal().cornerRadius(0, corners: [.topLeft, .topRight])
}
but it didn't work.
I know I can just use fullScreenCover but I still want to know if there is a solution to this.
According to my comment above you can create your own slide-in menu.
In the example below I added a close button as well as gesture control to close the view.
//
//
// SlideInMenu.swift
// SlideInMenu
//
// Created by Sebastian on 21.09.22.
//
import SwiftUI
var bounds = UIScreen.main.bounds
struct ContentView: View {
#State var selectedItem: String = ""
#State var showMenu = false
var body: some View {
ZStack() {
MainView(selectedItem: $selectedItem, showMenu: $showMenu)
.blur(radius: showMenu ? 3 : 0)
SlideView(selectedItem: $selectedItem, showMenu: $showMenu)
}
.edgesIgnoringSafeArea(.all)
}
}
struct MainView: View {
#Binding var selectedItem: String
#Binding var showMenu: Bool
var body: some View {
HStack(){
Spacer()
VStack() {
Spacer()
Text("This is your main View")
.foregroundColor(.white)
.padding()
Button(action: {
withAnimation(.linear(duration: 0.3)) {
self.showMenu.toggle()
}
}) {
Text("Show Menu")
.font(.system(size: 20, weight: .medium))
.foregroundColor(.white)
}
Spacer()
}
Spacer()
}.background(Color.blue)
}
}
struct SlideView: View {
#Binding var selectedItem: String
#Binding var showMenu: Bool
#State private var viewOffest: CGFloat = 100
#State private var offset = CGSize.zero
#State private var isDragging = false
var body: some View {
let dragGesture = DragGesture()
.onChanged { value in
withAnimation(.linear(duration: 0.2)) {
if value.translation.height >= 0 {
offset = value.translation
}
}
}
.onEnded { _ in
withAnimation(.linear(duration: 0.2)) {
isDragging = false
if offset.height > (bounds.height - viewOffest)/3 {
showMenu.toggle()
}
offset = .zero
}
}
ZStack() {
Color.black
.opacity(showMenu ? 0.5 : 0)
VStack() {
HStack() {
Spacer()
Spacer()
}
VStack(alignment: .leading) {
HStack() {
Spacer()
Text("Here is the menu")
.foregroundColor(.black)
Spacer()
}
HStack() {
Spacer()
Button(action: {
withAnimation(.linear(duration: 0.3)) {
self.showMenu.toggle()
}
}) {
Text("Close Menu")
.font(.system(size: 20, weight: .medium))
.foregroundColor(.red)
}
.padding()
Spacer()
}
Spacer()
}
.padding()
.background(Color.white)
.cornerRadius(0)
}
.offset(y: showMenu ? viewOffest + offset.height : bounds.height)
.gesture(dragGesture)
}
}
}
It is not possible for now perhaps we can in further update, nonetheless you can create your own custom view.
I have 2 user view called user1 and user2, I am updating user with button, and I want give a transition animation to update, but for some reason my transition does not work, as I wanted, the issue is there that Text animated correctly but image does not, it stay in its place and it does not move with Text to give a smooth transition animation.
struct ContentView: View {
#State var show: Bool = Bool()
var body: some View {
VStack {
if (show) {
UserView(label: { Text("User 1") })
.transition(AnyTransition.asymmetric(insertion: AnyTransition.move(edge: Edge.trailing), removal: AnyTransition.move(edge: Edge.leading)))
}
else {
UserView(label: { Text("User 2") })
.transition(AnyTransition.asymmetric(insertion: AnyTransition.move(edge: Edge.leading), removal: AnyTransition.move(edge: Edge.trailing)))
}
Button("update") { show.toggle() }
}
.padding()
.animation(Animation.linear(duration: 1.0), value: show)
}
}
struct UserView<Label: View>: View {
let label: () -> Label
#State private var heightOfLabel: CGFloat? = nil
var body: some View {
HStack {
if let unwrappedHeight: CGFloat = heightOfLabel {
Image(systemName: "person")
.resizable()
.frame(width: unwrappedHeight, height: unwrappedHeight)
}
label()
.background(GeometryReader { proxy in
Color.clear
.onAppear(perform: { heightOfLabel = proxy.size.height })
})
Spacer(minLength: CGFloat.zero)
}
.animation(nil, value: heightOfLabel)
}
}
the heightOfLabel doesn't have to be optional, and then it works:
struct UserView<Label: View>: View {
let label: () -> Label
#State private var heightOfLabel: CGFloat = .zero // not optional
var body: some View {
HStack {
Image(systemName: "person")
.resizable()
.frame(width: heightOfLabel, height: heightOfLabel)
label()
.background(GeometryReader { proxy in
Color.clear
.onAppear(perform: { heightOfLabel = proxy.size.height })
})
Spacer(minLength: CGFloat.zero)
}
.animation(nil, value: heightOfLabel)
}
}
I followed a tutorial to create a navigation menu, but I have a problem. On iPhone it is looks amazing, but on iPad I can't figure the problem.
It seems that I have a "back" button under the menu-button and If I press on it, it will show (kind-off) the page.
ScreenShoots will tell more.
Screens: https://imgur.com/a/i1Xv32t
Here is the code:
Main View
import SwiftUI
struct MainView: View {
#State var selectedTab = "Home"
#State var showMenu = false
#Environment(\.colorScheme) var colorScheme
var body: some View {
ZStack {
Color("myblue")
.ignoresSafeArea()
// Side Menu
ScrollView(getRect().height < 750 ? .vertical : .init(), showsIndicators: false, content: {
SideMenu(selectedTab: $selectedTab)
})
ZStack {
if colorScheme == .dark {
Color.black
.opacity(0.5)
.cornerRadius(showMenu ? 15 : 0)
.shadow(color: Color.black.opacity(0.07), radius: 5, x: -5, y: 0)
.offset(x: showMenu ? -25 : 0)
.padding(.vertical, 30)
Color.black
.opacity(0.4)
.cornerRadius(showMenu ? 15 : 0)
.shadow(color: Color.black.opacity(0.07), radius: 5, x: -5, y: 0)
.offset(x: showMenu ? -50 : 0)
.padding(.vertical, 60)
} else {
Color.white
.opacity(0.5)
.cornerRadius(showMenu ? 15 : 0)
.shadow(color: Color.black.opacity(0.07), radius: 5, x: -5, y: 0)
.offset(x: showMenu ? -25 : 0)
.padding(.vertical, 30)
Color.white
.opacity(0.4)
.cornerRadius(showMenu ? 15 : 0)
.shadow(color: Color.black.opacity(0.07), radius: 5, x: -5, y: 0)
.offset(x: showMenu ? -50 : 0)
.padding(.vertical, 60)
}
Home(selectedTab: $selectedTab)
.cornerRadius(showMenu ? 15 : 1)
}
// Scalling and Moving The View
.scaleEffect(showMenu ? 0.84 : 1)
.offset(x: showMenu ? getRect().width - 120 : 0)
.ignoresSafeArea()
.overlay(
// Menu Button
Button(action: {
withAnimation(.spring()) {
showMenu.toggle()
}}, label: {
VStack (spacing: 5) {
Capsule()
.fill(showMenu ? Color.white : Color.primary)
.frame(width: 30, height: 3)
.rotationEffect(.init(degrees: showMenu ? -50 : 0))
.offset(x: showMenu ? 2 : 0, y: showMenu ? 9 : 0)
VStack (spacing: 5) {
Capsule()
.fill(showMenu ? Color.white : Color.primary)
.frame(width: 30, height: 3)
Capsule()
.fill(showMenu ? Color.white : Color.primary)
.frame(width: 30, height: 3)
.offset(y: showMenu ? -8 : 0)
}
.rotationEffect(.init(degrees: showMenu ? 50 : 0))
}
})
.padding()
,alignment: .topLeading
)
}
}
}
struct MainView_Previews: PreviewProvider {
static var previews: some View {
MainView()
}
}
extension View {
func getRect()->CGRect {
return UIScreen.main.bounds
}
}
Home
import SwiftUI
struct Home: View {
#Binding var selectedTab: String
init(selectedTab: Binding<String>) {
self._selectedTab = selectedTab
UITabBar.appearance().isHidden = true
}
var body: some View {
// Tab View with Tabs
TabView(selection: $selectedTab) {
// Views
HomePage()
.tag("Home")
History()
.tag("History")
Notifications()
.tag("Notifications")
Settings()
.tag("Settings")
Help()
.tag("Help")
}
}
}
struct Home_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct HomePage: View {
var body: some View {
NavigationView {
Text("Home")
.font(.largeTitle)
.fontWeight(.heavy)
.foregroundColor(.primary)
.navigationTitle("Home")
}.navigationBarBackButtonHidden(true)
}
}
struct History: View {
var body: some View {
NavigationView {
Text("History")
.font(.largeTitle)
.fontWeight(.heavy)
.foregroundColor(.primary)
.navigationTitle("History")
}
}
}
struct Notifications: View {
var body: some View {
NavigationView {
Text("Notifications")
.font(.largeTitle)
.fontWeight(.heavy)
.foregroundColor(.primary)
.navigationTitle("Notifications")
}
}
}
struct Settings: View {
var body: some View {
NavigationView {
Text("Settings")
.font(.largeTitle)
.fontWeight(.heavy)
.foregroundColor(.primary)
.navigationTitle("Settings")
}
}
}
struct Help: View {
var body: some View {
NavigationView {
Text("Help")
.font(.largeTitle)
.fontWeight(.heavy)
.foregroundColor(.primary)
.navigationTitle("Help")
}
}
}
Found de issue!
Had to remove NavigationView { } from every struct XYZ: View { }.
I have an extension (.alertLinearProgressBar) like an .alert view to display a unzipping progress.
Before iOS 14 was working well, now if I put this alert, the navigation back button doesn't work anymore (if I drag right, is working and it come back to the list, so I guess the problem is a conflict with back button bar, only that button doesn't work, other button in top bar are working).
Anyone knows this problem?
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!
}
}
}
It looks like your AlertLinearProgressBar even with the opacity set to 0 blocks the NavigationBar.
You can see that the overlay's position when hidden is in the top left corner and overlapping with the navigation bar (try setting .opacity(self.isShowing ? 1 : 0.5)).
What you can do is to truly hide it with the hidden modifier.
Here is a possible solution using the 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
}
}
}
Alternatively you can just display the view conditionally:
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 {
// ...
}
}
}
}