Rectangle Progress Bar Swiftui

I can't modify circular progress bar trim in SwiftUI

struct ContentView: View {

@State private var progress: Double = 0.1

var body: some View {
VStack {
Circle()
.trim(from: 0, to: progress)
.stroke(lineWidth: 20)
.rotationEffect(Angle(degrees: 180))
.shadow(radius: 8)
.rotationEffect(.degrees(90))
.frame(width: 60, height: 60)
.padding()

Slider(value: $progress, in: 0...1)
.padding()
}
}
}

Change size (height) of ProgressView in SwiftUI

There's no direct way that I know of to change the height, but you can use the .scaleEffect modifier. Make sure to specify 1 for the x scale in order to only increase the height.

struct ContentView: View {
var body: some View {
ProgressBar()
.padding([.leading, .trailing], 10)
}
}

struct ProgressBar: View {
var body: some View {
VStack {
ProgressView(value: 50, total: 100)
.accentColor(Color.green)
.scaleEffect(x: 1, y: 4, anchor: .center)
}
}
}

Result:
Progress bar with larger height

A drawback to this is that you can't pass in a Label, because it will also get stretched.

ProgressView("Progress:", value: 50, total: 100)

The label "Progress:" stretched vertically

To work around this, just make your own Text above the ProgressView.

struct ProgressBar: View {
var body: some View {
VStack(alignment: .leading) {
Text("Progress:")
.foregroundColor(Color.blue)

ProgressView(value: 50, total: 100)
.accentColor(Color.green)
.scaleEffect(x: 1, y: 4, anchor: .center)
}
}
}

"Progress:" is not stretched

SwiftUI: Change the start position for animated progress bar (or Shape drawing in general)

You don't need to rotate it (moreover it might be not square), just create own rectangular shape with start point anywhere needed.

Demo prepared with Xcode 12.4 / iOS 14.4

demo

struct MyRectangle: Shape {
func path(in rect: CGRect) -> Path {
Path {
$0.move(to: CGPoint(x: rect.midX, y: rect.minY))
$0.addLine(to: CGPoint(x: rect.maxX, y: rect.minY))
$0.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
$0.addLine(to: CGPoint(x: rect.minX, y: rect.maxY))
$0.addLine(to: CGPoint(x: rect.minX, y: rect.minY))
$0.addLine(to: CGPoint(x: rect.midX, y: rect.minY))
}
}
}

struct ContentView: View {
@State private var progress: Double = 0
@State private var timer = Timer.publish(every: 0.1, on: .main, in: .common).autoconnect()

var body: some View {
VStack{
HStack{
ZStack {
MyRectangle()
.stroke(lineWidth: 20.0)
.opacity(0.3)
.foregroundColor(Color.gray)
.frame(width: 200, height:100)
MyRectangle()
.trim(from: 0.0, to: CGFloat(min(self.progress, 1.0)))
.stroke(style: StrokeStyle(lineWidth: 20.0, lineCap: .round, lineJoin: .round))
.foregroundColor(Color.blue)
.animation(.linear)
.frame(width: 200, height:100)
.onReceive(timer, perform: { _ in
if(progress < 100){
progress += 0.1/10
}
else{
progress = 0
}
})
}
}
.padding()
}
}
}

Creating a progress indicator with a rounded rectangle

I found a solution so the loading indicator works for round corners:

Sample Image

let queueShapeLayer = CAShapeLayer()

override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)

// Queue timer
let radius = addToQueue.layer.cornerRadius
let diameter = radius * 2
let totalLength = (addToQueue.frame.width - diameter) * 2 + (CGFloat.pi * diameter)

let queuePath = UIBezierPath(roundedRect: addToQueue.frame, cornerRadius: radius)
queueShapeLayer.path = queuePath.cgPath
queueShapeLayer.lineWidth = 5
queueShapeLayer.strokeColor = UIColor.white.cgColor
queueShapeLayer.fillColor = UIColor.clear.cgColor
queueShapeLayer.strokeStart = 0.25 - CGFloat.pi * diameter / 3 / totalLength // Change the '0.25' to 0.5, 0.75 etc. wherever you want the bar to start
queueShapeLayer.strokeEnd = queueShapeLayer.strokeStart + 0.5 // Change this to the value you want it to go to (in this case 0.5 or 50% loaded)
view.layer.addSublayer(queueShapeLayer)
}

