Create a border that follows the screen edge - ios

I'm trying to create a background view that looks like a blackjack table. I want a green background with brown borders that follow the edge of the screen like the edges of a card table. I've tried to implement the view like this:
var body: some View {
// Green background with a brown wood border
ZStack {
Color.green
.ignoresSafeArea()
}
.overlay(
RoundedRectangle(cornerRadius: 16)
.strokeBorder(.brown, lineWidth: 10)
.ignoresSafeArea()
)
}
The code produces this preview:
However, this produces a border that is cut off on the outline of the phone and the inside of the corners are not rounded so the table looks like it's overflowing the view.
How could I refactor the view to be a green background with a brown border that follows around the edge of the screen?

You can get your desired result by adding RoundedRectangle with cornerRadius on top of Color.brown in ZStack like this.
ZStack {
Color.brown
.ignoresSafeArea()
RoundedRectangle(cornerRadius: 40)
.fill(Color.green)
.padding()
.ignoresSafeArea()
}
Preview

Related

How to ignore parent view's padding SwiftUI

I have a VStack with multiple child views (the one with blue background). The VStack has horizontal padding. I want to have this padding set for each child, but sometimes I have exception where I want that child to reach edges of the display completely (Two grey lines above "Checkout" button). Is there any way how to allow this to happen? I don't wanna set padding for every single child separately.
You can apply a negative padding on the view that you applied on the VStack, that means if you applied a padding of 16 points to the VStack like this for example .padding(16) for all directions which is the default. then you can apply a .padding(.horizontal,-16) to the lines and they will stretch to the end of the screen
here is a sample code and a screenshot for the behavior you want.
struct VStackPadding: View {
var body: some View {
VStack{
RoundedRectangle(cornerRadius: 4)
.frame(width: .infinity,height: 3)
.padding(.horizontal, -16)
.padding(.bottom,16)
RoundedRectangle(cornerRadius: 4)
.frame(width: .infinity,height: 3)
}.padding(16)
}
}

Image at top of view extending under navigationbar

I am trying to make a "reusable" template for views in my app. As part of this I started prototyping this:
var body: some View {
NavigationView {
ZStack {
VStack {
// Spacer()
Image("progress_bar")
.resizable()
.scaledToFit()
.foregroundColor(Color.gray)
.background(Color.green)
HStack{
}
Spacer()
}
VStack{
}
}
}
.navigationBarHidden(true)
}
}
The ZStack contains 2 VStack. First one is my template and will be part of multiple of my screens later on. Second Stack is destined to be replaced by #ViewBuilder parameter so that I can reuse that in multiple screens easily.
The progress_bar image is a SVG file imported into assets, preserving Vector Data and rendered as template (So I can change colour).
My issue, as shown on the following screenshot, is that the image somehow extends toward the top of the screen. The green area correspond to the green coloured background added to the image. The progress bar is the grey line across the screen.
progress bar extending toward top of the screen
If I change my code to (commented out the spacer):
// Spacer()
Image("progress_bar")
.resizable()
.scaledToFit()
.foregroundColor(Color.gray)
.background(Color.green)
HStack{
}
Spacer()
}
I get this, progress bar shifts down in the screen (not wanted but expected) but the green area that was added on top of the image disappears:
updated screen with progress_bar shifted down and not over extending
I did try setting up a maxHeight to my Image view but this did not work out.
What am I missing? Is there a way I can stop this from happening?
Edit:
After more looking around, my issue is coming from the fact that the whole thing is embedded in a NavigationView. Apparently space is saved for the navigation bar even though it is hidden.

SwiftUI Path is pixelated when in second Navigation View

