Skip to content

Instantly share code, notes, and snippets.

@CodeSlicing
Last active August 14, 2021 21:59
Show Gist options
  • Save CodeSlicing/e76b46b001b3cddc43e713884e7fb808 to your computer and use it in GitHub Desktop.
Save CodeSlicing/e76b46b001b3cddc43e713884e7fb808 to your computer and use it in GitHub Desktop.
Source code for CodeSlicing episode on creating the new playing equaliser bars overlay - Part 1
//
// NowPlayingEqualiserBars.swift
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// Created by Adam Fordyce on 08/08/2021.
// Copyright © 2021 Adam Fordyce. All rights reserved.
//
import SwiftUI
import PureSwiftUI
private let numBars = 5
private let spacerWidthRatio: CGFloat = 0.2
private let barWidthScaleFactor = 1 / (numBars.asCGFloat + (numBars - 1).asCGFloat * spacerWidthRatio)
struct NowPlayingEqualiserBars: View {
@State private var animating = false
var body: some View {
GeometryReader { (geo: GeometryProxy) in
let barWidth = geo.widthScaled(barWidthScaleFactor)
let spacerWidth = barWidth * spacerWidthRatio
HStack(spacing: spacerWidth) {
ForEach(0..<numBars) { index in
Bar(minHeightFraction: 0.1, maxHeightFraction: 1, completion: animating ? 1 : 0)
.fillColor(.red)
.width(barWidth)
.animation(createAnimation())
}
}
}
.onAppear {
animating = true
}
}
private func createAnimation() -> Animation {
Animation
.easeInOut(duration: 0.8 + Double.random(in: -0.3...0.3))
.repeatForever(autoreverses: true)
.delay(Double.random(in: 0...0.75))
}
}
private struct Bar: Shape {
private let minHeightFraction: CGFloat
private let maxHeightFraction: CGFloat
var animatableData: CGFloat
init(minHeightFraction: CGFloat, maxHeightFraction: CGFloat, completion: CGFloat) {
self.minHeightFraction = minHeightFraction
self.maxHeightFraction = maxHeightFraction
self.animatableData = completion
}
func path(in rect: CGRect) -> Path {
var path = Path()
let heightFraction = minHeightFraction.to(maxHeightFraction, animatableData)
path.rect(rect.scaled(.size(1, heightFraction), at: rect.bottom, anchor: .bottom))
return path
}
}
struct NowPlayingEqualiserBars_Previews: PreviewProvider {
struct NowPlayingEqualiserBars_Harness: View {
var body: some View {
NowPlayingEqualiserBars()
.frame(50)
.greedyFrame()
.ignoresSafeArea()
}
}
static var previews: some View {
NowPlayingEqualiserBars_Harness()
.previewDevice(.iPhone_12_Pro_Max)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment