Vertically centering text in Swift UI - swiftui

I want the "$0.00" to be in the middle of the screen but I can't figure out how to do it.
This is my code:
var body: some View {
NavigationView {
ScrollView {
VStack(alignment: .center) {
Text(String(format: "$%.2f", (dolaresVM.dolares.last?.v)!))
.font(.largeTitle)
}.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
.edgesIgnoringSafeArea(.all)
}.navigationBarTitle("Test")
.onAppear(perform: self.dolaresVM.fetchDolares)
}
}
What am I doing wrong?

ScrollView has infinite inner space for its children. The VStack can't take all of this space. So VStack's height is defined by its content (in our case - Text).
Without ScrollView it will work like you want:
var body: some View {
NavigationView {
VStack(alignment: .center) {
Text(String(format: "$%.2f", 0)).font(.largeTitle)
}.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
.edgesIgnoringSafeArea(.all)
.navigationBarTitle("Test")
}
}
Providing idealHeight for the VStack can be helpful as well. You can use GeometryReader to get the 'outer' height of the ScrollView:
NavigationView {
GeometryReader { geometry in
ScrollView {
VStack(alignment: .center) {
Text(String(format: "$%.2f", 0)).font(.largeTitle)
}.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, idealHeight: geometry.size.height, maxHeight: .infinity)
.edgesIgnoringSafeArea(.all)
}.navigationBarTitle("Test")
}
}

As we discussed above, you don't need ScrollView so can write .navigationBarTitle("Test") inside NavigationView. So that NavigationBarTitle and Text("$0.00") both will be display on your screen.
Here i put static value of Text, you can replace it with dynamic value which you are setting up from your Model.
struct ContentView: View {
var body: some View {
NavigationView {
VStack(alignment: .center) {
Text("$0.00")
.font(.largeTitle)
}
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
.edgesIgnoringSafeArea(.all)
.navigationBarTitle("Test")
}
}
}

Related

Make a View the same size as another View which has a dynamic size in SwiftUI

I want to achieve this (For the screenshots I used constant heights, just to visualize what I am looking for):
The two Views containing text should together have the same height as the (blue) Image View. The right and left side should also be of the same width. The important part: The Image View can have different aspect ratios, so I can't set a fixed height for the parent View. I tried to use a GeometryReader, but without success, because when I use geometry.size.height as height, it takes up the whole screen height.
This is my code:
import SwiftUI
struct TestScreen: View {
var body: some View {
VStack {
GeometryReader { geometry in
HStack {
Image("blue-test-image")
.resizable()
.scaledToFit()
.frame(maxWidth: .infinity)
VStack {
Text("Some Text")
.padding()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.gray)
Text("Other Text")
.padding()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.gray)
}
.frame(maxWidth: .infinity, maxHeight: geometry.size.height /* should instead be the height of the Image View */)
}
}
Spacer()
}
.padding()
}
}
This is a screenshot of what my code leads to:
Any help would be appreciated, thanks!
As #Asperi pointed out, this solves the problem: stackoverflow.com/a/62451599/12299030
This is how I solved it in this case:
import SwiftUI
struct TestScreen: View {
#State private var imageHeight = CGFloat.zero
var body: some View {
VStack {
HStack {
Image("blue-test-image")
.resizable()
.scaledToFit()
.frame(maxWidth: .infinity)
.background(GeometryReader {
Color.clear.preference(
key: ViewHeightKeyTestScreen.self,
value: $0.frame(in: .local).size.height
)
})
VStack {
Text("Some Text")
.padding()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.gray)
Text("Other Text")
.padding()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.gray)
}
.frame(maxWidth: .infinity, minHeight: self.imageHeight, maxHeight: self.imageHeight)
}
.onPreferenceChange(ViewHeightKeyTestScreen.self) {
self.imageHeight = $0
}
Spacer()
}
.padding()
}
}
struct ViewHeightKeyTestScreen: PreferenceKey {
static var defaultValue: CGFloat { 0 }
static func reduce(value: inout Value, nextValue: () -> Value) {
value += nextValue()
}
}
struct TestScreen_Previews: PreviewProvider {
static var previews: some View {
TestScreen()
}
}

SwiftUI - remove space between TabView that has PageViewStyle

