watari開発 – Advent201921 –

NO IMAGE

stage22: 画像投稿機能

さて、テキスト投稿の機能作成してからしばらく経ってしまったが、次は画像投稿機能だ。順を追ってやっていくことにしよう。

画像の表示、写真の撮影、写真の保存、twitterへの投稿。
こんな流れでやっていこう

画像表示の方針

localにある画像を表示できるようにしよう。そういえばiPhoneのストレージへのアクセスってどこまでできるのかな。あとアプリにだけ画像を保持しているようなサービスとかあるけど、どっちの設計の方がいいんだろう。

https://gist.github.com/y-takagi/9f2cea659fb3f55b56aa04530bf0af39

https://qiita.com/tetsufe/items/9ff5fe190ee190afa1bb

https://stackoverflow.com/questions/29159432/how-to-save-photos-to-storage-within-app-rather-than-ios-camera-roll

などをみると

  • CoreDataに突っ込む
  • Document/に突っ込む
  • フォトライブラリに突っ込む
  • (クラウド上のどこかに入れる)

が打ち手としてはありそうだ。

いったんフォトライブラリから呼び出してみるか。既に自分のローカルにある画像を上げる必要もあるし。フワッとした設計だけど、

フォトライブラリ => 画像読み込み => 投稿 => アプリにデータを保存

かな。こうするとフォトライブラリから画像が削除されたとしてもアプリ内には画像を保持しておくことができる。デメリットはデータ二重になるので圧縮とかしてあげないと肥大化がやばそう。

よし、フォトライブラリから画像を読み込んで表示してみよう。

ページの追加

まず、選んだ画像をpreview的に表示するためのボタンとページを追加しよう。

file

Add New Fileをして、SwiftUI Viewを追加する。名前は ImageView.swift とした。

まずはこの画面がbutton tapの際に表示されるようにしよう。

// ContentView.swift

    var body: some View {
        NavigationView {
            VStack {
                List {
                    ForEach(self.entryTexts, id: \.self) {entryText in
                        VStack {
                            Text(entryText.body!)
                            Text("\(entryText.createdAt!)")
                        }
....

                                                  oauthToken: oauthToken,
                                                  oauthTokenSecret: oauthTokenSecret)
                            swifter.postTweet(status: entryText.body!, success: { _ in
                                print("succeeded.")
                            }, failure: { error in
                                print("failed.")
                                print(error)
                            })
                        }
                    })
                    NavigationLink("add image", destination: ImageView())
                    Button("twitter signin", action: {
                        self.provider.getCredentialWith(nil) { credential, error in
                            if error != nil {
                                // Handle error.
                            }
                            if credential != nil {
                                Auth.auth().signIn(with: credential!) { result, error in

....

NavtionViewとNavigationLinkを追加。遷移先を新しく作ったImageViewにする。

これで新しいViewを表示することができるようになる。

フォトライブラリへのアクセス

https://developer.apple.com/documentation/photokit/requesting_authorization_to_access_photos

info.plistNSPhotoLibraryUsageDescription に使用用途を記載する。

+       <key>NSPhotoLibraryUsageDescription</key>
+       <string>This app uses Photo Library to make content using images you stored.</string>

で、ImageViewを以下のようにする。

import SwiftUI
import Photos

struct ImageView: View {
    @State var image: UIImage?
    @State var showImagePicker: Bool = false

    var body: some View {
        VStack {
            if image != nil {
                Image(uiImage: image!)
                    .resizable()
                    .aspectRatio(contentMode: .fill)
                    .frame(width: 300, height: 300, alignment: .top)
                    .clipped()
            }
            Button("pick up image", action: {
                self.showImagePicker = true
            })
        }.sheet(isPresented: $showImagePicker) {
            ImagePickerController(image: self.$image)
        }
    }
}

struct ImagePickerController: UIViewControllerRepresentable {
    @Binding var image: UIImage?

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    func makeUIViewController(context: Context) -> UIImagePickerController {
        let imagePickerController = UIImagePickerController()
        imagePickerController.sourceType = .photoLibrary
        imagePickerController.delegate = context.coordinator
        return imagePickerController
    }

    func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {

    }

    class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
        var parent: ImagePickerController

        init(_ imagePickerController: ImagePickerController) {
            self.parent = imagePickerController
        }

        func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
            defer {
                picker.dismiss(animated: true)
            }

            guard let image = info[.originalImage] as? UIImage else {
                return
            }

            self.parent.image = image
        }

        func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
            picker.dismiss(animated: true, completion: nil)
        }
    }
}

struct ImageView_Previews: PreviewProvider {
    static var previews: some View {
        ImageView()
    }
}

これで、画像選択して表示するくらいはできそう。

実行

次回は

写真撮影を行えるようにしていこう。