I have a strange behavior on my SwiftUI app. I draw a Path with a linear gradient as stroke color.
This path is displayed like this in my view:
GeometryReader { proxy in
LinePath(width: proxy.size.width, height: proxy.size.height)
.stroke(gradient, lineWidth: 3)
}
.frame(height:200)
.padding()
(LinePath struct extends Shape and draw the Path)
When this code is displayed on first view, it draws correctly:
But if I display it in a second view after a NavigationLink, the drawn line is "pixelated":
As it may be difficult to understand and there is quite lot of code (for a web page), I did a demo project you can get here.
To test it, you can open testprojectApp.swift and comment/uncomment parts:
ContentView() // <--- Uncomment this to get the issue
//SubView() // <--- Uncomment this and it works
If you call SubView (where this LinePath is drawn), it works. If you call ContentView that will display a button to navigate to SubView, it draws a pixelated line.
Notes:
If I don't set a height, it is never pixelated (.frame(height:200)).
If I don't use a gradient but a simple color (as .blue), it is never pixelated.
EDIT: I've found a "workaround", to redraw the line 0.01s after it appeared and change its height. It visually "works" but of course it is not a good method...
#State var height: CGFloat = 0
var body: some View {
GeometryReader { proxy in
LinePath(width: proxy.size.width, height: proxy.size.height)
.stroke(gradient, lineWidth: 3)
}
.frame(height:height)
.padding()
.onAppear() {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
height = 200
}
}
}
You can add .drawingGroup() and it seems to resolve the issue without having to use the delay:
GeometryReader { proxy in
LinePath(width: proxy.size.width, height: proxy.size.height)
.stroke(gradient, lineWidth: 3)
}
.frame(height:200)
.padding()
.drawingGroup() //<-- here

How do I keep clipped content of one subview from blocking gestures in underlying subviews with SwiftUI?

I have a main view consisting of a zStack with a background image at the bottom, a user-loaded image above that, and two toolbars at the top. The toolbars are at the vertical top and bottom of the view. I want those toolbars to appear semi-transparent with background images matching the main view's background image in size and position. If the user drags or scales their image to overlap with the toolbars, it should be obscured by them. To accomplish that, I've built those toolbar views as zStacks with the same background image aligned to the top or bottom, respectively, and clipped the content to match the height of the toolbars.
My problem is that the clipped content of those toolbar views blocks gestures on the underlying, user-loaded image. To help visualize the problem, I've linked a screenshot of the debug view hierarchy for the main view (MockScoringView).
Here's the code for the main view:
GeometryReader { geometry in
ZStack {
Rectangle()
.foregroundColor(.blue)
.frame(width: geometry.size.width, height: geometry.size.height)
.offset(x: self.baseOffset.width + self.newOffset.width, y: self.baseOffset.height + self.newOffset.height)
.scaleEffect(self.scale)
.gesture(DragGesture()
.onChanged { value in
self.newOffset.width = value.translation.width / self.scale
self.newOffset.height = value.translation.height / self.scale
}
.onEnded { value in
self.baseOffset.width += self.newOffset.width
self.baseOffset.height += self.newOffset.height
self.newOffset = CGSize.zero
}
)
VStack(spacing: 0) {
MockTopToolbarView()
Spacer()
MockBottomToolbarView()
}
}
}
And the code for the top toolbar view, which is very similar to the one on bottom:
ZStack {
Image("Background")
.resizable()
.scaledToFill()
.frame(height: 56, alignment: .top)
.clipped()
Rectangle()
.foregroundColor(.toolbarGray)
.frame(height: 56)
}
I'd like to modify the content of the toolbar views' backgrounds so they do not block gestures from reaching the user-loaded image.
Debug View Hierarchy
You could set allowsHitTesting(false) on the background images.
Alternatively, since you do not want the user image to be visible behind the toolbars you could also just add the Rectangle and toolbars to a single VStack.
ZStack {
Image("Background")
.resizable()
.scaledToFill()
VStack {
TopToolBar()
Rectangle()
.foregroundColor(.blue)
...
BottomToolBar()
}
}

SwiftUI - How to align bottom edge to center of sibling

Please have a look at this image before reading the question
I'm currently trying to align the red view's vertical center (/centerY) to the bottom edge of the green view in a SwiftUI View.
I'm coming from UIKit where I would solve this with something like viewA.centerYAnchor.constraint(toEqual: viewB.bottomAnchor)
But how would you solve this the SwiftUI way? I have kind of the following hierachy:
VStack {
ZStack {
Image("someImage")
Text("Awesome Title") // <- align center to the Image's bottom edge
.frame(width: 200, height: 130)
}
Spacer()
}
I found the solution:
Set the ZStacks alignment to .bottom. Now the red view will be aligned to the green views bottom edge. Thanks to #Andrew. But this is not enough:
Set the red views .alignmentGuide to the following:
-> .alignmentGuide(.bottom) { d in d[.bottom] / 2 }
Explanation: Now the green view's bottom edge will be aligned to 50% of the red view's height! Awesome!
If you remove the frame from the text and add a bottom alignment to the ZStack, it will give you the desired effect.
VStack {
ZStack (alignment: .bottom) {
Image("someImage")
Text("Awesome Title") // <- align center to the Image's bottom edge
}
Spacer()
}
result:

Resources