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

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?

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

Problem implementing scrollTo in SwiftUI using Slider and Button

I'm trying to reposition a list using scrollTo with a Slider and Button and have 2 problems. The list has Sections with id's and nested Texts below, so the scrolling (by Slider or Button) is to the Section heads only.
Here are the problems:
Slider works fine except that the animation briefly shows the list at the top (you can see the navigationTitle go to full size) before it scrolls to the desired position correctly.
The Button moves the Slider but appears to not find the id's in the Section, so it doesn't reposition to the right Section.
Anyone have any ideas?
struct Floors: Identifiable {
let id: String
let name: String
}
struct UnitLine: Identifiable {
let id=UUID()
let name: String
}
struct UnitKeyboardView: View {
#Environment(\.presentationMode) var presentationMode
#State private var sliderValue = 8.0
var body: some View {
let floors=[Floors(id:"8",name:"8"),Floors(id:"9",name:"9"),Floors(id:"10",name:"10"),Floors(id:"11",name:"11"),Floors(id:"12",name:"12"),Floors(id:"13",name:"13"),Floors(id:"14",name:"14"),Floors(id:"15",name:"15"),Floors(id:"16",name:"16"),Floors(id:"17",name:"17"),Floors(id:"18",name:"18"),Floors(id:"19",name:"19"),Floors(id:"20",name:"20"),Floors(id:"21",name:"21"),Floors(id:"22",name:"22"),Floors(id:"23",name:"23"),Floors(id:"24",name:"24"),Floors(id:"25",name:"25"),Floors(id:"26",name:"26"),Floors(id:"27",name:"27"),Floors(id:"28",name:"28"),Floors(id:"29",name:"29")]
let unitlines=[UnitLine(name:"01"),UnitLine(name:"02"),UnitLine(name:"03"),UnitLine(name:"04"),UnitLine(name:"05"),UnitLine(name:"06"),UnitLine(name:"07"),UnitLine(name:"08")]
NavigationView {
ScrollViewReader {proxy in
VStack {
Spacer()
List {
ForEach(floors) { i in
Section(header: Text("Floor \(i.name)")) {
ForEach(unitlines) { u in
Text(u.name)
.padding(.trailing,200)
.contentShape(Rectangle())
.onTapGesture {
print("tapped")
presentationMode.wrappedValue.dismiss()
}
}
}
}
}
HStack {
Spacer()
Text("Flr: \(Int(sliderValue))")
.font(.title)
.padding(10)
Button {
if sliderValue>8 {
sliderValue=sliderValue-1.0
proxy.scrollTo(String(Int(sliderValue)), anchor: .topLeading)
}
else { print("slider below (\(sliderValue))") }
print(proxy)
} label: {
Image(systemName: "minus")
}
.frame(width:30, height:15)
.padding(10)
.background(Color.red)
.clipShape(Capsule())
Slider(value: $sliderValue, in: 8...42, step: 1.0, onEditingChanged: {_ in
withAnimation {
proxy.scrollTo(String(Int(sliderValue)), anchor: .topLeading)
print(String(Int(sliderValue)))
}
}
)
.background(Color.cyan)
.border(Color.blue, width: 1)
.padding(10)
Button {
if sliderValue<42 {
print(sliderValue)
sliderValue=sliderValue+1.0
print(sliderValue)
proxy.scrollTo(String(Int(sliderValue)), anchor: .topLeading)
}
} label: {
Image(systemName: "plus")
}
.frame(width:30, height:15)
.padding(10)
.background(Color.green)
.clipShape(Capsule())
Spacer()
}
.background(Color.gray)
}
}
.navigationTitle("Select Address")
}
}
}

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

SwiftUI 2.0: Close button is not dismissing the view - how do I get the Close button to return to the previous view?

I have tried to use Buttons and Navigation Links from various examples when researched on this channel and on the net. The NavigationLink would be ok, except that the NavigationView is pushing everything down in my view.
I have a view that contains an image and a text like this: ( x Close) but when I use the code below, the Close button is not doing anything.
In ContentView() I have a (?) button that takes me from WalkthroughView(), then to the PageTabView, then to this view, TabDetailsView:
ContentView():
ZStack {
NavigationView {
VStack {
Text("Hello World")
.padding()
.font(.title)
.background(Color.red)
.foregroundColor(.white)
}
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button {
withAnimation {
showOnBoarding = true
}
} label: {
Image(systemName: "questionmark.circle.fill")
}
}
}
}
.accentColor(.red)
.disabled(showOnBoarding)
.blur(radius: showOnBoarding ? 3.0 : 0)
if showOnBoarding {
WalkthroughView(isWalkthroughViewShowing: $isWalkthroughViewShowing)
}
}
.onAppear {
if !isWalkthroughViewShowing {
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
withAnimation {
showOnBoarding.toggle()
isWalkthroughViewShowing = true
}
}
}
}
WalkthroughView():
var body: some View {
ZStack {
GradientView()
VStack {
PageTabView(selection: $selection)
// shows Previous/Next buttons only
ButtonsView(selection: $selection)
}
}
.transition(.move(edge: .bottom))
}
PageTabView():
var body: some View {
TabView(selection: $selection) {
ForEach(tabs.indices, id: \.self) { index in
TabDetailsView(index: index)
}
}
.tabViewStyle(PageTabViewStyle())
}
below, is the TabDetailsView():
At the top of the view is this Close button, when pressed, should send me back to ContentView, but nothing is happening.
struct TabDetailsView: View {
#Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
let index: Int
then, inside the body:
VStack(alignment: .leading) {
Spacer()
VStack(alignment: .leading) {
// Button to close each walkthrough page...
Button(action: {
self.presentationMode.wrappedValue.dismiss()
}) {
Image(systemName: "xmark.circle.fill")
Text("Close")
}
.padding(.leading)
.font(.title2)
.accentColor(.orange)
Spacer()
VStack {
Spacer()
Image(tabs[index].image)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 415)
.padding(.leading, 10)
Text(tabs[index].title)
.font(.title)
.bold()
Text(tabs[index].text)
.padding()
Spacer()
}
.foregroundColor(.white)
}
}
if showOnBoarding {
WalkthroughView(isWalkthroughViewShowing: $isWalkthroughViewShowing)
}
Inserting view like above is not a presentation in standard meaning, that's why provided code does not work.
As this view is shown via showOnBoarding it should be hidden also via showOnBoarding, thus the solution is to pass binding to this state into view where it will be toggled back.
Due to deep hierarchy the most appropriate way is to use custom environment value. For simplicity let's use ResetDefault from https://stackoverflow.com/a/61847419/12299030 (you can rename it in your code)
So required modifications:
if showOnBoarding {
WalkthroughView(isWalkthroughViewShowing: $isWalkthroughViewShowing)
.environment(\.resetDefault, $showOnBoarding)
}
and in child view
struct TabDetailsView: View {
#Environment(\.resetDefault) var showOnBoarding
// .. other code
Button(action: {
self.showOnBoarding.wrappedValue.toggle()
}) {
Image(systemName: "xmark.circle.fill")
Text("Close")
}