Skip to content

Instantly share code, notes, and snippets.

@skhavari
Last active December 6, 2024 19:14
Show Gist options
  • Save skhavari/8217920fd1b4c0007068f98765b9b204 to your computer and use it in GitHub Desktop.
Save skhavari/8217920fd1b4c0007068f98765b9b204 to your computer and use it in GitHub Desktop.
Bare bones camera view with radial dial
//
//
// Created by Sam Khavari on 12/6/24.
//
import SwiftUI
struct Camera: View {
var body: some View {
VStack(spacing: 0) {
//header
HStack {
Color.clear.frame(height: 80)
}
// camera viewfinder
ZStack(alignment: .bottom) {
Image("")
.resizable()
.clipped()
.scaledToFill()
RadialDial()
}
// footer
HStack {
Spacer()
CameraButton()
Spacer()
}.frame(height: 200)
}
.background(.black.gradient)
}
@ViewBuilder
func CameraButton() -> some View {
Circle()
.fill(.white)
.frame(height: 64)
.overlay {
Circle().fill(.clear).stroke(.white, lineWidth: 4).padding(-5)
}
}
}
struct RadialDial: View {
let dialRadius = 210.0
let tickHeight = 30.0
let numTicks = 33
@State private var dialRotation = Angle.degrees(0)
@State private var haptic = false
var body: some View {
ZStack {
// background
Circle()
.fill(.black.opacity(0.8))
.frame(width: dialRadius * 2, height: dialRadius * 2)
// ticks
ForEach(0..<numTicks, id: \.self) { index in
Tick(index)
}
}
.rotationEffect(dialRotation, anchor: .center)
.frame(height: dialRadius / 2, alignment: .top)
.gesture(Drag())
.clipped()
.sensoryFeedback(.alignment, trigger: haptic)
}
func angleForTick(_ index: Int) -> Angle {
let delta = 180 / Double(numTicks - 1)
let degrees = -90 + (delta * Double(index))
return .degrees(degrees)
}
func offsetForTick(_ index: Int, _ angle: Angle) -> CGSize {
let y = (dialRadius - tickHeight / 2) * cos(angle.radians) * -1
let x = (dialRadius - tickHeight / 2) * sin(angle.radians)
return .init(width: x, height: y)
}
@ViewBuilder
func Tick(_ index: Int) -> some View {
let angle = angleForTick(index)
let offset = offsetForTick(index, angle)
ZStack(alignment: .top) {
Rectangle()
.fill(.white.opacity(0.5))
.frame(width: 1, height: tickHeight - 5)
Text("\(index)")
.font(.caption)
.foregroundStyle(.white)
.opacity(index % 4 == 0 ? 1 : 0)
.offset(y: 32)
}
.padding(.top, 5)
.rotationEffect(angle)
.offset(offset)
}
func Drag() -> some Gesture {
DragGesture()
.onChanged { value in
let d = max(min(value.translation.width * 0.25, 90), -90)
dialRotation = .degrees(d)
let delta = 180 / Double(numTicks - 1)
let onBoundry = Int(dialRotation.degrees.rounded()) % Int(delta)
if onBoundry == 0 {
haptic.toggle()
}
}
.onEnded { value in }
}
}
#Preview {
Camera()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment