I want to fill a forEach loop on a specified field.
I tried to fit it in a space by trying VStack and HStach codes, but it didn't work. Plus, I tried .scaledToFill() and .fixedSize(), still didn't work.
Related
I'm noticing some unexpected behavior when working with the SwiftUI TextField axis property in iOS 16. The code snippet below shows I have a TextField with a vertical axis set (I expect this should expand the text field to unlimited entry, scrolling down), and I've added a .lineLimit(5, reservesSpace: true) modifier to set the TextField's "box size" to 5 lines, but continue to scroll if I'm beyond 5 lines.
The image below shows the unexpected behavior. Scrolling works as expected until I hit line 10 on an iPhone 14 Pro. Then, even though the TextField box remains the same size, it pushes down any elements below it in the List. You can see the "Completed" Toggle moving down slightly. It's also curious that the "push" happens not when I paste in a line, but when I backspace to type a new number - for example the third image shows the Completed area after I pasted in a new line (the 13th line, but starts saying "Line 10", and when I backspace over the "0" in "10" to write "13", that's when the "push" is triggered (it can be seen in the 4th image). This push will continue until the Views below the TextField are eventually pushed off screen (you can see this happening in the last image). Since I'm in a List, I can, of course, scroll to access the last items, but I hadn't expected the TextField to push items lower, even more so that it starts with Line 10 in a 5 line-limited TextField. Am I misunderstanding these Views and modifiers, or is there a bug in the implementation? Thanks for insight!
List {
Group {
...
Text("Notes:")
.padding(.top)
TextField("Notes", text: $notes, axis: .vertical)
.lineLimit(5, reservesSpace: true)
.textFieldStyle(.roundedBorder)
Toggle("Completed:", isOn: $isCompleted)
.padding(.top)
}
.listRowSeparator(.hidden)
}
.listStyle(.plain)
I don't think that this approach will work, because .textLimit() modifier is used only for Text() view and not for TextField() as you are trying to do.
In the SwiftUI Spacer() documentation:
https://developer.apple.com/documentation/swiftui/spacer
we can see:
"A flexible space that expands along the major axis of its containing stack layout, or on both axes if not contained in a stack."
What is the mechanism that is used by the Spacer to know if it is inside a VStack or HStack?
I also see one of the initialisers uses that:
init(minLength: CGFloat? = nil)
"The minimum length this spacer can be shrunk to, along the axis or axes of expansion"
How can I know that "axes of expansion"?
My app has like 5 predefined spacing constants, in an enumeration, and I would like to create a custom initialiser (or static method) that returns a Spacer() with one of my space constants that is used as a min and max at the same time, but in the current axis.
For example, I can do something like:
extension Spacer {
static func spacing(_ spacing: SpaceEnum) -> some View {
return Spacer().frame(width: spacing.value, height: spacing.value)
}
}
The problem with that, is that the spacer is a square, it doesn't know if we are inside a VStack or HStack.
How can I achieve that?
Ideally, I would like to have something like:
Spacer(space: .small)
that inside a VStack does this:
Spacer().frame(height: SpaceEnum.small.value)
Or
Spacer(space: .extraLarge)
that inside an HStack does this:
Spacer().frame(width: SpaceEnum.extraLarge.value)
Any ideas or suggestions?
Maybe, somehow I have to use GeometryReader or alignmentGuide, I don't know.
Using a square is not a good idea, it is causing issues when if the VStack width is smaller than the space. Or when the HStack height is smaller than the custom space constant.
Thanks a lot.
I'm trying to create a view with two nested List Views. That means the main view has rows and each row also has rows. Because of a reordering requirement (edit mode) and swipe to delete (inner rows only), I can't just use ForEach-loops with a scroll view.
The problem is the following: the inner rows don't show when I tap "+add set" although debugging shows they are actually added. the reason might be that view of the outer row doesn't adapt its height. I know this because if I only use ForEach and no List, the rows do appear. but then I can't use swipe to delete. this is the code for the inner rows (as List view) :
List {
ForEach(self.exercise.sets) { set in
SetRow(set: set, exercise: self.exercise)
}.onDelete { (offsets) in
self.exercise.sets.remove(atOffsets: offsets)
}
}
each outer row has VStack before this List (for the exercise name and the column titles) and a button below. see the screen shots
seems I found a workaround... The outer list looks like this in code:
List() {
Text("New workout").font(.title)
ForEach(self.workoutModel.exercises) {exercise in
ExerciseWorkoutView(exercise: exercise)
}
}
I simply added the following frame modifier to the ExerciseWorkoutView (which is a VStack with a List view in it:
.frame(height: self.minFrameHeight + self.setRowHeight * CGFloat(self.exercise.sets.count)).animation(.default)
minFrameHeight and setRowHeight are constants I set.
self.exercise is an observed object with a sets array as #Published instance variable. that's why the frame height adapts automatically...
if anyone knows a better solution, thanks for posting. I tried several variations of .fixedSize(...) already but it didn't work.
It's a bit hard to word in the title but here's my situation.
I have a VStack and I need to animate a gap forming below a tapped item in the stack.
I've tried adding .padding(.bottom, isTapped ? 50 : 0) to the items for example, but it doesn't produce the desired result. For example if item X is the item i've tapped this is the behaviour that I get with this method
empty space --> X-1
X-1 --> X
X --> space created
X+1 --> X+1
empty space --> empty space
basically, as I tried to illustrate, it pushes the items above X upwards as well as pushing the items below it downward.
Is there any way to go about doing what I'm trying to do? A way to fix X and the items above it in place so that only the items below will shift?
edit:
Here's a more real example, when the middle item is tapped, it causes padding to be added below it, but unfortunately that doesn't just leave the first 2 where they are and push the third one down. But it moves the first 2 up and the third down. (Also the padding doesn't lay as shown in the example below, but within the Item View itself if that makes a difference)
VStack{
Item().padding(.bottom, isTapped ? 50: 0)
Item().padding(.bottom, isTapped ? 50: 0)
Item().padding(.bottom, isTapped ? 50: 0)
}
The VStack picks its size according to its contents. It sounds like the view containing the VStack has a lot of extra vertical space. Since the VStack is smaller than its superview, it is centered within its superview (leaving the empty space above and below.) When the VStack grows vertically (when the padding is added), it remains centered and will grow in both directions, up and down.
You could pin your VStack at the top of its superview by adding a Spacer below it and wrapping both in a new VStack. Then, when the padding is added to an item, the VStack will remain pinned at the top and any additional space will be added below.
VStack {
VStack {
// Items: X-1, X, X + 1
}
Spacer()
}
If this doesn't solve your problem, please show your code. The alignment and placement of views is dependent on your specific code.
I am trying using
var body: some View {
Text("This is large text. Is there a way that I can unwrap the large text as discussed").lineLimit(2)
}
FYI:
I knew
var body: some View {
Text("This is large text. Is there a way that I can unwrap the large text as discussed").lineLimit(nil)
}
It will wrap the text to say n number of lines.
Call .lineLimit(3) on the Text element. (Technically, it can be called on any View, in which case it will limit the lines of all Text elements in that view.)
From SwiftUI.View:
/// Sets the maximum number of lines that text can occupy in this view.
///
/// The line limit applies to all `Text` instances within this view. For
/// example, an `HStack` with multiple pieces of text longer than three
/// lines caps each piece of text to three lines rather than capping the
/// total number of lines across the `HStack`.
///
/// - Parameter number: The line limit. If `nil`, no line limit applies.
/// - Returns: A view that limits the number of lines that `Text` instances
/// display.
public func lineLimit(_ number: Int?) -> Self.Modified<_EnvironmentKeyWritingModifier<Int?>>
because .linelimit is not working as expected for the time being you can simply wrap the Text element inside a VStack if you have multible text like title and subtitme give that VStack multilineTextAlignment modifier and center that text with the parameter .center or you can add .multilineTextAlignment(.center) modifier directly to the Text element :
VStack{
Text("This is large text. Is there a way that I can unwrap the large text as discussed")
.font(.title3)
.foregroundColor(.white)
}.padding()
.multilineTextAlignment(.center)