How to add icon to circle progress bar tip? - swiftui

I'm trying to add an icon to the tip of the progress bar in the circle. This is the code I have so far:
Circle()
.trim(from: 0, to: 0.6)
.stroke(style: StrokeStyle(lineWidth: 24, lineCap: .round, lineJoin: .round))
.rotationEffect(.degrees(-90))
.foregroundColor(.green)
.frame(width: 300, height: 300)
.padding()
This ends up looking like this:
But I'm trying to add an icon to the tip of the progress like this:
How can I achieve this in SwiftUI?

Use the left arrow icon and put it into the ZStack and set y offset in minus.
struct DemoView: View {
#State private var angle: Double = -90
var body: some View {
ZStack {
Circle()
.trim(from: 0, to: 0.6)
.stroke(style: StrokeStyle(lineWidth: 24, lineCap: .round, lineJoin: .round))
.rotationEffect(.degrees(angle))
.foregroundColor(.green)
Image("left-arrow")
.offset(y: -150)
.rotationEffect(.degrees(305 + angle))
}.frame(width: 300, height: 300)
.padding()
}
}
I have added an image arrow using progress code from here
struct ProgressBar: View {
#Binding var progress: Float
var body: some View {
GeometryReader { geo in
ZStack(alignment: .center) {
Circle()
.stroke(lineWidth: 20.0)
.opacity(0.3)
.foregroundColor(Color.red)
Circle()
.trim(from: 0.0, to: CGFloat(min(self.progress, 1.0)))
.stroke(style: StrokeStyle(lineWidth: 20.0, lineCap: .round, lineJoin: .round))
.foregroundColor(Color.red)
.rotationEffect(Angle(degrees: 270.0))
.animation(.linear)
Text(String(format: "%.0f %%", min(self.progress, 1.0)*100.0))
.font(.largeTitle)
.bold()
Image("left-arrow")
.offset(y: -(geo.size.height/2))
.rotationEffect(.degrees(Double(self.progress) * 360))
.animation(.linear)
}
}
}
}

Related

SwiftUI: Countdown Circle with Animation

I've created a progress circle for my App, that will count down from 28 to 0 (number of days until Payday)
I've got the circles to display how I'd like, but I want it to animate when I navigate to the view, is there a way to do this?
More a UI question, but do you think maybe an arrow within the circle to indicate the direction would look any good?
P.s - any ideas how to truncate all the zeros off my Double?
image of how this looks
Thanks!
ZStack {
let progressPeriod = Double(PayData.daysUntilPay) ?? 0
let progressPeriod2 = 1 - (progressPeriod / 28)
Circle()
.stroke(lineWidth: 30.0)
.opacity(0.3)
.foregroundColor(Color(red: 0.941, green: 0.426, blue: 0.004))
.frame(width:150)
.frame(height: 200)
Circle()
.trim(from: 0.0, to: progressPeriod2)
.stroke(style: StrokeStyle(lineWidth: 30.0, lineCap: .round, lineJoin: .round))
.foregroundColor(Color(red: 0.941, green: 0.426, blue: 0.004))
.rotationEffect(Angle(degrees: 270.0))
.frame(width:150)
.frame(height: 200)
Text("\(progressPeriod) days")
.bold()
}
Try to combine following code with your countdown:
struct ContentView: View {
#State var progressValue: Float = 0.0
var body: some View {
ZStack {
Color.yellow
.opacity(0.1)
.edgesIgnoringSafeArea(.all)
VStack {
ProgressBar(progress: self.$progressValue)
.frame(width: 150.0, height: 150.0)
.padding(40.0)
Button(action: {
self.incrementProgress()
}) {
HStack {
Image(systemName: "plus.rectangle.fill")
Text("Increment")
}
.padding(15.0)
.overlay(
RoundedRectangle(cornerRadius: 15.0)
.stroke(lineWidth: 2.0)
)
}
Spacer()
}
}
}
func incrementProgress() {
let randomValue = Float([0.012, 0.022, 0.034, 0.016, 0.11].randomElement()!)
self.progressValue += randomValue
}
}
struct ProgressBar: View {
#Binding var progress: Float
var body: some View {
ZStack {
Circle()
.stroke(lineWidth: 20.0)
.opacity(0.3)
.foregroundColor(Color.red)
Circle()
.trim(from: 0.0, to: CGFloat(min(self.progress, 1.0)))
.stroke(style: StrokeStyle(lineWidth: 20.0, lineCap: .round, lineJoin: .round))
.foregroundColor(Color.red)
.rotationEffect(Angle(degrees: 270.0))
.animation(.linear)
Text(String(format: "%.0f %%", min(self.progress, 1.0)*100.0))
.font(.largeTitle)
.bold()
}
}
}

