Skip to content

Instantly share code, notes, and snippets.

@CodeSlicing
Created August 14, 2021 21:59
Show Gist options
  • Save CodeSlicing/99d8a34a60450d1443f64b3b26c09121 to your computer and use it in GitHub Desktop.
Save CodeSlicing/99d8a34a60450d1443f64b3b26c09121 to your computer and use it in GitHub Desktop.
Source code for CodeSlicing episode on creating the new playing equaliser bars overlay - Part 2
//
// NowPlayingEqualiserBarsUsageDemo.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.
//
// Copyright © 2021 Adam Fordyce. All rights reserved.
//
import SwiftUI
import PureSwiftUI
struct NowPlayingEqualiserBarsUsageDemo: View {
@State private var nowPlayingIndex = -1
var body: some View {
let numItems = 20
let opacityDeltaPerItem = 1 / numItems.asDouble
LazyVGrid(columns: [GridItem(.adaptive(minimum: 80))]) {
ForEach(0..<numItems) { index in
let isNowPlaying = index == nowPlayingIndex
Button {
withAnimation(.easeInOut) {
if isNowPlaying {
nowPlayingIndex = -1
} else {
nowPlayingIndex = index
}
}
} label: {
ZStack(alignment: .bottomLeading) {
Rectangle()
.fill(Color.blue.opacity(opacityDeltaPerItem + opacityDeltaPerItem * index.asDouble))
.height(120)
if isNowPlaying {
NowPlayingEqualiserBars()
.frame(20)
.padding()
.shadow(1)
.zIndex(1)
.transition(AnyTransition.scale.combined(with: .opacity))
}
}
}
}
}
}
}
private struct NowPlayingEqualiserBars: View {
var numBars = 5
var spacerWidthRatio: CGFloat = 0.2
private var barWidthScaleFactor: CGFloat {
1 / (numBars.asCGFloat + (numBars - 1).asCGFloat * spacerWidthRatio)
}
@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(.white)
.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 NowPlayingEqualiserBarsUsageDemo_Previews: PreviewProvider {
struct NowPlayingEqualiserBarsUsageDemo_Harness: View {
var body: some View {
NowPlayingEqualiserBarsUsageDemo()
.greedyFrame()
.foregroundColor(.white)
.backgroundColor(.white(0.1))
.ignoresSafeArea()
}
}
static var previews: some View {
NowPlayingEqualiserBarsUsageDemo_Harness()
.previewDevice(.iPhone_12_Pro_Max)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment