Force SwiftUI view to overflow in trailing direction instead of expanding in both directions - swiftui

I have a SwiftUI view in an HStack that will be wider than it's parent. When this happens, the HStack expands in both leading and trailing directions, despite being put in a VStack with alignment set to .leading (which I had hoped would anchor the leading edge).
Consider the following simplified Playground code:
import SwiftUI
import PlaygroundSupport
struct TestView : View {
var body: some View {
VStack {
VStack(alignment: .leading) {
HStack {
Rectangle()
.foregroundColor(Color.red)
.frame(width: 20)
Rectangle()
.foregroundColor(.green)
}.frame(height: 40)
HStack {
Rectangle()
.foregroundColor(.orange)
.frame(width: 10)
Rectangle()
.foregroundColor(.green)
.frame(width: 200)
Rectangle()
.foregroundColor(.yellow)
.frame(width: 10)
}
.frame(height: 40)
HStack {
Rectangle()
.foregroundColor(.orange)
.frame(width: 10)
Rectangle()
.foregroundColor(.green)
.frame(width: 200)
Rectangle()
.foregroundColor(.yellow)
.frame(width: 10)
}
.frame(height: 40)
}
.border(Color.red)
}
.frame(width: 300, height: 400)
.border(Color.black)
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = UIHostingController(rootView: TestView())
In the above example, everything fits within the view and behaves as expected:
However, change the last green Rectangle to a width of 500 instead of 200, and the following happens:
You can see that the orange leading Rectangle has disappeared, pushed off to the left. Similarly, the yellow Rectangle at the end has been pushed to the right.
What I would like to have happen is have the orange Rectangle visible, meaning that all 3 HStacks would have the same x origin (0). So, the result would be seeing the orange Rectangle on the left, the green visible, but spilling over to the right, and the yellow invisible, since it would be pushed off the screen.
I assume this may have to do with using clipped() or fixedSize but I haven't been able to find a working solution yet.

The easiest solution (no workarounds or other views required) is to set the alignment in the frame modifier of the outer VStack to .leading like this:
struct TestView : View {
var body: some View {
VStack {
VStack(alignment: .leading) {
HStack {
Rectangle()
.foregroundColor(Color.red)
.frame(width: 20)
Rectangle()
.foregroundColor(.green)
}.frame(height: 40)
HStack {
Rectangle()
.foregroundColor(.orange)
.frame(width: 10)
Rectangle()
.foregroundColor(.green)
.frame(width: 200)
Rectangle()
.foregroundColor(.yellow)
.frame(width: 10)
}
.frame(height: 40)
HStack {
Rectangle()
.foregroundColor(.orange)
.frame(width: 10)
Rectangle()
.foregroundColor(.green)
.frame(width: 500)
Rectangle()
.foregroundColor(.yellow)
.frame(width: 10)
}
.frame(height: 40)
}
.border(Color.red)
}
.frame(width: 300, height: 400, alignment: .leading) //<= here
.border(Color.black)
}
}

struct TestView : View {
var body: some View {
VStack {
VStack(alignment: .leading) {
HStack {
Rectangle()
.foregroundColor(Color.red)
.frame(width: 20)
Rectangle()
.foregroundColor(.green)
}.frame(height: 40)
HStack {
Rectangle()
.foregroundColor(.orange)
.frame(width: 10)
Rectangle()
.foregroundColor(.green)
.frame(width: 200)
Rectangle()
.foregroundColor(.yellow)
.frame(width: 10)
}
.frame(height: 40)
HStack {
GeometryReader{_ in
Rectangle()
.foregroundColor(.orange)
.frame(width: 10)
.offset(x: 0)
Rectangle()
.foregroundColor(.green)
.frame(width: 500)
.offset(x: 10)
Rectangle()
.foregroundColor(.yellow)
.frame(width: 10)
.offset(x: 510)
}
}
.frame(height: 40)
}
.border(Color.red)
}
.frame(width: 300, height: 400)
.border(Color.black)
}
}

Related

How to set a Limit for the the views inside a HStack?

Good day, beginner SwiftUI & iOS developer here.
I'm not quite sure how else I could've worded this question, but I'll try my best to explain what I would like to achieve.
Right now, I have a VStack that contains a WebImage and Text view, and this VStack is nested inside a HStack. The views inside the VStack are inside a ForEach loop and are generated dynamically with the data I fetch.
When I display these on a screen, all of these views appear in a single line, as shown below.
However I would like for there to only be max two views per "line", not all four of them stacked into a single line. Is there a way to achieve this?
Here is the code:
HStack(spacing: 20) {
ForEach(attViewModel.students, id: \.self) { student in
VStack {
WebImage(url: URL(string: student.photo))
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 40, height: 40)
.clipShape(Circle())
.overlay(Circle().stroke(Color("DarkGreen"), lineWidth: 3))
.compositingGroup()
Text("\(student.name)")
.bold()
.compositingGroup()
CustomRadioButton()
}
.padding()
.overlay(RoundedRectangle(cornerRadius: 10)
.stroke(Color.orange, lineWidth: 2))
.shadow(radius: 7)
}
}
.frame(maxWidth: .infinity)
Here a possible approach for you:
struct ContentView: View {
let arrayOfStudents: [String] = ["jessy", "joy", "joly", "jack"]
var body: some View {
GeometryReader { proxy in
ScrollView(.horizontal) {
HStack(spacing: .zero) {
ForEach(arrayOfStudents, id: \.self) { student in
VStack {
Image(systemName: "person")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 40, height: 40)
.padding()
.clipShape(Circle())
.overlay(Circle().stroke(Color.green, lineWidth: 3))
Text(student)
.bold()
Circle()
.strokeBorder(style: .init(lineWidth: 2))
.frame(width: 10, height: 10)
}
.compositingGroup()
.padding()
.overlay(RoundedRectangle(cornerRadius: 10).stroke(Color.orange, lineWidth: 2))
.shadow(radius: 7)
.padding()
.frame(width: proxy.size.width/2.0)
}
}
}
.position(x: proxy.size.width/2.0, y: proxy.size.height/2.0)
}
}
}

