うるおいらんど

【Swift 3】UICollectionViewのセル上に乗せた画像をピンチで拡大する【UICollectionView】

追記があります。

どもも。

UICollectionViewを用いてiPhoneのデフォルト写真アプリのようなものを作成しています。イメージビューアーっていうのかなそんな感じのやつです。

やりたいこととしては、UICollectionView上のUIImageViewの拡大となります。

こんな感じ。

ちなみにこの拡大だけで2日くらい行き詰まってました(´・ω・`)

UICollectionViewはUIScrollViewのサブクラス!

UICollectionViewはUIScrollViewのサブクラスになります。

UIScrollViewならviewForZooming(in:)を使えば簡単に拡大することができます。

 

どうにかこれを使いたいのですが、viewForZooming(in:)がそもそも呼ばれない。原因がわからず色々プロジェクト外で試行錯誤してたんですが、ようやく解決方法がわかったので紹介していきます。

 

UICollectionViewを設置しよう

適当にコレクションビューを設置します。

この辺は普通に設置するだけでおkです。

 

まずはデリゲートを書いておきます。

class ViewController: UIViewController,UICollectionViewDelegate,UICollectionViewDataSource{
    /* ~~  */
}

設置します。

class ViewController: UIViewController,UICollectionViewDelegate,UICollectionViewDataSource{
    private var myCollectionView:UICollectionView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let width = self.view.frame.width
        
        //レイアウト
        let imageLayout = UICollectionViewFlowLayout()
        imageLayout.itemSize = CGSize(width:width,height:width * 4 / 3)
        //スクロールの向き
        imageLayout.scrollDirection = .horizontal
        imageLayout.minimumLineSpacing = 0
        imageLayout.minimumInteritemSpacing = 0
        imageLayout.sectionInset = UIEdgeInsetsMake(0,0,0,0)
        
        
        //コレクションビュー
        myCollectionView = UICollectionView(frame: CGRect(x:0,y:49,width:width,height:width * 4 / 3), collectionViewLayout: imageLayout)
        
        //デリゲートをつける
        myCollectionView.delegate = self
        myCollectionView.dataSource = self
        
        //カスタムセルを指定
        myCollectionView.register(SingleViewCell.self, forCellWithReuseIdentifier: "singleCell")
        
        //スクロールバーを表示するかどうか
        myCollectionView.showsHorizontalScrollIndicator = false
        myCollectionView.showsVerticalScrollIndicator = false
        
        myCollectionView.backgroundColor = UIColor.gray
        
        //ページングをするかどうか
        myCollectionView.isPagingEnabled = true
        self.view.addSubview(myCollectionView)
        

    }

    /********************* CollectionView *********************/
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 10
    }
    
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        print("タップ")
    }
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        //セルの設定
        let cell:SingleViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "singleCell", for: indexPath) as! SingleViewCell
        
            let image = UIImage(named:"picture.jpg")
            cell.imageView.image = image
        
        return cell
    }
}

 

適当に同じ画像を10枚横スクロールで表示しています。

 

カスタムセルを作ろう

UICollectionViewCellのサブクラスを作成します。

File > New > File

 

 

Source > Cocoa Touch Class > Next

 

Sublclass of:をUICollectionViewCellにして作成

 

この中にScrollViewを設置して、さらにその上にImageViewを置きます。

import UIKit

class SingleViewCell: UICollectionViewCell,UIScrollViewDelegate {
    public var imageView:UIImageView!
    public var scrollView:UIScrollView!
    
    required init(coder aDecoder:NSCoder){
        super.init(coder: aDecoder)!
    }
    
    override init(frame:CGRect){
        super.init(frame:frame)
        
        //スクロールビューを設置
        scrollView = UIScrollView()
        scrollView.frame = CGRect(x:0,y:0,width:self.frame.width,height:self.frame.height)
        
        //デリゲートを設定
        scrollView.delegate = self
        
        //最大・最小の大きさを決める
        scrollView.maximumZoomScale = 4.0
        scrollView.minimumZoomScale = 1.0
        
        self.contentView.addSubview(scrollView)
        
        //imageViewを生成
        imageView =  UIImageView()
        imageView.frame = CGRect(x:0,y:0,width:self.frame.width,height:self.frame.height)

        //scrollViewにのせる
        scrollView.addSubview(imageView)
        
    }
    func viewForZooming(in scrollView: UIScrollView) -> UIView? {
        //実際に拡大したいscrollViewのサブビューを返す
        return self.imageView
    }
    func scrollViewDidZoom(_ scrollView: UIScrollView) {
        print("zoomおわり")
    }
    
    func scrollViewWillBeginZooming(_ scrollView: UIScrollView, with view: UIView?) {
        print("zoomはじまり")
    }
}

 

これで無事拡大ができます。

ダブルタップでの拡大を記事の最後に付け足しました。

 

失敗ポイント

躓いた原因はいくつかあります(´・ω・`)

・UICollectionViewがUIScrollViewのサブクラスだということで、自動的にviewForZooming(in:)が呼ばれると思っていた。

