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

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

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

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

Q&A

0回答

841閲覧

UIImageViewのキャッシュをリリースしたい

shom

総合スコア0

Swift

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

0グッド

1クリップ

投稿2020/05/25 06:48

前提・実現したいこと

初めて質問させていただきます。

Swift5でカメラアプリを作っています。
複数枚の写真を連続で撮影した後に、各画像を確認できるような以下の機能を開発しています。
・撮影した写真をUIImageの配列に格納(デバイスには保存しない)
・スライドバーを移動し、任意の写真をプレビュー表示

機能自体は実現しているのですが、以下の問題が発生しております。

発生している問題・エラーメッセージ

撮影した写真をプレビュー表示すればするほど、使用中のメモリが増大し、アプリが落ちる。
私の環境下(iPhone7)では、10枚程度で300MB程になります。

おそらく、UIImageViewを生成する度に、どこかに画像のキャッシュが蓄積されているものと考えます。

なお、写真を撮影した後に、プレビュー表示しない場合は上記のメモリ増大は起こりません。
あくまで、UIImageの配列に格納された写真をプレビューする度に、メモリが大きく使用されます。

該当のソースコード

Swift5

1 2// 3// ViewController.swift 4// 5 6import UIKit 7import AVFoundation 8 9class ViewController: UIViewController { 10 11 @IBOutlet weak var previewView: UIView! 12 13 @IBOutlet weak var shutterButton: UIButton! 14 @IBAction func touchUpShutterButton(_ sender: Any) { 15 // キャプチャのセッティング 16 let captureSetting = AVCapturePhotoSettings() 17 captureSetting.flashMode = .off 18 captureSetting.isAutoStillImageStabilizationEnabled = true 19 captureSetting.isHighResolutionPhotoEnabled = false 20 21 // キャプチャのイメージ処理はデリゲートに任せる 22 photoOutputObj.capturePhoto(with: captureSetting, delegate: self) 23 } 24 25 @IBOutlet weak var checkPhotoSlider: UISlider! 26 @IBAction func touchDownCheckPhotoSlider(_ sender: UISlider) { 27 if (checkPhotoView != nil) { 28 checkPhotoView!.image = nil 29 checkPhotoView!.layer.sublayers = nil 30 checkPhotoView = nil 31 } 32 for imageView in self.previewView.subviews { 33 imageView.removeFromSuperview() 34 } 35 } 36 @IBAction func touchUpCheckPhotoSlider(_ sender: UISlider) { 37 checkPhotoView = UIImageView(image: images[Int(sender.value)]) 38 previewView.addSubview(checkPhotoView!) 39 } 40 41 var images: [UIImage] = [] 42 var checkPhotoView: UIImageView? 43 44 // インスタンスの作成 45 var session = AVCaptureSession() 46 var photoOutputObj = AVCapturePhotoOutput() 47 48 // 入出力の設定 49 func setupInputOutput(){ 50 //解像度の指定 51 session.sessionPreset = AVCaptureSession.Preset.photo 52 53 // 入力の設定 54 do { 55 //デバイスの取得 56 let device = AVCaptureDevice.default( 57 AVCaptureDevice.DeviceType.builtInWideAngleCamera, 58 for: AVMediaType.video, // ビデオ入力 59 position: AVCaptureDevice.Position.back) // バックカメラ 60 // 入力元 61 let input = try AVCaptureDeviceInput(device: device!) 62 if session.canAddInput(input){ 63 session.addInput(input) 64 } else { 65 print("セッションに入力を追加できなかった") 66 return 67 } 68 } catch let error as NSError { 69 print("カメラが使えない \n (error.description)") 70 // カメラのプライバシー設定を開くためのアラートを表示する 71 showAlert(appName: "カメラ") 72 return 73 } 74 75 // 出力の設定 76 if session.canAddOutput(photoOutputObj) { 77 session.addOutput(photoOutputObj) 78 } else { 79 print("セッションに出力を追加できなかった") 80 return 81 } 82 } 83 84 // プライバシー認証のアラートを表示する 85 func showAlert(appName:String){ 86 let aTitle = appName + "のプライバシー認証" 87 let aMessage = "設定>プライバシー>" + appName + "で利用を許可してください。" 88 let alert = UIAlertController(title: aTitle, message: aMessage, preferredStyle: .alert) 89 // 許可しないボタン(シャッターボタンを利用できなくする) 90 alert.addAction( 91 UIAlertAction(title: "許可しない",style: .default, 92 handler: { action in 93 self.shutterButton.isEnabled = false 94 }) 95 ) 96 // 設定を開くボタン 97 alert.addAction( 98 UIAlertAction( 99 title: "設定を開く",style: .default, 100 handler: { action in 101 UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!, options: [:], completionHandler: nil) 102 }) 103 ) 104 // アラートを表示する 105 self.present(alert, animated: false, completion:nil) 106 } 107 108 // プレビューレイヤの設定 109 func setPreviewLayer(){ 110 // プレビューレイヤを作る 111 let previewLayer = AVCaptureVideoPreviewLayer(session: session) 112 previewLayer.frame = view.bounds 113 previewLayer.masksToBounds = true 114 previewLayer.videoGravity = AVLayerVideoGravity.resizeAspect 115 // previewViewに追加する 116 previewView.layer.addSublayer(previewLayer) 117 } 118 119 override func viewDidLoad() { 120 super.viewDidLoad() 121 // Do any additional setup after loading the view. 122 123 // セッション実行中ならば中断する 124 guard !session.isRunning else { 125 return 126 } 127 // シャッターボタンを有効にする 128 shutterButton.isEnabled = true 129 // 入出力の設定 130 setupInputOutput() 131 // プレビューレイヤの設定 132 setPreviewLayer() 133 // セッション開始 134 session.startRunning() 135 } 136} 137

Swift5

1// 2// ExtensionViewController.swift 3// 4 5import UIKit 6import Photos 7 8extension ViewController: AVCapturePhotoCaptureDelegate { 9 10 // 映像をキャプチャする 11 func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) { 12 // Dataを取り出す 13 guard let photoData = photo.fileDataRepresentation() else { 14 return 15 } 16 // Dataから写真イメージを作る 17 if let stillImage = UIImage(data: photoData) { 18 images.append(stillImage) 19 checkPhotoSlider.minimumValue = 0 20 checkPhotoSlider.maximumValue = Float(images.count - 1) 21 } 22 } 23} 24

試したこと

UIImage(named:)のキャッシュ問題については認識済みです。

UIImageの配列に格納した時点ではメモリ使用の異常な増大は起きていないため、UIImageViewの生成過程に原因(内部的にどこかでUIImage(named:)と似たような処理をしている?)があるのではないかと考えています。

そこで、提示のコードのように、スライドバーをタッチダウンする度に、既存のUIImageViewの持つ画像やインスタンスを破棄するように実装したのですが、効果がありません。

補足情報(FW/ツールのバージョンなど)

知見をお持ちの方がいらっしゃいましたら、ご教示お願いいたします。

また、Swift初心者のため、UIImageViewの使い方が良くない、とか、UIImageViewよりももっと適したクラスがある、等の情報をいただけると幸いです。

よろしくお願いいたします。

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

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

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

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

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

guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだ回答がついていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問