After I had did this though, I was having problems that I couldn't animate the whole way round. To get around this, I created a second animation (setting strokeStart to 0) and then I placed completion blocks so I could trigger the animations at the correct time.

Tip:

Add animation.fillMode = CAMediaTimingFillMode.forwards & animation.isRemovedOnCompletion = false when using a CABasicAnimation for the animation to wait until you remove it.

I hope this formula helps anyone in the future!

If you need help, you can always message me and I am willing to help. :)

SwiftUI: How to use a linear gradient in a progress bar?

I think your best bet is to "mask" the gradient, rather than trying to calculate the endPoint.

Here's an example with 5 "bars" in an HStack - the first bar has a max value of 10, then the bars' values go 9, 7.5, 5, 2:

Sample Image

Sample code:

import SwiftUI

struct GradBars: View {

var maxValue: CGFloat = 10

var val1: CGFloat = 10
var val2: CGFloat = 9
var val3: CGFloat = 7.5
var val4: CGFloat = 5
var val5: CGFloat = 2

@State var open: Bool = true

let gradient = LinearGradient(gradient: Gradient(colors: [ Color(UIColor(red: 0.98, green: 0.85, blue: 0.38, alpha: 1.00)) ,
Color(UIColor(red: 0.63, green: 0.02, blue: 0.11, alpha: 1.00))]),
startPoint: .bottom,
endPoint: .top)

var body: some View {
VStack {
HStack(alignment: .bottom, spacing: 8, content: {

Rectangle()
.fill(gradient)
.frame(width: 40, height: open ? maxValue * 10 : 0, alignment: .bottom)
.mask(
VStack {
RoundedRectangle(cornerRadius: 5.0).frame(height: val1 * 10)
}.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottom)
)

Rectangle()
.fill(gradient)
.frame(width: 40, height: open ? maxValue * 10 : 0, alignment: .bottom)
.mask(
VStack {
RoundedRectangle(cornerRadius: 5.0).frame(height: val2 * 10)
}.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottom)
)

Rectangle()
.fill(gradient)
.frame(width: 40, height: open ? maxValue * 10 : 0, alignment: .bottom)
.mask(
VStack {
RoundedRectangle(cornerRadius: 5.0).frame(height: val3 * 10)
}.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottom)
)

Rectangle()
.fill(gradient)
.frame(width: 40, height: open ? maxValue * 10 : 0, alignment: .bottom)
.mask(
VStack {
RoundedRectangle(cornerRadius: 5.0).frame(height: val4 * 10)
}.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottom)
)

Rectangle()
.fill(gradient)
.frame(width: 40, height: open ? maxValue * 10 : 0, alignment: .bottom)
.mask(
VStack {
RoundedRectangle(cornerRadius: 5.0).frame(height: val5 * 10)
}.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottom)
)

})
}
}

}

struct GradBars_Previews: PreviewProvider {
static var previews: some View {
GradBars()
}
}

Update progress value from fileImporter modifier in SwiftUI

I'd inject progress item in DataManager via constructor

class DataManager {
private var progressBar: ProgressItem? = nil

init(progressBar: ProgressItem? = nil) {
self.progressBar = progressBar
}

func importData(fileURL: URL, delimeter: String = ",") {

// ... other code

// if importing on background queue
//DispatchQueue.main.async { // this better update async on main queue
progressBar?.progress = Double(index)/Double(numlines) * 100
//}
}

and then inject observed item from view into DataManager, like

    // ... other code

DataManager(progressBar: self.progress).importData(fileURL: selectedFile)

// ... other code

of course somewhere/somewhen you need to reset your current progress item. I don't see any completion in your code, but if you have one it might be inside it.

SwiftUI - Capsule fill till StrokeBorder

I think this is what you are after.

struct ContentView: View {

var body: some View {

HStack{
TextField("Search", text: .constant(""))
}
.padding()
.frame(maxWidth:300)
.background(
Capsule()
.strokeBorder(Color.black,lineWidth: 0.8)
.background(Color.blue)
.clipped()
)
.clipShape(Capsule())
}
}

Xcode preview of HStack with capsule border



Related Topics



Leave a reply



Submit