みんなの「教えて(疑問・質問)」にみんなで「答える」Q&Aコミュニティ

こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

Swiftについて教えてください

カメラロールにある写真をUICollectionViewに読み込み表示させようと思い、いろいろ調べながら下記のようにしてみたのですが、表示されません。swiftでALAssetをUICollectionViewで表示させるなどのやり方を調べてみたのですが、わかりませんでした。(英語のサイトなどにあったかもしれませんが…)swiftでALAssetをUICollectionViewで表示させかた教えてください。

import UIKit
import AssetsLibrary

class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {

var assets : NSMutableArray = []
var groups = ALAssetsGroup()

override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.

self.groups.enumerateAssetsUsingBlock({
(asset: ALAsset!, index: Int, stop: UnsafeMutablePointer<ObjCBool>) -> Void in
if asset != nil {
self.assets.insertObject(asset!, atIndex: 0)
}
})


}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}


func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.assets.count;
}


func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
var cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath)as! UICollectionViewCell

let asset :ALAsset = self.assets[indexPath.item] as! ALAsset;

(cell.viewWithTag(1) as! UIImageView).image = UIImage(CGImage: asset.thumbnail().takeUnretainedValue())

return cell
}

投稿日時 - 2015-12-19 07:56:55

QNo.9098020

すぐに回答ほしいです

質問者が選んだベストアンサー

掲載していただいたコードを検証してみましたが、大きな問題が2つあると思います。

まず、self.groups.enumerateAssetsUsingBlockのgroupsは、ALAssetsGroup()で初期化しただけで使っていますが、ここは、enumerateGroupsWithTypesで取得したグループを使う必要があります。
swiftでALAssetLibraryを使った例はあまりないので、Objective-Cの例になりますが、
http://dev.classmethod.jp/smartphone/iphone/assetslibrary/
のloadAssetsLibraryをお手本にして、これをswiftに変換して使用するとよいと思います。

もう1つの問題は、enumerateAssetsUsingBlockは非同期に実行されるので、この処理をviewDidLoadに書いても実際に実行されるのは、numberOfItemsInSectionの実行より後になるはずです。すると、self.assets.countの値は0なので、numberOfItemsInSectionの返却値も0となり、何も表示されないことになります。この問題はself.assets.insertObjectとnumberOfItemsInSectionを実行する箇所でログを取ってみるとよくわかると思います。
1個目の問題で紹介した参考サイトでは、loadAssetsLibraryの実行を「一覧表示をしたい画面の一つ前の画面に挟み込むと良い」と書いていますが、もしそういうタイミングを取るのが難しければ、insertObjectすると同時にコレクションビューに対してreloadData()を実行してもよいと思います。そうすれば、insertObjectするたびにコレクションビューが最新の状態に更新されるはずです。

投稿日時 - 2015-12-20 21:57:22

補足

二つ目の部分ですが、ご指摘いただいたようにログを取ってみるとself.assets.countの値は0で、その後にself.assets.countの値が取得されてます。
self.assets.addObject(asset!)
self.collectionView!.reloadData()
としたのですが、Cannot invoke 'reloadData' with no argumentsというエラーがでます。引数なしでは関数を実行出来ないというエラーということはわかったのですが、どんな引数が必要か分かりません。
swift UICollectionView reloadDataで検索をしてみたのですが、引数についてよく分かりませんでした。

@IBOutlet weak var collection:UICollectionView!としてみたら、エラーが消えたのですが、アプリが落ちてしまいます。この宣言は必要ないのですか?教えてください。


import UIKit
import AssetsLibrary

class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {

var library = ALAssetsLibrary()
var assets = NSMutableArray()

override func viewDidLoad() {
super.viewDidLoad()

library.enumerateGroupsWithTypes(

ALAssetsGroupType(ALAssetsGroupSavedPhotos),

usingBlock: {
(group: ALAssetsGroup!, stop: UnsafeMutablePointer<ObjCBool>) -> Void in
if group != nil {

group.setAssetsFilter(ALAssetsFilter.allPhotos())

group.enumerateAssetsUsingBlock({
(asset: ALAsset!, index: Int, stop: UnsafeMutablePointer<ObjCBool>) -> Void in


if asset != nil {
self.assets.addObject(asset!)
print(asset)
self.collectionView!.reloadData()

} else {}


})

}
},

failureBlock: {
(myerror: NSError!) -> Void in
print("error occurred: \(myerror.localizedDescription)")
}

)

}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
//return imageArray.count

return self.assets.count;

}


func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
var cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath)as! UICollectionViewCell


// Configure the cell
let assetImg :ALAsset = self.assets[indexPath.item] as! ALAsset;

//print(assetImg)
(cell.viewWithTag(1) as! UIImageView).image = UIImage(CGImage: assetImg.thumbnail().takeUnretainedValue())

return cell
}
}

投稿日時 - 2015-12-25 07:10:06

お礼

回答ありがとうございます。一つ目の問題は、教えていただいたサイトを参考にしたり、その他のいろいろ調べて補足コメントのようにしてクリアできました。assetを取得することができるようになりました。二つ目の問題がクリアできず困っています。補足コメントに追加質問させてください。

投稿日時 - 2015-12-25 07:08:24

このQ&Aは役に立ちましたか?

0人が「このQ&Aが役に立った」と投票しています

回答(3)

ANo.3

No.2補足の質問について回答します。

> collectionView!.delegate = self
> collectionView!.dataSource = self
がなくても動いているのは、その替わりに
storyboardを使ってdelegateとdataSourceを
ViewControllerに接続しているのだと思います。

例えば、
http://www.dcom-web.co.jp/technology/swift3/
のtableViewの使い方の説明では、storyboardを使って
delegateとdataSourceを接続する方法が説明されています。

どちらの方法を使ってもよいですが、どちらかの方法で
delegateとdataSourceを設定しない限りコレクションビューは
動作できません。


> IBOutlet weak var collectionView:UICollectionView
は、
self.collectionView!.reloadData()
のように、制御(reloadData等)したいコレクションビューを
指定するのに必要です。
そういう制御をする必要がなく、デフォルトの動作に任せて表示できれば
いいだけなら定義する必要はありません。
また、delegateで呼び出されるメソッドは、そのパラメータで
collectionViewが渡されるものがあるので、これらのメソッド処理の中で
コレクションビューを制御したい場合はcollectionViewパラメータを
使えばよいので、collectionViewプロパティとそのアウトレット接続は
不要です。

今回は、AssetsLibraryの処理の延長でreloadDataを実施したかったので
その場合は、collectionViewプロパティを定義し、それを使う必要が
あります。

投稿日時 - 2015-12-27 00:10:06

お礼

いくつもの質問に対する丁寧な回答ありがとうございました。とても参考になりました。

投稿日時 - 2015-12-27 05:37:33

ANo.2

No.2の補足の質問について回答します。

> @IBOutlet weak var collection:UICollectionView!としてみたら、エラーが消えたのですが、アプリが落ちてしまいます。この宣言は必要ないのですか?教えてください。

self.collectionView!.reloadData()
を実行するために必要です。
が、「collection」ではなく「collectionView」です。
(どっちでもいいですが、reloadDataを実行しているコードと合わせる必要があります)
たぶん、これを追加してエラーが消えたということは、
実際には「collectionView」を指定しているのだと思います。

が、実行して落ちたということは、たぶんこれをどこかの
サイトからコピペして書いただけでアウトレット接続してないか、
接続したつもりだけど誤った接続になっているのだと思います。

その接続作業は、storyboardに貼りつけたラベルやボタンを
コードから操作したい時にいつも実施する作業で、たぶんよく知っている
作業だと思うのですが、もしなんのことかわからなれば、
http://www.atmarkit.co.jp/ait/articles/1501/20/news023.html
とかを参考にしてください。

もし誤って一度「collection」という名前で接続したことがあり、
その後それを「collectionView」に訂正したのであれば、
コード上の「collection」を「collectionView」に修正するだけでなく、
storyboardから再接続する作業が必要です。
そうしないと、storyboardは古い「collection」に接続していると
認識したままなのでやはり落ちます。
正しく接続されていれば、コードの左端の接続ポイントを示す
○記号が塗りつぶされていますが、接続されていない場合は
白抜きの○記号になります。

グループの取得処理やreloadData()を追加している場所は正しいので、
もうあとこれだけだと思います。

投稿日時 - 2015-12-25 18:00:50

補足

いくつかのサンプルを見ていく中で、オブジェクトライブラリからstoryboardにUICollectionViewをドラッグ&ドロップして作っていく場合に、
@ IBOutlet weak var collectionView:UICollectionView?のアウトレット接続や
collectionView!.delegate = self
collectionView!.dataSource = self
などの記述がなくてもUICollectionViewがきちんと表示されるものがありました。今回の自分にはこれから必要だったのですが、違いはなんなんでしょうか?よろしくお願いします。

投稿日時 - 2015-12-26 18:41:31

お礼

回答ありがとうがざいます。アウトレット接続しなおし、
collectionView!.delegate = self
collectionView!.dataSource = self
を加えたら、うまくいきました。ありがとうございます。
今回教えていただいたことや調べたことから疑問があります。追加コメントに質問させてください。

投稿日時 - 2015-12-26 18:41:14