commit 8a2e87718b2c28209726ab8de5c2dfcd0f29f4c3
parent 88b3c6fe8d70624f6484044ad7fd5e1e672cdec4
Author: Suhail Saqan <suhail.saqan@gmail.com>
Date: Tue, 19 Sep 2023 13:35:52 -0700
camera: add CameraPreview for displaying the view the camera is reading
Diffstat:
2 files changed, 107 insertions(+), 0 deletions(-)
diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj
@@ -383,6 +383,7 @@
501F8C822A0224EB001AFC1D /* KeychainStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 501F8C812A0224EB001AFC1D /* KeychainStorageTests.swift */; };
504323A72A34915F006AE6DC /* RelayModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504323A62A34915F006AE6DC /* RelayModel.swift */; };
504323A92A3495B6006AE6DC /* RelayModelCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504323A82A3495B6006AE6DC /* RelayModelCache.swift */; };
+ BA3759972ABCCF360018D73B /* CameraPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA3759962ABCCF360018D73B /* CameraPreview.swift */; };
5053ACA72A56DF3B00851AE3 /* DeveloperSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5053ACA62A56DF3B00851AE3 /* DeveloperSettingsView.swift */; };
50A16FFB2AA6C06600DFEC1F /* AVPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A16FFA2AA6C06600DFEC1F /* AVPlayerView.swift */; };
50A16FFD2AA7525700DFEC1F /* DamusVideoPlayerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A16FFC2AA7525700DFEC1F /* DamusVideoPlayerViewModel.swift */; };
@@ -940,6 +941,7 @@
4CA352A92A76BF3A003BB08B /* LocalNotificationNotify.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalNotificationNotify.swift; sourceTree = "<group>"; };
4CA352AB2A76C07F003BB08B /* NewUnmutesNotify.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewUnmutesNotify.swift; sourceTree = "<group>"; };
4CA352AD2A76C1AC003BB08B /* FollowedNotify.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowedNotify.swift; sourceTree = "<group>"; };
+ BA3759962ABCCF360018D73B /* CameraPreview.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CameraPreview.swift; sourceTree = "<group>"; };
4CA3FA0F29F593D000FDB3C3 /* ZapTypePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZapTypePicker.swift; sourceTree = "<group>"; };
4CA5588229F33F5B00DC6A45 /* StringCodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringCodable.swift; sourceTree = "<group>"; };
4CA9275C2A28FF630098A105 /* LongformView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LongformView.swift; sourceTree = "<group>"; };
@@ -1623,6 +1625,7 @@
4C75EFA227FA576C0006080F /* Views */ = {
isa = PBXGroup;
children = (
+ BA3759952ABCCF360018D73B /* Camera */,
F71694E82A66221E001F4053 /* Onboarding */,
4C190F232A547D1700027FD5 /* NostrScript */,
4C7D09692A0AEA0400943473 /* CodeScanner */,
@@ -2268,6 +2271,14 @@
path = Camera;
sourceTree = "<group>";
};
+ BA3759952ABCCF360018D73B /* Camera */ = {
+ isa = PBXGroup;
+ children = (
+ BA3759962ABCCF360018D73B /* CameraPreview.swift */,
+ );
+ path = Camera;
+ sourceTree = "<group>";
+ };
F71694E82A66221E001F4053 /* Onboarding */ = {
isa = PBXGroup;
children = (
@@ -2610,6 +2621,7 @@
4C32B9582A9AD44700DC3548 /* VeriferOptions.swift in Sources */,
4CA2EFA0280E37AC0044ACD8 /* TimelineView.swift in Sources */,
4C30AC7629A5770900E2BD5A /* NotificationItemView.swift in Sources */,
+ BA3759972ABCCF360018D73B /* CameraPreview.swift in Sources */,
4C86F7C42A76C44C00EC0817 /* ZappingNotify.swift in Sources */,
4C363A8428233689006E126D /* Parser.swift in Sources */,
3AAA95CA298DF87B00F3D526 /* TranslationService.swift in Sources */,
diff --git a/damus/Views/Camera/CameraPreview.swift b/damus/Views/Camera/CameraPreview.swift
@@ -0,0 +1,95 @@
+//
+// CameraPreview.swift
+// damus
+//
+// Created by Suhail Saqan on 8/5/23.
+//
+
+import UIKit
+import AVFoundation
+import SwiftUI
+
+public struct CameraPreview: UIViewRepresentable {
+ public class VideoPreviewView: UIView {
+ public override class var layerClass: AnyClass {
+ AVCaptureVideoPreviewLayer.self
+ }
+
+ var videoPreviewLayer: AVCaptureVideoPreviewLayer {
+ return layer as! AVCaptureVideoPreviewLayer
+ }
+
+ let focusView: UIView = {
+ let focusView = UIView(frame: CGRect(x: 0, y: 0, width: 30, height: 30))
+ focusView.layer.borderColor = UIColor.white.cgColor
+ focusView.layer.borderWidth = 1.5
+ focusView.layer.cornerRadius = 15
+ focusView.layer.opacity = 0
+ focusView.backgroundColor = .clear
+ return focusView
+ }()
+
+ @objc func focusAndExposeTap(gestureRecognizer: UITapGestureRecognizer) {
+ let layerPoint = gestureRecognizer.location(in: gestureRecognizer.view)
+
+ guard layerPoint.x >= 0 && layerPoint.x <= bounds.width &&
+ layerPoint.y >= 0 && layerPoint.y <= bounds.height else {
+ return
+ }
+
+ let devicePoint = videoPreviewLayer.captureDevicePointConverted(fromLayerPoint: layerPoint)
+
+ self.focusView.layer.frame = CGRect(origin: layerPoint, size: CGSize(width: 30, height: 30))
+
+ NotificationCenter.default.post(.init(name: .init("UserDidRequestNewFocusPoint"), object: nil, userInfo: ["devicePoint": devicePoint] as [AnyHashable: Any]))
+
+ UIView.animate(withDuration: 0.3, animations: {
+ self.focusView.layer.opacity = 1
+ }) { (completed) in
+ if completed {
+ UIView.animate(withDuration: 0.3) {
+ self.focusView.layer.opacity = 0
+ }
+ }
+ }
+ }
+
+ public override func layoutSubviews() {
+ super.layoutSubviews()
+
+ videoPreviewLayer.videoGravity = .resizeAspectFill
+
+ self.layer.addSublayer(focusView.layer)
+
+ let gRecognizer = UITapGestureRecognizer(target: self, action: #selector(VideoPreviewView.focusAndExposeTap(gestureRecognizer:)))
+ self.addGestureRecognizer(gRecognizer)
+ }
+ }
+
+ public let session: AVCaptureSession
+
+ public init(session: AVCaptureSession) {
+ self.session = session
+ }
+
+ public func makeUIView(context: Context) -> VideoPreviewView {
+ let viewFinder = VideoPreviewView()
+ viewFinder.backgroundColor = .black
+ viewFinder.videoPreviewLayer.cornerRadius = 20
+ viewFinder.videoPreviewLayer.session = session
+ viewFinder.videoPreviewLayer.connection?.videoOrientation = .portrait
+
+ return viewFinder
+ }
+
+ public func updateUIView(_ uiView: VideoPreviewView, context: Context) {
+
+ }
+}
+
+struct CameraPreview_Previews: PreviewProvider {
+ static var previews: some View {
+ CameraPreview(session: AVCaptureSession())
+ .frame(height: 300)
+ }
+}