SwiftUI - closing modal by swipe is breaking view constraints - swiftui

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")
})
}
}

Related

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?

Detect if user tapped the screen, including Buttons, in SwiftUI

I've been trying to use .onTapGesture to detect if the user taps on the screen. However, I noticed that it doesn't get triggered if the user taps on actionable elements like buttons and pickers. Is there a way to detect if the user taps on the screen, including these actionable elements without having to manually call my viewTapped function for each button, picker, etc. separately?
Here's the code I used to test this.
struct ContentView: View {
#State var mode = 0
var body: some View {
ZStack {
VStack {
Text("Hello, world!")
.padding()
Picker(
selection: $mode,
label: Text("Picker")) {
Text("Option 1").tag(0)
Text("Option 2").tag(1)
}
.pickerStyle(SegmentedPickerStyle())
.frame(width: 200)
.foregroundColor(.white)
.padding()
Button {
print("button clicked")
} label: {
Text("Click me")
}
.padding()
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.green)
.onTapGesture {
viewTapped()
}
}
public func viewTapped() {
print("view tapped")
}
}
If you need to detect touches simultaneity, you need the simultaneousGesture modifier instead. like:
.simultaneousGesture(
TapGesture()
.onEnded {
viewTapped()
}
)

How to position text and a button in this manner?

This is an example of what I am trying to do.Link to image of design to implement. I am unsure how to position a button and text at the top of the screen in this manner coding in swiftui. Alternatively I thought I could use the navigation bar inline and customise that but I am unsure.
var body: some View {
VStack (alignment: .trailing) {
HStack(spacing:10) {
Button(action: {
}) {
Image(systemName: "line.horizontal.3")
.font(.headline)
}
Text("WeCollab")
.foregroundColor(.white)
.font(.title)
//padding(.leading,40)
Spacer()
}
.padding(.top,UIApplication.shared.windows.first?.safeAreaInsets.top)
.background(customPurpleColour)
Spacer()
}
.edgesIgnoringSafeArea(.top)
}
}
Seems like you're on the right track. In order to keep the title centered, it seemed easier to make a ZStack. The menu button gets it's own .leading-aligned VStack and then the title goes on top of that.
The edgesIgnoringSafeArea and padding can be simplified so that you don't have to use the screen size safe areas.
struct ContentView: View {
var body: some View {
VStack {
ZStack {
VStack {
Button(action: { }) {
Image(systemName: "line.horizontal.3")
.font(.headline)
}.padding(.leading)
}.frame(maxWidth: .infinity, alignment: .leading)
Text("WeCollab")
.foregroundColor(.white)
.font(.title)
}
.background(Color.purple.edgesIgnoringSafeArea(.top))
Spacer() //other content goes here
}
}
}

Is it possible to replace the default back button and sidebar icon in iPad's Navigation Bar using NavigationView in SwiftUI?

I'm trying to make an iOS app that uses SwiftUI's NavigationView to build a side menu.
On iPhone it works perfectly, but on iPad I cannot get Rid of the Sidebar button and the back button text and icon. I would like to replace those buttons with the three horizontal lines icon named line.horizontal.3
Landscape screenshot with side menu icon I'd like to replace with three lines icon
Portrait screenshot with back button (woth icon and text) I'd like to replace with three lines icon
The code I'm using is the following:
import SwiftUI
struct ContentView_iPad: View {
#State var showMenu: Bool = false
var body: some View {
NavigationView{
MenuView()
iPad_menu()
.navigationBarTitle("Side Menu", displayMode: .inline)
.navigationBarItems(leading:
(Button(action: {
withAnimation{
self.showMenu.toggle()
}
}){
Image(systemName: "line.horizontal.3").imageScale(.large)
}
))
.navigationViewStyle(StackNavigationViewStyle())
}
}
}
The MainView:
import SwiftUI
struct iPad_menu: View {
var body: some View {
EmptyView()
.background(Color.black)
}
}
The MenuView:
import SwiftUI
struct MenuView: View {
var body: some View {
VStack(alignment: .leading) {
HStack {
Image(systemName: "person")
.foregroundColor(.gray)
.imageScale(.large)
Text("Profile")
.foregroundColor(.gray)
.font(.headline)
}
.padding(.top, 100)
HStack {
Image(systemName: "envelope")
.foregroundColor(.gray)
.imageScale(.large)
Text("Messages")
.foregroundColor(.gray)
.font(.headline)
}
.padding(.top, 30)
HStack {
Image(systemName: "gear")
.foregroundColor(.gray)
.imageScale(.large)
Text("Settings")
.foregroundColor(.gray)
.font(.headline)
}
.padding(.top, 30)
}
.padding()
.frame(maxWidth: .infinity, alignment: .leading)
.background(Color(red: 32/255, green: 32/255, blue: 32/255))
.edgesIgnoringSafeArea(.all)
}
}

TabView with PageTabViewStyle on tvOS navigationlink or buttons not working

Neither navigation or Button's actions working in the pages of the TabView with PageTabViewStyle. Anybody has any ideas or workarounds? Repro code:
var body: some View {
VStack {
TabView(selection: $selection) {
Button(action: {
print("First selected")
}){
Text("Fist")
}
.tag(0)
.buttonStyle(PlainButtonStyle()
Button(action: {
print("Second selected")
}){
Text("Second").tag(1)
}
.tag(1)
Button(action: {
print("Third selected")
}){
Text("Third")
}
.tag(2)
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .always))
.frame(width: 900, height: 400)
.background(Color.blue)
}
}
Button action is working in your shared code but the issue is with the tappable area of Button, which is very less.
Hence, you are unable to sense the button action.
Increase the Button tappable area like this :
Text("Fist")
.frame(width: 200, height: 200, alignment: .center)
.background(Color.red)