質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.47%
Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

解決済

2回答

637閲覧

delegateでdissmissが機能しない

ttah

総合スコア35

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

2クリップ

投稿2020/03/07 08:26

編集2020/03/07 09:19

親viewからdelegateを使って子viewでdissmissを実行したいのですが上手く行きません
現在、子viewに処理を任せる所まではlogで出来ているのを確認しました

swift

1 2import UIKit 3import RSKImageCropper 4 5class UserDataInputViewController: UIViewController, UIImagePickerControllerDelegate { 6 7 8 var delegate: RSKImageCropViewControllerDelegate? = nil 9 10 @IBAction func call_PhotoLibrary(_ sender: Any) { 11 if UIImagePickerController.isSourceTypeAvailable(.photoLibrary){ 12 let pickerView = UIImagePickerController() 13 pickerView.sourceType = .photoLibrary 14 pickerView.delegate = self 15 self.present(pickerView, animated: true) 16 } 17 } 18 19 20 func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { 21 22 let image = info[.originalImage] as! UIImage // 選択した写真を取得する 23 self.dismiss(animated: true) 24 25 let imageCropVC = RSKImageCropViewController(image: image, cropMode: .circle) 26 imageCropVC.moveAndScaleLabel.text = "切り取り範囲を選択" 27 imageCropVC.cancelButton.setTitle("キャンセル", for: .normal) 28 imageCropVC.chooseButton.setTitle("完了", for: .normal) 29 present(imageCropVC, animated: true) 30 31 32 let buttonset = Buttonset() 33 imageCropVC.delegate = buttonset 34 self.delegate = buttonset 35 } 36}

子view

swift

1import UIKit 2import RSKImageCropper 3 4 5class Buttonset: UIViewController, UIImagePickerControllerDelegate, RSKImageCropViewControllerDelegate { 6 7 8 func imageCropViewController(_ controller: RSKImageCropViewController, didCropImage croppedImage: UIImage, usingCropRect cropRect: CGRect, rotationAngle: CGFloat) { 9 10 11 print("完了を押した後の処理") ←logでprintされている 12 13 dismiss(animated: true)   ← これが機能してない?(view閉じない) 14 } 15} 16 17

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2020/03/07 08:41

ぱっと見で全体像を把握できないのでなんとなく引っかかったとこだけ、 let Buttonset = buttonset() のところで、新しくインスタンスを作って代入してるだけに見えます 処理をさせたいインスタンス?(dissmissだから画面上に表示されてるのかな?)を捕まえてdelegateを設定してあげたらどうなるでしょうか? それと let Buttonset = buttonset() → let buttnSet = ButtonSet() class buttonSet → class ButtonSet 大文字小文字に意味がありますので使い分けたほうが読む人が読みやすくまります。
hoshi-takanori

2020/03/07 09:26

buttonset を UIViewController のサブクラスにしていますが、実際には ViewController として正しく扱われてないために dismiss できないのだと思います。delegate の使い方もおかしいですし…。
ttah

2020/03/07 09:32

回答有難うございます > 処理をさせたいインスタンス?(dissmissだから画面上に表示されてるのかな?) これは現在表示されているview(RSKImageCropViewController)という理解でいいでしょか? > 捕まえて 捕まえる。というのがどうするか分からなかったのですが let imageCropVC = RSKImageCropViewController() という事ですか? > delegateを設定 すみませんdelegateに関する知識不足でdelegateを設定。が何処にどの様に記述したら良いか分からないのですが... ButtonSetクラスに let imageCropVC = RSKImageCropViewController() imageCropVC.delegate = self とは違いますよね(試しに記述しても変わらなかった)
ttah

2020/03/07 09:51

hoshi-takanori様 回答有難うございます >実際には ViewController として正しく扱われてないために サブクラスを buttonset:UIViewController → buttonset:UserDataInputViewController に変えてみましたがダメでし正しくUIViewControllerとして扱われる為にどう変更したら良いでしょか? > delegateの使い方もおかしい しっかりdelegateについて学んだのが本日初めてでこの記述で唯logにprintされ他ので処理して欲しい所にアクセスは出来ているのかなと勘違いしこれで質問してしまいました 不自然な記述は正しくはどの様に記述すればよかったでしょか?
hoshi-takanori

2020/03/07 09:51

UserDataInputViewController 自体を RSKImageCropViewController の delegate にすべきで、ButtonSet はたぶん不要なんじゃないかと思います。あと、UserDataInputViewController の var delegate も不要です。
ttah

2020/03/07 09:56

oshi-takanori様 >serDataInputViewController 自体を RSKImageCropViewController の delegate にすべき これは以前やっていて問題なく出来ていました 今回他のクラスでもButtonSet に記述した処理をしていて重複記述していたので勉強のかねて汎用的にしたいと思いButtonSetを用意しました
hoshi-takanori

2020/03/07 09:58

汎用的というのは、いろんな画面から RSKImageCropViewController を呼び出せるように、ということですか?
ttah

2020/03/07 10:17

>hoshi-takanori様 RSKImageCropViewControllerを呼び出した後の処理(写真切り抜き)やRSKImageCropViewControllerをdismissさせる処理を汎用的にしたいと考えています 最終的にはUIImagePickerControllerの呼び出しやRSKImageCropViewControllerの呼び出し等もしたいですが、いっぺんいいろいろやるには自分のスキルでは難しそうなのでとりあえず、呼び出した後の処理のみをやってみようと始めました。
hoshi-takanori

2020/03/07 10:43

なるほど。ってか、RSKImageCropViewController は切り抜く処理自体はやってくれないんですね…。 とりあえず、切り抜く処理そのものは関数が一つあればいいだけだし、dismiss に関しては表示した ViewController が dismiss するのが原則なので、dismiss だけを汎用化するのはあまり意味ないと思います。delegate の説明も兼ねて、RSKImageCropViewController を呼び出して処理する流れを回答に書きますよ。
ttah

2020/03/07 11:44 編集

>hoshi-takanori様 有難うございます 本当はdissmissの後にボタンに選択した画像のセットや切り抜きの処理がありますが 今回はdissmissする事が目的だったので、割愛させて頂きました。 (dissmissだけを汎用化のつもりではなかったです。) RSKImageCropViewController の呼び出し`present(imageCropVC, animated: true)`や完了、キャンセルボタンのセットまでは出来ていてあとはdelegateでdissmissさせる所が出来ていない状態です
TsukubaDepot

2020/03/07 12:00

https://teratail.com/questions/245452 上の質問で RSKimageCropView のことを知って自分でもちょっと使ってみました。 こいつも処理には delegate の設定が必要なのですが、まずはその整理からやった方がいいかもしれません。 諸々の設定をやったあと、 func imageCropViewControllerDidCancelCrop(_ controller: RSKImageCropViewController) にキャンセル時の処理を、 func imageCropViewController(_ controller: RSKImageCropViewController, didCropImage croppedImage: UIImage, usingCropRect cropRect: CGRect, rotationAngle: CGFloat) にOkを押した時の処理を書けばいいのですが、その辺の理解はいかがでしょうか。 質問者さん: イメージピッカーで画像を選択し、それをRSKimageCropViewで範囲選択し、内容に応じてクロップし、UIImage に貼り付けるサンプルは実はできていますが、それをみてみますか?
guest

回答2

0

質問者さんはちょっと誤解しているのかもしれません。

一応UIImagePickerControllerRSKImageCropViewControllerもそれなりに汎用化されていて、それぞれのクラスが指定しているプロトコルを準拠し、delegateとして指定すべきメソッドを自分で記述すれば(つまり、そのメソッド内で具体的な処理を自分で追加すれば)普通は十分に使えるように考えられています。

なので、自分で新たな delegate を作らなくても大丈夫です。

たとえば、UIImagePickerControllerであれば、インスタンスを作り、諸々の設定などを行って、present()UIImagePickerControllerのインスタンスを指定して実行すれば、それで画像を選択するための画面が表示されます。

その後、RSKImageCropViewControllerのOkボタンを押せばimagePickerControllerDidCancel(_)が呼び出されますし、CancelであればimageCropViewControllerDidCancelCrop(_)が呼び出されます。

この時、注意しなければいけないのは、Calcelボタンを押しても、Okボタンを押しても、選択画面は自動では消えない、ということです。

じゃあどうやって消すのかというと、プログラムを書く人が「適切だ」と思ったタイミングで dismiss(animated:completion:)を呼びことで表示させた画面(ダイアログ)を消す、という流れになっています。

整理すると、

  1. UIImagePickerController のインスタンスを作る
  2. 諸々の設定(カメラや画像ライブラリのうち、どれから画像を選択するのかなど)をする
  3. delegate として呼び出されるクラスを設定する
  4. present(_:animated:completion:)を呼び出す -> 画像選択のダイアログが開く
  5. [Ok]が押されたら、imagePickerControllerDidCancel(_)が呼びだされるので、必要な処理を記述する。必要であればdismiss(animated:completion:)で画像選択ダイアログを消す。
  6. [Cancel]が押されたらimageCropViewControllerDidCancelCrop(_)が呼び出されるので、必要な処理を記述する。必要であればdismiss(animated:completion:)で画像選択ダイアログを消す。

という流れです。
RSkImageCropperControllerもほぼ同じ流れです。

昨日、質問者さん立てた別質問で詳解していた RSKImageCropper が面白そうだったので、自分でもちょっとしたサンプルを作ってたところです。

自分の記録も含めて逐一コメントを入れていますので、参考にして流れを追ってみてください。

StoryBoard で適切な大きさの UimageView と UIButton を配置して、適切に下記のコードと接続すれば、一応動くと思います。

必要に応じてprint()を入れたり、present()dismiss()をコメントアウトするなどして実験してみると、理解が促進するかもしれません。

Swift

1import UIKit 2import RSKImageCropper 3 4// UIImagePickerを使うためには、準拠するプロトコルとして UIImagePickerControllerDelegate, UINavigationControllerDelegate の両方が必要 5// RSKImageCropper を使うには、準拠するプロトコルとして RSKImageCropViewControllerDelegate が必要 6class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate, RSKImageCropViewControllerDelegate { 7 // UIImagePicker で選択し、RSImageCropper でクロップした画像を表示する UIImageView 8 @IBOutlet weak var imageView: UIImageView! 9 10 override func viewDidLoad() { 11 super.viewDidLoad() 12 // Do any additional setup after loading the view. 13 } 14 15 // UIImagePicker を起動するためのボタン 16 @IBAction func buttonClicked(_ sender: Any) { 17 // イメージピッカーのインスタンスを作る 18 let pickerController = UIImagePickerController() 19 20 // PhotoLibraryから画像を選択 21 pickerController.sourceType = UIImagePickerController.SourceType.photoLibrary 22 23 // デリゲートを設定する 24 // imagePickerで選択した画像の処理を行うクラスは自分自身なので self とする 25 pickerController.delegate = self 26 27 // ピッカーを表示する 28 // **ここの present はイメージピッカーの表示(一つ目の引数がイメージピッカーのインスタンスになっているから) 29 present(pickerController, animated: true, completion: nil) 30 } 31 32 // UIImagepicker の delegate 33 // 画像が選択されたら呼び出される 34 func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { 35 // モーダルビュー(つまり、イメージピッカー)を閉じる 36 // 正確には現在表示されているモーダルビューを閉じるが、その時表示されているのがイメージピッカーならそれを閉じる 37 dismiss(animated: true, completion: nil) 38 39 // 辞書型の配列 info[] から指定された画像を取り出す(正確にはオプショナルバインディングを行い、info[] がnilか否かを判断) 40 if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage { 41 //ボタンの背景に選択した画像を設定 42 imageView.image = image 43 44 // RS Image Cropper の処理 45 // RSKImageCropViewController のインスタンスを作成 46 let imageCropVC = RSKImageCropViewController(image: image, cropMode: .circle) 47 imageCropVC.moveAndScaleLabel.text = "切り取り範囲を選択" 48 imageCropVC.cancelButton.setTitle("キャンセル", for: .normal) 49 imageCropVC.chooseButton.setTitle("完了", for: .normal) 50 // RSImageCropperのデリゲートを設定する 51 // 選択した画像の処理を行うクラスは自分自身なので self とする 52 imageCropVC.delegate = self 53 54 // **RSImageCropperの**表示 55 present(imageCropVC, animated: true) 56 } else { 57 print("Error") 58 } 59 } 60 61 // UIImagepicker の delegate 62 //画像選択がキャンセルされた時に呼ばれる. 63 func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { 64 // モーダルビュー(つまり、イメージピッカー)を閉じる 65 dismiss(animated: true, completion: nil) 66 } 67 68 // RS Image Cropper の delegate 69 // キャンセルを押した時 70 func imageCropViewControllerDidCancelCrop(_ controller: RSKImageCropViewController) { 71 // RSImageCropper の View (ダイアログ)を閉じる 72 dismiss(animated: true, completion: nil) 73 } 74 75 // RS Image Cropper の delegate 76 // 完了 77 // crop 処理は Qiita を参照(https://qiita.com/KikurageChan/items/ffac3678101ecfe3de27) 78 func imageCropViewController(_ controller: RSKImageCropViewController, didCropImage croppedImage: UIImage, usingCropRect cropRect: CGRect, rotationAngle: CGFloat) { 79 // RSImageCropper の View (ダイアログ)を閉じる 80 dismiss(animated: true) 81 82 // 円形に crop したときには、画像を円形に加工する 83 if controller.cropMode == .circle { 84 UIGraphicsBeginImageContext(croppedImage.size) 85 86 let layerView = UIImageView(image: croppedImage) 87 88 layerView.frame.size = croppedImage.size 89 layerView.layer.cornerRadius = layerView.frame.size.width * 0.5 90 layerView.clipsToBounds = true 91 92 let context = UIGraphicsGetCurrentContext()! 93 94 layerView.layer.render(in: context) 95 96 let capturedImage = UIGraphicsGetImageFromCurrentImageContext()! 97 98 UIGraphicsEndImageContext() 99 100 // ImageView に切り取った」画像を表示 101 imageView.image = capturedImage 102 } else { 103 // ImageView にオリジナルの画像を表示 104 imageView.image = croppedImage 105 } 106 } 107}

投稿2020/03/07 12:39

編集2020/03/07 12:43
TsukubaDepot

総合スコア5086

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ttah

2020/03/08 00:11

>TsuKubaDepot様 回答有難うございました。回答の様に普通にUivewControllerniに書けば実装は出来ていたので ベストアンサーには出来なくてすみません 今回全く同じ処理をするViewControllerがもう一つあってこれだけの量の同じコードを2箇所に書くことはよろしくないのかな?(fatviewcontroller)になっている? と思い勉強もかねてdelegateで汎用化したかったのですが思う様に行かず質問させて頂きました。 TsuKubaDepot様の回答にはコード1つ1つが何をしているか丁寧に書かれているので、ライブラリ導入時にこれを拝見出来たら相当嬉しかったと思います。 今後私の様な初心者がこれを見たら大変力になる回答だと思いますし、回答の仕方が大変上手いと感じました
TsukubaDepot

2020/03/08 01:02

いえいえ、ベストアンサーはベストの人がもらうべきなので、謝る必要はないですよ。 delegateは理論でわかっても、実際使う場面に合わないとピンときませんよね。 私は復習を兼ねてやっただけですし、「さらなる汎用化」という意味では勉強になったのでよかったです。
guest

0

ベストアンサー

RSKImageCropViewController を使って結果を得るまでを汎用化する方法について書きます。

まず、UIViewController のサブクラスを作るべきではありません。UIViewController は OS とのやり取りを行う重要な役割があって、必要もないのに作っても仕方がないからです。(一応、画面の一部を管理する SubViewContoller というものもありますが、きちんと作法に従って作る必要があります。)

次に、delegate を理解する必要があります。その前に、普通の関数呼び出しでは、呼び出しされた関数 sub が return しない限り、main に制御は戻ってきません。つまり、sub の計算に時間がかかる場合、sub の計算中は main は何もできません。

swift

1func main() { 2 let result = sub(x: 100) 3 print(result) 4} 5 6func sub(x: Int) -> Int { 7 var result = 0 8 for i in 1...x { result += i } 9 return result 10}

単なる計算ならこれでもいいのですが、GUI の場合、例えば RSKImageCropViewController を表示して切り取る範囲を選択するまで返ってこない関数というものを作ることはできません。GUI の処理を行うためには、いったん OS に制御を返さないといけないからです。

swift

1 let imageCropVC = RSKImageCropViewController(image: image, cropMode: .circle) 2 // 略 3 present(imageCropVC, animated: true) // present 自体はすぐに return するが、結果はまだ得られていない

ここで出てくるのが delegate で、結果が得られた時にこのオブジェクトの特定のメソッドを呼んでね、とあらかじめ渡しておくものです。通常は UIViewController が delegate になることが多いですが、たまに別のオブジェクトに delegate をやらせたい場合があるので、その方法を書きます。

まず、delegate のプロトコルを定義します。プロトコルというのはオブジェクト(に限らないけど)が持つべきメソッドの宣言だけを書いたもので、メソッドの中身は各クラスが自由に定義することができます。
なお、delegate 用のプロトコルは NSObject プロトコルを継承するのが一般的です。

swift

1protocol ButtonSetDelegate: NSObject { 2 func buttonSetDidCropImage(image: UIImage) 3}

次に ButtonSet クラス本体です。ちなみに ButtonSetDelegate と ButtonSet は同じファイル (ButtonSet.swift) に書くのが普通です。(ButtonSet という名前も微妙な気がするけど、まぁいいや。)

swift

1class ButtonSet: NSObject, RSKImageCropViewControllerDelegate { 2 3 weak var delegate: (UIViewController & ButtonSetDelegate)? 4 5 func present(image: UIImage) { 6 let imageCropVC = RSKImageCropViewController(image: image, cropMode: .circle) 7 imageCropVC.moveAndScaleLabel.text = "切り取り範囲を選択" 8 imageCropVC.cancelButton.setTitle("キャンセル", for: .normal) 9 imageCropVC.chooseButton.setTitle("完了", for: .normal) 10 imageCropVC.delegate = self 11 delegate?.present(imageCropVC, animated: true) 12 } 13 14 func imageCropViewController(_ controller: RSKImageCropViewController, didCropImage croppedImage: UIImage, usingCropRect cropRect: CGRect, rotationAngle: CGFloat) { 15 delegate?.dismiss(animated: true) 16 17 // ここで画像の切り出しを行う 18 delegate?.buttonSetDidCropImage(image: croppedImage) 19 } 20 21 func imageCropViewControllerDidCancelCrop(_ controller: RSKImageCropViewController) { 22 delegate?.dismiss(animated: true) 23 } 24}

ButtonSet は NSObject のサブクラスで、RSKImageCropViewControllerDelegate プロトコルを実装したクラスになります。(NSObject という同じ名前のクラスとプロトコルがあって、ButtonSetDelegate プロトコルは NSObject プロトコルを継承して、ButtonSet クラスは NSObject クラスを継承しています。ややこしい…。)

また、ButtonSet のプロパティとして UIViewController のサブクラスかつ ButtonSetDelegate プロトコルを継承した delegate を持たせています。(present や dismiss のために UIViewController のサブクラスである必要があります。)また、循環参照になるので delegate は weak にする必要があります。

最後に ButtonSet を使う側の ViewController です。buttonSet をローカル変数にすると didFinishPickingMediaWithInfo から抜けた時に buttonSet への強参照がなくなってオブジェクトが削除されるので、weak でないプロパティにする必要があります。

swift

1class UserDataInputViewController: UIViewController, UIImagePickerControllerDelegate, ButtonSetDelegate { 2 3 var buttonSet: ButtonSet? 4 5 // 略 6 7 func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { 8 self.dismiss(animated: true) 9 10 buttonSet = ButtonSet() 11 buttonSet?.delegate = self 12 buttonSet?.present(image: info[.originalImage] as! UIImage) 13 } 14 15 func buttonSetDidCropImage(image: UIImage) { 16 imageView.image = image 17 } 18}

まとめると、UserDataInputViewController が ButtonSet の delegate になり、ButtonSet が RSKImageCropViewController の delegate になります。

present present UserDataInputViewController -------> ButtonSet -------> RSKImageCropViewController <------- <------- delegate delegate

書いてみたら思ってた以上に説明が長くなってしまいました…。

投稿2020/03/07 11:53

編集2020/03/07 12:00
hoshi-takanori

総合スコア7895

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ttah

2020/03/08 00:17 編集

大変詳しい回答有難うございました。 全部は理解しきれてませんがdelegateに関する知識が深まりました。 NSobjectにする所やGUIの部分も知らずに使っていたので大変勉強になりました。 それとやり方だけでなくコードも書いて下さっていたので、そこも大変ありがたかったです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.47%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問