Center Image in VStack leading alignment

I'm having troubles figuring out the proper solution to centre an Image inside a VStack with an alignment of .leading. I've attempted and got these results but is there a more efficient way instead of adding two Spacer()'s in an HStack?
struct ContentView: View {
var body: some View {
ZStack {
ScrollView {
VStack (alignment: .leading, spacing: 10) {
Text("Title ")
HStack {
Spacer()
Image(systemName:"star.fill")
.resizable()
.frame(width: 20, height: 20, alignment: .center)
Spacer()
}
Divider()
}
}
}
.padding(.all)
}
}
Here is a solution. Tested with Xcode 12.1 / iOS 14.1
ScrollView {
VStack (alignment: .leading, spacing: 10) {
Text("Title ")
Image(systemName:"star.fill")
.resizable()
.frame(width: 20, height: 20)
.frame(maxWidth: .infinity, alignment: .center)
Divider()
}
}

VStack .leading alignment

I can't get rid of this "leading" padding inside one of my VStacks.
I set alignment to "leading" for its parent VStack but it still shows some spacing on the left (for the text and red rounded rectangle). They were supposed to be placed on the left next to the blue edge on screenshot.
Any ideas on why this padding appears there?
Here's the code:
import SwiftUI
struct RegisterView: View {
var body: some View {
VStack(spacing: 0) {
HStack(alignment: .top) {
Text("Register")
.font(.custom("NotoSans-Regular", size: 24))
.fontWeight(.bold)
.foregroundColor(Color.tertiaryTitleColor)
}
.frame(width: 299, height: 39)
.padding(.top, 78)
HStack(spacing: 13) {
BroviderButton(imageName: "googleLogo")
BroviderButton(imageName: "facebookLogo")
}
.padding(.top, 47)
VStack(alignment: .leading, spacing: 0) {
Text("Register with E-mail")
.font(.custom("NotoSans-Regular", size: 16))
RoundedRectangle(cornerRadius: 25)
.fill(Color.red)
.frame(width: 40, height: 20)
}
.frame(width: 279)
.padding(.top, 61)
Spacer()
}
.frame(maxHeight: /*#START_MENU_TOKEN#*/.infinity/*#END_MENU_TOKEN#*/)
.frame(maxWidth: /*#START_MENU_TOKEN#*/.infinity/*#END_MENU_TOKEN#*/)
.background(Color.loginBackgroundColor)
.ignoresSafeArea()
}
}
struct RegisterView_Previews: PreviewProvider {
static var previews: some View {
RegisterView()
}
}
struct BroviderButton: View {
var imageName: String
var body: some View {
ZStack {
RoundedRectangle(cornerRadius: 27.5)
.frame(width: 133, height: 56, alignment: /*#START_MENU_TOKEN#*/.center/*#END_MENU_TOKEN#*/)
.shadow(color: Color.dropShadowColor, radius: 1, x: 10, y: 20)
.offset(y: 15)
.opacity(0.2)
.blur(radius: 20)
.opacity(0.2)
VStack {
VStack {
Image(imageName)
.resizable()
.scaledToFit()
}
.frame(height: 28, alignment: /*#START_MENU_TOKEN#*/.center/*#END_MENU_TOKEN#*/)
}
.frame(width: 133, height: 56, alignment: /*#START_MENU_TOKEN#*/.center/*#END_MENU_TOKEN#*/)
.foregroundColor(/*#START_MENU_TOKEN#*/.blue/*#END_MENU_TOKEN#*/)
.background(Color.loginBackgroundColor)
.cornerRadius(27.5)
.opacity(1)
}
}
}
You need also apply alignment to frame of container, like
VStack(alignment: .leading, spacing: 0) {
Text("Register with E-mail")
.font(.custom("NotoSans-Regular", size: 16))
RoundedRectangle(cornerRadius: 25)
.fill(Color.red)
.frame(width: 40, height: 20)
}
.frame(width: 279) // << do you really need this? why?
.frame(maxWidth: .infinity, alignment: .leading) // << here !!
.padding(.top, 61)

