//tips
//Coordinatorクラスの内容理解
以下のCoordinatorクラスの内容理解を進める。
class Coordinator:NSObject,UINavigationControllerDelegate,UIImagePickerControllerDelegate{
var parent:ImagePicker
init(_ parent:ImagePicker){
self.parent = parent
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
let uiImage = info[.originalImage] as! UIImage
UIImageWriteToSavedPhotosAlbum(uiImage, nil, nil, nil)
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
}
}
まずこのクラスのプロパティparentについて確認する。
以下をプロパティ理解の参考にした。
https://tea-leaves.jp/swift/content/%E3%83%97%E3%83%AD%E3%83%91%E3%83%86%E3%82%A3
Var parent:ImagePicker
Parentプロパティは変数と考えておけば良い。
基本的にはvar name: Stringのように変数名と変数のタイプと考えれば良く、ここではImagePicker構造体をいれる入れ物としている。
この背景には、contentviewをアプリの開始時に起動し、ボタンタップによる切り替え時に、ImagePickerの内容を読み込み、
struct ImagePicker: UIViewControllerRepresentable {
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
このようにCoordinatorクラスを生成する。
Coordinatorクラスの中にinitというイニシャライザが見られたので、イニシャライザについて確認する。
イニシャライザは、インスタンス生成時に自動で呼び出されるメソッドであるが、省略されることもあり、記述される場合は、事前にインスタンスの要素を明確にしておきたいなどの背景がある。
例えば、下のツイートインスタンスを見ると
var tweet1: TweetData = TweetData()
tweet1.userId = "_ha1f"
tweet1.content = "まじか"
tweet1.postedDate = NSCalendar(identifier: NSCalendarIdentifierGregorian)!.dateWithEra(1, year: 2016, month: 6, day: 21, hour: 14, minute: 51, second: 20, nanosecond: 0)!
var tweet2: TweetData = TweetData()
tweet2.userId = "_ha1f"
tweet2.content = "コーヒー、粉はいい"
tweet2.postedDate = NSCalendar(identifier: NSCalendarIdentifierGregorian)!.dateWithEra(1, year: 2016, month: 6, day: 21, hour: 10, minute: 14, second: 40, nanosecond: 0)!
のように、TweetData()でインスタンス生成を行なっているが、
init() {
self.userId = nil
self.content = nil
self.postedDate = nil
}
と、見えない内部でイニシャライザが生成されている。
このツイートごとに変数を宣言するやり方が面倒な場合、先にイニシャライザを使用しておき、
class TweetData {
var userId: String!
var content: String!
var postedDate: NSDate!
init(userId: String, content: String, postedDate: NSDate) {
self.userId = userId
self.content = content
self.postedDate = postedDate
}
}
実際の各ツイートでは記載内容を要素を羅列していくコンパクトな形にできる。
var tweet1 = TweetData(userId: "_ha1f", content: "まじか", postedDate: NSCalendar(identifier: NSCalendarIdentifierGregorian)!.dateWithEra(1, year: 2016, month: 6, day: 21, hour: 14, minute: 51, second: 20, nanosecond: 0)!)
ここでイニシャライザを使用した理由は、初期値として参照される画像データImagePicker構造体がないので、自身で作成する必要があったから。自動で生成するための素材がないが画像取得フォルダは開きたいということのよう。
https://tea-leaves.jp/swift/content/%E3%82%A4%E3%83%8B%E3%82%B7%E3%83%A3%E3%83%A9%E3%82%A4%E3%82%B6
https://swift-ios.keicode.com/lang/classes-structs-initizlizer.php
https://qiita.com/_ha1f/items/1378dc6926e29bd01206
写真撮影を完了した後に、撮影画像はdidFinishPickingMediaWithInfo infoのinfoの中に格納され、このinfoから画像を取り出すために、UIImagePickerController.InfoKey.originalImageキーを使用する。
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
let uiImage = info[.originalImage] as! UIImage
UIImageWriteToSavedPhotosAlbum(uiImage, nil, nil, nil)
}
let uiImage = info[.originalImage] でオリジナルの画像を取得。この際にUIImageクラスに変換し、画像としてiOSで保存できるようにし、UIImageWriteToSavedPhotosAlbumで取得画像を写真アルバムへ保存させている。
infoの中身にAnyと書かれているのは、info内には、辞書型のため、値として画像だけでなく、時刻や場所などの文字列データも格納されているので、全ての型に対応できるようにAny型としている。
Any型を利用することで変数にあらゆる種類の型を代入できる。
例えば、
var string:Any = “こんにちは”
string = Image(“card”)
文字列型の変数にimageを代入することも可能になる。一見すごく便利なように見えるが、検証の際に非常に分かりづらくなるので、あまり使われない。
なので、今回のinfoは、最初から様々な型の要素を含んでいるので、仕方なくanyを使っていると言える。
このanyの状態では画像を保存するメソッドをうまく活用できないので、iosの画像として認識されるUIImage型に変更する。
変更は、ダウンキャスティングと呼ばれる、「値 as! 型」という形でなされ、
let uiImage = info[.originalImage] as! UIImage
のように示される。
https://developer.apple.com/documentation/uikit/uiimagepickercontrollerdelegate/1619126-imagepickercontroller
https://developer.apple.com/documentation/uikit/uiimagepickercontroller/infokey
ImagePicker構造体についても同時に見ていく。
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
はUIImagePIckerControllerの調整役となるインスタンスをつくるメソッドで、この調整役がCoordinatorクラス。
関数の理解は下記を見ると分かりやすい。
func increment(number: Int) -> Int {
let answer = number + 1
return answer
}
関数名 incrementはint型の引数numberをとり、関数実行後の戻り値はInt型の{}内での計算式の結果が返される。
これを踏まえると、func makeCoordinator() -> Self.Coordinatorの表現方法も理解しやすい。
https://developer.apple.com/documentation/swiftui/uiviewcontrollerrepresentable/makecoordinator()-32trb
makeUIViewControllerメソッド内も確認すると、
func makeUIViewController(context: Context) -> UIImagePickerController {
let picker = UIImagePickerController()
picker.sourceType = .camera
picker.delegate = context.coordinator
return picker
}
これもfunc makeUIViewController(context: Self.Context) -> Self.UIViewControllerTypeの形式に従って、UIKitフレームワークのUI部品をインスタンス化し、変数pickerに格納している。
https://developer.apple.com/documentation/swiftui/uiviewcontrollerrepresentable/makeuiviewcontroller(context:)
変数pickerの前は変数なのでvarではないのかと考えたが、letは値型と参照型で固定するものが違い、この場合はletでも大丈夫であるよう。
https://qiita.com/koher/items/bcdbf6578b6edd1f9e0c
picker.sourceType = .cameraで画像の取得方法をカメラに指定。
picker.delegate = context.coordinatorでは、delegateで別のクラスに代行させ、別クラスメソッドを利用している。
context型はcoordinatorプロパティをもち、先にmakeCoordinatorで返したインスタンスが格納されている。
そして、delegateの定義には、weak open var delegate: (UIImagePickerControllerDelegate & UINavigationControllerDelegate)?と書かれており、この2つのプロトコルがCoordinatorクラスに宣言されていたことから、Coordinatorクラスはこのdelegateの部分を代行していると理解することができる。
Coordinatorクラスで使用されているのは、UIImagePickerControllerDelegateで、プロトコルに宣言された下記メソッドにより、
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any])
カメラアプリはユーザーが撮影した画像を引数としてiosから受け取ることができるようになる。
ImagePickerで写真を取得したら、代理人であるCoordinatorクラスに渡し、適切な形に写真の処理をして返してもらう、という流れになっている。