マンガ読書管理アプリの設計メモ(15)

今回は、読んだ漫画を新規登録する画面の実装について記載したいと思います。画像、日付、評価入力のインタフェース周りで参考にできればと思っています。

1.StoryboardでUI部品をポチポチ配置する。

お決まりのUI部品をポチポチ配置。キモなのは評価のところかと思いますが、こちらは「 Cosmos」というライブラリを使って、設定しています。このライブラリを使うだけで、storyboardで表示されている「星」が実装できます。


2.UIImageViewをタップすると、ライブラリから画像を取得

まずライブラリから画像を取得する場合は、info.plistに、画像読み込みするよ、という用途の記述をする必要があります。また、その他画像ライブラリからデータを取得するやり方は以下のサイトを参考にしました。

画像をタップすると、ライブラリが表示するようにしたのですが、タップする時のイベントはこちらを参考にして実装しました。

    // image登録
   @objc func imageViewTapped(_ sender: UITapGestureRecognizer) {
       callPhotoLibrary()
   }
   
   func callPhotoLibrary(){
       if UIImagePickerController.isSourceTypeAvailable(UIImagePickerController.SourceType.photoLibrary) {
           
           let picker = UIImagePickerController()
           picker.modalPresentationStyle = UIModalPresentationStyle.popover
           picker.delegate = self
           picker.sourceType = UIImagePickerController.SourceType.photoLibrary
           picker.allowsEditing = false
           self.present(picker, animated: true, completion: nil)
       }
   }

加えて、ライブラリを取得したのち、画像データを圧縮するために、サイズを小さくするような処置も合わせて施しています。

    func imagePickerController(_ picker: UIImagePickerController,didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
       
       if info[UIImagePickerController.InfoKey.originalImage] != nil {
           let image = info[UIImagePickerController.InfoKey.originalImage] as! UIImage
           let resizedImage = image.resized(withPercentage: 0.1)
           //画像を設定する
           bookImageView.image = resizedImage
       }
       dismiss(animated: true,completion: nil)
   }
extension UIImage {
   func resized(withPercentage percentage: CGFloat) -> UIImage? {
       let canvas = CGSize(width: size.width * percentage, height: size.height * percentage)
       return UIGraphicsImageRenderer(size: canvas, format: imageRendererFormat).image {
           _ in draw(in: CGRect(origin: .zero, size: canvas))
       }
   }
   func resized(toWidth width: CGFloat) -> UIImage? {
       let canvas = CGSize(width: width, height: CGFloat(ceil(width/size.width * size.height)))
       return UIGraphicsImageRenderer(size: canvas, format: imageRendererFormat).image {
           _ in draw(in: CGRect(origin: .zero, size: canvas))
       }
   }
}


3.「読んだ日付」をタップすると、ドラムロールが表示されるようにする。

基本はこのサイトから参考に実装しました。

    func usePickerSetting(){
       // ピッカー設定
       datePicker.datePickerMode = UIDatePicker.Mode.date
       datePicker.locale = Locale(identifier: "ja")
       readedDayTextField.inputView = datePicker
       
       // 決定バーの生成
       let toolbar = UIToolbar(frame: CGRect(x: 0, y: 0, width: view.frame.size.width, height: 35))
       let spacelItem = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: self, action: nil)
       let doneItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(pickerDone))
       doneItem.tintColor = blueThemeColor
       toolbar.setItems([spacelItem, doneItem], animated: true)
       
       // インプットビュー設定
       readedDayTextField.inputView = datePicker
       readedDayTextField.inputAccessoryView = toolbar
       
   }
       
   // 決定ボタン押下
   @objc func pickerDone() {
       readedDayTextField.endEditing(true)
       // 日付のフォーマット
       let formatter = DateFormatter()
       formatter.dateFormat = "yyyy/MM/dd"
       readedDayTextField.text = "\(formatter.string(from: datePicker.date))"
   }


4.ライブラリを使って評価を星で入力

ライブラリの使い方に沿って実装していきます。

https://github.com/evgenyneu/Cosmos

        // StarRatingSetting
       starCosmonView.settings.fillMode = .precise
       starCosmonView.didFinishTouchingCosmos = didFinishTouchingCosmos

100点満点にしたかったので、苦肉の策で、5段階評価に20を乗算して100点にすることにした汗

    // Rating
   private func didFinishTouchingCosmos(_ rating: Double) {
       self.starRating = rating * 20
   }


5.入力したデータを登録

入力してきたデータを登録するのですが、ポイントとなるのは「画像データ」の保存かなと思いますので、そこを補足します。

登録としては、画像データはDB内には保存せず、Documentフォルダに格納し、DBにはファイル名を登録するようにしました。
DB内にフルパスを登録する方法もありますが、確かアップデートなどをすると、フルパスが変わるという話が聞いたこともあり、DBにはファイル名を保存し、画像データをDocumentファイル直下に置くことで、フルパスが変わっても対応可能にしています。

まず以下のサイトを参考にしました。

            // 登録パス取得
           let path = fileInDocumentsDirectory(filename: "\(addBook.key).png")
           addBook.imagePath = "\(addBook.key).png"

            // image登録
            _ = saveImage(image: image, path: path)    
    // DocumentディレクトリのfileURLを取得
   func getDocumentsURL() -> NSURL {
       let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] as NSURL
       return documentsURL
   }

   // ディレクトリのパスにファイル名をつなげてファイルのフルパスを作る
   func fileInDocumentsDirectory(filename: String) -> String {
       let fileURL = getDocumentsURL().appendingPathComponent(filename)
       return fileURL!.path
   }
   
   func saveImage (image: UIImage, path: String ) -> Bool {
       let pngImageData = image.pngData()
       do {
           try pngImageData!.write(to: URL(fileURLWithPath: path), options: .atomic)
       } catch {
           print(error)
           return false
       }
       return true
   }


このあたりの入力方法などは初めてやったので、少し時間がかかりましたが、ユーザーインタフェースとしては今後も必要になりそうな機能なので、勉強になってよかったです。

次は、登録したい漫画をAPIを活用して、データを検索、取得して表示するところをやりたいと思います。

また、今回の部分で、この辺りはもっと詳しく、という話があれば、コメントいただければと思います!

素敵なアプリやサービスが作れるようにひとりで開発を頑張っています。応援してくれると嬉しいです!