・UIScrollViewを設置してデリゲートをつけると、自動的にviewForZooming(in:)が呼ばれると思っていた。

・そもそもUICollectionView上のスクロールビューを拡大しても意味がないことに気がついていなかった。

などなど。

 

まずviewForZooming(in:)が呼ばれなかった原因はこちら。

        //最大・最小の大きさを決める
        scrollView.maximumZoomScale = 4.0
        scrollView.minimumZoomScale = 1.0

このzoomに関する設定をしていなかったから呼ばれなかったわけです。

setZoomScale(scale:animated:)だとかzoom(to:animated:)だとかを書いてもviewForZooming(in:)は呼ばれていました。

この辺がないと呼ばれない。ここに気づくまでが時間かかりました・・・疲れてたのかな・・・・ 気づいてからはすぐでした。

 

そしてUICollectionView上のスクロールビューを拡大しようとしていたので、枠だけが大きくなって画像が大きくならないという。すぐに気づいたけどね・・・?

ちなみに、UICollectionViewを使っている場合はUIScrollViewDelegateではなくUICollectionViewDelegateを使ってください。ちゃんとscrollviewのも含まれているよ(`・ω・´)

 

あとは色々UIGestureRecognizerを用いてピンチしようとしたのですが、色々競合しちゃったのかうまくいきませんでした。そしてごちゃごちゃしてるし煩雑です。

 

ダブルタップで拡大機能もつけようと思っているので、またすぐ記事書くかもです〜。

ほぼ写しただけで済んでしまったので、ダブルタップで拡大を追加したコード載せておきます。

参考はこちら「画像をダブルタップとピンチイン・ピンチアウトで拡大・縮小する

変なところにひっかかってなかったらこの記事だけで実装できてたな・・・(´・ω・`)

import UIKit

class SingleViewCell: UICollectionViewCell,UIScrollViewDelegate {
    public var imageView:UIImageView!
    public var scrollView:UIScrollView!
    
    required init(coder aDecoder:NSCoder){
        super.init(coder: aDecoder)!
    }
    
    override init(frame:CGRect){
        super.init(frame:frame)
        
        //スクロールビューを設置
        scrollView = UIScrollView()
        scrollView.frame = CGRect(x:0,y:0,width:self.frame.width,height:self.frame.height)
        
        //デリゲートを設定
        scrollView.delegate = self
        
        //最大・最小の大きさを決める
        scrollView.maximumZoomScale = 4.0
        scrollView.minimumZoomScale = 1.0
        
        self.contentView.addSubview(scrollView)
        
        //imageViewを生成
        imageView =  UIImageView()
        imageView.frame = CGRect(x:0,y:0,width:self.frame.width,height:self.frame.height)
        scrollView.addSubview(imageView)
        
        let doubleTap = UITapGestureRecognizer(target:self,action:#selector(SingleViewCell.doubleTap(gesture:)))
        doubleTap.numberOfTapsRequired = 2
        imageView.isUserInteractionEnabled = true
        imageView.addGestureRecognizer(doubleTap)
        
    }
    func viewForZooming(in scrollView: UIScrollView) -> UIView? {
        return self.imageView
    }
    func scrollViewDidZoom(_ scrollView: UIScrollView) {
        print("zoomおわり")
    }
    
    func scrollViewWillBeginZooming(_ scrollView: UIScrollView, with view: UIView?) {
        print("zoomするよ")
    }
    
    // ダブルタップ
    func doubleTap(gesture: UITapGestureRecognizer) -> Void {
       // if ( self.scrollView.zoomScale < self.scrollView.maximumZoomScale ) {
         if ( self.scrollView.zoomScale < 3 ) {
            let newScale:CGFloat = self.scrollView.zoomScale * 3
            let zoomRect:CGRect = self.zoomRectForScale(scale: newScale, center: gesture.location(in: gesture.view))
            self.scrollView.zoom(to: zoomRect, animated: true)
            
        } else {
            self.scrollView.setZoomScale(1.0, animated: true)
        }
    }
    // 領域
    func zoomRectForScale(scale:CGFloat, center: CGPoint) -> CGRect{
        var zoomRect: CGRect = CGRect()
        zoomRect.size.height = self.scrollView.frame.size.height / scale
        zoomRect.size.width = self.scrollView.frame.size.width / scale
        
        zoomRect.origin.x = center.x - zoomRect.size.width / 2.0
        zoomRect.origin.y = center.y - zoomRect.size.height / 2.0
        
        return zoomRect
    }
}

 

 

ではでは。

 

どうでもいいけど一番最初のUICollectionViewはUIScrollViewのサブクラスっていうとこ、いらなくない・・・?

 

参考リンク

Additional Notes追記

gistに↑age↑ヾ(`・ω・´)ノ シャキーン

Swift4に対応したものをGistにあげておきました〜。

ついでに最近のコードの書き方に変えておきました。 今の方が見やすいと信じている。

Comments

コメントはありません。

現在コメントフォームは工事中です。