This question already has answers here:
How to print() to Xcode console in SwiftUI?
(15 answers)
Closed 5 months ago.
var body: some View {
ScrollView(.horizontal,showsIndicators: false){
GeometryReader{
ViewGeoIndex in
And I get the Error called "Type '()' cannot conform to 'View'" in the following print statement.
I guess maybe there should not be any statements except for the views and stacks(something to be shown on the view)
If so, then where should I put my debug statements like print.
print("The centerX of the screen is \(ViewGeoIndex.frame(in:.global).midX)")
HStack(alignment:.center,spacing:0){
ForEach(CardName.allCases,id:\.self){
option in
// Circle().frame(width:10,height: 30)
GeometryReader{
BarGeoIndex in
Button {
activeCard=option.title
print("Success")
} label:{
ZStack{
Circle().frame(width: 60, height:60, alignment: .center)
.foregroundColor(.white)
.shadow(color: .gray, radius: 0.5, x: 0.5, y: 0.5)
Text(option.image)
}//content of the button
.frame(width:100)
}
var BarMidX = BarGeoIndex.frame(in: .global).midX
Here occurs the same problem like the last one.
if (BarMidX == ViewGeoIndex.frame(in: .global).midX)
{
print("Success")
}
```
}.frame(width:100)
}
}//HStack
.padding([.horizontal],0)
}
// Text("\(index.frame(in: CoordinateSpace.global).midX)")
}//ScrollView
.padding([.horizontal],0)
}
I have been stuck here for 1 hour. Realllly appreciate if you help me out.
The closure you are in expects to return only Views. But print does not.
You can use the print this way:
let _ = print(...
Related
I'm creating a chart in SwiftUI. It's weather related and I have the data successfully displaying. Now, I'm attempting to display the temperature on each symbol in the chart, but it only displays on the first symbol.
Here's what I've done:
Chart {
ForEach(temperatures, id: \.tempType) { series in
ForEach(series.data) { element in
LineMark(x: .value("Day", element.day), y: .value("Temps", element.temp))
.interpolationMethod(.catmullRom)
.foregroundStyle(by: .value("TemperatureType", series.tempType))
.symbol {
Circle()
.fill(.yellow)
.frame(width: 10)
.shadow(radius: 2)
}
.lineStyle(.init(lineWidth: 5))
} // end for each
} // end for each
}
This works. Then, I attempt to add text using this modifier on the LineMark:
.annotation(position: .overlay, alignment: .bottom) {
let text = "\(element.temp)"
Text(text)
}
It only displays the text on the first symbol's data point:
Since the annotation modifier is within the ForEach loop, I thought it would display the temperature at each data point, but it doesn't. What's the best way to have the temperature displayed at each symbol instead of only the first?
The short answer is that the .annotation refers to the type of "Mark" that you attach it to - and you are attaching it to a LineMark, so it is the entire line you are "annotating", not the individual points.
Had you used BarMarks or PointMarks, the annotation will attach to the individual bar or point. So try this instead:
Chart {
ForEach(Array(zip(numbers, numbers.indices)), id: \.0) { number, index in
LineMark(
x: .value("Index", index),
y: .value("Value", number)
)
.lineStyle(.init(lineWidth: 5))
PointMark(
x: .value("Index", index),
y: .value("Value", number)
)
.annotation(position: .overlay,
alignment: .bottom,
spacing: 10) {
Text("\(number)")
.font(.largeTitle)
}
}
}
To make it compatible with your nice symbols, we need to add a couple of extra steps:
Chart {
ForEach(Array(zip(numbers, numbers.indices)), id: \.0) { number, index in
LineMark(
x: .value("Index", index),
y: .value("Value", number)
)
.interpolationMethod(.catmullRom)
.lineStyle(.init(lineWidth: 5))
.symbol {
// This still needs to be associated
// with the LineMark
Circle()
.fill(.yellow)
.frame(width: 10)
.shadow(radius: 2)
}
PointMark(
x: .value("Index", index),
y: .value("Value", number)
)
// We need .opacity(0) or it will
// overlay your `.symbol`
.opacity(0)
.annotation(position: .overlay,
alignment: .bottom,
spacing: 10) {
Text("\(number)")
.font(.largeTitle)
}
}
}
This question already has an answer here:
Overlay views one by one in SwiftUI
(1 answer)
Closed 3 months ago.
Is it possible to use an if before an overlay?
The example code shows a blue square with an overlay of text and a highlight just below the text. And if the number in the text matches the day number of the month, then the blue box shows a white border.
Getting the code to work is fairly easy if you only have one if statement, because you can place all objects inside the if. Although if there are multiple if scenarios (such as different border colours according to different situations) the most efficient way (from the perspective of making it readable) would be to insert the if between the Color object and the .overlay.
As a result of trying to make it more efficient I have tried a variety of methods (such as self.overlay etc).
This is the example and shortened code - I have inserted the if statement after the "//" in lines 7 to ten that I think would make it more efficient (to read) if I could get it to work (where I would put a series of ifs):
struct exampleCode: View {
let textRef : Int
var body : some View {
Color(red:20/255,green: 45/255, blue:50/255)
.frame(width:50, height:56)
.cornerRadius(6)
//if(Calendar.current.component(.day, from: Date()) == dayRef){
// self.overlay(RoundedRectangle(cornerRadius: 5)
// .stroke(Color.white, lineWidth: 2))
//}
.overlay(alignment:.center){
VStack (spacing: -4){
Spacer()
Text(String(textRef))
.foregroundColor(.white)
.font(Font.system(size: 26, weight: .bold))
Spacer()
Color(red:20/255,green: 255/255, blue:71/255)
.frame(width:20, height:6)
}
}
}
}
No, it's not possible.
One solution is a conditional ViewModifier but this creates a new view and maybe will break animations.
There is an alternative: Apply the ternary operator and pass nil for no change.
struct ExampleCode: View { // please name struct uppercase
let textRef : Int
let isSameDay = Calendar.current.component(.day, from: Date()) == dayRef // where does dayRef come from?
var body : some View {
Color(red:20/255,green: 45/255, blue:50/255)
.frame(width:50, height:56)
.cornerRadius(6)
.overlay(isSameDay
? RoundedRectangle(cornerRadius: 5)
.stroke(Color.white, lineWidth: 2)
: nil)
.overlay(alignment:.center){
VStack (spacing: -4){
Spacer()
Text(String(textRef))
.foregroundColor(.white)
.font(Font.system(size: 26, weight: .bold))
Spacer()
Color(red:20/255,green: 255/255, blue:71/255)
.frame(width:20, height:6)
}
}
}
}
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
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)
}
}
}
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)
}
}
}
}
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
I have a view that gets data from an API what I want to do is rank rows based on points from highest to lowest (I am able to do that now) however issue is that I want to show row number like #1, #2 etc .. as you can see from below image in the red background it says 1 2 for both rows instead of the first saying 1 and the second row saying 2 . This is my code below . I have put a comment of // issue is here which is were the problem lies . As you can see I'm already using a forEach that passes in api data . Is there a way I can use that foreach ForEach(result) and number the rows from 1...n and still be able to retrieve the data like I'm doing . I have seen examples of people doing that but it is usually for a simple string array . Any suggestions would be great .
ScrollView(showsIndicators: false) {
LazyVStack {
let result = model.sorted {
$0.points! > $1.points!
}
ForEach(result) { value in
VStack(alignment: .leading) {
Spacer()
HStack {
ForEach(0..<result.count) { i in
Text("\(i + 1)")
.frame(width: 20, height: 20, alignment: .center)
.foregroundColor(.white)
.background(Color.red)
} // issue is here
Text(value.initials!)
.fontWeight(.bold)
.frame(width: 50, height: 50, alignment: .center)
.foregroundColor(Color(SystemColorPicked(picked: value.color)))
.padding()
.overlay(
Circle()
.stroke(Color.white, lineWidth: 4)
.padding(6)
)
VStack(alignment: .leading, spacing: 1) {
Text("\(value.fullname ?? "NA")")
.foregroundColor(.white)
.fontWeight(.bold)
Text("\(value.points ?? 0) | points")
.foregroundColor(.white)
}
}
}
}
}
}
The problem is you have a ForEach running within in item of your other ForEach. The second one is the one that is displaying "1 2" each time, since it iterates through all the results every time.
I don't have all of your code, so I'm not sure what type results is, but here's a sample that you can adapt to your use case. Keep in mind you'll be doing this to the outer (ie first) ForEach loop:
struct ContentView : View {
#State private var results = ["Test1","Test2","Test3"]
var body: some View {
VStack {
ForEach(Array(results.enumerated()), id: \.1) { (index, value) in
Text("\(index + 1) \(value)")
}
}
}
}
What's going on:
For the ForEach, I'm using results (or, result in your code) .enumerated -- this gives an index/item pair. I'm wrapping it in Array() because otherwise the ForEach won't want to take that specific type of sequence.
ForEach has to have an id -- here I'm just using the String itself. For your purpose, it might be different. .1 refers to the second item of the tuple (ie the item, not the index)
In the ForEach closure, you now get index and value
I have a little problem on the lastest SwiftUI, the error is "The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions"
My code is like that :
let cards = ["Azertyuiop", "Bzertyuiop", "Czertyuiop", "Dzertyuiop", "Ezertyuiop", "Fzertyuiop", "Gzertyuiop", "Hzertyuiop", "Izertyuiop", "Jzertyuiop", "Kzertyuiop", "Lzertyuiop", "Bzertyuiop", "Czertyuiop", "Dzertyuiop", "Ezertyuiop", "Fzertyuiop", "Gzertyuiop", "Lzertyuiop", "Bzertyuiop", "Czertyuiop", "Dzertyuiop", "Ezertyuiop", "Fzertyuiop", "Gzertyuiop", "Lzertyuiop", "Bzertyuiop", "Czertyuiop", "Dzertyuiop", "Ezertyuiop", "Fzertyuiop", "Gzertyuiop"]
var body: some View {
ScrollView{
VStack (spacing: 0, content: {
ForEach(0..<cards.count/3) { row in // create number of rows
HStack (spacing: 0, content: {
ForEach(0..<3) { column in // create 3 columns
ZStack(alignment: .bottomLeading){
Image("ghost").resizable().aspectRatio(contentMode: .fill)
.overlay(Rectangle().fill (LinearGradient(gradient: Gradient(colors: [.clear, .black]),startPoint: .center, endPoint: .bottom)).clipped())
Text(self.cards[row * 3 + column]) // this cause the error
.fontWeight(.semibold)
}
}
})
}
})
}
}
I guess that the error comes from : row * 3 + column
So I tried to put the integer 1 instead of this calculation, and it worked.
How to do this calculation in my body and my View? because SwiftUI does not allow me and shows me "Expected pattern"
Thanks a lot !
There are two problems. One is using ZStack with overlay, the other is Linear Gradient is a view and you can use it directly.
ZStack(alignment: .bottomLeading){
Image("ghost").resizable().aspectRatio(contentMode: .fill)
LinearGradient(gradient: Gradient(colors: [.clear, .black]),startPoint: .center, endPoint: .bottom).clipped()
Text(self.cards[row * 3 + column]) // this cause the error
.fontWeight(.semibold)
}