이미지를 저장하는 방식은 크게 두 가지가 있다고 볼 수 있다.
첫번째는 사진을 데이터 타입으로 변환해서 Realm에 저장하는 것 인데 Realm 뿐만 아니라 다른 DB에서도 권장하지 않는 방법이다.
두번째는 애플이 관리하고 있는 Document 폴더 내에 저장하는 것 이다. 따라서 이번에는 Realm을 이용하여 원하는 이미지를 Document 폴더 내에 저장하는 과정을 구현해보았다.
현재는 사진을 단 한 장만 저장하기 때문에 테이블에 column을 더 추가하지 않고 유니크한 키를 이용하여 경로를 설정하여 저장하기로 했다.
먼저 사진을 저장하는 과정의 단계가 꽤나 되기 때문에 사진 저장하는 과정을 별도의 함수로 구현한다.
func saveImageToDocumentDirectory(imageName: String, image: UIImage) {
// 1. 이미지를 저장할 경로를 설정해줘야함 - 도큐먼트 폴더,File 관련된건 Filemanager가 관리함(싱글톤 패턴)
guard let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {return}
// 2. 이미지 파일 이름 & 최종 경로 설정
let imageURL = documentDirectory.appendingPathComponent(imageName)
// 3. 이미지 압축(image.pngData())
// 압축할거면 jpegData로~(0~1 사이 값)
guard let data = image.pngData() else {
print("압축이 실패했습니다.")
return
}
// 4. 이미지 저장: 동일한 경로에 이미지를 저장하게 될 경우, 덮어쓰기하는 경우
// 4-1. 이미지 경로 여부 확인
if FileManager.default.fileExists(atPath: imageURL.path) {
// 4-2. 이미지가 존재한다면 기존 경로에 있는 이미지 삭제
do {
try FileManager.default.removeItem(at: imageURL)
print("이미지 삭제 완료")
} catch {
print("이미지를 삭제하지 못했습니다.")
}
}
// 5. 이미지를 도큐먼트에 저장
// 파일을 저장하는 등의 행위는 조심스러워야하기 때문에 do try catch 문을 사용하는 편임
do {
try data.write(to: imageURL)
print("이미지 저장완료")
} catch {
print("이미지를 저장하지 못했습니다.")
}
}
해당 함수를 저장 버튼을 클릭했을 때 실행해주면 되는데 이때 이미지 파일 이름은 해당 row의 PK값으로 설정해준다.
@objc func saveButtonClicked() {
let task = UserDiary(diaryTitle: titleTextField.text!, content: contentTextView.text, writeDate: Date(), regDate: Date())
try! localRealm.write {
localRealm.add(task)
saveImageToDocumentDirectory(imageName: "\(task._id).png", image: diaryImageVIew.image!)
}
dismiss(animated: true, completion: nil)
}
저장이 완료된다면 다음과 같이 Documents 폴더 안에 이미지가 저장되는 것을 확인할 수 있다.
저장되어있는 이미지를 불러오는 과정은 도큐먼트 폴더 경로 구성 ➡️ 이미지 찾기 ➡️ UIImage로 불러오기 ➡️ UIImageView에 구성하기 단계로 나눠서 생각해볼 수 있다.
func loadImageFromDocumentDirectory(imageName: String) -> UIImage? {
// 1. 도큐먼트 폴더 경로가져오기
let documentDirectory = FileManager.SearchPathDirectory.documentDirectory
let userDomainMask = FileManager.SearchPathDomainMask.userDomainMask
let path = NSSearchPathForDirectoriesInDomains(documentDirectory, userDomainMask, true)
if let directoryPath = path.first {
// 2. 이미지 URL 찾기
let imageURL = URL(fileURLWithPath: directoryPath).appendingPathComponent(imageName)
// 3. UIImage로 불러오기
return UIImage(contentsOfFile: imageURL.path)
}
return nil
}
위의 메서드로 UIImage를 받아온 뒤 cellForRowAt 메서드에서 cell내의 UIImageView를 업데이트시켜주면 저장돼있는 이미지를 불러올 수 있다
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: searchTableViewCell.identifier, for: indexPath) as? searchTableViewCell else {
return UITableViewCell()
}
let task = tasks[indexPath.row]
cell.titleLabel.text = task.diaryTitle
cell.contentLabel.text = task.content
cell.diaryImageView.image = loadImageFromDocumentDirectory(imageName: "\(task._id).png")
return cell
}
이미지를 삭제하는 코드는 처음에 이미지를 저장하는 코드와 거의 동일하다.
이미지를 삭제하고자 하는 해당 URL에 이미지가 저장돼있다면 FileManager를 이용하여 삭제하면 된다.
func deleteImageFromDocumentDirectory(imageName: String) {
guard let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {return}
let imageURL = documentDirectory.appendingPathComponent(imageName)
if FileManager.default.fileExists(atPath: imageURL.path) {
do {
try FileManager.default.removeItem(at: imageURL)
print("이미지 삭제 완료")
} catch {
print("이미지를 삭제하지 못했습니다.")
}
}
}