Cell row autosizing broken when add Rectangle() in SwiftUI - ios

I'm having trouble positioning a simple Rectangle() in a horizontal stack.
If I add it, the Text() stops resizing like this:
If I remove the Rectangle(), woks fine:
I tried changing frame, relativeSize, layoutPriority and much more, but nothing works. I think that is a bug, but fails with any type of geometric types like Circle, RoundedRectangle, etc.
On the other hand, with an Image() it works fine.
Any suggestion?
Thanks!

Just writing out of my head, could be wrong, but try to add rectangle in VStack so that it does not wrap cell around it.
VStack {
Rectangle()
Spacer()
}
Let me know if it works.
Edit*
Had to try it out, and find a "kinda" solution, it may lead you to correct answer, you just have to position rectangle in top right corner. Make this your RowItem.
ZStack {
Rectangle()
.foregroundColor(Color.red)
.frame(width: 10, height: 10)
Text("Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.")
.lineLimit(nil)
.layoutPriority(999)
.padding(.horizontal, 20)
}

Final solution! Thanks #Markicevic for the base idea
struct RowItem: View {
var body: some View {
ZStack(alignment: .leading) {
Rectangle()
.foregroundColor(.red)
.frame(width: 10)
HStack {
Text("Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.")
.lineLimit(nil)
}
.layoutPriority(1)
.padding(.horizontal, 20)
}
.background(Color.white)
.cornerRadius(5.0)
.shadow(color: Color.black.opacity(0.3), radius: 4.0, x: 0.0, y: 3.0)
}
}
However, I think it's not the best solution and it's a SwiftUI error.

The reason for such behavior is that HStack provides infinity as available width for its children while laying out. And so, there no reason for Text to break into several lines in infinite available width.
Flutter provides Expanded widget for cases like this:
Row(
// mainAxisSize: MainAxisSize.min,
children: <Widget>[
RaisedButton(
child: Text('data'),
onPressed: () {}
),
Expanded(child: Text(s))
],
)
Since SwiftUI is based on Flutter's ideas (I beleave) it may provide something similar.

