I want the status bar and at the bottom to be white (Same as root background color), but no idea, do i need to get status bar height and add margin top and bottom?
Here is my code and the preview below
import SwiftUI
struct ContentView: View {
var body: some View {
VStack(
alignment: .leading,
spacing: 10
) {
Text("Title")
.font(
.system(size: 32)
.weight(.heavy)
)
Text("Content")
}
.frame(
minWidth: 0,
maxWidth: .infinity,
minHeight: 0,
maxHeight: .infinity,
alignment: .topLeading
)
.padding(
EdgeInsets(
top: 0,
leading: 20,
bottom: 0,
trailing: 20
)
)
.background(Color.gray)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
Group {
ContentView()
}
}
}
If it were me tackling this kind of UI, I would use some other nice Views that SwiftUI provides for us (like ZStack).
The ZStack places objects one on top of another from the bottom up. So you would want your color first, then the VStack after. It would look something like this:
struct ContentView: View {
var body: some View {
ZStack {
Color.gray
VStack(alignment: .leading, spacing: 10) {
Text("Title")
.font(.system(size: 32).weight(.heavy))
Text("Content")
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
.padding()
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
add a vertical padding of 1:
struct ContentView: View {
var body: some View {
VStack(alignment: .leading, spacing: 10) {
Text("Title")
.font(.system(size: 32).weight(.heavy))
Text("Content")
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
.padding(.horizontal, 20)
.background(Color.gray)
.padding(.vertical, 1) // here
}
}
Let me add another answer that goes with a different approach, without using .frame(). Instead, it uses the full width and height of HStack and VStack to fill the screen. For the status bar and the bottom area, this approach uses a .layoutPriority() modifier to the gray color but not allowing it to overlap the safe area.
While the other answers work quite fine, my purpose with this example is to open the range of possibilities.
struct Example: View {
var body: some View {
HStack {
VStack(alignment: .leading, spacing: 10) {
Text("Title")
.font(
.system(size: 32)
.weight(.heavy)
)
Text("Content")
Spacer() // This spacer will extend the VStack to full height
}
Spacer() // This spacer will extend the HStack to full width
}
.padding()
.background {
VStack {
// Status bar
Color.clear
.ignoresSafeArea()
// Rest of the view: gray has the priority but can't overlap
// the status bar
Color.gray
.layoutPriority(1)
}
}
}
}
Related
Ok I have tried previous suggestions of just placing it in another vstack but still my view is showing the rectangle at the mid of the screen is their any reason why, also the edges have no padding around them making it edge to edge which I don't want?
struct ProfileView : View
{
var body: some View {
GeometryReader{ geo in
VStack {
VStack(alignment: .trailing, spacing: 10) {
RoundedRectangle(cornerRadius: 20)
.stroke(Color.primary, lineWidth: 2)
.frame(width: geo.size.width, height: 200)
.padding(.horizontal)
}.frame(width: geo.size.width, height: geo.size.height) // <<=== Here }
}
}
}
My Views are being injected into the content area via this page
struct ContentView: View {
var body: some View {
GeometryReader { geometry in
TabView{
HomeView().tabItem {
Image(systemName: "house")
Text("Home")
}
ProfileView()
.tabItem {
Image(systemName: "person.circle.fill")
Text("Profile")
}
ZStack {
StatsView()
.tabItem {
Image(systemName: "murry").renderingMode(.original).padding()
Text("Plus")
}
}
StatsView()
.tabItem {
Image(systemName: "dumbbell.fill").renderingMode(.original).padding()
Text("Stats")
}
StatsView()
.tabItem {
Image(systemName: "dumbbell.fill").renderingMode(.original).padding()
Text("Notes")
}
}
}.safeAreaInset(edge: .bottom, alignment: .center, spacing: 0) {
Color.clear
.frame(height: 20)
.background(Material.bar)
}
}}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.previewInterfaceOrientation(.landscapeLeft)
}
}
On Simlualtor and real device its placing it in the middle of the screen.
I want this frame so that I can place other elements in side it is a rounded rectangle the best way to go for this?
You are using GeometryReader as the outer most view in the ProfileView, and GeometryReader has the behavior to take as much space as there is available, if you want your rectangle to show in the top or bottom of the screen consider using a Spacer() and if you want to add padding to your rectangle so it does not go to the edges of the screen try removing the .frame(width: geo.size.width) because you are forcing the rectangle to be the width of the screen, in nature the rectangle will take as much space as it can so you do not have to specify that.
This is your code but with some tweaks:
struct ProfileView : View {
var body: some View {
VStack {
RoundedRectangle(cornerRadius: 20)
.stroke(Color.primary, lineWidth: 2)
.frame(height: 200)
.padding(.horizontal)
Spacer()
}
}
}
I'm trying to create a view composed of two parts - the top part is an image with a piece of text overlaid in the top left corner. The top part should take up 2/3 of the height of the view. The bottom part is text information and a button, contained in an HStack, and takes up the remaining 1/3 of the view height.
struct MainView: View {
var body: some View {
GeometryReader { gr in
VStack(spacing: 0) {
ImageInfoView()
.frame(height: gr.size.height * 0.66)
BottomInfoView()
}
.clipShape(RoundedRectangle(cornerRadius: 20))
}
}
}
struct ImageInfoView: View {
var body: some View {
ZStack(alignment: .topLeading) {
Image("testImage")
.resizable()
.aspectRatio(contentMode: .fill)
.overlay(Rectangle()
.fill(Color.black.opacity(0.5))
)
HStack {
Text("Top left text")
.foregroundColor(.white)
.padding()
}
}
}
}
struct BottomInfoView: View {
var body: some View {
HStack {
VStack(alignment:.leading) {
Text("Some title")
Text("Some subtitle")
}
Spacer()
Button("BUTTON") {
}
}
.padding()
.background(.gray)
}
}
In the below sample code, if I set the frame height to 400 I will see the top left text, but when I set it to 200, I do not. What is happening here? The text should be anchored to the top left corner of ImageInfoView no matter what. Further, when set to 400, why does the ImageInfoView take up more than 66% of the height?
struct TestView: View {
var body: some View {
MainView()
.frame(height: 200)
.padding([.leading, .trailing], 16)
}
}
An image's fill pushes ZStack boundaries, so Text goes off screen (see this for details).
A possible solution is to put image into overlay of other container so it respects provided space instead of imposing own.
Tested with Xcode 13.4 / iOS 15.5
ZStack(alignment: .topLeading) {
Color.clear.overlay( // << here !!
Image("testImage")
.resizable()
.aspectRatio(contentMode: .fill)
.overlay(Rectangle()
.fill(Color.black.opacity(0.5))
)
)
// ... other code as-is
This fixes the problem:
ZStack(alignment: .topLeading) {
GeometryReader { gr in
Image("testImage")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(maxWidth: gr.size.width, maxHeight: gr.size.height)
.overlay(Rectangle()
.fill(Color.black.opacity(0.5))
)
HStack {
Text("Top left text")
.foregroundColor(.white)
.padding()
}
}
}
}
I am experimenting with SwiftUI ToolbarItem in various positions in my view, such as in bottomBar and navigation. I am wondering if it is possible to center the ToolBarItem vertically in the view, as opposed to the top or bottom. When setting up the placement for ToolBarItem, I am not seeing a placement property for centering. Any idea how this ToolBarItem could be centered in the view?
Here is my code for the ToolBarItem, currently set to .bottomBar:
struct ContentView: View {
#State private var cityName = ""
var body: some View {
NavigationView {
VStack() {
//some content
}.navigationTitle("Weather")
.toolbar {
ToolbarItem(placement: .bottomBar) {
HStack {
TextField("Enter City Name", text: $cityName)
.frame(minWidth: 100, idealWidth: 150, maxWidth: 240, minHeight: 30, idealHeight: 40, maxHeight: 50, alignment: .leading)
Spacer()
Button(action: {
// some action
}) {
HStack {
Image(systemName: "plus")
.font(.title)
}
.padding(15)
.foregroundColor(.white)
.background(Color.green)
.cornerRadius(40)
}
}
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Try this approach for placing your toolbar elements in the middle of the ContentView.
It should look exactly like the .toolbar {...} you have, and it functions exactly as you expect.
The main difference with this approach is that
you can put the toolbar anywhere you like.
You can also use GeometryReader for very fine placement in your view.
struct ContentView: View {
#State private var cityName = ""
var toolbar: some View {
HStack {
Spacer()
TextField("Enter City Name", text: $cityName)
.frame(minWidth: 100, idealWidth: 110, maxWidth: 140, minHeight: 30, idealHeight: 40, maxHeight: 50, alignment: .leading)
Spacer()
Button(action: {}) {
Image(systemName: "plus").font(.title)
.padding(15)
.foregroundColor(.white)
.background(Color.green)
.cornerRadius(40)
}
Spacer()
}
}
var body: some View {
NavigationView {
VStack {
Spacer()
toolbar
Spacer()
}.navigationTitle("Weather")
}
}
}
I'm trying to make the circles fit into the HStack such that the HStack size does not increase.
How can I make the circles fit without specifying a fixed frame?
struct ContentView: View {
var body: some View {
NavigationView {
Form {
HStack {
Circle()
.fill(Color.red)
.aspectRatio(1, contentMode: .fit)
Text("Hello")
}
HStack {
Circle()
.fill(Color.blue)
.aspectRatio(1, contentMode: .fit)
Text("Hello")
}
}
}
}
}
Here is a sample of various containers to chose from. SwiftUI will do all the layout, automatically handle rotations and device resolutions.
struct CirclesView: View {
var body: some View {
VStack(spacing: 0) {
Label("Circles", systemImage: "circle").font(.system(size: 24, weight: .black, design: .rounded)).foregroundColor(.pink)
HStack {
Circle()
.foregroundColor(.yellow)
.frame(width: 32, height: 32)
Text("This is a yellow circle")
Spacer()
}
Circle()
.foregroundColor(.orange)
.shadow(radius: 10)
.frame(width: 75)
Divider()
HStack {
VStack {
Circle().foregroundColor(.blue)
Text("Blue").font(.title3)
HStack {
Circle().foregroundColor(.purple)
Text("Purple").font(.caption)
}
}
.padding()
.background(Color.yellow)
ZStack(alignment: Alignment(horizontal: .center, vertical: .center)) {
Circle().foregroundColor(.green)
Text("Green").foregroundColor(.primary)
}
}
}
}
}
In my SwiftUI application, I have a VStack (VStack 1) container that get full screen height. Inside this VStack, I have an other VStack (VStack 2) that get height according to its childrens (Text), and I want to put it at the bottom of this parent (VStack 1). On the VStack 2, I need to put a GeometryReader to get the height of VStack (VStack 2). The problem is that the GeometryReader get automaticaly full screen height. So the VStack 2 is placed on the middle of the screen. It's not that I want. I don't know if I can set the height of the GeometryReader with the same height of the VStack 2?
My test code:
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
GeometryReader { geometry in
VStack {
Text("Text n°1")
Text("Text n°2")
Text("Text n°3")
Text("Text n°4")
}
.border(Color(.red))
}
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: nil, alignment: .bottom)
.border(Color(.black))
}
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .bottom)
}
}
The result:
That I want:
Thank you for your help.
Following is to set child view height to parent view when using GeometryReader
import SwiftUI
struct ContentView: View {
#State private var totalHeight = CGFloat(100) // no matter - just for static Preview !!
var body: some View {
HStack(alignment:.top) {
Text("Title 1")
GeometryReader { geo in
VStack(spacing: 10) {
Text("Subtitle 1")
.frame(maxWidth: .infinity)
Text("Subtitle 2")
.frame(maxWidth: .infinity)
Text("Subtitle 3")
.frame(maxWidth: .infinity)
}
.background(Color.blue)
.frame(width: geo.size.width * 0.6)
.background(GeometryReader {gp -> Color in
DispatchQueue.main.async {
// update on next cycle with calculated height of ZStack !!!
self.totalHeight = gp.size.height
}
return Color.clear
})
}
.background(Color.yellow)
}
.frame(maxWidth: .infinity)
.frame(height: totalHeight)
.background(Color.green)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Ref - https://stackoverflow.com/a/61315678/815864
I think you're overcomplicating it by using a GeometryReader. Have you tried just using a Spacer()?
struct ContentView: View {
var body: some View {
VStack {
Text("Haha")
Spacer()
VStack {
Text("1")
Text("2")
Text("3")
}
}
}
}