SwiftUI, Firebase : How I can handle the completion come from Login result? - firebase

First, this is the AuthService I made
import Foundation
import FirebaseAuth
class AuthService {
static let instance = AuthService()
var isSignIn : Bool = false
func signUpWithEmail(email : String, password : String, completion : #escaping (_ result : Bool) -> ()) {
Auth.auth().createUser(withEmail: email, password: password) { [weak self] result, error in
if let error = error {
print("error to make account")
completion(false)
}
completion(true)
}
}
}
Next one is the View related with the Auth,
import SwiftUI
struct SignUpView: View {
#Environment(\.presentationMode) var presentationMode
#State private var email : String = ""
#State private var password : String = ""
#State private var firstName : String = ""
#State private var lastName : String = ""
#State private var userName : String = ""
#Binding var showMainView : Bool
var body: some View {
NavigationView {
ScrollView {
VStack(alignment : .leading) {
textFieldStyle(title: "E-mail", bindingText: $email)
textFieldStyle(title: "Password", bindingText: $password)
textFieldStyle(title: "Last name", bindingText: $lastName)
textFieldStyle(title: "First name", bindingText: $firstName)
textFieldStyle(title: "User name", bindingText: $userName)
Button(action: {
AuthService.instance.signUpWithEmail(email: email, password: password) { result in
if result {
presentationMode.wrappedValue.dismiss()
showMainView.toggle()
} else {
print("cannot close the view")
}
}
}, label: {
Text("Sign Up")
.font(.title3.bold())
.foregroundColor(.white)
.frame(maxWidth : .infinity)
.frame(height : 60)
.background(.blue)
.cornerRadius(12)
.shadow(color: .gray.opacity(0.2), radius: 12, x: 1, y: 1)
})
}//vst
.padding()
.navigationTitle("Sign Up ")
.navigationBarTitleDisplayMode(.large)
}
}
}
}
And this is the HomeView,
import SwiftUI
import AuthenticationServices
struct HomeView: View {
#State private var showSignUpSheet : Bool = false
#State private var showMainView : Bool = false
var body: some View {
NavigationView {
VStack(spacing : 40) {
HStack {
VStack(alignment : .leading) {
Text("Welcome to")
.font(.title)
.fontWeight(.bold)
Text("CranMarket")
.font(.largeTitle)
.fontWeight(.heavy)
.foregroundColor(.blue)
}
Spacer()
}
.padding()
Text("This is the application for the students and families who want to sell and buy some second use things! Start it right now!")
.padding()
VStack(spacing : 0) {
appleSignInButton
googleSignInButton
emailSignInButton
HStack {
Spacer()
Button(action: {
showSignUpSheet.toggle()
}, label: {
Text("Don't you have account yet?")
.font(.headline)
})
.fullScreenCover(isPresented: $showMainView) {
MainView()
}
.sheet(isPresented: $showSignUpSheet) {
SignUpView(showMainView: $showMainView)
}
}
.padding(.horizontal)
}
}//vst
}//nav
}
}
And this is the picture of result when I failed to sign up,
If you see the picture, the view is not dismissed and navigationLink is triggered.
But I can't understand why that is triggered because the result of signup is 'false' with completion.
How I can handle this situation? and why is the view not working?

Related

Generic parameter 'Success' could not be inferred; Key path value type '_' cannot be converted to contextual type '_'; Cannot find 'Response' in scope

I think this might be the last push for my project of mobile GitHub query repository search, but I get 3 errors I cannot find out how to cope with.
The code:
import SwiftUI
import Combine
struct Root: Codable {
let items: [Item]
enum CodingKeys: String, CodingKey {
case items
}
}
struct Item: Identifiable, Codable {
let id: Int
let urlCode: String
let fullName: String
enum CodingKeys: String, CodingKey {
case id
case urlCode = "url"
case fullName = "full_name"
}
}
private final class ContentViewState: ObservableObject {
#Published var isLoading = false
#Published var query = ""
#Published var stuff = [String]()
private var subscription: AnyCancellable?
func fetchRepos(query: String) {
isLoading = true
subscription = Just("test")
.delay(for: 2, scheduler: RunLoop.main)
.sink(receiveValue: {[weak self] (title: String) in
self?.isLoading = false
self?.stuff.append(title)
})
}
}
struct ContentView: View {
#StateObject private var state = ContentViewState()
#State private var items = [Item]()
var body: some View {
VStack {
if state.isLoading {
ProgressView()
} else {
HStack {
TextField("Enter search", text: $state.query)
Button("Search") {
state.fetchRepos(query: state.query)
}
}
List(items, id: \.id) { item in
VStack(alignment: .leading) {
Text(item.fullName).font(.headline)
Text(item.urlCode)
}
}.task {
await loadData()
}
}
}
}
func loadData() async {
guard let url = URL(string: "https://api.github.com/search/repositories?q=" + state.query + "&per_page=20") else
{
print("Invalid URL")
return
}
do {
let (data, _) = try await URLSession.shared.data(from: url)
if let decodedResponse = try? JSONDecoder().decode(Root.self, from: data) {
items = decodedResponse.items
}
} catch {
print("Invalid data ")
}
}
}
The errors:
"Generic parameter 'Success' could not be inferred" on line:
TextField("Enter search", text: $state.query)
"Key path value type '' cannot be converted to contextual type ''" on line:
await loadData()
}
}
"Cannot find 'Response' in scope" on line:
} catch {
print("Invalid data ")
}
}
}
Please help :)
The answer was to move the code to another file, change its structure a bit and everything works fine now!

Abrupt transition between views within a Navigation View

I'm having trouble figuring out how to smoothly navigate from my SignInView() to my FirstView(). I have my FirstView() inside of a Navigation Stack, but the transition between the views is very abrupt and devoid of the transition that you normally get with the use of a NavigationLink. How can I get the transition to work?
Much appreciated!
Here is the relevant code...
struct ContentView: View {
#EnvironmentObject var viewModel: AppViewModel
var body: some View {
VStack{
NavigationView {
if viewModel.signedIn {
FirstView()
.transition(.slide)
} else {
//.onAppear method is used for keyboard management (See Misc Functions...)
SignInView()
.onAppear(perform: UIApplication.shared.addTapGestureRecognizer)
.navigationBarHidden(true)
}
}
.onAppear {
viewModel.listen()
}
}
}
}
class AppViewModel: ObservableObject {
private var db = Firestore.firestore()
#Published var userInfo: User?
#Published var signedIn: Bool = false
var handle: AuthStateDidChangeListenerHandle?
let authRef = Auth.auth()
var authHandle : AuthStateDidChangeListenerHandle?
var rootInfoCollection : CollectionReference!
var userIdRef = ""
func fetchUserData(){
db.collection("Users").document("\(userIdRef)").getDocument { document, error in
// Check for error
if error == nil {
// Check that this document exists
if document != nil && document!.exists {
self.userInfo = document.map { (documentSnapshot) -> User in
let data = documentSnapshot.data()
let uid = data?["uid"] as? UUID ?? UUID()
let company = data?["company"] as? String ?? ""
let name = data?["name"] as? String ?? ""
let admin = data?["admin"] as? Bool ?? false
let photo = data?["photo"] as? String ?? ""
return User(uid: uid, company: company, name: name, admin: admin, photo: photo)
}
withAnimation {
self.signedIn = true
}
}
}
}
}
func listen(){
handle = authRef.addStateDidChangeListener({ auth, user in
print(user?.email ?? "No User Found")
if let user = auth.currentUser {
self.userIdRef = user.uid
self.rootInfoCollection = Firestore.firestore().collection("/Users/")
DispatchQueue.main.async {
self.fetchUserData()
}
} else {
self.signedIn = false
}
})
}
func signIn(email: String, password: String){
authRef.signIn(withEmail: email, password: password) { result, error in
guard result != nil, error == nil else {
return
}
}
}
}
struct SignInView: View {
#EnvironmentObject var viewModel: AppViewModel
#State private var username : String = ""
#State private var password : String = ""
#State private var shouldShowLoginAlert: Bool = false
#State var selectedImageArray : [Image] = []
var disableLoginButton : Bool {
return self.username.isEmpty || self.password.isEmpty
}
var body: some View {
VStack{
Image(uiImage: #imageLiteral(resourceName: "awText"))
.resizable()
.frame(width: 180, height: 100)
.padding(.bottom, 50)
TextField("Email", text: $username)
.padding(.leading)
.disableAutocorrection(true)
.autocapitalization(.none)
Rectangle().fill(Color.gray.opacity(0.25)).frame(height: 1, alignment: .center).padding(.bottom)
.padding(.bottom)
.onChange(of: self.username, perform: { value in
if value.count > 10 {
self.username = String(value.prefix(20)) //Max 10 Characters for Username.
}
})
SecureField("Password", text: $password)
.padding(.leading)
.disableAutocorrection(true)
.autocapitalization(.none)
Rectangle().fill(Color.gray.opacity(0.25)).frame(height: 1, alignment: .center)
.onChange(of: self.username, perform: { value in
if value.count > 10 {
self.username = String(value.prefix(10)) //Max 10 Characters for Password.
}
})
//SignIn Button
Button(action: {
viewModel.signIn(email: username, password: password)
}, label: {
Text("Sign In")
.disabled(disableLoginButton)
.frame(width: 300, height: 50)
.background(Color.green)
.clipShape(RoundedRectangle(cornerRadius: 20, style: .continuous))
.padding()
})
}
Replacing the default NavigationView behavior with your own animations isn't necessarily totally straightforward. I'll lay out one possibility, but another would be to use a real NavigationView transition, but just hide the back button once you're on FirstView.
To do the transition yourself, you'll need one root element to NavigationView, an if clause, a transition(.slide) and withAnimation. Here's a simplified version of your code showing just these elements:
class AppViewModel: ObservableObject {
#Published var signedIn = false
}
struct FirstView : View {
var body: some View {
Text("Signed in")
}
}
struct ContentView: View {
#StateObject var viewModel = AppViewModel()
var body: some View {
NavigationView {
VStack {
if viewModel.signedIn {
FirstView()
.transition(.slide)
} else {
Button("Sign me in") {
withAnimation {
viewModel.signedIn = true
}
}
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.navigationBarHidden(true)
}
}
}

Why does the Navigation Bar from the View still remain after Signing Out a User

I'm working on a very simple app to try to wrap my mind around SwiftUI and Firebase. I have a Login View, a SignUp View, and the FirstView that gets presented once the user is logged in.
Once a User is logged in and the SignOut button is tapped, the Navigation Bar that was only visible in the FirstView, all of a sudden appears superimposed on the LoginView as well. That's not supposed to happen and I'm trying to understand why that happens. Perhaps I'm not dismissing the FirstView correctly?
As a side question, I'm also running into an issue where my User Data is presented/Updated on the FirstView after Signing In, but with a noticeable delay between the Text change for the fields within the view.
Any help would be greatly appreciated.
Here is the relevant code...
#main
struct AWSupportLoggerApp: App {
#UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
#StateObject var viewModel = AppViewModel()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(viewModel)
}
}
class AppDelegate:NSObject,UIApplicationDelegate{
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
FirebaseApp.configure()
return true
}
}
}
class AppViewModel: ObservableObject {
private var db = Firestore.firestore()
#Published var userInfo: User?
#Published var signedIn: Bool = false
var handle: AuthStateDidChangeListenerHandle?
let authRef = Auth.auth()
var authHandle : AuthStateDidChangeListenerHandle?
var rootInfoCollection : CollectionReference!
var userIdRef = ""
func fetchUserData(){
db.collection("Users").document("\(userIdRef)").getDocument { document, error in
// Check for error
if error == nil {
// Check that this document exists
if document != nil && document!.exists {
self.userInfo = document.map { (documentSnapshot) -> User in
let data = documentSnapshot.data()
let uid = data?["uid"] as? UUID ?? UUID()
let company = data?["company"] as? String ?? ""
let name = data?["name"] as? String ?? ""
let admin = data?["admin"] as? Bool ?? false
let photo = data?["photo"] as? String ?? ""
return User(uid: uid, company: company, name: name, admin: admin, photo: photo)
}
}
}
}
}
func listen(){
handle = authRef.addStateDidChangeListener({ auth, user in
print(user?.email ?? "No User Found")
if let user = auth.currentUser {
self.userIdRef = user.uid
self.rootInfoCollection = Firestore.firestore().collection("/Users/")
DispatchQueue.main.async {
self.fetchUserData()
}
self.signedIn = true
} else {
self.signedIn = false
}
})
}
func signIn(email: String, password: String){
authRef.signIn(withEmail: email, password: password) { result, error in
guard result != nil, error == nil else {
return
}
}
}
func signOut(){
do {
try authRef.signOut()
} catch {
print(error)
}
}
func signUp(email: String, password: String, company: String, name: String, admin: Bool, photo: String){
authRef.createUser(withEmail: email, password: password) { result, error in
guard result != nil, error == nil else {
return
}
let db = Firestore.firestore()
//Success
db.collection("Users").document("\(result!.user.uid)").setData(["company" : "\(company)", "name" : "\(name)", "admin" : admin, "photo" : "\(photo)", "uid":result!.user.uid]) { error in
if error != nil {
print(error!)
}
}
}
}
func unbind() {
if let handle = handle {
authRef.removeStateDidChangeListener(handle)
}
}
}
struct ContentView: View {
#EnvironmentObject var viewModel: AppViewModel
var body: some View {
NavigationView {
if viewModel.signedIn {
FirstView()
} else {
//.onAppear method is used for keyboard management (See Misc Functions...)
SignInView()
.onAppear(perform: UIApplication.shared.addTapGestureRecognizer)
}
}
.onAppear {
viewModel.listen()
}
}
}
struct FirstView: View {
#EnvironmentObject private var appViewModel: AppViewModel
var body: some View {
VStack{
Spacer()
VStack(spacing: 50){
NavigationLink(destination: Text("Test")){
awButton(content: "Request Support", backColor: Color(#colorLiteral(red: 0, green: 0.723585546, blue: 0.9907287955, alpha: 1)))
.shadow(color: Color.primary.opacity(0.5), radius: 20, x: 0, y: 20)
.rotation3DEffect(Angle(degrees:10), axis: (x: 10.0, y: 0, z: 0))
}
NavigationLink(destination: Text("Test")){
awButton(content: "Request Quote", backColor: Color(#colorLiteral(red: 0.9372549057, green: 0.3490196168, blue: 0.1921568662, alpha: 1)))
.shadow(color: Color.primary.opacity(0.5), radius: 20, x: 0, y: 20)
.rotation3DEffect(Angle(degrees:10), axis: (x: 10.0, y: 0, z: 0))
}
NavigationLink(destination: Text("Test")){
awButton(content: "Ticket Status", backColor: Color(#colorLiteral(red: 0.4666666687, green: 0.7647058964, blue: 0.2666666806, alpha: 1)))
.shadow(color: Color.primary.opacity(0.5), radius: 20, x: 0, y: 20)
.rotation3DEffect(Angle(degrees:10), axis: (x: 10.0, y: 0, z: 0))
}
}
Spacer()
}
.navigationBarBackButtonHidden(true)
.navigationTitle(appViewModel.userInfo?.company ?? "Test")
.navigationBarItems(leading: Button(action: {
appViewModel.signOut()
}) {
HStack {
Text("Sign Out")
}
},trailing: HStack{
Image(systemName: "bell")
Text(appViewModel.userInfo?.name ?? "Tester")
.font(.system(size: 20))
})
}
}
Try to hide it explicitly, like
if viewModel.signedIn {
FirstView()
.navigationBarHidden(true) // << here !!
} else {
Hiding the navigationBar will cause lots of problems if you need it in other views .the better answer in my opinion is to
1 - create a splash view and set it as your starting view in #main file
2 - add a StateObject for tracking user loginState :
class LoginState: ObservableObject {
#Published var isLogin : Bool = false
}
3 - add navigationView to splash view
4 - pass loginState , StateObject as .environmentObject(loginState) to navigationView
5- now you can change login state when ever you want from anywhere without need of hiding the navigation bar

Slight delay/lag when fetching Firestore User data to update View

I'm currently building a simple app with a SignIn/SignUp View in order to better understand how to integrate SwiftUI with Firebase.
I've run into a bit of an issue when I try to update User Data. I noticed that after Signing Out as one User and then Signing In as a different User, there is a split second delay before the User information is updated on the FirstView after the Sign In button is tapped. I'm trying to figure out how to avoid this delay from happening.
As a side, I'm also having trouble figuring out how to smoothly navigate from my LoginView() to my FirstView(). I have my FirstView() inside of a Navigation Stack, but the transition between the views is very abrupt and devoid of the NavigationLink animation. How can I correct this issue?
Much appreciated!
Here is the relevant code...
#main
struct AWSupportLoggerApp: App {
#UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
#StateObject var viewModel = AppViewModel()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(viewModel)
}
}
class AppDelegate:NSObject,UIApplicationDelegate{
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
FirebaseApp.configure()
return true
}
}
}
struct ContentView: View {
#EnvironmentObject var viewModel: AppViewModel
var body: some View {
NavigationView {
if viewModel.signedIn {
FirstView()
} else {
//.onAppear method is used for keyboard management (See Misc Functions...)
SignInView()
.onAppear(perform: UIApplication.shared.addTapGestureRecognizer)
.navigationBarHidden(true)
}
}
.onAppear {
viewModel.listen()
}
}
}
class AppViewModel: ObservableObject {
private var db = Firestore.firestore()
#Published var userInfo: User?
#Published var signedIn: Bool = false
var handle: AuthStateDidChangeListenerHandle?
let authRef = Auth.auth()
var authHandle : AuthStateDidChangeListenerHandle?
var rootInfoCollection : CollectionReference!
var userIdRef = ""
func fetchUserData(){
db.collection("Users").document("\(userIdRef)").getDocument { document, error in
// Check for error
if error == nil {
// Check that this document exists
if document != nil && document!.exists {
self.userInfo = document.map { (documentSnapshot) -> User in
let data = documentSnapshot.data()
let uid = data?["uid"] as? UUID ?? UUID()
let company = data?["company"] as? String ?? ""
let name = data?["name"] as? String ?? ""
let admin = data?["admin"] as? Bool ?? false
let photo = data?["photo"] as? String ?? ""
return User(uid: uid, company: company, name: name, admin: admin, photo: photo)
}
}
}
}
}
func listen(){
handle = authRef.addStateDidChangeListener({ auth, user in
print(user?.email ?? "No User Found")
if let user = auth.currentUser {
self.userIdRef = user.uid
self.rootInfoCollection = Firestore.firestore().collection("/Users/")
DispatchQueue.main.async {
self.fetchUserData()
}
self.signedIn = true
} else {
self.signedIn = false
}
})
}
func signIn(email: String, password: String){
authRef.signIn(withEmail: email, password: password) { result, error in
guard result != nil, error == nil else {
return
}
}
}
func signOut(){
do {
try authRef.signOut()
} catch {
print(error)
}
}
func signUp(email: String, password: String, company: String, name: String, admin: Bool, photo: String){
authRef.createUser(withEmail: email, password: password) { result, error in
guard result != nil, error == nil else {
return
}
let db = Firestore.firestore()
//Success
db.collection("Users").document("\(result!.user.uid)").setData(["company" : "\(company)", "name" : "\(name)", "admin" : admin, "photo" : "\(photo)", "uid":result!.user.uid]) { error in
if error != nil {
print(error!)
}
}
}
}
func unbind() {
if let handle = handle {
authRef.removeStateDidChangeListener(handle)
}
}
}
struct FirstView: View {
#EnvironmentObject private var appViewModel: AppViewModel
var body: some View {
VStack{
Spacer()
VStack(spacing: 50){
NavigationLink(destination: Text("Test")){
awButton(content: "Request Support", backColor: Color(#colorLiteral(red: 0, green: 0.723585546, blue: 0.9907287955, alpha: 1)))
.shadow(color: Color.primary.opacity(0.5), radius: 20, x: 0, y: 20)
.rotation3DEffect(Angle(degrees:10), axis: (x: 10.0, y: 0, z: 0))
}
NavigationLink(destination: Text("Test")){
awButton(content: "Request Quote", backColor: Color(#colorLiteral(red: 0.9372549057, green: 0.3490196168, blue: 0.1921568662, alpha: 1)))
.shadow(color: Color.primary.opacity(0.5), radius: 20, x: 0, y: 20)
.rotation3DEffect(Angle(degrees:10), axis: (x: 10.0, y: 0, z: 0))
}
NavigationLink(destination: Text("Test")){
awButton(content: "Ticket Status", backColor: Color(#colorLiteral(red: 0.4666666687, green: 0.7647058964, blue: 0.2666666806, alpha: 1)))
.shadow(color: Color.primary.opacity(0.5), radius: 20, x: 0, y: 20)
.rotation3DEffect(Angle(degrees:10), axis: (x: 10.0, y: 0, z: 0))
}
}
Spacer()
}
.navigationBarBackButtonHidden(true)
.navigationTitle(appViewModel.userInfo?.company ?? "Test")
.navigationBarItems(leading: Button(action: {
appViewModel.signOut()
}) {
HStack {
Text("Sign Out")
}
},trailing: HStack{
Image(systemName: "bell")
.font(.system(size: 30))
// selectedImageArray.first!
// .resizable()
// .scaledToFit()
// .clipShape(Circle())
// .frame(width: 50, height: 50)
Text(appViewModel.userInfo?.name ?? "Tester")
.font(.system(size: 20))
})
}
}
Your navigation state depends on signedIn. In your auth listener, you do this:
DispatchQueue.main.async {
self.fetchUserData()
}
self.signedIn = true
This will set signedIn to true, which will change your navigation state and then at an indeterminate time in the future, fetchUserData will finish and update the user data (once the network call has completed).
To avoid this delay (or, really, to just avoid seeing the information not update on the screen -- the delay is an inevitability of the network call), don't set signedIn until fetchUserData completes. So, remove the line inside the listener and instead, set it after your self.userInfo = line.

swiftUI - Button Camera in TabBar

There is a button in the TabBar, this button should open the camera
"Button(action: {...}, label: { ..."
The camera code is written in "CameraView" and "CameraModel" :
struct CameraView: View {
#StateObject var camera = CameraModel()
var body: some View {
ZStack{
// Camera preview...
CameraPreview(camera: camera)
.ignoresSafeArea(.all, edges: .all)
VStack{
if camera.isTaken{
HStack {
Spacer()
Button(action: {}, label: {
Image(systemName: "arrow.triangle.2.circlepath.camera")
.foregroundColor(.black)
.padding()
.background(Color.white)
.clipShape(Circle())
})
.padding(.trailing,10)
}
}
Spacer()
HStack{
if camera.isTaken{
Button(action: {}, label: {
Text("Save")
.foregroundColor(.black)
.fontWeight(.semibold)
.padding(.vertical,10)
.padding(.horizontal,20)
.background(Color.white)
.clipShape(Capsule())
})
.padding(.leading)
Spacer()
}
else{
Button(action: {camera.isTaken.toggle()}, label: {
ZStack{
Circle()
.fill(Color.white)
.frame(width: 65, height: 65)
Circle()
.stroke(Color.white,lineWidth: 2)
.frame(width: 75, height: 75)
}
})
}
}
.frame(height: 75)
}
}
.onAppear(perform: {
camera.Check()
})
}
}
class CameraModel: ObservableObject{
#Published var isTaken = false
#Published var session = AVCaptureSession()
#Published var alert = false
// since were going to read pic data....
#Published var output = AVCapturePhotoOutput()
// preview....
#Published var preview : AVCaptureVideoPreviewLayer!
func Check(){
// first checking camerahas got permission...
switch AVCaptureDevice.authorizationStatus(for: .video) {
case .authorized:
setUp()
return
// Setting Up Session
case .notDetermined:
// retusting for permission....
AVCaptureDevice.requestAccess(for: .video) { (status) in
if status{
self.setUp()
}
}
case .denied:
self.alert.toggle()
return
default:
return
}
}
func setUp(){
// setting up camera...
do{
// setting configs...
self.session.beginConfiguration()
// change for your own...
let device = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back)
let input = try AVCaptureDeviceInput(device: device!)
// checking and adding to session...
if self.session.canAddInput(input){
self.session.addInput(input)
}
// same for output....
if self.session.canAddOutput(self.output){
self.session.addOutput(self.output)
}
self.session.commitConfiguration()
}
catch{
print(error.localizedDescription)
}
}
}
// setting view for preview...
struct CameraPreview: UIViewRepresentable {
#ObservedObject var camera : CameraModel
func makeUIView(context: Context) -> UIView {
let view = UIView(frame: UIScreen.main.bounds)
camera.preview = AVCaptureVideoPreviewLayer(session: camera.session)
camera.preview.frame = view.frame
// Your Own Properties...
camera.preview.videoGravity = .resizeAspectFill
view.layer.addSublayer(camera.preview)
// starting session
camera.session.startRunning()
return view
}
func updateUIView(_ uiView: UIView, context: Context) {
}
}
How to call the camera on click Button in TabBar "Button(action: {}, label: {
)"?
This is just an example of how I did it in the past (after all permissions etc... have been done).
Use your code to do the same.
struct ContentView: View {
#State var image: UIImage?
#State private var showCamera = false
var body: some View {
VStack {
Button(action: { self.showCamera.toggle() }) {
Image(systemName: "camera.circle").resizable().frame(width: 100, height: 100)
}
if image != nil {
Image(uiImage: image!).resizable().frame(width: 200, height: 200)
}
} // this is where it happens
.sheet(isPresented: $showCamera, onDismiss: {self.showCamera = false}) {
CameraViewController(photo: $image)
}
}
}
struct CameraViewController: UIViewControllerRepresentable {
#Environment(\.presentationMode) var presentationMode
#Binding var photo: UIImage?
func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {
}
func makeUIViewController(context: UIViewControllerRepresentableContext<CameraViewController>) -> UIImagePickerController {
let vc = UIImagePickerController()
vc.sourceType = .camera
vc.delegate = context.coordinator
return vc
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate, AVCapturePhotoCaptureDelegate {
var parent: CameraViewController
var captureSession: AVCaptureSession!
var capturePhotoOutput: AVCapturePhotoOutput!
var theCamera: AVCaptureDevice!
var videoPreviewLayer: AVCaptureVideoPreviewLayer?
let photoQualityPrioritizationMode = AVCapturePhotoOutput.QualityPrioritization.speed
init(_ imagePickerController: CameraViewController) {
self.parent = imagePickerController
}
// called when a picture has been taken
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info:[UIImagePickerController.InfoKey : Any]) {
guard let image = info[.originalImage] as? UIImage else {
print("No image found")
return
}
parent.photo = image // <--- the photo image
parent.presentationMode.wrappedValue.dismiss()
}
}
}

Resources