Last active
December 6, 2024 19:14
-
-
Save skhavari/8217920fd1b4c0007068f98765b9b204 to your computer and use it in GitHub Desktop.
Bare bones camera view with radial dial
This file contains hidden or 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
// | |
// | |
// 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