SwiftUI Two Row NavigationBar

Is there a way using SwiftUI to get a two row NavigationBar like instagram has? Even better would be two rows with a fullwidth search bar like in the image.
ex.
Here you go an example close to the image, of course it can be redesigned to suit your needs I did it very quickly it doesn't look nice, but as I said it can be redesigned to look much nicer:
#State var testok = ""
var body: some View {
VStack {
VStack {
TextField("search here man", text: $testok)
.frame(maxWidth: .infinity)
.frame(height: 40)
.background(Color.red)
.cornerRadius(10)
.padding()
ScrollView(.horizontal){
HStack(spacing: 0) {
Text("ok")
.frame(width: 80, height: 50)
.background(Color.green)
.cornerRadius(8)
.padding(.horizontal, 10)
Text("yumyum")
.frame(width: 80, height: 50)
.background(Color.green)
.cornerRadius(8)
.padding(.horizontal, 10)
Text("lool")
.frame(width: 80, height: 50)
.background(Color.green)
.cornerRadius(8)
.padding(.horizontal, 10)
Text("pewpew")
.frame(width: 80, height: 50)
.background(Color.green)
.cornerRadius(8)
.padding(.horizontal, 10)
}
}
}
NavigationView {
NavigationLink(destination: Text("ok")) {
Text("ok")
.navigationTitle(Text("navigation title"))
}
}
}
}

SwiftUI: How to restrict view to only resizing in one direction?

How can I restrict the view to resize only downwards?
I am trying to add more buttons & have the view resize to accommodate the buttons without overlapping the red rectangle above.
This is the code I am currently using:
struct ContentView: View {
var body: some View {
VStack{
RoundedRectangle(cornerRadius: 20)
.frame(width: .infinity, height: 55)
.foregroundColor(.red)
HStack{
Spacer()
ZStack{
RoundedRectangle(cornerRadius: 20)
.foregroundColor(.green)
VStack{
Button(action: {}) {
Text("Test Button")
.foregroundColor(.white)
.font(.headline)
.frame(width: .infinity)
Spacer()
}.padding()
Button(action: {}) {
Text("Test Button")
.foregroundColor(.white)
.font(.headline)
.frame(width: .infinity)
Spacer()
}.padding()
Button(action: {}) {
Text("Test Button")
.foregroundColor(.white)
.font(.headline)
.frame(width: .infinity)
Spacer()
}.padding()
}
}.frame(width: 250, height: 100)
}
Spacer()
}
}
}
Change your .frame(width: 250, height: 100) to .frame(width: 250, height: 100, alignment: .top) Your original example defaults to .center, which is why it expands into the red rectangle.