I have a TabView with 7 pages. Each one of the pages has 100 points less than the screen's width.
struct ContentView: View {
var body: some View {
GeometryReader { reader in
VStack(alignment: .center) {
TabView {
ForEach(0..<7) { index in
VStack {
}
.frame( maxHeight: .infinity)
.frame(width: reader.size.width - 100)
.background(Color.red)
.cornerRadius(15)
}
}
.background(Color.yellow)
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .always))
.frame(width: reader.size.width, height: 500)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
}
TabView has as much as much width as the screen's width.
There are 50 points on both sides the Vstack's though each one of them has 100 points less than the screen width.
I need to remove the space between the red views.
how about something different using a "ScrollView" and a "HStack", like this:
struct ContentView: View {
var body: some View {
GeometryReader { reader in
ScrollView (.horizontal) {
HStack (spacing: 0) {
ForEach(0..<7) { index in
VStack {
Text("\(index)")
}
.frame(maxHeight: .infinity)
.frame(minWidth: reader.size.width - 100)
.background(Color.red)
.cornerRadius(20)
}
}.frame(maxWidth: .infinity, maxHeight: 500)
}.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
}
EDIT2: using paging
struct ContentView: View {
var body: some View {
GeometryReader { reader in
VStack(alignment: .center) {
TabView {
ForEach(0..<7) { index in
VStack {
Text("\(index)")
}
.frame(maxHeight: .infinity)
.frame(width: reader.size.width)
.background(Color.red)
.cornerRadius(25)
}
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .always))
.frame(width: reader.size.width, height: 500)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
}

pin view to bottom of screen and ignore bottom safe area

Layout question - how do I setup for a view block to expand over the bottom safe area? I've looked through various sources for ignoresSafeAreas() but can't achieve quite the result I'm looking for.
I want, later, to be able to expand this view upwards but start it short. If that makes sense.
var body: some View {
VStack{
Spacer()
HStack(alignment: .center) {
Text ("Expand to fill bottom safe area ...?")
.foregroundColor(.white)
}
.frame(minWidth: 100, maxWidth: .infinity, minHeight: 50, maxHeight: 100)
.background(Color.red)
.ignoresSafeArea()
}
}
Option 1
Put ignoresSafeArea inside background. This will let the red color extend over to the device edges, but the HStack's position will stay the same.
struct ContentView: View {
var body: some View {
VStack{
Spacer()
HStack(alignment: .center) {
Text ("Expand to fill bottom safe area ...?")
.foregroundColor(.white)
}
.frame(minWidth: 100, maxWidth: .infinity, minHeight: 50, maxHeight: 100)
.background(Color.red.ignoresSafeArea()) /// inside `background`
}
}
}
Option 2
Put ignoresSafeArea on the VStack, and everything will ignore the safe area.
struct ContentView: View {
var body: some View {
VStack{
Spacer()
HStack(alignment: .center) {
Text ("Expand to fill bottom safe area ...?")
.foregroundColor(.white)
}
.frame(minWidth: 100, maxWidth: .infinity, minHeight: 50, maxHeight: 100)
.background(Color.red)
}
.ignoresSafeArea() /// attached to `VStack`
}
}
Result:
Option 1
Option 2

How to access safeArea insets while the parent view used IgnoringSafeArea()

As you can see the picture I want to place the search bar exactly to the top of the safeArea but the proxy.safeAreaInsets has not the proper value because in the PreviewProvider the parent uses edgesIgnoringSafeArea.
what can I do ? is there any way to access safeAreaInsets?
struct FindView: View {
// MARK: - Properties
#ObservedObject var viewModel: FindViewModel
init(viewModel: FindViewModel){
self.viewModel = viewModel
}
var body: some View {
GeometryReader { proxy in
ScrollView(.vertical, showsIndicators: true, content: {
VStack(alignment: .leading, spacing: 8){
SearchBar()
.frame(height: 48, alignment: .center)
.padding(.all, 16)
Text("Categories")
.multilineTextAlignment(.leading)
.font(.system(size: 21))
.padding(.all, 16)
}.frame(width: proxy.size.width)
})
}
}
}
struct FindView_Previews: PreviewProvider {
static var previews: some View {
ZStack(alignment: .center, content: {
Rectangle().foregroundColor(.red)
FindView(viewModel: FindViewModel())
}).edgesIgnoringSafeArea(.all)
}
}
simple way;
struct test: View {
var body: some View {
VStack{
Text("Your view comes here")
Spacer()
}
.frame(minWidth:0, maxWidth: .infinity, minHeight: 0,maxHeight: .infinity, alignment: .center)
.padding(.top,UIApplication.shared.windows.first?.safeAreaInsets.top ?? 40)
.background(Color.red)
.edgesIgnoringSafeArea(.all)
}
}
You should apply the .edgesIgnoringSafeArea(.all) only to the rectangle, not to the ZStack. This way, only it should fill the screen, while everything else remains in place.
Then, to make it so that the FindView fills the screen (respecting safe area), you need make its frame extend with .frame(maxWidth: .infinity, maxHeight: .infinity)
Code sample:
struct ContentView: View {
var body: some View {
ZStack {
Rectangle()
.fill(Color.red)
.edgesIgnoringSafeArea(.all)
Text("This should respect the safe area")
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
}
}
}

Swift UI Button doesnt compile

I am trying to understand SwiftUI and got stuck on a problem where Xcode refuses to compile.
I have no clue why it allows to add 2 buttons, but complains when adding a third one.
Couldn't find an answer on SO.
Here's the code:
import SwiftUI
struct LoginUI: View {
#State private var name = "dfdfdfdfdf"
#State private var logintitle = "LOGIN/LOGOUT"
#State private var xxx = 0
var body: some View {
VStack{
Text("login")
.font(.largeTitle)
.multilineTextAlignment(.center)
HStack {
VStack {
Text("loginusernametext")
}
.frame(minWidth: 0, maxWidth: .infinity)
.background(Color.green)
VStack {
TextField(/*#START_MENU_TOKEN#*/"Placeholder"/*#END_MENU_TOKEN#*/, text: $name)
}
.frame(minWidth: 0, maxWidth: .infinity)
.background(Color.green)
}
HStack {
VStack {
Text("loginpassword")
}
.frame(minWidth: 0, maxWidth: .infinity)
.background(Color.green)
VStack {
TextField("Password", text: $name)
}
.frame(minWidth: 0, maxWidth: .infinity)
.background(Color.green)
}
HStack {
VStack {
Text("databasereference")
}
.frame(minWidth: 0, maxWidth: .infinity)
.background(Color.green)
VStack {
TextField("Database", text: $name)
}
.frame(minWidth: 0, maxWidth: .infinity)
.background(Color.green)
}
HStack {
VStack {
Text("carplate")
}
.frame(minWidth: 0, maxWidth: .infinity)
.background(Color.green)
VStack {
TextField("Database", text: $name)
}
.frame(minWidth: 0, maxWidth: .infinity)
.background(Color.green)
}
HStack {
//GeometryReader { metrics in
// self.heigth = metrics.size.heigth
Button(action: {
self.doLogin()
}) {
Text(String("jaja"))
.font(.largeTitle)
.foregroundColor(.red)
.background(Color.blue)
}
//}
//Image("aa14b")
// .background(Color.green)
// .frame(minWidth: 0, maxWidth: 80, minHeight: 0, maxHeight: self.heigth, alignment: .topLeading)
Button(action: {
self.doPause()
}) {
Text(String(self.xxx))
.font(.largeTitle)
.foregroundColor(.red)
.background(Color.blue)
}
}
HStack {
Button(action: {
self.doLogin()
}) {
Text(String("odometerdelta"))
.font(.largeTitle)
.foregroundColor(.red)
}
}
HStack{
Text(String("Loginexplain"))
.font(.largeTitle)
.multilineTextAlignment(.center)
}
}
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .topLeading)
}
func loginauthdelta(){
}
func doPause(){
xxx += 1
}
func doLogin(){
}
struct LoginUI_Previews: PreviewProvider {
static var previews: some View {
LoginUI()
}
}
}
and here's the error
SO says: "
It looks like your post is mostly code; please add some more details.", dont knwo how much I have to write until that error message goes away. Never seen this message before...
SwiftUI has a hard limit on the number of elements in a view.
Try wrapping some of your top-level elements in a group...