Place text between top and center of the screen - ios

Is there a simple way to place text relatively between the top and center of the screen? With 'simple' I mean preferably without GeometryReader and overt calculations.
I've tried various combinations of VStack, Zstack and Spacer() but to no avail.
Code and screenshot as illustration:
struct ContentView: View {
var body: some View {
Text("Between top and center")
.offset(y: -150)
Text("Center")
}
}

A possible solution is to create two views on each side of the centred text (one hidden):
struct ContentView: View {
var body: some View {
VStack {
Spacer()
Text("Between top and center")
Spacer()
Text("Center")
Spacer()
Text("")
Spacer()
}
}
}
Alternatively, to be fool-proof (in case they change the behaviour of Text("") in the future release):
struct ContentView: View {
var body: some View {
VStack {
Spacer()
Text("Between top and center")
Spacer()
Text("Center")
Spacer()
Text("Between top and center")
.opacity(0)
Spacer()
}
}
}
(If needed, Text("Between top and center") can be extracted as a subview to avoid code duplication.)
As you said:
Spacer() [...] ensure[s] that the empty spaces between the views are equal in size
That's exactly the reason why I recommended to extract Text("Between top and center") as a subview. Then, you can change the font, size etc. of the view in one place only.
The adapted solution is:
struct ContentView: View {
var body: some View {
ZStack {
VStack {
Spacer()
subview
Spacer()
Spacer()
subview
.opacity(0)
Spacer()
}
VStack {
Spacer()
Text("Center")
Spacer()
}
}
}
var subview: some View {
Text("Between top and center")
.font(.caption)
// apply all other modifiers if you want
}
}

Here is possible solution (tested with Xcode 12.1 / iOS 14.1)
struct ContentView: View {
var body: some View {
VStack {
Color.clear.overlay(
Text("Between top and center") // << here !!
)
// Text("Between top and center")
// .frame(maxWidth: .infinity, maxHeight: .infinity) // alternate !!
Text("Center")
Color.clear
}
}
}
Update: added alternate with .frame but it is valid only if there should be the only one Text, whereas in .overlay it is possible to put anything.

Here is another solution using the method of VStack Spacers, I also used a little padding to get you closer to the center.
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
Text("Top").bold()
.foregroundColor(.blue)
Spacer()
Text("Your desired area")
Spacer()
Text("Middle Text").bold()
.foregroundColor(.blue)
.padding(.bottom, 200)
Spacer()
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

Related

How to set the view on top in ios SwiftUI?

I had tried this code, I have also tried with stacks but not able to align with top .
I just want the view to appear on top with auto layout as like UIKit .
import SwiftUI
struct ItemDetail: View {
let item : MenuItem
var body: some View {
HStack(alignment: .top){
VStack{
Image(item.mainImage)
Text(item.description)
}
.padding(10)
.background(.red)
.navigationTitle(item.name)
}
}
}
struct ItemDetail_Previews: PreviewProvider {
static var previews: some View {
ItemDetail(item: MenuItem.example)
}
}
Swap the HStack for a VStack and add a Spacer() at the end. e.g.:
struct ItemDetail: View {
let item : MenuItem
var body: some View {
VStack{
VStack{
Image(item.mainImage)
Text(item.description)
}
.padding(10)
.background(.red)
.navigationTitle(item.name)
Spacer()
}
}
}
This should do it:
VStack()
{
VStack
{
Image(item.mainImage)
Text(item.description)
}
.padding(10)
.background(.red)
.navigationTitle(item.name)
Spacer()
}

SwiftUI ScrollView extra padding when go to another screen with showed keyboard

The default "Keyboard Avoidance" SwiftUI is used.
First GIF
If you put the code in VStack and add another View to it, then the container rises
Second GIF
I don't want to delete Keyboard Avoidance. I need to remove extra spacing
scrollDismissesKeyboard for ScrollView is not an option
minimal iOS version is iOS 16
struct ContentView: View {
#State var text: String = "Bu bu?"
var body: some View {
NavigationStack {
ScrollViewReader { proxy in
VStack {
ScrollView(showsIndicators: false) {
VStack(spacing: 0) {
Spacer()
.frame(height: 500)
TextField("", text: $text)
.padding(.bottom, 70)
.frame(height: 40)
.frame(maxWidth: .infinity)
.background(Color.red)
NavigationLink("Screen 2", destination: {
Text("SwiftUI - Nice to meet you, let's never meet again")
})
}
}
Text("I'm in VSTack after scroll view")
}
}
}
}
}
I looked it up with a hierarchy view, and noticed that a UIInputSetHostView is created with a height of 216
View hierarchy 1
View hierarchy 2
disableAutocorrection not working

