I'm posting this question again, with a simplified view of my code and views. I'll delete the old one since I think I didn't explain my issue properly there (and it has no answers yet).
I'm trying to position a HStack with an initial height at the bottom of the screen. With the top portion being a scroll view. The HStack might expand to a certain extent as more text is typed into the Text Editor.
I am able to achieve this, as long as there is no Text Editor.
I need help with figuring out how to do this with Text Editor.
Please see below -
With just a text view
With a text editor instead of text view
Here's the code for it -
struct ContentView: View {
#State var myText: String = "This Text Editor is screaming \n\n THIS IS SPARTA!!! \n\n at me and kicking me into the abyss of similarly worded Stackoverflow questions."
var body: some View{
VStack(spacing:0){
GeometryReader {geo in
ScrollView(.vertical, showsIndicators: false){
ForEach(1 ..< 200, id: \.self){ num in
Text("\(num)")
.frame(width: geo.size.width)
.padding(5)
.background(.yellow)
}
}
.background(.orange)
.frame(minWidth: geo.size.width, maxHeight: geo.size.height * 0.96 , alignment: .top)
}
HStack(spacing: 0){
Text("This HStack is supposed to contain a text editor that expands as needed to a max height but starts off at this height.")
.padding()
.background(.teal)
/*TextEditor(text: $myText)
.multilineTextAlignment(.center)
.font(.title3)
.padding()*/
}
.frame(minHeight:50)
.background(.teal)
}
}}
Any help in the right direction is greatly appreciated!
See the following with comments:
struct ContentView: View {
#State var myText: String = "This Text Editor is screaming \n\n THIS IS SPARTA!!! \n\n at me and kicking me into the abyss of similarly worded Stackoverflow questions."
var body: some View{
VStack(spacing: 0) {
ScrollView(.vertical, showsIndicators: false) {
ForEach(1 ..< 200, id: \.self) { num in
Text("\(num)")
// The GeometryReader is unnecessary. Use a frame with .infinity to expand your row.
.frame(maxWidth: .infinity)
.background(.yellow)
.padding(5)
}
}
.background(.orange)
HStack(spacing: 0) {
TextEditor(text: $myText)
.multilineTextAlignment(.center)
.font(.title3)
// Use .fixedSize to reduce the height of the TextEditor to its minimal height.
.fixedSize(horizontal: false, vertical: true)
// this .padding() gives you space around your TextEditor for margins
.padding()
// this background gives you white margins
.background(Color.white)
// this .padding() keeps the background above from resizing to the size of the teal background.
.padding()
}
.frame(minHeight: 50)
.background(.teal)
}
}
}
Related
I'm looking how to align text to the top of screen, do you know how to?
import SwiftUI
struct RegisterSignInScreen: View {
var body: some View {
VStack {
Text("Welcome Back!")
.font(.title)
.fontWeight(.bold)
.multilineTextAlignment(.center)
.padding(.bottom, 5.0)
Text("Please sign in to your account")
.font(.subheadline)
.fontWeight(.bold)
.foregroundColor(Color.gray)
.multilineTextAlignment(.center)
}
}
}
struct RegisterSignInScreen_Previews: PreviewProvider {
static var previews: some View {
RegisterSignInScreen()
}
}
image
I tried to embed my VStack in a ZStack:
ZStack(alignment: .top)
But it didn't work.
To see why this is happening you can add a background to your VStack or ZStack
ZStack {
VStack {
//Text items
}
}
.background(.red)
What you will notice (maybe unexpectedly) is that the stack is only as big as it needs to be to contain the text items.
When you add alignment to the stack it aligns the content within the stack to the specified edge of the relatively small stack. It doesn't align the stack to an edge of the screen, or change the size of the stack. What you're actually looking to do is align the stack, not it's content.
In order to align to the top of the screen you can make the stack fill the entire height of the screen. Either by adding a Spacer() at the bottom of the VStack that will fill the remaining vertical space pushing the content upwards, or by applying a frame with .infinity maxHeight: and top aligned content to the VStack.
VStack {
Text("Hello World!")
Spacer()
}
VStack {
Text("Hello World!")
}
.frame(maxHeight: .infinity, alignment: .top)
Alternatively if required for your view, you can do a similar thing with a second stack containing your text stack like so:
var body: some View {
VStack {
welcomeText
Spacer()
}
}
var welcomeText: some View {
VStack {
Text("Welcome Back!")
.font(.title)
.fontWeight(.bold)
.multilineTextAlignment(.center)
.padding(.bottom, 5.0)
Text("Please sign in to your account")
.font(.subheadline)
.fontWeight(.bold)
.foregroundColor(Color.gray)
.multilineTextAlignment(.center)
}
}
You can do this in a couple of ways, but the most obvious way is to simply add a Spacer() in the bottom of the VStack
Like so:
struct RegisterSignInScreen: View {
var body: some View {
VStack {
Text("Welcome Back!")
Text("Please sign in to your account")
Spacer() // <- HERE
}
}
}
This will push your content in the VStack to the top.
Alternatively, you can add a frame modifier and force your VStack height and add the alignment there using .frame
struct RegisterSignInScreen: View {
var body: some View {
VStack {
Text("Welcome Back!")
Text("Please sign in to your account")
}
.frame(maxHeight: .infinity, alignment: .top) // <- HERE
}
}
ZStack(alignment: .top) works, but not the same way as you think)
try to do:
ZStack(alignment: .top){
}
.background(Color.green)
and you will understand why so :)
you need to use spacer :)
VStack {
YourView()
Spacer()
}
I am trying to make an adaptive CommentViewRow for a social app where the whole comment text can be displayed within the row.
So far I am achieving this but I have 3 issues:
1 When the comment-text is short it is being centred within the row even when I specify ".alignment: .leading" in the VStack.
2 When the comment-text uses more than one line there is a mysterious padding between the user's profile picture & the comment-text?? see image below.
3 I am not sure if my .frame modifier is the best way to achieve what I am doing, it seems mickey-mouse, I was reading about .frame(idealWith, idealHeight, etc..) and not sure if that would help.
Any idea on how I can fix this so that each CommentViewRow displays like your average social-media comment view??
Thank you!
struct CommentViewRow: View {
var comment: Comment
var body: some View {
HStack {
KFImage("profilePicture")
// COMMENT
VStack(alignment: .leading, spacing: 5) {
Text("**\(comment.username)** \(comment.comment)")
.font(.caption)
.frame(width: 310)
.fixedSize(horizontal: true, vertical: false)
Text(comment.createdAt.timeAgoDisplay())
.bold()
.font(.caption)
}
Spacer()
}.padding([.leading, .trailing], 10)
}
}
1st option: If you really need that view to be 310 wide
You can change .frame(width: 310) to .frame(width: 310, alignment: .leading)
2nd option: Let the view adjust itself based on content, you just need to specify the alignment (.leading in this case)
struct CommentViewRow: View {
var comment: Comment
var body: some View {
HStack {
KFImage("profilePicture")
VStack(alignment: .leading, spacing: 5) {
Text("**\(comment.username)** \(comment.comment)")
.font(.caption)
Text(comment.createdAt.timeAgoDisplay())
.font(.caption.bold())
}
.frame(maxWidth: .infinity, alignment: .leading)
}
.padding([.horizontal], 10)
}
}
yes, get rid of the frame:
struct ContentView: View {
var comment = "Thjfhg jhfgjhdfg jdfhgj dfhdfsjjdfgh djdshfg hjdfgjfdh ghjkf gdhjdfgh jkh fjg dfjkhgj dfglkhdfsg"
var body: some View {
HStack {
Image(systemName: "person.circle")
.font(.largeTitle)
VStack(alignment: .leading, spacing: 5) {
Text("\(comment)")
.font(.caption)
Text("3 minutes ago")
.bold()
.font(.caption)
}
Spacer()
}.padding([.leading, .trailing], 10)
}
}
I'm following a SwiftUI tutorial and have made this view that updates with data when a user drags or zooms into an image. After I detoured from the tutorial to round these strings to the nearest hundredth, I noticed this behavior where the text is moving back and forth (and kind of clipping for a second) when it's updating the values. I've tried various combinations of the .frame, lineLimit, minimumScaleFactor modifiers to no avail. The behavior I want is for the system icons to not move and the text to be left aligned against them and then the Text frames should take up all the available space left (and not clip into the text when the text goes from 4 characters long to 5 characters long)
Current Behavior:
InfoPanelView.swift:
...
struct InfoPanelView: View {
var scale: CGFloat
var offset: CGSize
#State private var isInfoPanelVisible: Bool = false
var body: some View {
HStack {
Image(systemName: "circle.circle")
.symbolRenderingMode(.hierarchical)
.resizable()
.frame(width: 30, height: 30)
.onLongPressGesture(minimumDuration: 1) {
withAnimation(.easeOut) {
isInfoPanelVisible.toggle()
}
}
Spacer()
HStack(spacing: 2) {
Image(systemName: "arrow.up.left.and.arrow.down.right")
Text(String(format: "%.2f", scale))
Spacer()
Image(systemName: "arrow.left.and.right")
Text(String(format: "%.2f", offset.width))
Spacer()
Image(systemName: "arrow.up.and.down")
Text(String(format: "%.2f", offset.height))
Spacer()
}
.font(.footnote)
.padding(8)
.background(.ultraThinMaterial)
.cornerRadius(8)
.frame(maxWidth: 420)
.opacity(isInfoPanelVisible ? 1 : 0)
Spacer()
}
}
}
...
I ended up creating a new subview:
struct ExpandingText: View {
var value: CGFloat
var body: some View {
ZStack {
Text(String(describing: (0..<500).map{letter in letter}))
.foregroundColor(Color.clear)
.lineLimit(1)
Text(String(format: "%.2f", value))
.id(value)
.transition(AnyTransition.opacity.animation(.easeInOut(duration:0)))
.multilineTextAlignment(.leading)
.lineLimit(1)
.frame(width: 75, alignment: .leading)
}
}
}
The 500 character invisible string that's limited to 1 line ensures that the ZStack takes up as much width as that text ever could (so the icons no longer move) and the id modifier combined with making the transition override duration being 0 fixes the text box clipping issue.
Edit:
Adding a larger width frame with alignment set to .leading as per xTwisteDx's suggestion makes it left aligned as well
I am currently creating a content view for my application and am experiencing some strange behavior with padding. As seen in the photo below, there is quite a bit of space below the navigation bar at the top of the phone. I don't specify any padding here so I'm wondering why there is so much space between the top and where the image is displayed. The image doesn't have that large of a white box around it either.
My code does not specify any kind of margin or padding. I'm new to Swift and SwiftUI so I'm curious if there is some automatic padding applied to navigation views?
import Kingfisher
struct BottleView: View {
let bottle: Bottle
var body: some View {
VStack {
KFImage(URL(string: bottle.image ?? "")!)
.resizable()
.frame(width: 128, height: 256)
VStack(alignment: .leading) {
HStack {
Text(bottle.name)
.font(.title)
Spacer()
Text("Price")
}
HStack {
Text(bottle.varietal ?? "")
Spacer()
Text("$\(bottle.price ?? "")")
}
.font(.subheadline)
.foregroundColor(.secondary)
VStack(alignment: .leading) {
Text("Information")
.font(.title2)
Text(bottle.information ?? "")
}
}
}
}
}
If you apply a .background(Color.red) to the VStack, you'll see that it's centered in the screen.
var body: some View {
VStack {
Image("TestImage")
.resizable()
.frame(width: 128, height: 256)
/// ... more code
}
.background(Color.red)
}
This is because, by default, most SwiftUI views are centered. For example, try just a Text:
struct ContentView: View {
var body: some View {
Text("Hello, I'm centered!")
}
}
So if you don't want it centered, this is where Spacers come in. These expand to fill all available space, pushing all other views. Here's the code that gets rid of the "bit of space below the navigation bar at the top of the phone":
(note that I replaced your Bottle properties with static text, make sure you change them back)
struct BottleView: View {
// let bottle: Bottle
var body: some View {
VStack {
Image("TestImage")
.resizable()
.frame(width: 128, height: 256)
VStack(alignment: .leading) {
HStack {
Text("Blantons")
.font(.title)
Spacer()
Text("Price")
}
HStack {
Text("Bourbon")
Spacer()
Text("$59.99")
}
.font(.subheadline)
.foregroundColor(.secondary)
VStack(alignment: .leading) {
Text("Information")
.font(.title2)
Text("Product info here")
}
}
Spacer() /// spacer right here! pushes the other views up
}
.background(Color.red)
.navigationBarTitleDisplayMode(.inline) /// get rid of the additional top gap that the default Large Title navigation bar produces
}
}
Result:
I am trying to replicate a calculator app. I cant seem to modify my 'Text' view to look similarly.
Text("0")
.background(Color.gray)
.cornerRadius(10)
.font(.largeTitle)
But its far from what I am trying to replicate.
I tried offset, but it offsets the entire 'Text' view.
Basically I want my Text to look like what is pointed in the image
You can achieve this playing around with ZStack, VStack, HStack and Spacer(). Here is a quick example:
struct CalculatorText: View {
var body: some View {
ZStack {
Rectangle()
.cornerRadius(10)
.foregroundColor(.gray)
VStack {
Spacer() // now the text will be on the bottom of ZStack
HStack {
Spacer() // and now the text will be on the right side of ZStack
Text("0")
.bold()
.font(.system(size: 30))
.foregroundColor(.white)
.multilineTextAlignment(.trailing)
.padding()
}
}
}
.frame(height: 100)
.padding()
}
}
and the result will be: