Ch.15 Silver: Removing an Image

# 이미지 삭제 버튼

  • 아이템의 이미지를 삭제할 수 있는 버튼 추가하기

  • 버튼이라고 하기는 했는데 버튼 버전도 하고 이미지를 꾹 눌렀을 때 삭제 옵션이 뜨는 방식으로도 했다...


# 삭제 버튼

  • 스토리보드에서 버튼을 하나 추가하고 deleteImage(_:) action method 에서 imageStore.deleteImage(forKey:) 메서드를 호출해서 이미지를 삭제했다. 이렇게 하면 삭제는 되는데 뷰는 바로 업데이트 되지 않고 사진이 계속 남아있어서 imageView.image = nil 로 설정해서 사라지도록 했다.
class DetailViewController: UIViewController, UITextFieldDelegate, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
    ...
    @IBOutlet var imageDeleteButton: UIButton!
    
    @IBAction func deleteImage(_ sender: UIButton) {
        imageStore.deleteImage(forKey: item.itemKey)
        imageView.image = nil
        imageDeleteButton.isEnabled = false
    }
}

  • 이미지가 있을 때만 버튼을 활성화하고 싶어서

         1) 뷰가 나타날 때
         2) 이미지를 선택했을 때
         3) 이미지를 삭제할 때

    각각 버튼의 상태를 상황에 맞게 업데이트하도록 했다.

class DetailViewController: UIViewController, UITextFieldDelegate, UINavigationControllerDelegate, UIImagePickerControllerDelegate {

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        
        nameField.text = item.name
        serialNumberField.text = item.serialNumber
        valueField.text = numberFormatter.string(from: NSNumber(value: item.valueInDollars))
        dateLabel.text = dateFormatter.string(from: item.dateCreated)
        
        let key = item.itemKey

        let imageToDisplay = imageStore.image(forKey: key)
        imageView.image = imageToDisplay
        
        if imageToDisplay == nil {
            imageDeleteButton.isEnabled = false  // 버튼 비활성화
        }
    }
    
        func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        
        let image = info[.originalImage] as! UIImage
        imageStore.setImage(image, forKey: item.itemKey)
        imageView.image = image
        
        imageDeleteButton.isEnabled = true  // 버튼 활성화
        
        dismiss(animated: true)
    }

    @IBAction func deleteImage(_ sender: UIButton) {
        imageStore.deleteImage(forKey: item.itemKey)
        imageView.image = nil
        imageDeleteButton.isEnabled = false  // 버튼 비활성화
    }
}

# 이미지를 꾹 누르면 삭제 버튼 뜨게 하기

  • 해볼까 말까 고민했지만...연습 겸 해봤다..

  • 일단 스토리보드에서 UIImageView 에 Long Press Gesture Recognizer 를 추가한 다음, UIImageView 는 유저 이벤트에 반응하지 않는 게 디폴트이므로 isUserInteractionEnabled 프로퍼티를 true 로 바꿔줘서 이미지를 꾹 눌렀을 때 반응할 수 있도록 했다.

class DetailViewController: UIViewController, UITextFieldDelegate, UINavigationControllerDelegate, UIImagePickerControllerDelegate {

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        
        nameField.text = item.name
        serialNumberField.text = item.serialNumber
        valueField.text = numberFormatter.string(from: NSNumber(value: item.valueInDollars))
        dateLabel.text = dateFormatter.string(from: item.dateCreated)
        imageView.isUserInteractionEnabled = true  // 유저 이벤트 허용!
        
        let key = item.itemKey
        
        let imageToDisplay = imageStore.image(forKey: key)
        imageView.image = imageToDisplay
    }
}

  • 그 다음에 imageLongPressed action method 를 추가하고, 이미지가 있는 경우에만 presentDeleteImageActionSheet() 메서드를 호출하도록 했다.

  • presentDeleteImageActionSheet() 에서는 UIAlertController 를 커스텀해서 삭제 버튼을 띄울 수 있도록 했고, 삭제 버튼을 누르는 경우에 삭제 작업은 아까와 마찬가지로 imageStore.deleteImage(forKey:) 메서드를 호출하고 imageView.image = nil 로 설정해줬다.

class DetailViewController: UIViewController, UITextFieldDelegate, UINavigationControllerDelegate, UIImagePickerControllerDelegate {

    @IBAction func imageLongPressed(_ sender: UILongPressGestureRecognizer) {
        if imageView.image != nil {
            presentDeleteImageActionSheet()
        }
    }
    
    private func presentDeleteImageActionSheet() {
        let alertController = UIAlertController(title: nil,
                                                message: nil,
                                                preferredStyle: .actionSheet)
        alertController.modalPresentationStyle = .popover
        alertController.popoverPresentationController?.sourceView = imageView
        alertController.popoverPresentationController?.sourceRect = imageView.frame
        
        let deleteAction = UIAlertAction(title: "Delete Image",
                                         style: .destructive) { _ in
            self.imageView.image = nil
            self.imageStore.deleteImage(forKey: self.item.itemKey)
        }
        alertController.addAction(deleteAction)
        
        let cancelAction = UIAlertAction(title: "Cancel",
                                         style: .cancel,
                                         handler: nil)
        alertController.addAction(cancelAction)
        
        present(alertController, animated: true, completion: nil)
    }
}

  • 아이패드에서 popover anchor 를 이미지에 딱 맞게 설정하고 싶었는데 해보다가 잘 안돼서 일단 포기... 하려고 했는데 내가 공식 문서를 제대로 안봐서 생긴 문제였다. popover 의 anchor 를 설정할 때는

         1) barButtonItem 프로퍼티를 설정
         2) sourceView 프로퍼티 그리고 sourceRect 프로퍼티를 설정

    하는 두 가지 방법이 있는데 나는 sourceView 만 설정하고 sourceRect 는 설정하지 않아서 popover 의 위치가 이상해진 거였다.... alertController.popoverPresentationController?.sourceRect = imageView.frame 를 추가해줬더니 위치가 잘 조정됐다!

    • sourceRect 에 super view 에서의 imageView 의 위치와 크기를 넘기는 게 맞다고 생각해서 frame 으로 넘겼는데, 궁금해서 bounds 로도 해봤더니 위치가 다시 이상하게 나왔다...frame 이 맞는 접근인 거 같다!

좋은 웹페이지 즐겨찾기