CrossBridge Lab

技術ネタ、デバイスネタを...

【Swift3】画像に合わせて UIImageView のアスペクト比を変更する

2016/11/20 修正 本文中のコードをSwift3対応にしました

はじめに

UIImageView に画像をアスペクト比を保ったまま全体を表示するには UIImageView にAspect Fitを設定します。この時に UIImageView のアスペクト比と画像のアスペクト比が異なると上下、または左右に余白ができてしまいます。今回は Auto Layout を使って画像に合わせて UIImageView のアスペクト比を変更する方法を解説します。

Aspect Ratio

Auto Layout にはAspect Ratioというアスペクト比を設定して View のサイズを決定する成約があります。Storyboard上で3:216:9などの比率を設定することが出来ます。UIImageViewに表示する画像のアスペクト比が固定であればこの方法で解決します。

しかし、UIImageViewに表示する画像のアスペクト比がバラバラの場合ではこの方法では解決することが出来ません。例えば、UIImageView を含むカスタムの UITableViewCell を作って、各セルに表示する画像のアスペクト比が異なる場合に困ってしまいます。

ソースコードで画像の比率に合わせて Auto Layout の制約を設定する

UIImageView の画像を設定するときにこの制約も設定して画像のアスペクト比に合わせたサイズで表示させます。NSLayoutConstraintクラスを以下のように生成して、activateConstraintsメソッド設定します。

thumbnailView.image = image

let constraint = NSLayoutConstraint(
    item: thumbnailView,
    attribute:NSLayoutAttribute.height,
    relatedBy:NSLayoutRelation.equal,
    toItem: thumbnailView,
    attribute: NSLayoutAttribute.width,
    multiplier: (image?.size.height)! / (image?.size.width)!,
    constant:0)

NSLayoutConstraint.activate([constraint])

UIViewクラスのaddConstraintメソッドとaddConstraints`メソッドでも追加することが出来ますがiOS8からは非推奨となっています。コメントにも将来的に使えなくなるからな、と書かれています。

public func addConstraints(constraints: [NSLayoutConstraint]) // This method will be deprecated in a future release and should be avoided.  Instead use +[NSLayoutConstraint activateConstraints:].

TableViewのセルに異なったアスペクト比の画像を表示する

私が必要に迫られたのは UITableViewCell の中に配置した UIImageView だったのでこちらを簡単に説明します。

まずUIImageViewには上下左右の制約のみ設定しておきます。実際にはセルの中に他のUIパーツもあるかと思います。

f:id:crossbridge-lab:20160229125726p:plain

カスタムセルのクラスに画像を設定 & Auto Layout の制約を行うメソッドを追加します。

func setThumbnailImage(image: UIImage?) {
    
    thumbnailView.image = image
    
    let constraint = NSLayoutConstraint(
        item: thumbnailView,
        attribute:NSLayoutAttribute.height,
        relatedBy:NSLayoutRelation.equal,
        toItem: thumbnailView,
        attribute: NSLayoutAttribute.width,
        multiplier: (image?.size.height)! / (image?.size.width)!,
        constant:0)
    
    NSLayoutConstraint.activate([constraint])
}

ViewControllerの(正確にはUITableViewDataSourceプロトコルを実装しているクラスの)tableView:cellForRowAtIndexPath:メソッドの中でCellを準備するときにこのメソッドを呼び出すようにします。

最後にUITableViewの各セルの高さを Auto Layout によって決定させるようにtableView:estimatedHeightForRowAtIndexPath:メソッドでUITableViewAutomaticDimensionを返すようにします。

func  tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath-> CGFloat {
    return UITableViewAutomaticDimension
}

これで各セルで UIImageView がそれぞれ画像のアスペクト比に合わせた比率で表示され、セルの高さもそれに合わせて設定されるようになります。

f:id:crossbridge-lab:20160229125553p:plain

まとめ

表示する画像に合わせたアスペクト比で Auto Layout のAspect Ratioという制約を設定すれば画像とUIImageViewが同じアスペクト比になり、Aspect Fit を設定した時に余白無しで表示されます。

簡単ですが今回のサンプルを以下に置いておきます。

github.com