I have built this code
struct StarDifficultyView: View {
var numberOfStarsToShow: Int
var numberOfTotalStarsToShow: Int = 5
var body: some View {
HStack{
var numberLeftToShow = numberOfStarsToShow
ForEach(1..<numberOfTotalStarsToShow 1){_ in
if(numberLeftToShow > 0){
Image(systemName: "star.fill")
.foregroundColor(Color.yellow)
numberLeftToShow -= 1
}else{
Image(systemName: "star.fille")
.foregroundColor(Color.yellow)
}
}
}
}
}
It gives me an error on the line if(numberLeftToShow > 0){ saying "Type '()' cannot conform to 'View'"
Can anyone tell me what I'm doing wrong
CodePudding user response:
Don't throw away the closure parameter for the ForEach!
var body: some View {
HStack{
ForEach(0..<numberOfTotalStarsToShow){ i in // don't ignore the "i" here by writing "_"
// "i" will be different in each "iteration"
// use that to figure out which image to show
if(i < numberOfStarsToShow){
Image(systemName: "star.fill")
.foregroundColor(Color.yellow)
} else {
Image(systemName: "star")
.foregroundColor(Color.yellow)
}
}
}
}
CodePudding user response:
Explaining the issue:
You should not add expressions inside the view builder. So numberLeftToShow -= 1 will throw and error because it returns a void ('aka' type()) and this does not conform to View! that is the exact reason for the compiler!
Note 1
Don't use SwiftUI like the UIKit! SwiftUI views may execute over time on any state change and should not be used for calculating anything in this way
Note 2
You can convert 1..<numberOfTotalStarsToShow 1 to a closed range like 1...numberOfTotalStarsToShow (Although you don't need it at all for this question)
Note 3
Try not to use branch and convert your if/else code to something like:
Image(systemName: numberLeftToShow > 0 ? "star.fill" : "star.fille")
.foregroundColor(Color.yellow)
Note 4:
The lower bound of a range can not be less than the upper range, but you can iterate over a reversed range like:
(1...numberOfTotalStarsToShow).reversed()
Note 5:
try using a single source of truth like the forEach parameter itself!
Note 6:
Swift can infer the type and you don't need to pass it again:
so change Color.yellow to .yellow
Final Result:
Here is the code reviewed answer (based on the answer you have provided yourself):
var body: some View {
HStack {
ForEach(1...numberOfTotalStarsToShow, id:\.self) { i in
Image(systemName: "star.fill")
.foregroundColor(i > numberOfStarsToShow ? .gray : .yellow)
}
}
}
CodePudding user response:
Never mind, I just did this
struct StarDifficultyView: View {
var numberOfStarsToShow: Int
var numberOfTotalStarsToShow: Int = 5
var body: some View {
HStack{
ForEach(1..<numberOfStarsToShow 1){_ in
Image(systemName: "star.fill")
.foregroundColor(Color.yellow)
}
ForEach(1..<numberOfTotalStarsToShow-numberOfStarsToShow 1){_ in
Image(systemName: "star.fill")
.foregroundColor(Color.gray)
.opacity(0.7)
}
}
}
}
Basically, it just loops through the number of yellow stars to show and then works out how many grey ones to show and does another ForEach to display the leftover ones needed
