Swiftui:使用文本字段用户输入重命名文档目录中的音频文件

问题描述

我无法弄清楚如何重命名涉及多个视图的音频文件。我从 Voice Recorder App 找到了大部分代码,并尝试添加重命名功能,该功能接受来自用户点击编辑铅笔时触发的文本字段警报的用户输入。

  1. 在警报保存时,应从警报文本字段 userRecordingName(主 inputRecordingName 视图)更新 Communicator 属性
  2. 应触发警报保存 rename 功能(RecordingsList 视图)
  3. renameRecordings 函数被触发(AudioRecorder 视图)
  4. 如果不为空,则使用新的 userRecordingName 字符串重新加载主视图中的记录行

我知道这很多,但是如果有人知道从文本字段重命名 URL lastPathComponent 并更新列表视图,那将非常有用!!

Main View

数据模型:

class Recording: ObservableObject {
    let fileURL: URL
    let createdAt: Date
    @Published var userRecordingName: String
    
    init(fileURL: URL,createdAt: Date,userRecordingName: String ) {
        self.fileURL = fileURL
        self.createdAt = createdAt
        self.userRecordingName = userRecordingName
    }  
}

主视图(需要 RecNameAlert 帮助):

struct Communicator: View {
    
    @ObservedObject var audioRecorder: AudioRecorder = .shared
    @State var isPresented: Bool = false
    @State var inputRecordingName: String = ""
  
    var body: some View {
            NavigationView {
                ZStack{
                VStack {
                    HStack {
                        EditButton()
                    }
                    RecordingsList(audioRecorder: audioRecorder,isRenameClicked: $isPresented)
                    
                    if self.isPresented {
                    Button(action: {
                        self.isPresented = true
                    }) {
                            Image(systemName: "pencil.circle")
                    }.buttonStyle(BorderlessButtonStyle())
                    }
                    
                    if audioRecorder.recording == false {
                        Button(action: {print(self.audioRecorder.startRecording())}) {
                            Image(systemName: "circle.fill")
                        }
                    } else {
                        Button(action: {self.audioRecorder.stopRecording()}) {
                            Image(systemName: "stop.fill")
                        }
                    }
                } //End VStack
                    .navigationBarTitle("Voice recorder")
            
                    RecNameAlert(title: "Name Recording",isShown: $isPresented,inputRecordingName: $inputRecordingName,onSave: { text in
                        // ??? userRecordingName from Recording Object = text ???
                    })
                    
                    }// End ZStack
            }
        }
    }

录音提醒:

struct RecNameAlert: View {
    
    let screenSize = UIScreen.main.bounds
    
    var title: String = ""
    @Binding var isShown: Bool
    @Binding var inputRecordingName: String
    var onSave: (String) -> Void = { _ in}
    var onCancel: () -> Void = { }
    
    var body: some View {
        
        VStack {
            Text(title)
            TextField("",text: $inputRecordingName)
            .textFieldStyle(RoundedBorderTextFieldStyle())
            HStack{
                Button("Save") {
                    self.isShown = false
                    self.onSave(self.inputRecordingName)
                }
                Button("Cancel") {
                    self.isShown = false
                    self.onCancel()
                }
            }
        } // End VStack
            .padding()
            .frame(width: screenSize.width * 0.7,height: screenSize.height * 0.3)
            .background(Color(.lightGray))
            .clipShape(RoundedRectangle(cornerRadius: 20.0,style: .continuous))
            .offset(y: isShown ? 0 : screenSize.height)
            .animation(.spring())
            .shadow(color: Color(.white),radius: 6,x:-9,y:-9)
    }
}

录音列表和行:

struct RecordingsList: View {
    
   @ObservedObject var audioRecorder: AudioRecorder = .shared
   @ObservedObject var audioPlayer = AudioPlayer()
   @Binding var isRenameClicked: Bool
    
    var body: some View {
            List {
                ForEach(audioRecorder.recordings,id: \.createdAt) { recording in
                    RecordingRow(rowRecording: recording,isRenameClicked: self.$isRenameClicked )
                }// End For Each
                    .onDelete(perform: delete)
            } // End List
        } // End Body View
    
        func delete(at offsets: IndexSet) {
            
            var urlsToDelete = [URL]()
            for index in offsets {
                urlsToDelete.append(audioRecorder.recordings[index].fileURL)
            }
            audioRecorder.deleteRecording(urlsToDelete: urlsToDelete)
        }
        
        // ??? On save in the alert box,I need to run this function ???
        func rename(at offsets: IndexSet) {

            var urlToRename = [URL]()
            for index in offsets {
                urlToRename.append(self.audioRecorder.recordings[index].fileURL)
            }
            audioRecorder.renameRecordings(urlToRename: urlToRename)
        }
    }

struct RecordingRow: View {

    @ObservedObject var audioPlayer = AudioPlayer()
    @ObservedObject var audioRecorder = AudioRecorder()
    @ObservedObject var rowRecording: Recording
    @Binding var isRenameClicked: Bool
    
    var body: some View {
        HStack {
            VStack{
            // If userRecordingName not empty,present rowRecording.userRecodingName text instead of fileURL
            Text(self.rowRecording.fileURL.lastPathComponent)
            }
            Spacer()
            Button(action: {
                self.isRenameClicked = true
            }) {
                    Image(systemName: "pencil.circle")
            }.buttonStyle(BorderlessButtonStyle())
            
        } //End HStack
    }

AudioRecorder 和函数(播放、停止、删除、重命名)

import Foundation
import SwiftUI
import AVFoundation
import Combine

class AudioRecorder: NSObject,ObservableObject {
    
    override init() {
        super.init()
        fetchRecordings()
    }
    let objectWillChange = PassthroughSubject<AudioRecorder,Never>()
    var audioRecorder: AVAudioRecorder!
    static let shared = AudioRecorder()
    @Published var recordings = [Recording]()
    
    var recording = false {
        didSet {
            objectWillChange.send(self)
        }
    }
   //Folded
    func startRecording() { }
    
    func stopRecording() {}
    
    func fetchRecordings() {
        recordings.removeAll()
        
        let fileManager = FileManager.default
        let documentDirectory = fileManager.urls(for: .documentDirectory,in: .userDomainMask)[0]
        let directoryContents = try! fileManager.contentsOfDirectory(at: documentDirectory,includingPropertiesForKeys: nil)
        for audio in directoryContents {
            let recording = Recording(fileURL: audio,createdAt: getCreationDate(for: audio),userRecordingName: audio.lastPathComponent)
            recordings.append(recording)
        }
        recordings.sort(by: { $0.createdAt.compare($1.createdAt) == .orderedAscending})
        
        objectWillChange.send(self)
    }
    
    func deleteRecording(urlsToDelete: [URL]) {
        
        for url in urlsToDelete {
            print(url)
            do {
               try FileManager.default.removeItem(at: url)
            } catch {
                print("File could not be deleted!")
            }
        }
        fetchRecordings()
    }
    // *Need Help Here*
    func renameRecordings(urlToRename: [URL]) {
            
            let fileManager = FileManager.default
            let documentDirectory = fileManager.urls(for: .documentDirectory,in: .userDomainMask)[0]
            
            for url in urlToRename {
                                //*Needs to be text variable from alert box*
                let dstURL = documentDirectory.appendingPathComponent("NewName.m4a")
            do {
                try FileManager.default.moveItem(at: url,to: dstURL)
                try FileManager.default.removeItem(at: url)
            } catch {
                print(error.localizedDescription)
                }
        }
        fetchRecordings()
    }
} //End AudioRecorder

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...