Using a SwiftUI menu in the top-trailing corner - swiftui

When placing a SwiftUI Menu button in the top-trailing corner, using ignoresSafeArea(), it works correctly in the SwiftUI preview:
But not on the actual device/in simulator:
When I use ignoresSafeArea() again on the menu itself, a strange behavior occurs (iOS 14.5): without padding the menu looses function, with padding, the button moves down when the Menu appears and the Menu will have an odd spacing between button and Menu:
Is there a way to get a Menu to appear correctly in the top-trailing corner?
Example code:
struct MenuButtonExampleView: View {
var body: some View {
Color.yellow
.ignoresSafeArea()
.overlay(self.menuButton, alignment: .topTrailing)
}
#ViewBuilder var menuButton: some View {
Menu(
content: {
Button(
action: {
debugPrint("Action")
},
label: {
Label("Action", systemImage: "xmark")
}
)
},
label: {
Button(
action: {},
label: {
ZStack {
Circle()
.foregroundColor(.green)
.frame(width: 30, height: 30)
Image(systemName: "ellipsis")
.foregroundColor(.white)
}
}
)
.padding(20)
}
)
}
}

This may be due to the navigation bar.
You can try to add this code.
.navigationBarHidden(true)

Create a let constant and get the complete screen height and assign it to that constant and after that change the body code as you can see in the code below and it will work for every device
let customHeight = UIScreen.main.bounds.height
var body: some View {
Color.yellow
.overlay(self.menuButton, alignment: .topTrailing)
.statusBar(hidden: true)
.frame(height: customHeight + (customHeight/18))
.ignoresSafeArea()
}

Related

SwiftUI - closing modal by swipe is breaking view constraints

I have an issue in SwiftUI with modals combined with custom backgrounds.
If I move the app into the background when a modal is open (e.g. home button on the simulator), then returning the app back to the foreground and closing the modal via swipe, the main screen constraints are broken.
The rendering is correct but the click responding constraints are off.
See screenshots about the constraints before moving the app to the background and after moving it back to the foreground:
Here is the code to reproduce the issue.
struct ContentView: View {
#State var modal = false
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundColor(.accentColor)
Text("Hello, world!")
Spacer()
Button{ modal = true } label: {
Text("Show modal")
.contentShape(Rectangle())
}
Spacer()
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
.background(
Color.yellow.ignoresSafeArea(.all)
)
.sheet(isPresented: $modal, content: {
Text("modal")
})
}
}
Am I applying any of the modifiers incorrectly or this is an iOS bug?
I have tried to apply the modifiers in a different order but it did not help.
I suppose it's the .contentShape inside Button. The following works for me:
struct ContentView: View {
#State var modal = false
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundColor(.accentColor)
Text("Hello, world!")
Spacer()
Button{ modal = true } label: {
Text("Show modal")
// .contentShape(Rectangle())
}
Spacer()
}
.frame(maxWidth: .infinity) // reduce to minimum
.background(
Color.yellow.ignoresSafeArea(.all)
)
.sheet(isPresented: $modal, content: {
Text("modal")
})
}
}

how to use bottom sheet transparent background in swiftui like dialog?

I am using the bottom sheet. When the bottom opens, it covers the whole page over the parent. I want to use a bottom sheet like an alert dialog transparent. Thant means parent content not covered whole
here is the image:
The green portion is the content of the bottom sheet. But it covered the whole page.
Here is the code:
calling bottom sheet:
struct CustomDialogSV: View {
#State private var showSheet = false
var body: some View {
Button("Show Sheet") {
self.showSheet = true
}
.sheet(isPresented: $showSheet) {
ZStack {
Color.clear
.edgesIgnoringSafeArea(.all)
CustomAlert()
.background(Color.red)
.cornerRadius(10)
.shadow(radius: 10)
.padding()
// .center()
}
}
}
}
Bottom sheet:
struct CustomAlert: View {
var body: some View {
VStack(alignment: .leading) {
Text("Alert Title")
.font(.headline)
.padding(.bottom)
Text("This is the alert message. It can be any amount of text.")
.padding(.bottom)
HStack {
Button(action: {
// handle action for first button
}) {
Text("First Button")
}
.padding(.trailing)
Button(action: {
// handle action for second button
}) {
Text("Second Button")
}
}
}
.padding()
.background(Color.green)
.cornerRadius(10)
.shadow(radius: 10)
}
}
Is it possible to remove the background without green portions?

SwiftUI - Custom NavigationBar back button disables sliding gesture

