Skip to content

Instantly share code, notes, and snippets.

@CodeSlicing
Last active August 14, 2021 21:59
Show Gist options
  • Save CodeSlicing/f951d7b7fe312567ac2e2180aa0e2eed to your computer and use it in GitHub Desktop.
Save CodeSlicing/f951d7b7fe312567ac2e2180aa0e2eed to your computer and use it in GitHub Desktop.
Native source code for CodeSlicing episode on creating the new playing equaliser bars overlay - part 1
//
// NowPlayingEqualiserBarsNative.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
private let numBars = 5
private let spacerWidthRatio: CGFloat = 0.2
private let barWidthScaleFactor = 1 / ( CGFloat(numBars) + CGFloat(numBars - 1) * spacerWidthRatio)
struct NowPlayingEqualiserBarsNative: View {
@State private var animating = false
var body: some View {
GeometryReader { (geo: GeometryProxy) in
let barWidth = geo.size.width * barWidthScaleFactor
let spacerWidth = barWidth * spacerWidthRatio
HStack(spacing: spacerWidth) {
ForEach(0..<numBars) { index in
Bar(minHeightFraction: 0.1, maxHeightFraction: 1, completion: animating ? 1 : 0)
.fill(Color.red)
.frame(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 heightFractionDelta = maxHeightFraction - minHeightFraction
let heightFraction = minHeightFraction + heightFractionDelta * animatableData
let rectHeight = rect.height * heightFraction
let rectOrigin = CGPoint(x: rect.minX, y: rect.maxY - rectHeight)
let rectSize = CGSize(width: rect.width, height: rectHeight)
let barRect = CGRect(origin: rectOrigin, size: rectSize)
path.addRect(barRect)
return path
}
}
struct NowPlayingEqualiserBarsNative_Previews: PreviewProvider {
struct NowPlayingEqualiserBarsNative_Harness: View {
var body: some View {
NowPlayingEqualiserBarsNative()
.frame(width: 50, height: 50)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.ignoresSafeArea()
}
}
static var previews: some View {
NowPlayingEqualiserBarsNative_Harness()
.previewDevice("iPhone 12 Pro Max")
.previewDisplayName("iPhone 12 Pro Max")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment