Created
October 2, 2020 10:07
-
-
Save izakpavel/e8dc69037304901beed93895fd4e7a68 to your computer and use it in GitHub Desktop.
A loading indicator in the style of this years Apple Event animation. Created for the first #SwiftUIWeeklyChallenge
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ContentView.swift | |
// SwiftUIWeeklyChallenge01 | |
// | |
// Created by @myridiphis on 02/10/2020. | |
// | |
// not very tidy, but enjoy changing many of magical constants | |
import SwiftUI | |
func interpolateColor(_ i:Double) -> Color { | |
let colors = [UIColor(named: "c4"), UIColor(named: "c3"), UIColor(named: "c2"), UIColor(named: "c1")] | |
var interpolator: CGFloat = 0.0 | |
var sIndex: Int = 0 | |
if (i<0.33) { | |
interpolator = CGFloat(i)*3 | |
} | |
else if (i<0.66) { | |
interpolator = (CGFloat(i)-0.333)*3 | |
sIndex = 1 | |
} | |
else { | |
interpolator = (CGFloat(i)-0.666)*3 | |
sIndex = 2 | |
} | |
var r1: CGFloat = 0.0 | |
var g1: CGFloat = 0.0 | |
var b1: CGFloat = 0.0 | |
var r2: CGFloat = 0.0 | |
var g2: CGFloat = 0.0 | |
var b2: CGFloat = 0.0 | |
var a: CGFloat = 0.0 | |
colors[sIndex]?.getRed(&r1, green: &g1, blue: &b1, alpha: &a) | |
colors[sIndex+1]?.getRed(&r2, green: &g2, blue: &b2, alpha: &a) | |
let r = r2*interpolator + r1*(1.0-interpolator) | |
let g = g2*interpolator + g1*(1.0-interpolator) | |
let b = b2*interpolator + b1*(1.0-interpolator) | |
return Color(red: Double(r), green: Double(g), blue: Double(b)) | |
} | |
struct LoadingView: View { | |
let circles = 100 | |
let diameter: CGFloat = 100.0 | |
let circleSize: CGFloat = 40.0 | |
let cutOut: CGFloat = 0.8 | |
@State var flipped: Bool = false | |
@State var startAngle: CGFloat = 0 | |
func circlePosition(index: Int)-> CGPoint { | |
/*let baseAngle = CGFloat(index)/CGFloat(circles)*CGFloat.pi*2*cutOut | |
let angle:CGFloat = baseAngle + startAngle | |
let xCoord: CGFloat = diameter + (diameter-circleSize)*cos(angle) | |
let yCoord: CGFloat = flipped ? diameter + (diameter-circleSize)*sin(baseAngle) : diameter - (diameter-circleSize)*sin(baseAngle) | |
*/ | |
let baseAngle = CGFloat(index)/CGFloat(circles)*CGFloat.pi*2*cutOut | |
let flipValue = 2*CGFloat.pi-baseAngle | |
let angle:CGFloat = flipped ? baseAngle + startAngle + flipValue : baseAngle + startAngle | |
let xCoord: CGFloat = diameter + (diameter-circleSize)*cos(angle) | |
let yCoord: CGFloat = diameter + (diameter-circleSize)*sin(angle) | |
return CGPoint(x: xCoord, y: yCoord) | |
} | |
func circleDelay(index: Int)-> Double { | |
//let angle:Double = Double(index)/Double(circles)*Double.pi*2*Double(cutOut) | |
return 5*Double(circles - index)/Double(circles)//(-cos(angle) + 1.0)*0.5 | |
} | |
func animate() { | |
withAnimation { | |
self.flipped = !self.flipped | |
self.startAngle += CGFloat.pi/3 | |
} | |
DispatchQueue.main.asyncAfter(deadline: .now() + 0.8) { | |
self.animate() | |
} | |
} | |
var body: some View { | |
ZStack { | |
ForEach (0 ..< circles) { index in | |
Circle() | |
.fill(interpolateColor(Double(index)/100.0)) | |
.frame(width: circleSize, height: circleSize) | |
.position(self.circlePosition(index: index)) | |
//.modifier(OnPathEffect(offsetValue: self.offset)) | |
.animation(Animation.easeInOut(duration: 1.0).delay(self.circleDelay(index: index))) | |
} | |
} | |
.frame(width: 2*diameter, height: 2*diameter) | |
.onAppear() { | |
self.animate() | |
} | |
} | |
} | |
struct ContentView: View { | |
var body: some View { | |
ZStack { | |
VStack { | |
LoadingView() | |
Text("#SwiftUIWeeklyChallenge") | |
.font(Font.title.italic()) | |
.foregroundColor(Color("c2")) | |
} | |
} | |
.frame(maxWidth: .infinity, maxHeight: .infinity) | |
.background(Color.white) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment