skip to Main Content

New coder here, and I am learning SwiftUI for the first time. I started an iOS coding journey this year after not having coded since 2005, when I last coded a social media website using HTML, CSS, and Cold Fusion7. I have been following tutorials for a while and cannot get past this bug that doesn’t generate any errors. I may have embedded the .toolbar in the wrong area of SwiftUI View. But the toolbar refuses to show at the bottom of the screen. See the two screenshots. One shows the toolbar, but the report title and select button are not at the top of the screen. The other is where I fixed the select/edit button, but the toolbar is missing.

Screenshot With Toolbar

Missing Toolbar

import SwiftUI
import PhotosUI

struct ReportDetailView: View {
    @Binding var report: Report
    @State private var showPhotoLibrary = false
    @State private var showCamera = false
    @State private var selectedImages: [UIImage] = []
    @State private var selectedImage: UIImage?
    @State private var selectedImageIndex: Int?
    @State private var showEditCaption = false
    @State private var showEmailSheet = false
    @State private var isSelecting = false
    @State private var selectedPhotos = Set<Int>()

    var body: some View {
        VStack {
            TextField("Report Title", text: Binding(
                get: { report.title ?? "" },
                set: { report.title = $0 }
            ))
            .font(.largeTitle)
            .padding(.top)
            .background(Color.clear)
            .frame(maxWidth: .infinity, alignment: .leading)
            .textInputAutocapitalization(.words)
            .onTapGesture {
                self.dismissKeyboard()
            }
            .padding(.horizontal)

            if report.photos.isEmpty {
                Text("No Photos")
                    .foregroundColor(.gray)
                    .padding()
                    .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
            } else {
                ScrollView {
                    LazyVGrid(columns: [GridItem(.adaptive(minimum: 100))]) {
                        ForEach(report.photos.indices, id: .self) { index in
                            VStack {
                                if let photo = report.photos[index].image {
                                    Image(uiImage: photo)
                                        .resizable()
                                        .aspectRatio(contentMode: .fit)
                                        .onTapGesture {
                                            if isSelecting {
                                                if selectedPhotos.contains(index) {
                                                    selectedPhotos.remove(index)
                                                } else {
                                                    selectedPhotos.insert(index)
                                                }
                                            } else {
                                                selectedImage = report.photos[index].image
                                                selectedImageIndex = index
                                                showEditCaption = true
                                            }
                                        }
                                        .overlay(
                                            isSelecting ?
                                            Circle()
                                                .stroke(selectedPhotos.contains(index) ? Color.blue : Color.clear, lineWidth: 3)
                                                .frame(width: 30, height: 30)
                                                .padding(5)
                                                .background(Color.white.opacity(0.7).clipShape(Circle()))
                                                .padding()
                                            : nil
                                        )
                                }
                                Text(report.photos[index].caption)
                                    .font(.caption)
                            }
                        }
                    }
                }
            }
        }
        .navigationBarItems(trailing: Button(action: {
            isSelecting.toggle()
            selectedPhotos.removeAll()
        }) {
            Text(isSelecting ? "Cancel" : "Edit")
        })
        .toolbar {
            ToolbarItem(placement: .bottomBar) {
                HStack {
                    if isSelecting {
                        Button(action: deleteSelectedPhotos) {
                            Image(systemName: "trash")
                                .foregroundColor(.red)
                        }
                        Spacer()
                    }
                    
                    Button(action: { showPhotoLibrary = true }) {
                        Image(systemName: "photo.on.rectangle.angled")
                    }
                    Spacer()
                    Button(action: { showCamera = true }) {
                        Image(systemName: "camera")
                    }
                    Spacer()
                    Button(action: { showEmailSheet = true }) {
                        Image(systemName: "envelope")
                    }
                }
            }
        }
        .sheet(isPresented: $showPhotoLibrary) {
            UnifiedPicker(pickerType: .photoLibrary(single: false), selectedImages: $selectedImages)
                .onDisappear {
                    for image in selectedImages {
                        if let imageData = image.jpegData(compressionQuality: 1.0) {
                            report.photos.append(Photo(imageData: imageData, caption: ""))
                        }
                    }
                    selectedImages.removeAll()
                }
        }
        .sheet(isPresented: $showCamera) {
            UnifiedPicker(pickerType: .camera, selectedImages: $selectedImages)
                .onDisappear {
                    if let image = selectedImages.first, let imageData = image.jpegData(compressionQuality: 1.0) {
                        report.photos.append(Photo(imageData: imageData, caption: ""))
                    }
                    selectedImages.removeAll()
                }
        }
        .sheet(isPresented: $showEditCaption) {
            if let selectedIndex = selectedImageIndex {
                EditCaptionView(photo: $report.photos[selectedIndex])
            }
        }
        .sheet(isPresented: $showEmailSheet) {
            if let emailData = emailData {
                MailComposeViewController(mailData: emailData)
            }
        }
    }

    private func deleteSelectedPhotos() {
        for index in selectedPhotos.sorted(by: >) {
            report.photos.remove(at: index)
        }
        selectedPhotos.removeAll()
        isSelecting.toggle()
    }

    private func dismissKeyboard() {
        UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
    }

    private var emailData: Data? {
        return PDFGenerator.generatePDF(for: report)
    }
}

struct ReportDetailView_Previews: PreviewProvider {
    @State static var report = Report(title: "Sample Report", date: Date(), photos: [])
    static var previews: some View {
        ReportDetailView(report: $report)
    }
}

I have tried moving the .toolbar into different areas of the SwiftUI View, but all I have done is successfully crash the app and generate errors. This code is as close as I have been able to get.

2

Answers


  1. Chosen as BEST ANSWER

    I figured it out. I needed to wrap the entire VStack in a NavigationView.


  2. Wrapping your entire VStack in a NavigationStack should also fix it. NavigationView is depreciated as of recent.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search