I have simple Hstack in Zstack but issue is Its to showing in centre I have added a padding which is showing on right side not in left because its not in centre.
My code
ZStack (alignment: .center){
HStack (alignment: .center) {
}
.frame(width: geometry.size.width, height: geometry.size.height/12)
.background(Color("primarycolor").shadow(radius: 2))
}.padding()
Here is preview
Here the issue is with the simple math. so that I have extracted the padding variable out.
Note: Geometry reader gets space as much as possible and we access with the given proxy.
struct ContentView: View {
var body: some View {
var padding: CGFloat = 10
GeometryReader { geometry in
ZStack (alignment: .center){
HStack (alignment: .center) {
}
.frame(width: geometry.size.width - (padding * 2), height: geometry.size.height/12)
.background(Color.red.shadow(radius: 2))
.border(.gray, width: 5)
}
.padding(padding)
.border(.gray, width: 5)
}
}
}
Related
I am trying to build a List that I want to look like a timeline.
Each cell will represent a milestone.
Down the left hand side of the table, I want the cells to be 'connected', by a line (the timeline).
I have tried various things to get it to display as I want but I have settled with basic geometric shapes , i.e Circle() and Rectangle().
This is sample code to highlight the problem:
import SwiftUI
struct ContentView: View {
var body: some View {
let roles: [String] = ["CEO", "CFO", "Managing Director and Chairman of the supervisory board", "Systems Analyst", "Supply Chain Expert"]
NavigationView{
VStack{
List {
ForEach(0..<5) { toto in
NavigationLink(
destination: dummyView()
) {
HStack(alignment: .top, spacing: 0) {
VStack(alignment: .center, spacing: 0){
Rectangle()
.frame(width: 1, height: 30, alignment: .center)
Circle()
.frame(width: 10, height: 10)
Rectangle()
.frame(width: 1, height: 20, alignment: .center)
Circle()
.frame(width: 30, height: 30)
.overlay(
Image(systemName: "gear")
.foregroundColor(.gray)
.font(.system(size: 30, weight: .light , design: .rounded))
.frame(width: 30, height: 30)
)
//THIS IS THE RECTANGLE OBJECT FOR WHICH I WANT THE HEIGHT TO BE VARIABLE
Rectangle()
.frame(width: 1, height: 40, alignment: .center)
.foregroundColor(.green)
}
.frame(width: 32, height: 80, alignment: .center)
.foregroundColor(.green)
VStack(alignment: .leading, spacing: 0, content: {
Text("Dummy operation text that will be in the top of the cell")
.font(.subheadline)
.multilineTextAlignment(.leading)
.lineLimit(1)
Label {
Text("March 6, 2021")
.font(.caption2)
} icon: {
Image(systemName: "calendar.badge.clock")
}
HStack{
HStack{
Image(systemName: "flag.fill")
Text("In Progress")
.font(.system(size: 12))
}
.padding(.horizontal, 4)
.padding(.vertical, 3)
.foregroundColor(.blue)
.background(Color.white)
.cornerRadius(5, antialiased: true)
HStack{
Image(systemName: "person.fill")
Text(roles[toto])
.font(.system(size: 12))
}
.padding(.horizontal, 4)
.padding(.vertical, 3)
.foregroundColor(.green)
.background(Color.white)
.cornerRadius(5, antialiased: true)
HStack{
Image(systemName: "deskclock")
Text("in 2 Months")
.font(.system(size: 12))
}
.padding(.horizontal, 4)
.padding(.vertical, 3)
.foregroundColor(.red
)
.background(
Color.white
)
.cornerRadius(5, antialiased: true)
}
})
}.listRowInsets(.init(top: 0, leading: 0, bottom: 0, trailing: 0))
}
}
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct dummyView: View {
var body: some View {
Text("Hello, World!")
}
}
struct dummyView_Previews: PreviewProvider {
static var previews: some View {
dummyView()
}
}
but as you can see in the enclosed picture, there are unwanted gaps
So other content in the cell is making the height of the entire cell 'unpredictable' and break the line.
Is there a way to determine the height of the cell and extend the dimensions of the Rectangle, so that it extends to the full height of the cell?
Is there a better approach you recommend for trying to build such a timeline ?
PS: I have tried playing around with .frame and .infinity but that does work.
Many thanks.
Why not just draw the line based on the size of the row. See Creating custom paths with SwiftUI. Remember, everything is a view.
First, you need to decompose what you are doing into subviews. You have too many moving parts in one view to get it correct. Also, I would avoid setting specific padding amounts as that will mess you up when you change devices. You want a simple, smart view that is generic enough to handle different devices.
I would have a row view that has a geometry reader so it knows its own height. You could then draw the line so that it spanned the full height of the row, regardless of the height. Something along the lines of this:
struct ListRow: View {
var body: some View {
GeometryReader { geometry in
ZStack {
HStack {
Spacer()
Text("Hello, World!")
Spacer()
}
VerticalLine(geometry: geometry)
}
}
}
}
and
struct VerticalLine: View {
let geometry: GeometryProxy
var body: some View {
Path { path in
path.move(to: CGPoint(x: 20, y: -30))
path.addLine(to: CGPoint(x: 20, y: geometry.size.height+30))
}
.stroke(Color.green, lineWidth: 4)
}
}
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()
}
}
I would like my LoadingTitle to have a width of 70% of the screen so I use the GeometryReader but it makes the vertical size expand and my LoadingTitle takes much more vertical space. I would like it to remain as compact as possible.
When using hardcoded width: 300 I get the correct layout (except the relative width):
struct ContentView: View {
var body: some View {
VStack(alignment: .leading, spacing: 0) {
LoadingTitle()
Color.blue
}
}
}
struct LoadingTitle: View {
var body: some View {
HStack() {
Color.gray
}
.frame(width: 300, height: 22)
.padding(.vertical, 20)
.border(Color.gray, width: 1)
}
}
Now if I wrap the body of my LoadingTitle in the GeometryReader I can get the correct relative size but then the GeometryReader expands my view vertically:
struct LoadingTitle: View {
var body: some View {
GeometryReader { geo in
HStack() {
Color.gray
.frame(width: geo.size.width * 0.7, height: 22, alignment: .leading)
Spacer()
}
.padding(.vertical, 20)
.border(Color.gray, width: 1)
}
}
}
I tried using .fixedSize(horizontal: false, vertical: true) on the GeometryReader as other suggested but then the resulting view is too much compact and all its paddings are ignored:
How could I achieve the layout of the 1st screenshot with a relative width?
Here is possible approach. Tested with Xcode 11.4 / iOS 13.4 (w/ ContentView unchanged)
struct LoadingTitle: View {
var body: some View {
VStack { Color.clear }
.frame(height: 22).frame(maxWidth: .infinity)
.padding(.vertical, 20)
.overlay(
GeometryReader { geo in
HStack {
HStack {
Color.gray
.frame(width: geo.size.width * 0.7, height: 22)
}
.padding(.vertical, 20)
.border(Color.gray, width: 1)
Spacer()
}
}
)
}
}
Since you have a fixed height for title,
struct LoadingTitle1: View {
var body: some View {
GeometryReader { geo in
HStack {
Color.gray.frame(width: geo.size.width * 0.7)
.padding(.vertical, 20)
.border(Color.gray, width: 1)
Spacer()
}
}.frame(height: 62)
}
}
i have an image that i want to strech only it's height to fit different content, how do i do that in swiftUI? right now it looks like this
struct ContentView: View {
var body: some View {
VStack {
HStack {
VStack(alignment: .leading, spacing: 130) {
Text("Title")
.font(.headline)
.foregroundColor(.primary)
Text("text")
.font(.subheadline)
.foregroundColor(.secondary)
Text("padding")
}
.padding(.vertical)
Spacer()
Image("rightTag")
.resizable(capInsets: .init(top: 0, leading: 0, bottom: 0, trailing: 0), resizingMode: .stretch)
.aspectRatio(contentMode: .fit)
.frame(maxWidth: 20)
}
.frame(maxWidth: screen.width - 60)
.padding(.leading)
.background(.white)
.cornerRadius(20)
}
}
}
how can i stretch its height to fit this outer frame? ragular resizable and stuff can't get it done.
any helped would be wonderful! Thanks!
sry i didn't make myself clear earllier.
Here is possible approach. However as I see now you'd rather need to stretch not the entire original image, but only middle of it, so in real project it would be needed to make your image tri-part and apply below stretching approach only to middle (square) part.
Approach uses asynchronous state update, so works in Live Preview / Simulator / RealDevice. (Tested with Xcode 11.2 / iOS 13.2)
struct ContentView: View {
#State private var textHeigh: CGFloat = .zero
var body: some View {
VStack {
HStack(alignment: .top) {
VStack(alignment: .leading, spacing: 130) {
Text("Title")
.font(.headline)
.foregroundColor(.primary)
Text("text")
.font(.subheadline)
.foregroundColor(.secondary)
Text("padding")
}
.padding(.vertical)
.alignmentGuide(.top, computeValue: { d in
DispatchQueue.main.async {
self.textHeigh = d.height // !! detectable height of left side text
}
return d[.top]
})
Spacer()
Image("rightTag")
.resizable()
.frame(maxWidth: 20, maxHeight: max(60, textHeigh)) // 60 just for default
}
.frame(maxWidth: screen.width - 60)
.padding(.leading)
.background(Color.white)
}
}
}
Sorry if this is a simple question but I'm just starting out with SwiftUI and I'm trying to figure out how to make a 2x2 grid of views based on the width of the screen. Meaning that each square has a width and height of have the screen width and they are arranged in a 2x2 grid with no padding.
I've been trying with two HStacks with two views in each placed on top of each other but the view size inside the HStack seems to dictate the HStack's height.
Code for Views I'm trying to arrange into the 2x2 grid:
var body: some View {
VStack {
TextField("0", text: $value)
.multilineTextAlignment(.center)
.textFieldStyle(PlainTextFieldStyle())
.font(.system(size: 40, weight: .semibold, design: .rounded))
.border(Color.black)
.padding()
Text(title)
.padding([.leading, .bottom, .trailing])
.font(.system(size: 14, weight: .regular, design: .rounded))
}
.background(Color.green)
.cornerRadius(10)
.overlay(RoundedRectangle(cornerRadius: 10).stroke(Color.black, lineWidth: 5))
}
I find that using A combination of HStacks, VStacks and aspectRatio(_ aspectRatio: CGFloat? = nil, contentMode: ContentMode) works best for forcing certain proportions on views:
struct ContentView: View {
var body: some View {
VStack(spacing: 0) {
HStack(spacing: 0) {
Rectangle().fill(Color.red)
.aspectRatio(1.0, contentMode: .fit)
Rectangle().fill(Color.green)
.aspectRatio(1.0, contentMode: .fill)
}
HStack(spacing: 0) {
Rectangle().fill(Color.blue)
.aspectRatio(1.0, contentMode: .fit)
Rectangle().fill(Color.yellow)
.aspectRatio(1.0, contentMode: .fill)
}
}.aspectRatio(contentMode: .fit)
}
}
Results in a layout like this:
This can be done using a GeometryReader:
struct ContentView: View
{
var body: some View
{
GeometryReader
{ geometry in
self.useProxy(geometry)
}
}
func useProxy(_ geometry: GeometryProxy) -> some View
{
let dimension = min(geometry.size.width, geometry.size.height)
return VStack
{
HStack(spacing: 0)
{
Text("Top Left")
.frame(width: dimension / 2, height: dimension / 2)
.border(Color.black)
Text("Top Right")
.frame(width: dimension / 2, height: dimension / 2)
.border(Color.black)
}
HStack(spacing: 0)
{
Text("Bottom Left")
.frame(width: dimension / 2, height: dimension / 2)
.border(Color.black)
Text("Bottom Right")
.frame(width: dimension / 2, height: dimension / 2)
.border(Color.black)
}
}
}
}
Watch (the first part of) this WWDC video to hear about the layout system of SwiftUI.