On Xcode 11.4.1 the code bellow should work no problem.
struct ContentView: View {
var body: some View {
List {
ForEach(0..<10) { _ in
RowCell()
}
}
}
}
struct RowCell: View {
var body: some View {
HStack(spacing: 5.0) {
Rectangle()
.foregroundColor(.red)
.frame(width: 10)
Text("Sed ut persipiciatis unde omnis iste natur error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eque ipsa quake ab illo inventore veritas et quasi architetto beata vitae dicta sunt explicabo.")
}
.background(Color.white)
.cornerRadius(3)
.shadow(color: Color.black.opacity(0.3), radius: 4, x: 0, y: 3)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

Related

ContentView not expanding outside of overlay - SwiftUI

I want to display a view as a popup/tooltip from a view. I beleive the best way to acheive this is by presenting it as an overlay. But, the view is not expanding outside of bounds of where its being presented.
import SwiftUI
struct ContentView: View {
let message = "It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like)"
var body: some View {
VStack {
Button {
} label: {
Text("Tap Me")
.background(
Rectangle()
.fill(.red)
)
}
.overlay {
contentView
}
}
.padding()
}
var contentView: some View {
Text(message)
.foregroundColor(Color.white)
.padding()
.background(Color.black)
.foregroundColor(Color.white)
.clipShape(RoundedRectangle(cornerRadius: 5))
.offset(y: 60)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
return ContentView()
}
}
Why is the contentView not expanding outside of the view from where it is being overlayed in my SwiftUI code?
I tried setting fixedSize, frame(maxWidth, but none of them have correct behaviour.
The .overlay modifier always takes the size of its parent view as its maximum size. To put any size view in front of another, you should use a ZStack, e.g.
ZStack {
Button {
} label: {
Text("Tap Me")
.background(
Rectangle()
.fill(.red)
)
}
contentView
}
This is what it looks like (with .opacity applied)

Change View depending on dynamic widths (SwiftUI)

I'm struggling to make a View which changes depending on how large its contents' widths are. Perhaps I'm not approaching the problem correctly?
Take this example View, designed to be one of many items in a vertical List:
Hstack {
Text(leftText)
Spacer()
Text(rightText)
}
Given that leftText and rightText are subject to change, how does one change this view to look cleaner if the Text Views become too large to fit on the same row? e.g.
VStack (alignment: .leading) {
Text(leftText)
HStack {
Spacer()
Text(rightText)
}
}
The only solution I've come up with is measuring the rightText View by duplicating it and hiding it elsewhere (in a ZStack, so it doesn't mess up the UI spacing). This ends up being rather messy and wasteful, as the right View may not be as cheap as simple Text Views. Does anyone know a better way to approach this?
I agree with Lorem that the question is a little vague, but here are a few suggestions that might (perhaps) fit your needs.
Could you live with having both leftText and rightText multi-lined? The modifier is .lineLimit(n) where n is the maximum number of lines you want to allow.
Would a different truncation pattern work? The default is to have the trailing end of the text truncated. However, you can specify an alternative mode, such as .truncationMode(.middle) instead.
One caveat about combining these solutions, it seems to me that truncationMode only works when the lineLimit is exactly 1.
In case you want some thing similar like this
Here is the code
fileprivate let loremipsumText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit"
fileprivate func getRandomText() -> String {
String(loremipsumText.prefix(Int.random(in: 1 ..< loremipsumText.count)))
}
struct TextContainer: View {
var text: String
var body: some View {
ZStack {
Color.orange.cornerRadius(4)
Text(text)
.frame(maxWidth: UIScreen.main.bounds.width * 0.4)
.padding(3)
}
}
}
struct TestListViewDoubleText: View {
var body: some View {
List {
ForEach(0..<20) { _ in
HStack {
TextContainer(text: getRandomText())
Spacer()
TextContainer(text: getRandomText())
}
}
}
}
}

How to get rid of vertical text padding in SwiftUI?

I want to make a rectangle with text inside. the rectangle should be pinned to the right and left side of the display (or superview). It's height should be determined by the text.
I tried the following code:
struct DescriptionView: View {
var description =
"""
02.11.2021
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Illud urgueam, non intellegere eum quid sibi dicendum sit, cum dolorem summum malum esse dixerit. Omnis enim est natura diligens sui. Quamquam haec quidem praeposita recte et reiecta dicere licebit. Duo Reges: constructio interrete. Idem iste, inquam, de voluptate quid sentit? Ergo instituto veterum, quo etiam Stoici utuntur, hinc capiamus exordium. Tum ille: Tu autem cum ipse tantum librorum habeas, quos hic tandem requiris? Bona autem corporis huic sunt, quod posterius posui, similiora..
Bild © Lorem Lipsum
"""
var body: some View {
ZStack(alignment: .bottom) {
Color.red
.edgesIgnoringSafeArea(.all)
Text(description)
.foregroundColor(.white)
.font(.headline)
.background(
Rectangle()
)
.edgesIgnoringSafeArea(.horizontal)
}
}
}
This results looks like this:
As you can see, the text has some padding on the left and right side. How can I get rid of this? The rectangle should always be as wide as possible, while the text determines the height of the rectangle.
Update:
I am using Xcode Version 13.1 (13A1030d).
When I embed DescriptionView() in A TabView, the padding goes away in Xcode Preview. However, when I launch the app in the simulator, the padding appears again.
struct ContentView: View {
var body: some View {
ZStack {
Color.black
.edgesIgnoringSafeArea(.all)
TabView {
DescriptionView()
DescriptionView()
DescriptionView()
}
.tabViewStyle(PageTabViewStyle())
.indexViewStyle(PageIndexViewStyle(backgroundDisplayMode: .interactive))
}
}
}
Xcode Preview:
Simulator:
Update 2:
As one of the answers suggested, I tried the geometry reader. I got rid of the padding, but the alignment is now wrong. Also I don't think it should be that complicated (with a geometry reader and a stack):
var body: some View {
ZStack(alignment: .bottom) {
Color.red
.edgesIgnoringSafeArea(.all)
GeometryReader { geometry in
VStack() {
Text(description)
.foregroundColor(.white)
.font(.headline)
.frame(maxWidth: .infinity)
.background(
Color.black
)
}
}
}
}
Your Text view is calculating its size using the max width (in this case screen width) and the result is a width that is smaller than the screen width because there isn't a line of text that fits the screen perfectly.
If you want the Text view to expand completely you should use the frame modifier:
var body: some View {
ZStack(alignment: .bottom) {
Color.red
.edgesIgnoringSafeArea(.all)
Text(description)
.foregroundColor(.white)
.font(.headline)
.frame(maxWidth: .infinity, alignment: .leading)
.background(
Rectangle()
)
}
}
I don't know this is right for your needs, but this way can be answer.
struct ContentView: View {
var body: some View {
GeometryReader { geometry in
VStack() {
Text("Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to mak")
.font(.title)
.background(Color.blue)
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

Get width of Text in SwiftUI

how can I get the width of a Text component?
I want to make my Button that is under the Text, the same width as the Text itself.
VStack(alignment: .center, spacing: 20) {
Text("Finde heraus, was gerade in der Welt los ist.")
.font(.largeTitle).bold().lineLimit(3).multilineTextAlignment(.leading)
Button(action: {
}, label: {
Text("Account erstellen").bold()
.frame(minWidth: 0, maxWidth: .infinity)
.frame(height: 40).padding(.top, 0)
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(20)
})
}.padding([.leading, .trailing], 25)
Which is giving me this result, its pretty ugly in my opinion:
On the iPad the width between the text and the button is even bigger
How can I solve this issue.
Thanks In advance.
It seems there is a bug in the bold() attribute. Removing it caused the text to align as you are expecting. You can try filing a bug, or wait it out and see if it gets fixed over the course of the beta period.
Text("Finde heraus, was gerade in der Welt los ist.")
.font(.largeTitle)
.lineLimit(3)
.multilineTextAlignment(.leading)

Adding unlimited lines in a Text (SwiftUI)

Just figuring out how I can achieve multiple lines of text in a Text. It seems like the Text has the same default as UILabel (one line), but I can't find any function which meets this criteria.
struct ContentView : View {
var body: some View {
VStack(alignment: .leading, spacing: 10) {
HStack {
Text("Avocado Toast").font(.system(size: 24))
}
// This Text does cut, and I wonder how I can achieve multiple rows
Text("Ingredients: Avocado, Almond Butter, Bread")
.font(.system(size: 20))
}
}
}
Edit
.lineLimit(X), did the trick. But is it possible to not set a specific amount, for instance. With just a 0?
For wrapping Text in a Form .lineLimit(Int.max) did not work for me. It seems there needs to be some width for it to know when to wrap. I believe the official way is with .fixedSize:
Text(message)
.fixedSize(horizontal: false, vertical: true)
The documentation states:
This example shows the effect of fixedSize(horizontal:vertical:) on a text view that is wider than its parent, preserving the ideal, untruncated width of the text view.
https://developer.apple.com/documentation/swiftui/view/fixedsize(horizontal:vertical:)
Use .lineLimit() to limit the amount of lines of text. It takes an optional Int (Int?) as an argument, and .lineLimit(nil) allows unlimited lines.
Edit: As of SwiftUI Beta 5, Text has a default line limit of nil, so text in Text will wrap by default.
My text kept truncating even with no line limit applied. Wrapping my content in a ScrollView {} solved it.
See more info here: https://www.hackingwithswift.com/quick-start/swiftui/how-to-add-horizontal-and-vertical-scrolling-using-scrollview
Text("Professional Burnout and how to overcome it")
.font(.callout)
.fixedSize(horizontal: false, vertical: true)
.frame(width: 220)
I had to add 2 different modifiers for it to work:
Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam pharetra neque ex, at consectetur leo semper eu. Ut finibus maximus ex et maximus. Proin vel sagittis sapien, sed tincidunt orci.")
.frame(maxWidth: .infinity, alignment: .leading)
.fixedSize(horizontal: false, vertical: true)
If you don't add the frame, it will have your text out of the bounds of the screen, and fixedSize allows it to have multiple lines visible
lineLimit and fixedSize work together.
Setting .fixedSize with (horizontal: false, vertical: true) will allow the Text to grow.
Setting lineLimit determines up to how many lines the Text will grow to
a nil value is no limit
lineLimit(nil) doesn't seem to be necessary in conjunction with .fixedSize as .fixedSize alone will allow the label to grow as far as it can.
Example:
Text("Using `___VARIABLE_\(identifier): identifier___` in your template will replace that macro with `\(defaultValue)`")
.fixedSize(horizontal: false, vertical: true)
.lineLimit(2)
lineLimit(1)
If lineLimit(nil) is not working for you, try setting layoutPriority manually to whatever it suits you .layoutPriority(0.5)
None of the previous answers worked for me, but .lineLimit(Int.max) did the trick.

Resources