Post

Replies

Boosts

Views

Activity

Label each segment on a segmented circle
Hi community, newbie here and decided to tackle an app with a big element using Shape. I have created a view which draws segments of a circle and puts them together into said circle. I am now trying to add text to each segment to label them (as they act as buttons). I have tried using simple attempts such as .overlay as well as searching forums for solutions such as this one: https://stackoverflow.com/questions/66187564/how-can-i-insert-uiimages-in-my-circle-segments-with-swiftui Unfortunately I am not able to get the label to sit over its respective segment. The code I am sharing removes all attempts at labelling to show its current state. Here is my code to draw the segmented wheel: struct EmotionWheelView: View { // Prepare array of emotions for the selector let emotions: [Emotion] = Bundle.main.decode("emotions.json") // Bringing in the tapped and selected emotions to set in the parent @Binding public var tappedEmotion: String @Binding public var selectedEmotion: String @Binding public var selectedSegment: Int? var selectableEmotions: [Emotion] { var results: [Emotion] = [] if selectedEmotion.isEmpty { for emotion in emotions { if emotion.level == 1 { results.append(emotion) } } } else { var baseEmotion: Emotion { var result: Emotion = emotions[0] for emotion in emotions { if emotion.name == selectedEmotion { result = emotion } } return result } for emotion in emotions { for subID in baseEmotion.subID { if emotion.id == subID { results.append(emotion) } } } } return results } var data: [Double] { var results: [Double] = [] for _ in selectableEmotions { results.append(2.0) } return results } var colors: [Color] { var results: [Color] = [] var color: Color = .red for emotion in selectableEmotions { switch emotion.color { case "purple": color = Color.purple case "pink": color = Color.pink case "orange": color = Color.orange case "yellow": color = Color.yellow case "green": color = Color.green case "teal": color = Color.teal case "blue": color = Color.blue default: color = Color.black } results.append(color) } return results } var body: some View { EmotionWheel(selectableEmotions: selectableEmotions, data: data, colors: colors, tappedEmotion: $tappedEmotion, selectedSegment: $selectedSegment) .frame(maxHeight: 200) } } struct EmotionWheel: View { var selectableEmotions: [Emotion] var data: [Double] var colors: [Color] @Binding var tappedEmotion: String @Binding var selectedSegment: Int? private var totalValue: Double { data.reduce(0, +) } private var startAngles: [Double] { var angles: [Double] = [] var currentAngle = -Double.pi / 2 for value in data { angles.append(currentAngle) currentAngle += value / totalValue * 2 * .pi } return angles } private var endAngles: [Double] { var angles: [Double] = [] var currentAngle = -Double.pi / 2 for value in data { currentAngle += value / totalValue * 2 * .pi angles.append(currentAngle) } return angles } var body: some View { GeometryReader { geo in ZStack { ForEach(0..<data.count, id: \.self) { index in WheelSegment(startAngle: startAngles[index], endAngle: endAngles[index]) .stroke(Color(UIColor.systemBackground), lineWidth: 1) .fill(colors[index].gradient) .onTapGesture { withAnimation { selectedSegment = index tappedEmotion = selectableEmotions[index].name } } .scaleEffect(selectedSegment == index ? 1.1 : 1.0) .animation(.spring(dampingFraction: 0.8), value: selectedSegment) } Circle() .foregroundStyle(Color(UIColor.systemBackground)) .frame(width: geo.size.width / 2) .overlay { Text(tappedEmotion) .fontWeight(.bold) .foregroundStyle(getTappedColor(tappedEmotion: tappedEmotion)) .padding() } } .padding(40) } } } struct WheelSegment: Shape { var startAngle: Double var endAngle: Double func path(in rect: CGRect) -> Path { var path = Path() let center = CGPoint(x: rect.midX, y: rect.midY) let radius = min(rect.width, rect.height) path.move(to: center) path.addArc(center: center, radius: radius, startAngle: Angle(radians: startAngle), endAngle: Angle(radians: endAngle), clockwise: false) path.closeSubpath() return path } } func getTappedColor(tappedEmotion: String) -> Color { let emotions: [Emotion] = Bundle.main.decode("emotions.json") var color: Color = .red var toColor: Emotion { for emotion in emotions { if emotion.name == tappedEmotion { return emotion } } return emotions[0] } switch toColor.color { case "purple": color = Color.purple case "pink": color = Color.pink case "orange": color = Color.orange case "yellow": color = Color.yellow case "green": color = Color.green case "teal": color = Color.teal case "blue": color = Color.blue default: color = Color.black } return color } Result: Crude representation of goal (you get the idea):
0
0
294
Nov ’23