I am trying to loop through a list of 2-6 items. They are all Text() views for this example, and I want them to all squeeze to the left. This code below is as close as I have gotten
Why? I am simplifying the actual problem to make solving easier
HStack{
ForEach(array, id: \.self) { pageItem in
HStack{
PageDataItemView(pageItem: pageItem)
}.fixedSize()
}
VStack(){
Spacer()
}
}
.listRowInsets(.init(top: 0, leading: 0, bottom: 0, trailing: 0))
.frame(height: 40.0)
PageDataItemView for this example is just Text().
Anyway what I can't seem to solve is to make PageDataItemView's width based on the text content. In the code above they squeeze, but they all overlap each other, rather than taking their own space. Removing .fixedSize() above causes the items to fill the full width evenly.
In Android's XML I could just use "wrap_content" to produce this. While I understand this is not the ideal way to build UI here, it has to do with a complex client request.
Update:
GeometryReader { geometry in
Seems to be causing my views to break as this is inside PageDataItemView. Even when geometry isn't even used at all. Once removed everything worked as expected, curious why this effects a view indirectly. Let me know if I should remove this question.
Related
struct Test: View {
var body: some View {
ScrollView{
RoundedRectangle(cornerRadius: 5)
.fill(.red)
RoundedRectangle(cornerRadius: 5)
.fill(.red)
Text("test")
}
}
}
So I have this view, and from what I know RoundedRectangles are supposed to be flexible and take up all the space so how come when I put two in a ScrollView like this they seem to take the minimum possible space?
The scroll view sets its content size based on the size of its children. The shapes try to fill their parent. Both are trying to figure out how big to be.
Your going to need to put them in a different container that has a fixed size.
I'm making use of the new .refreshable() function. For Testing purposes I'm having this simple sleep function:
func load() async {
await Task.sleep(2 * 1_000_000_000)
}
Appending this to a list works fine:
.refreshable {
await load()
}
However: When I'm trying to use it with a ForEach Loop it's not working. Do you know how to overcome this issue? Background: I need ForEach to ensure custom styling. In the List I'm too limited. I already tried to style it using some attributes like
.buttonStyle(.plain)
.listRowSeparator(.hidden)
But I'm not able to remove the badges or the distance in blue as well as impacting the spacing between the list objects.
Would be great if you can let me know about your thoughts on this. I wan to avoid building up my own pull to refresh view with coordinators etc. like it was done back in SwiftUI 2.0 times :D Really love the .refreshable action.
Many Thanks!
If you want to use refreshable on anything besides List, it's up to you to provide the user interface for it. The refreshable documentation only says it works for List:
When you apply this modifier to a view, you set the refresh value in the view’s environment to the specified action. Controls that detect this action can change their appearance and provide a way for the user to execute a refresh.
When you apply this modifier on iOS and iPadOS to a List, the list provides a standard way for the user to refresh the content. When the user drags the top of the scrollable content area downward, the view reveals a refresh control and executes the provided action. Use an await expression inside the action to refresh your data. The refresh indicator remains visible for the duration of the awaited operation.
To fully customize the look of a List, I did the following:
Use ForEach to iterate within the List.
Add .listStyle(.plain) to List.
Use .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)) with ForEach to get rid of any inner paddings.
Use .listRowSeparator(.hidden, edges: .all) with ForEach to hide separators. I found it a little bit annoying to customize it to my needs. However, there are some way in iOS 16 to do that as mentioned here.
Finally use overlay with ForEach as in the code block below to get rid of arrows on the right.
You can now whatever design you want within the list and get benefits of its capabilities.
List {
ForEach(presenter.items) { item in
ItemView(item: item)
}
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
.listRowSeparator(.hidden, edges: .all)
.overlay {
NavigationLink(destination: { Text("Detail View") }, label: { EmptyView() })
.opacity(0)
}
}
.listStyle(.plain)
I cannot understand why in the simulator the layout is different from the layout displayed in xcode/preview.
struct ContentView : View {
var body: some View {
ZStack(alignment: .bottom) {
ARViewContainer()
Text("hello")
}
}
}
here the screenshots:
TLDR; I don't know why your simulator and preview don't match, but I do know why it's appearing the way that it is on the device. Are you getting any errors in the debug?
Any container views in SwiftUI will only take up the required space that they need. they will also distribute according to your settings. For example, you have a ZStack that contains a bottom alignment. You also have a ARViewContainter() that takes up a portion of that stack. They are aligned behind each other on the Z axis where the text is in front and the other container is behind. A quick way to prove this and test it is to include a background shape behind everything for example.
var body: some View {
ZStack {
Rectangle().edgesIgnoringSafeArea(.all)
.foregroundColor(Color.red)
//Your Views
}
}
This will force the ZStack to take up all available space and your other views should then align as expected. Basically, your Text is aligning to the bottom of the maximum provided space, which is being provided by your ARViewContainer()
Further Reading and Understanding
Views only take up the space required for what's in them, unless otherwise specified.
ZStacks operate on the Z axis, forward/backwards.
In your case you have a view with a set size called ARViewContainer() which takes up the width of the screen and a portion of the height. Since it's the largest view you have, the ZStack inherits that size.
Your text is smaller than the ZStack so the ZStack does NOT inherit the size. You do however have a .bottom assignment. So your text is over your ARViewContainer() and aligned to the .bottom edge of that container.
Finally the ZStack is centered in the remaining space available, giving it the impression that your .bottom isn't doing anything, when in reality it is.
Reproducing the Issue
Here is a code snippet that reproduces your issue and makes it a bit clearer and easier to understand.
var body: some View {
ZStack(alignment: .bottom) {
Rectangle().foregroundColor(Color.yellow).frame(width: 100, height: 100, alignment: .center)
Text("Test")
}
}
This is my example that I am trying to get to work:
struct ContentView: View {
let links = ["Item 1", "Item 2", "Item 3", "Item 4"]
var body: some View {
NavigationView {
ScrollView {
Text("My Title")
List(links, id: \.self) {
link in
NavigationLink(destination: TestView()) {
Text(link)
.padding(.vertical, 4)
.frame(maxWidth: .infinity, alignment: .leading)
}
}
.frame(height: 178)
Text("Some more content here")
}
}
}
}
Note: TestView is just some view with the text hello world on it.
I am trying to copy Apple Music's style of navigation. I tried putting a Button in the NavigationLink but tapping it on the text wouldn't change views, and I couldn't find a way to reliably change the color of the row when tapped, at the same time. Also in some approach, I managed to make it work, but the way the colors animate is different, i.e. it fades from A to B, over ~100ms whereas what I'm trying to achieve is to animate between the states instantly (like in Apple Music).
My current approach is using a List, putting NavigationLinks inside it and then cutting off the whole view by giving it a height. This way I can put it alongside other content.
It's working fine for now, but whenever I click on an row and go back, the row is still highlighted, when it shouldn't. Is there a way to make it so that it deselects when going back to the screen somehow?
I think this bug is being caused by the List being inside a ScrollView, since when I removed ScrollView, the list worked properly, and there wasn't this highlight bug. But I need to be able to put my content with the list, and I don't intend to have a list take up the whole screen.
Is there any way to fix this bug with this approach? I'm also willing for other ways to achieve the same result without using List.
Trying to use ForEach instead ofList?
With a view for row (CustomRow) where you can pass link item and set custom dividing line, background etc ...
ForEach(links, id: \.self) { link in
NavigationLink(destination: TestView()) {
CustomRow(item: link)
}
}
.frame(height: 178)
Here is a screen shot of iOS 13 Health app - User profile. I recently started with swiftui and wondering how to develop a screen like below. I tried list styles plain and grouped. But I couldn't get the look of below layout.
Can UI like this develop purely using swiftui-list?
I am specially looking for rounded sections and including a image inside the list.
As of iOS 14, you can use the code below. It works just perfectly, just like in UIKit.
List {
Section {
Text("Item 1")
Text("Item 2")
Text("Item 3")
}
Section {
Text("Item 4")
Text("Item 5")
Text("Item 6")
}
}.listStyle(InsetGroupedListStyle()) // this has been renamed in iOS 14.*, as mentioned by #Elijah Yap
.environment(\.horizontalSizeClass, .regular)
Thank you.
As of iOS 14.0, this list-style is .listStyle(InsetGroupedListStyle()) or .listStyle(.insetGrouped)
I run into the same problem and it looks like you can not achive this with list or section.
When you put cornerRadius modifier onto a section or list it is just applied to whatever the internals are making cells rounded while section itself being unchanged.
My solution was to avoid lists and sections entirely and build custom ones.
Something like
SectionView: View {
body: some View = {
VStack {
ForEach {
CellView()
}
}
.background(Color.white)
.cornerRadius(10)
.padding()
}
}
And put those sections over a Color view in some ZStack to have a gray background.
Take in account that the huge downside is that a ForEach will render all the cells right away, like if you have a thousand of cells then onAppear() will be called for each cell and strangely enough they will be called in backward order.
Hope it helps.
According to my findings and as of Xcode 11 GM seed 2 (11A420a), making this UI is not possible just from swiftui list.
There is a new list style available for storyboards named, insetGrouped. It has the exact same look.
https://developer.apple.com/documentation/uikit/uitableview/style