Why after .trim Circle() its borders lose smoothness?

After trimming a Circle(), its borders lose smoothness.
struct WidgetsEntryView : View {
var entry: Provider.Entry
var body: some View {
VStack {
ZStack {
Circle()
.trim(from: 0.0, to: min(CGFloat(0.5), 1.0))
.stroke(Color.green, style: StrokeStyle(lineWidth: 10, lineCap: .round, lineJoin: .round))
.rotationEffect(Angle(degrees: 270))
}
Text("Subtitle")
.foregroundColor(.white)
}
.padding()
.background(Color.black)
}
}
If you remove the padding, text, or the trimming itself anti-aliasing returned.

SwiftUI, Circular progress bar with image on progress

I'm trying to implement a circular progress bar in SwiftUI with an image on the progress and animation. Any help or idea would be appreciated.
Here is what I plan to implement:imageLink
This is what I could implement so far: image2
And this is my code:
struct CircularView: View {
#State var progress:Double = 0
var total:Double = 100
let circleHeight:CGFloat = 217
#State var xPos:CGFloat = 0.0
#State var yPos:CGFloat = 0.0
var body: some View {
let pinHeight = circleHeight * 0.1
VStack {
Circle()
.trim(from: 0.0, to: 0.6)
.stroke(Color(.systemGray5),style: StrokeStyle(lineWidth: 8.0, lineCap: .round, dash: [0.1]))
.frame(width: 278, height: 217, alignment: .center)
.rotationEffect(.init(degrees: 162))
.overlay(
Circle()
.trim(from: 0.0, to: CGFloat(progress) / CGFloat(total))
.stroke(Color.purple,style: StrokeStyle(lineWidth: 8.0, lineCap: .round, dash: [0.1]))
.rotationEffect(.init(degrees: 162))
.rotation3DEffect(
.init(degrees: 1),
axis: (x: 1.0, y: 0.0, z: 0.0)
)
.animation(.easeOut)
.overlay(
Circle()
.frame(width: pinHeight, height: pinHeight)
.foregroundColor(.blue)
.offset(x: 107 - xPos, y: 0 + yPos)
.animation(.easeInOut)
.rotationEffect(.init(degrees: 162))
)
)
.foregroundColor(.red)
Button(action: {
progress += 5
if progress >= 65 {
progress = 0
}
}, label: {
Text("Button")
})
}
}
}
Without changing much of your initial code, the idea is to rotate also the image when the circular progress bar value changes
Your code would look like this:
struct CircularView: View {
#State var progress:Double = 0
var total:Double = 100
let circleHeight:CGFloat = 217
#State var xPos:CGFloat = 0.0
#State var yPos:CGFloat = 0.0
var body: some View {
let pinHeight = circleHeight * 0.1
VStack {
Circle()
.trim(from: 0.0, to: 0.6)
.stroke(Color(.systemGray5),style: StrokeStyle(lineWidth: 8.0, lineCap: .round, dash: [0.1]))
.frame(width: 278, height: 217, alignment: .center)
.rotationEffect(.init(degrees: 162))
.overlay(
Circle()
.trim(from: 0.0, to: CGFloat(progress) / CGFloat(total))
.stroke(Color.purple,style: StrokeStyle(lineWidth: 8.0, lineCap: .round, dash: [0.1]))
.rotationEffect(.init(degrees: 162))
.rotation3DEffect(
.init(degrees: 1),
axis: (x: 1.0, y: 0.0, z: 0.0)
)
// .animation(.easeOut)
.overlay(
Circle()
.frame(width: pinHeight, height: pinHeight)
.foregroundColor(.blue)
.offset(x: 107 - xPos, y: 0 + yPos)
// .animation(.easeInOut)
.rotationEffect(Angle(degrees: progress * 3.6))
.rotationEffect(.init(degrees: 162))
)
)
.foregroundColor(.red)
.animation(.easeOut) // Animation added here
Button(action: {
progress += 5
if progress >= 65 {
progress = 0
}
}, label: {
Text("Button")
})
}
}
}
1. Apply one common animation to have a better transition
I Have removed .animation modifiers in 2 places and place only one on the parent Circle View to allow a smooth animation ( You can still change and adapt it to the output you desire )
2. Calculate Image Angle rotation Degree
Perform simple calculation to determine rotation Angle, how much degree should I rotate the Image view
So, basically it is: .rotationEffect(Angle(degrees: (progress / 60) * (0.6 * 360)) => .rotationEffect(Angle(degrees: progress * 3.6))

Circle view animation misbehavior

I've created a Loading view on swiftUI, using this code:
var body: some View {
ZStack {
Circle()
.trim(from: 0, to: 0.2)
.stroke(AngularGradient(gradient: .init(colors: [settings.colors[self.settings.accentColor]]), center: .center), style: StrokeStyle(lineWidth: 6, lineCap: .round))
.rotationEffect(Angle(degrees: isLoading ? 360 : 0))
Circle()
.trim(from: 0.2, to: 0.4)
.stroke(AngularGradient(gradient: .init(colors: [.red]), center: .center), style: StrokeStyle(lineWidth: 6, lineCap: .round))
.rotationEffect(Angle(degrees: isLoading ? -360 : 0))
.frame(height: aspect / 2)
Circle()
.trim(from: 0.5, to: 0.7)
.stroke(AngularGradient(gradient: .init(colors: [settings.colors[self.settings.accentColor]]), center: .center), style: StrokeStyle(lineWidth: 6, lineCap: .round))
.rotationEffect(Angle(degrees: isLoading ? 360 : 0))
Circle()
.trim(from: 0.7, to: 0.9)
.stroke(AngularGradient(gradient: .init(colors: [.red]), center: .center), style: StrokeStyle(lineWidth: 6, lineCap: .round))
.rotationEffect(Angle(degrees: isLoading ? -360 : 0))
.frame(height: aspect / 2)
.onAppear() {
self.isLoading = true
}
}
.animation(Animation.linear(duration: 0.5).repeatForever(autoreverses: false))
.frame(width: aspect, height: aspect, alignment: .center)
}
On the swiftui preview canvas everything seems fine:
But when run on simulator or real device, animation seems broken and it modifies view location while animating like this:
Anyone knows if this is a bug, or I'm missing something. Thanks in advance.

why is Color view transparent in swiftUI

i have this card thing in the picture to build, here're the code:
struct ArticleCard: View {
var article: Article
var body: some View {
ZStack {
Color.white
.border(Color(red: 0, green: 0, blue: 0, opacity: 0.2))
.shadow(color: Color(red: 0, green: 0, blue: 0, opacity: 0.2), radius: 0.0, x: 5, y: 5)
VStack{
Text(article.title)
.padding(.top, 5)
.padding(.leading)
.padding(.trailing)
.font(.title2)
Image(article.coverUrl).resizable()
.frame(height: 200)
.padding(.leading)
.padding(.trailing)
Text(article.title)
.padding(.leading)
.padding(.trailing)
.padding(.bottom)
}
}
}
}
in the ZStack i put a Color.white there as a background of the card, and give this color view a shadow, but the color seems to be transparent, therefor i got unwanted lines on the top and the left inside the borders, how do i get rid of them?
You are using border and shadow in wrong place, try this:
PS: there is no reason of using Color(red: 0, green: 0, blue: 0, opacity: 0.2) you can use this: Color.black.opacity(0.2)
import SwiftUI
struct ContentView: View {
var body: some View {
ZStack {
Color.white
VStack {
Text("article.title")
.padding()
Image(systemName: "star")
.resizable()
.scaledToFit()
Text("article.title")
.padding()
}
}
.frame(width: 300, height: 300, alignment: .center)
.border(Color(red: 0, green: 0, blue: 0, opacity: 0.2))
.compositingGroup()
.shadow(color: Color(red: 0, green: 0, blue: 0, opacity: 0.2), radius: 0.0, x: 5, y: 5)
}
}
I refactored your code for better and less code as possible you can use Image instead of Text in your App.
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
Text("your custom Text")
.padding()
Text("🥩")
.font(Font.system(size: 150))
Text("your custom Text")
.padding()
}
.frame(width: 300, height: 300, alignment: .center)
.background(Color.white)
.border(Color.black.opacity(0.2))
.compositingGroup()
.shadow(color: Color.black.opacity(0.2), radius: 0.0, x: 5, y: 5)
}
}