performance decrease with two foreach loop? - swiftui

performance decrease in two foreach loop?
the code in below , is one foreach loop,the performance is well,but i need to build mutiple scrollview.
ScrollView(){
VStack(spacing: 10){
Text("Water")
LazyVGrid(columns: layout2, spacing: 20){
ForEach(Array(Waters.enumerated()), id: \.element){ (index,item) in
Button(action: {}, label: {
})
}
}
}
so I made two foreach loops like below, then the performance is decrease and lag.
ScrollView(.horizontal,showsIndicators: false){
HStack(spacing: 25){
ForEach(Array(total.enumerated()), id: \.element){ (index1,item1) in
VStack(spacing: 15){
Text(item1.name)
ScrollView(showsIndicators: false){
LazyVGrid(columns:Array(repeating: GridItem(.flexible()), count: item1.grip), spacing: 20){
ForEach(Array(item1.playeritem.enumerated()), id: \.element){(index,item) in
Button(action: {
audiocontrol.tota[index1].playeritem[index].player.play()
}, label: { })
}}}
Is the two foreach loop cause the performance decrease?
I have totally 100 - 150 element
I remove the scollview and change enumerated to indice,the performance is better but still lag.
maybe i should only use one foreach loop

Well, have you tried using a List instead of ForEach? Lists are more performable than ForEach, and interpolating them can bring a little lag to your app.

Related

How to manage Int range used in nested ForEach loop based on higher ForEach loop

I'd like to show HexagonView via two nested ForEach loops like shown in code below
ForEach(1...7, id: \.self) { row in
VStack {
HStack {
ForEach(1...row, id: \.self) { col in
HexagonView(center: CGPoint(x: getX(...), y: getY(...)), radius: 25)
}
}
}
}
Final look should be like triangle from little hexagons (first row 1 hexagon, second row two, third row three hexagons, etc.) but it looks weird on the screen even if centers of each hexagons seems to be calculated correctly. My suspicion is that using of row as upper bound of nested range is the issue. What better approach I could use?
Though #loremipsum is right, in your case you just have to move the VStack above the first ForEach:
VStack { // here
ForEach(1...7, id: \.self) { row in
HStack {
ForEach(1...row, id: \.self) { col in
Image(systemName: "hexagon")
}
}
}
}

SwiftUI: How do you animate from multiple values? iOS 15

With iOS 15 you can no longer use .animation() without specifying the value you want to animate on. While this can be extremely useful you cannot animate on multiple values.
Example: If I want to animate the movement of two buttons, but the trigger is based on two values rather than just a simple bool. See code below.
VStack(spacing: 0) {
if self.gvm.canPlot && self.gvm.points.count > 0 {
Button {
self.gvm.points = self.gvm.points.dropLast()
self.gvm.hostFence = self.gvm.hostFence.dropLast()
} label: {
Image(systemName: "arrow.counterclockwise")
.font(.system(size: 19))
.padding(12)
}
} else if !self.gvm.canPlot {
Button {
self.gvm.satelliteMap.toggle()
} label: {
Image(systemName: self.gvm.satelliteMap ? "map.fill" : "map")
.font(.system(size: 19))
.padding(12)
}
}
Button {
self.gvm.canPlot.toggle()
} label: {
Image(systemName: self.gvm.canPlot ? "arrow.up.and.down.and.arrow.left.and.right" : "mappin.and.ellipse")
.font(.system(size: 19))
.padding(12)
}
}
.background(Color.theme.background)
.cornerRadius(10)
.shadow(color: Color.theme.background.opacity(0.5), radius: 5, x: 3, y: 3)
.animation(.spring(), value: self.gvm.canPlot)
As you can see, .canPlot and .points.count > 0 are both used to trigger UI movement. However, .animation only allows for one value. Of course I could change other code to make this work. EX. Add a secondary .animation - or even not specify a value and live with the deprecation warning until it becomes an error.
Is there any simple way to use .animation based on multiple values? The older .animation would animate everything on the view, good and bad. Please let me know how .animation is intended to be used in this case.
EDIT:
Currently I have it working with this code:
.animation(.spring(), value: self.gvm.canPlot)
.animation(.spring(), value: self.gvm.points)
Can you try something like
.animation(.spring(), value: (self.gvm.canPlot && self.gvm.points.count > 0))

LazyVGrid where every item takes only the place it needs

I just wondered how you could make a LazyVGrid where every item takes only the place it needs, not less and not more.
I know about .flexible() but the problem is: My Items are different sized, that means I don't know how many of them will fit in a row.
Do you got any ideas?
Thanks for your help!
Boothosh
EDIT:
LazyVGrid(columns: [GridItem(.flexible())]) {
Color.blue
.frame(width: 200, height: 200)
Color.blue
.frame(width: 100, height: 100)
Color.blue
.frame(width: 100, height: 100)
}
This is a example what Im talking about. I want to achieve that this items are placed with evenly space around them. (Not below each other, like they are now)
You just need to specify what you want by using variables.
Try this :
struct ContentView: View {
let data = (1...100).map { "Item \($0)" }
let columns = [
// The number of grid Items here represent the number of columns you will
//see. so you can specify how many items in a row thee are .
// 2 grid Items = 2 items in a row
GridItem(.flexible()),
GridItem(.flexible()),
]
var body: some View {
ScrollView {
LazyVGrid(columns: columns, spacing: 20) {
ForEach(data, id: \.self) { item in
Text(item)
}
}
.padding(.horizontal)
}
.frame(maxHeight: 300)
}
}

How to create a responsive grid layout in SwiftUI?

It's common practice to have some data that has multiple properties in an array to be displayed in a device of a viewport, that is either compact or wide (iPhone, iPad, portrait/landscape, etc). For example, we might want to show a 1 column of 2 items in "portrait compact view", or a 1 column and 4 items in a "portrait wide view".
Code wise, we have the UserInterfaceSizeClass, that can be used as follows:
#Environment(\.horizontalSizeClass) var horizontalSizeClass: UserInterfaceSizeClass?
#Environment(\.verticalSizeClass) var verticalSizeClass: UserInterfaceSizeClass?
...
HStack {
if horizontalSizeClass == .compact {
...
} else {
...
}
}
Or, something like calculating the ratio:
GeometryReader { proxy in
if proxy.size.width > 324.0/2.0 {
WideView()
} else {
NarrowView()
}
}
And a grid can be understood as a 2D array, that we can iterate over the desired number of columns and nested in the loop, loop through the desired number of rows.
VStack {
ForEach(0 ..< self.rows, id: \.self) { row in
HStack {
ForEach(0 ..< self.columns, id: \.self) { column in
...
}
}
}
}
I hope that this far makes sense and comprehended as some basic principles used independently of the tech stack.
So, given a list of 1-dimensional collection of data (please assume a big number of planets):
class Planet {
var name: String
var size: Double
init(name:String, size:Double) {
self.name = name
self.size = size
}
}
var planets = [Planet]()
let planet = Planet(name: "Mars", size: 30.5)
planets.append(planet)
The data needs to be allocated to the response grid view.
So, what's the best approach to create a responsive grid layout in SwiftUI, considering the data and the different viewports and device portrait/landscape modes exposed above?
Let know if there are good practices to approach this!

How do you implement list paging in SwiftUI or infinite list view?

I want help on how to implement infinite list scrolling or paging list in SwiftUI.Thanks in advance
Your best bet is to use .onAppear and calculate if it's time to fetch your next page. This is a contrived example because typically you're hitting a network or disk which is much slower than this, but it will give you an idea. Tune getNextPageIfNecessary(_:) for your particular use-case.
#State var rows: [String] = Array(repeating: "Item", count: 20)
var body: some View {
List(0..<rows.count, id: \.self) { index in
Text(verbatim: self.rows[index])
.onAppear {
self.getNextPageIfNecessary(encounteredIndex: index)
}
}
}
private func getNextPageIfNecessary(encounteredIndex: Int) {
guard encounteredIndex == rows.count - 1 else { return }
rows.append(contentsOf: Array(repeating: "Item", count: 20))
}