What I want to do is almost identical to the badge on MacOS
and iOS
devices I tried to use SFSymbols
for this but it only supports %0
, %25
, %50
, %75
and %100
but I want to update my battery badge to the current battery level.
I've tried to use battery.0
icon in SFSymbols
with an RoundedRectangle
overlapping it like this:
struct BatteryView : View {
var body: some View {
ZStack {
Image(systemName: "battery.0")
RoundedRectangle(cornerRadius: 15)
.foregroundColor(.green)
}
}
}
but it acts really weird when I try to scale it up or down because of the RoundedRectangle
.
Thanks in advance!
Here's what I came up with:
As far as I can see, the battery
icon in SFSymbols
consists of only 3 elements, a rounded rectangle, a half circle that stands on top of the battery, and another rounded rectangle that indicates the current battery level.
struct BatteryView : View {
var body: some View {
GeometryReader { geo in
RoundedRectangle(cornerRadius: 15)
.stroke(lineWidth: 3)
}
}
}
and it looks like this(with a frame
of 180, 60):
I just used a rounded rectangle for the battery case as you suggested.
I created a simple shape called HalfCircleShape
for this.
struct HalfCircleShape : Shape {
func path(in rect: CGRect) -> Path {
var path = Path()
path.move(to: CGPoint(x: rect.minX, y: rect.midY))
path.addArc(center: CGPoint(x: rect.minX, y: rect.midY), radius: rect.height , startAngle: .degrees(90), endAngle: .degrees(270), clockwise: true)
return path
}
}
and in the GeometryReader
I give a frame of 1/7 of the current frame because it looks elegant.
and I combine these with a HStack
like this:
GeometryReader { geo in
HStack {
RoundedRectangle(cornerRadius: 15)
.stroke(lineWidth: 3)
HalfCircleShape()
.frame(width: geo.size.width / 7, height: geo.size.height / 7)
}
}
I create an extension in Color
to choose the color with the current battery level
extension Color {
static var BatteryLevel : Color {
let batteryLevel = 0.6
print(batteryLevel)
switch batteryLevel {
// returns red color for range %0 to %20
case 0...0.2:
return Color.red
// returns yellow color for range %20 to %50
case 0.2...0.5:
return Color.yellow
// returns green color for range %50 to %100
case 0.5...1.0:
return Color.green
default:
return Color.clear
}
}
}
And I just created another RoundedRectangle
and combine the two RoundedRectangle
s in a ZStack
Final code:
import SwiftUI
struct ContentView: View {
var body: some View {
BatteryView()
.frame(width: 170, height: 60)
.padding()
}
}
struct HalfCircleShape : Shape {
func path(in rect: CGRect) -> Path {
var path = Path()
path.move(to: CGPoint(x: rect.minX, y: rect.midY))
path.addArc(center: CGPoint(x: rect.minX, y: rect.midY), radius: rect.height , startAngle: .degrees(90), endAngle: .degrees(270), clockwise: true)
return path
}
}
struct BatteryView : View {
init() {
UIDevice.current.isBatteryMonitoringEnabled = true
}
var body: some View {
// UIDevice.current.batteryLevel always returns -1, and I don't know why. so here's a value for you to preview
let batteryLevel = 0.4
GeometryReader { geo in
HStack(spacing: 5) {
GeometryReader { rectangle in
RoundedRectangle(cornerRadius: 15)
.stroke(lineWidth: 3)
RoundedRectangle(cornerRadius: 15)
.padding(5)
.frame(width: rectangle.size.width - (rectangle.size.width * (1 - batteryLevel)))
.foregroundColor(Color.BatteryLevel)
}
HalfCircleShape()
.frame(width: geo.size.width / 7, height: geo.size.height / 7)
}
.padding(.leading)
}
}
}
extension Color {
static var BatteryLevel : Color {
let batteryLevel = 0.4
print(batteryLevel)
switch batteryLevel {
// returns red color for range %0 to %20
case 0...0.2:
return Color.red
// returns yellow color for range %20 to %50
case 0.2...0.5:
return Color.yellow
// returns green color for range %50 to %100
case 0.5...1.0:
return Color.green
default:
return Color.clear
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Also, in UIDevice
, there's a propety called batteryLevel
that gives you the current battery level, but in this case, it only returns -1, note: as discussed here, it seems like a bug in Swift.