iOS SwiftUI - disable ignoring the safe area

I read tons of questions about how to make the app ignore the safe area, but when I create a new app then the status bar space is ignored anyway without
.ignoresSafeArea()
Why is that? I don't want it to be ignored!
This is all I have:
import SwiftUI
struct ContentView: View {
var body: some View {
VStack{
Text("Hello, world!")
.padding()
Spacer()
}
.frame(width: 300)
.background(Color.teal)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
the result screenshot
You can do it like so:
VStack {
Color.teal
.overlay {
Text("Hello World!")
}
}
Ok I got it, inside a ZStack:
GeometryReader { reader in
Color.white
.frame(height: reader.safeAreaInsets.top, alignment: .top)
.ignoresSafeArea()
}
Use this modifier:
.edgesIgnoringSafeArea(.all)
if you don't want everything you can choose : .top, .bottom, .vertical, .horizontal,...

SwiftUI change view from first screen to tabview screen

I want to change views once the user taps 'get started' but due to having navigation view in my first view, it is showing back button on my next screen which I don't want. Please see the images attached below.
Code for the first view is below:
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
VStack {
Spacer()
Text("LifePath")
.font(.system(size: 48, weight: .semibold))
.padding(.bottom)
Spacer()
NavigationLink(destination: ViewChanger()) {
Text("Get Started")
.font(.headline)
.navigationBarBackButtonHidden(true)
}
}
.padding()
}
}
}
back button showing on screen 2
First view
Change the location of your navigationBackButtonHidden modifier so that it actually modifies the view that you're going to (and not the NavigationLink label):
struct ContentView: View {
var body: some View {
NavigationView {
VStack {
Spacer()
Text("LifePath")
.font(.system(size: 48, weight: .semibold))
.padding(.bottom)
Spacer()
NavigationLink(destination: ViewChanger()
.navigationBarBackButtonHidden(true) // <-- Here
) {
Text("Get Started")
.font(.headline)
}
}
.padding()
}
}
}
If you want not only the back button to be gone, but the entire header bar, you can use the .navigationBarHidden(true) modifier.
Also, if you run this on iPad at all, you probably want .navigationViewStyle(StackNavigationViewStyle()) added to the outside of your NavigationView
If you use a NavigationLink (in a NavigationView), the view will be pushed. If you want to replace the view, you can do this with an if statement.
For example, this could be implemented like this:
struct ContentView: View {
#State var showSecondView: Bool = false
var body: some View {
if !showSecondView {
NavigationView {
VStack {
Spacer()
Text("LifePath")
.font(.system(size: 48, weight: .semibold))
.padding(.bottom)
Spacer()
Button(action: { showSecondView = true }) {
Text("Get Started")
.font(.headline)
}
}
.padding()
} else {
TabView {
// ...
}
}
}
}

How to put a logo in NavigationView in SwiftUI?

I am trying to use a logo image instead of a NavigationView title at the top section of the app. Couldn't find any documentation of using images inside a NavigationView.
iOS 14+
Starting from iOS 14 you can create a ToolbarItem with the principal placement:
struct ContentView: View {
var body: some View {
NavigationView {
Text("Test")
.toolbar {
ToolbarItem(placement: .principal) {
Image(systemName: "ellipsis.circle")
}
}
}
}
}
See the ToolbarItemPlacement documentation for more placements.
NavigationView.navigationBarTitle() can only take a Text() argument right now. You could instead use .navigationBarItems() to set an Image as either the trailing or leading argument, but this is the SwiftUI equivalent of UINavigationItem.leftBarButtonItem[s] and UINavigationItem.rightBarButtonItem[s], which means that you're restricted to navigation bar button dimensions. But if you're ok with that, you may want to set a blank title so that you can specify a standard-height navigation bar.
Hard-Coded Positioning
If you can stand to live with yourself, you can fake a centered nav bar item by hard-coding padding around the image, like
.padding(.trailing, 125),
(Note that I deliberately positioned it off-center so that you can see that it's hard-coded.)
Slightly Less Hard-Coded Positioning
Even better would be to wrap the whole thing in a GeometryReader { geometry in ... } block to use the screen dimensions to calculate precise positioning, if you know the exact width of the image you're using:
GeometryReader { geometry in
NavigationView {
...
}
.navigationBarTitle(Text(""), displayMode: .inline)
.navigationBarItems(trailing:
PresentationButton(
Image(systemName: "person.crop.circle")
.imageScale(.large)
.padding(.trailing, (geometry.size.width / 2.0) + -30), // image width = 60
destination: ProfileHost()
)
)
If you don't want to hack it, here's what you can do:
Standard nav bar height, left button item
.navigationBarTitle(Text(""), displayMode: .inline)
.navigationBarItems(leading:
PresentationButton(
Image(systemName: "person.crop.circle")
.imageScale(.large)
.padding(),
destination: ProfileHost()
)
)
Standard nav bar height, right button item
.navigationBarTitle(Text(""), displayMode: .inline)
.navigationBarItems(trailing:
PresentationButton(
Image(systemName: "person.crop.circle")
.imageScale(.large)
.padding(),
destination: ProfileHost()
)
)
Expanded nav bar height, no title, left button item
.navigationBarItems(leading:
PresentationButton(
Image(systemName: "person.crop.circle")
.imageScale(.large)
.padding(),
destination: ProfileHost()
)
)
Use this:
NavigationView {
Text("Hello, SwiftUI!")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .principal) {
HStack {
Image(systemName: "sun.min.fill")
Text("Title").font(.headline)
}
}
}
}
Credit: https://sarunw.com/posts/custom-navigation-bar-title-view-in-swiftui/
With SwiftUIX, you can use navigationBarTitleView(View):
NavigationView() {
NavigationLink(destination:YourView().navigationBarTitleView(Image(systemName: "message.fill")))
}
I don't want to claim 100% accuracy whether title image positioned at center but visually it looks center to me. Do your judgment and adjust padding :)
Here is code:
.navigationBarTitle(
Text("")
, displayMode: .inline)
.navigationBarItems(leading:
HStack {
Button(action: {
}) {
Image(systemName: "arrow.left")
}.foregroundColor(Color.oceanWhite)
Image("oceanview-logo")
.resizable()
.foregroundColor(.white)
.aspectRatio(contentMode: .fit)
.frame(width: 60, height: 40, alignment: .center)
.padding(UIScreen.main.bounds.size.width/4+30)
}
,trailing:
HStack {
Button(action: {
}) {
Image(systemName: "magnifyingglass")
}.foregroundColor(Color.oceanWhite)
}
)
To extend on NRitH's answer, putting your logo in a different component (to borrow a React way of putting it) may help anyone looking to understand the concepts.
The actual Image can be wrapped in any container view such as a VStack, etc. An example of setting up a struct as a component to be used in our navigation items could be something like the following:
struct NavLogo: View {
var body: some View {
VStack {
Image("app-logo")
.resizable()
.aspectRatio(2, contentMode: .fit)
.imageScale(.large)
}
.frame(width: 200)
.background(Color.clear)
}
}
When the aspect ratio is set, only the width needs to be set on the frame on the container view. We could also set a property in the NavLogo to set width and/or height from property dependency injection. Regardless, our navigationBarItems becomes very straight forward and more readable 🙂
NavigationView {
Text("Home View")
.navigationBarItems(
leading: NavLogo()
trailing: ProfileButton()
)
}
On iOS 13, a little hacky way to achieve this:
private var logo: some View {
Image("logo-image")
}
var body: some View {
GeometryReader { g in
content()
.navigationBarTitle("")
.navigationBarItems(leading:
ZStack(alignment: .leading) {
logo.frame(width: g.size.width).padding(.trailing, 8)
HStack {
leadingItems().padding(.leading, 10)
Spacer()
trailingItems().padding(.trailing, 10)
}
.frame(width: g.size.width)
}
)
}
}
Try the following.
struct ContainerView: View {
var body: some View {
VStack {
Image(systemName: "person.crop.square")
ContentView()
}
}
}
It worked for me.
Make sure you change ContentView to ContainerView inside SceneDelegate.swift before running on simulator or device.

Resources