I made my own back button in SwiftUI without using the navigation bar. Everything works very well. But when I switch from the first view to the second view, I cannot slide back. I shared my codes with you, my avalanche for your solution suggestions.
First View:
struct LogInView: View {
#State var showSignUpScreen = false
var body: some View {
NavigationView{
NavigationLink(
destination: SignUpView(),
isActive: self.$showSignUpScreen,
label: {
Text("")
})
Button(action: {
self.showSignUpScreen.toggle()
}, label: {
Text("Sign Up")
.fontWeight(.semibold)
.foregroundColor(.blue)
.frame(width: UIScreen.main.bounds.width * 0.3, height: UIScreen.main.bounds.height * 0.06)
.overlay(RoundedRectangle(cornerRadius: 10)
.stroke(Color.blue, lineWidth: 1.5))
.padding(.bottom, 15)
})
}
.navigationBarTitle("")
.navigationBarHidden(true)
}
}
Second View:
struct SignUpView: View {
#Environment(\.presentationMode) var present
var body: some View {
NavigationView{
Button(action: {
self.present.wrappedValue.dismiss()
}, label: {
ZStack {
Image(systemName: "chevron.backward")
.font(.system(size: 25, weight: .semibold))
.foregroundColor(.blue)
.padding(.leading, 10)
}
})
}
.navigationBarTitle("")
.navigationBarHidden(true)
}
}
Add this to your SignUpView:
init() {
UINavigationBar.appearance().isUserInteractionEnabled = false
UINavigationBar.appearance().backgroundColor = .clear
UINavigationBar.appearance().barTintColor = .clear
UINavigationBar.appearance().setBackgroundImage(UIImage(), for: .default)
UINavigationBar.appearance().shadowImage = UIImage()
UINavigationBar.appearance().tintColor = .clear
}
And delete:
.navigationBarHidden(true)
The idea:
.navigationBarHidden(true) seems to not only hide the bar but also disable sliding. So, in order to be able to slide, we can't do that. Fourtunately, we can also just hide it globally using UINavigationBar.appearance(), without the need of .navigationBarHidden(true).
Note that if anywhere else inside your app you want the default UINavigationBar back, you would have to init() again and unhide it, since this sets the appearance globally.
I would recommend having a globally accessible function that contains the code to hide and unhide it if that's the case for you.

SwiftUI height animation of a text frame when #ObservedObject change

I try to make a smooth animation when the NetStatus change but it's not working like i want.
I want to get the same effect as when i press the button with the toggle animation. The commented button animation is working great and i try to replicate it with the scaling of the height of the text frame.
The commented button code is just for a working example of the animation effect that i want (expand and close gracefully), i don't need this code.
How can i do that?
import SwiftUI
struct NoNetwork: View {
let screenSize: CGRect = UIScreen.main.bounds
#ObservedObject var online = NetStatus()
var body: some View {
VStack{
Text("NoNetworkTitle")
.fontWeight(.bold)
.foregroundColor(Color.white)
.frame(width: screenSize.width, height: self.online.connected ? 0 : 40, alignment: .center)
// .animation(.easeIn(duration: 5))
.background(Color.red)
// Button(action: {
// withAnimation {
// self.online.connected.toggle()
// }
// }, label: {
// Text("Animate")
// })
}
}
}
struct NoNetwork_Previews: PreviewProvider {
static var previews: some View {
NoNetwork()
}
}
To animate when online.connected changes, put the .animation modifier on the VStack:
VStack{
Text("NoNetworkTitle")
.fontWeight(.bold)
.foregroundColor(Color.white)
.frame(width: screenSize.width, height: self.online.connected ? 0 : 40, alignment: .center)
.background(Color.red)
Button(action: {
self.online.connected.toggle()
}, label: {
Text("Animate")
})
}
.animation(.easeInOut(duration: 0.5))
This will animate the other views in the VStack as the Text appears and disappears.

Dismiss button (X) on an image - top right alignment HOW?

What is an effective & effecient way to get the Dismiss button (X) into the top right corner?
I'm struggling with container alignment... can't say I GROK it.
Needless to say ... this ain't it!
var body: some View {
ZStack {
Image("Biz-card_2020")
.resizable()
.edgesIgnoringSafeArea(.all)
HStack(alignment: .top) {
VStack() {
Spacer(minLength: 5) // vertical space
HStack() {
Spacer()
// close Welcome page (X) button
Button(action: {
//print(" - Button to dismiss page \(self.isPresented)")
self.isPresented = false // dismiss the Welcome view
//print(" - after action Button to dismiss Welcome page \(self.isPresented)")
}, label: {
Image(systemName: "xmark.circle" )
.scaledFont(name: "Georgia", size: Const.titleText)
.minimumScaleFactor(0.3)
.accentColor(.white)
.padding(10)
})
}
Spacer()
}
}
}
}
You need to remove Spacer(minLength: 5) and replace it with padding for HStack.
Spacer(minLength: 5) doesn't mean its length will be exactly 5 (only that the minimum length will be 5).
You may also want to extract close button to another function for clarity.
Try the following:
struct ContentView: View {
...
var body: some View {
ZStack {
Image("Biz-card_2020")
.resizable()
.edgesIgnoringSafeArea(.all)
closeButton
}
}
var closeButton: some View {
VStack {
HStack {
Spacer()
Button(action: {
...
}) {
Image(systemName: "xmark.circle")
.padding(10)
}
}
.padding(.top, 5)
Spacer()
}
}
}
ZStack can be configured using .topTrailing etc. You should use those alignments to configure your view. Using something like Spacer() is going to cause the rest of your views to get pushed down.
ZStack(alignment: .topTrailing) {
// your code here
}
If you need to move your code a bit more, use either padding, or offset modifiers.