How to display a black transparent view over a view in swiftUI? - swiftui

I am trying to present a transparent view with black background over current view, so that we can still see the content of the current view. I have worked around the opacity modifier but it is not doing the job.
this is my code:
ZStack {
Rectangle()
.fill(.black)
.ignoresSafeArea()
VZScrollViewIfNeeded {
VStack(alignment: .leading) {
// some code
}
}
later I am presenting this view on click of other view:
buttonActionView()
.fullScreenCover(isPresented: $isPresenting, content: transparentView.init)```

Try changing the order of your elements. Currently your Rectangle that should overlay the view is behind the ScrollView. Should look like this:
ZStack {
VZScrollViewIfNeeded {
VStack(alignment: .leading) {
// some code
}
Rectangle()
.foregroundColor(Color.black.opacity(0.5))
.edgesIgnoringSafeArea(.all)
}

Related

SwiftUI: Spacers won't scale down because BG Image will not adjust to device size

I use a ZStack to display a fullscreen background image underneath the main UI. The main UI consists of a VStack with multiple views separated by flexible Spacers to scale down or up on different device sizes. Now I experience that the Spacers will not scale down on small devices because the background image on small devices remains bigger than the screen size and keeps the ZStack tall, see screenshot of the preview of iPhone 8. What am I doing wrong here?
Code:
import SwiftUI
struct TestView: View {
var headerView: some View {
VStack {
Text("HEADER").padding([.leading,.trailing], 100)
}
}
var middleView: some View {
HStack {
Text("MIDDLE").padding([.leading,.trailing], 100)
}
}
var bottomView: some View {
VStack {
Text("BOTTOM").padding([.leading,.trailing], 100)
}
}
var body: some View {
ZStack {
// Background Image
Image("BgImg")
.resizable()
.aspectRatio(contentMode: .fill)
.edgesIgnoringSafeArea(.all)
// Main UI
VStack(spacing: 0) {
headerView
.frame(height:222)
.background(Color.red)
Spacer()
middleView
.frame(height:155)
.background(Color.orange)
Spacer()
bottomView
.frame(height:288)
.background(Color.yellow)
}
.padding(.bottom, 32)
.padding(.top, 54)
// END Main UI
}
.edgesIgnoringSafeArea(.all)
.statusBar(hidden: true)
// END ZStack
}
}
Preview Screenshot:
See blue border is bigger than device
BG Image:
In described scenario you need to use .background instead of ZStack, such so main view form needed full-screen layout and image in background will not affect it.
So the layout should be like
VStack(spacing: 0) {
// content here
}
.padding(.bottom, 32)
.padding(.top, 54)
.edgesIgnoringSafeArea(.all)
.statusBar(hidden: true)
.background(
// image here
)

Getting VStack to shrink to minWidth when containing Text SwiftUI

I'm still somewhat new to SwiftUI and I'm getting a weird case that I don't fully understand. Basically, I have a VStack that contains some Text Views but also has a background View. Ideally, I'd like the background to grow in width as much as it needs to up to a point. I figure that is what the minWidth and maxWidth are for in .frame()
I started with this and it seems to be working:
struct TestView: View {
var body: some View {
VStack {
Text("Title")
Text("Message")
}
.background(
Rectangle()
.foregroundColor(Color.blue)
.frame(minWidth: 0,
maxWidth: 270)
)
}
}
So far so good, but when I make the text big enough that it would need to wrap, this is what I get.
So it seems that by putting the frame around the background only makes the min/max affect that background View.
If I then try to put the frame around the VStack, I get this:
struct TestView: View {
var body: some View {
VStack {
Text("Title")
Text("Message")
}
.frame(minWidth: 0,
maxWidth: 270)
.background(
Rectangle()
.foregroundColor(Color.blue)
)
}
}
Even though I don't think I have something pushing it out, it still pushes out the the full maxWidth.
I've also tried moving the frame to the Text but that gives the same result.
What is the correct way to get a VStack with background to only grow with its contents up to a maxWidth?
Thank you!
Well I'm dumb, literally right after posting I remembered something about how the order of the modifiers on a View matter.
I put the frame after the background and it worked.
struct TestView: View {
var body: some View {
VStack {
Text("Title")
Text("Message abcdefghijklmnopqrstuvwxyz")
}
.background(
Rectangle()
.foregroundColor(Color.blue)
)
.frame(minWidth: 0,
maxWidth: 270)
}
}
I'll leave my question here just incase it somehow helps someone else someday.

How to Handle Safe Area Space in SwiftUI While Ignoring It

I want to completely color the background of my app while at the same time positioning content at the top so that its far enough from the status bar and top notch on those devices that have it.
If I do something like this:
#main
struct SampleApp: App {
var body: some Scene {
WindowGroup {
VStack {
Text("Top of my view")
Spacer()
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.yellow)
}
}
}
This displays the text at the top of the content, below the safe area. But only the content area is yellow. The safe areas are white.
So I add .edgesIgnoringSafeAreas(.all) below the .background modifier.
Now my text is below the notch (or smack up at the top of the screen below the Status bar text) and not visible.
I don't want to randomly guess at a top padding because that might be fine on phones with notches but look wrong on phones or iPads without notices.
If I place my VStack inside of a GeometryReader, the reader.safeAreaInsets.top is zero (0) when the top safe area is ignored.
I hope this a clear enough question. Has anyone run into this and have a solution?
You need to apply the edgesIgnoringSafeArea modifier to Color.yellow only.
A possible solution is to use a ZStack:
#main
struct SampleApp: App {
var body: some Scene {
WindowGroup {
ZStack {
Color.yellow
.edgesIgnoringSafeArea(.all)
VStack {
Text("Top of my view")
Spacer()
}
}
}
}
}
One way to fix it is to apply .edgesIgnoringSafeArea(.all) just to the Color.yellow inside of .background():
#main
struct SampleApp: App {
var body: some Scene {
WindowGroup {
VStack {
Text("Top of my view")
Spacer()
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.yellow.edgesIgnoringSafeArea(.all))
}
}
}

Images now showing within NavigationView/NagivationLink

I was trying to show multiple clickable images within NavigationView/NagivationLink container(s).
var body: some View {
NavigationView {
VStack {
ForEach(self.sets) { set in
NavigationLink(destination: ExerciseVideoView(items: set.items )) {
Image("group-\(set.purpose)")
.resizable()
.frame(height: 300, alignment: .leading)
}
}
}
}
}
But the images are not shown.
When i remove NavigationView & NagivationLink lines - everything goes well, apart from making the images clickable of course. Here's how the screen looks like when images are shown:
and this is what happens when i uncomment NavigationView & NagivationLink lines:
Could you please explain me why are not they shown when i use NagivationLink?
Thanks!
The NavigationLink interprets images by default as template rendering mode, so you have to specify explicitly
NavigationLink(destination: ExerciseVideoView(items: set.items )) {
Image("group-\(set.purpose)")
.resizable()
.renderingMode(.original) // << here !!
.frame(height: 300, alignment: .leading)
}

SwiftUI: Center Content in ScrollView

I'm trying to center a bunch of views in a VStack within a ScrollView in SwiftUI. To simplify things, I'm just trying to get it to work with a single Text view. Here's what I've come up with so far:
var body: some View {
ScrollView(alwaysBounceVertical: true){
HStack(alignment: .center) {
Spacer()
Text("This Is a Test")
Spacer()
} //HStack
.background(Color.green)
} //ScrollView
.background(Color.gray)
}
This results in this:
I want the text to be in the middle like this:
So the HStack should be full-width and the Text should be centered within it. It seems like this should be easy, but I don't get what I'm doing wrong. :)
Using GeometryReader, you can get information about the size of the containing view and use that to size your view.
var body: some View {
GeometryReader { geometry in <--- Added
ScrollView(alwaysBounceVertical: true){
HStack(alignment: .center) {
Spacer()
Text("This Is a Test")
Spacer()
} //HStack
.frame(width: geometry.size.width) <--- Added
.background(Color.green)
} //ScrollView
.background(Color.gray)
}
}
edit: after looking into this more, it seems that part of the problem is that you are using a ScrollView. If you remove that parent, the spacers in the HStack will automatically cause stretching to fill the view. I'm guessing the automatic stretching doesn't happen in ScrollViews because there's no finite limit to how big it can be, how much would it stretch? (because a ScrollView can scroll in any direction)
This seems to be a bug in Xcode 11.0 beta, ScrollView content wouldn't fill the scroll view. If you replace the ScrollView with a List it will work as expected. But if you have to use a scroll view, one workaround is to fix the scroll view's content width.
So your code will look something like this:
ScrollView(alwaysBounceVertical: true) {
HStack(alignment: .center) {
Spacer()
Text("This Is a Test")
Spacer()
} // HStack
.frame(width: UIScreen.main.bounds.width) // set a fixed width
.background(Color.green)
} // ScrollView
.background(Color.gray)
Result:
You should use the modifier frame and set its maxWidth: .infinity.
So it tells its parent: "the wider, the better" :)
var body: some View {
ScrollView(.vertical, showsIndicators: true){
HStack(alignment: .center) {
Spacer()
Text("This Is a Test")
.frame(maxWidth: .infinity) // <- this
Spacer()
} //HStack
.background(Color.green)
} //ScrollView
.background(Color.gray)
}
And this works regardless its parent.
Scrollview or whatever View it's set in.
Paul is doing a great job clarifying it to all of us here:
https://www.hackingwithswift.com/quick-start/swiftui/how-to-give-a-view-a-custom-frame
Answer compatible with Xcode 12.1 (12A7403)
I hope this helps 👍
dsa