web-dev-qa-db-ja.com

UIImageアスペクトは上部に合わせて整列します

aspect fitは、デフォルトで画像をフレームの下部に揃えます。 オーバーライドaspect fitそのままですか?

**編集**

この質問は自動レイアウトよりも前です。実際、この質問が行われたのと同じ週にWWDC 2012で自動レイアウトが明らかにされていました

40
Jacksonkr

つまり、UIImageViewを使用してこれを行うことはできません。

1つの解決策は、UIViewを含むUIImageViewをサブクラス化し、画像サイズに応じてフレームを変更することです。たとえば、1つのバージョンを見つけることができます here

22
Mundi

これを行う方法は、UIImageViewレイヤーのcontentsRectを変更することです。私のプロジェクト(UIImageViewのサブクラス)の次のコードは、scaleToFillを想定し、デフォルトの中央揃えの代わりに上下左右に揃えるように画像をオフセットします。 aspectFitは同様のソリューションです。

typedef NS_OPTIONS(NSUInteger, AHTImageAlignmentMode) {
    AHTImageAlignmentModeCenter = 0,
    AHTImageAlignmentModeLeft = 1 << 0,
    AHTImageAlignmentModeRight = 1 << 1,
    AHTImageAlignmentModeTop = 1 << 2,
    AHTImageAlignmentModeBottom = 1 << 3,
    AHTImageAlignmentModeDefault = AHTImageAlignmentModeCenter,
};

- (void)updateImageViewContentsRect {
    CGRect imageViewContentsRect = CGRectMake(0, 0, 1, 1);

    if (self.image.size.height > 0 && self.bounds.size.height > 0) {

        CGRect imageViewBounds = self.bounds;
        CGSize imageSize = self.image.size;

        CGFloat imageViewFactor = imageViewBounds.size.width / imageViewBounds.size.height;
        CGFloat imageFactor = imageSize.width / imageSize.height;

        if (imageFactor > imageViewFactor) {
            //Image is wider than the view, so height will match
            CGFloat scaledImageWidth = imageViewBounds.size.height * imageFactor;
            CGFloat xOffset = 0.0;
            if (BM_CONTAINS_BIT(self.alignmentMode, AHTImageAlignmentModeLeft)) {
                xOffset = -(scaledImageWidth - imageViewBounds.size.width) / 2;
            } else if (BM_CONTAINS_BIT(self.alignmentMode, AHTImageAlignmentModeRight)) {
                xOffset = (scaledImageWidth - imageViewBounds.size.width) / 2;
            }
            imageViewContentsRect.Origin.x = (xOffset / scaledImageWidth);
        } else if (imageFactor < imageViewFactor) {
            CGFloat scaledImageHeight = imageViewBounds.size.width / imageFactor;

            CGFloat yOffset = 0.0;
            if (BM_CONTAINS_BIT(self.alignmentMode, AHTImageAlignmentModeTop)) {
                yOffset = -(scaledImageHeight - imageViewBounds.size.height) / 2;
            } else if (BM_CONTAINS_BIT(self.alignmentMode, AHTImageAlignmentModeBottom)) {
                yOffset = (scaledImageHeight - imageViewBounds.size.height) / 2;
            }
            imageViewContentsRect.Origin.y = (yOffset / scaledImageHeight);
        }
    }
    self.layer.contentsRect = imageViewContentsRect;
}

スイフト版

class AlignmentImageView: UIImageView {

    enum HorizontalAlignment {
        case left, center, right
    }

    enum VerticalAlignment {
        case top, center, bottom
    }


    // MARK: Properties

    var horizontalAlignment: HorizontalAlignment = .center
    var verticalAlignment: VerticalAlignment = .center


    // MARK: Overrides

    override var image: UIImage? {
        didSet {
            updateContentsRect()
        }
    }

    override func layoutSubviews() {
        super.layoutSubviews()

        updateContentsRect()
    }


    // MARK: Content layout

    private func updateContentsRect() {
        var contentsRect = CGRect(Origin: .zero, size: CGSize(width: 1, height: 1))

        guard let imageSize = image?.size else {
            layer.contentsRect = contentsRect
            return
        }

        let viewBounds = bounds
        let imageViewFactor = viewBounds.size.width / viewBounds.size.height
        let imageFactor = imageSize.width / imageSize.height

        if imageFactor > imageViewFactor {
            // Image is wider than the view, so height will match
            let scaledImageWidth = viewBounds.size.height * imageFactor
            var xOffset: CGFloat = 0.0

            if case .left = horizontalAlignment {
                xOffset = -(scaledImageWidth - viewBounds.size.width) / 2
            }
            else if case .right = horizontalAlignment {
                xOffset = (scaledImageWidth - viewBounds.size.width) / 2
            }

            contentsRect.Origin.x = xOffset / scaledImageWidth
        }
        else {
            let scaledImageHeight = viewBounds.size.width / imageFactor
            var yOffset: CGFloat = 0.0

            if case .top = verticalAlignment {
                yOffset = -(scaledImageHeight - viewBounds.size.height) / 2
            }
            else if case .bottom = verticalAlignment {
                yOffset = (scaledImageHeight - viewBounds.size.height) / 2
            }

            contentsRect.Origin.y = yOffset / scaledImageHeight
        }

        layer.contentsRect = contentsRect
    }

}
6

ImageViewの下部レイアウト制約の優先順位を最低(つまり250)に設定するだけで、作業が実行されます。

5
protspace
  1. 画像の縦横比にアスペクト比制約を追加します。
  2. UIImageViewを下に固定しないでください。
  3. UIImageを動的に変更する場合は、アスペクト比の制約を更新することを忘れないでください。

同様の問題がありました。最も簡単な方法は、UIImageViewの独自のサブクラスを作成することでした。サブクラス3プロパティを追加し、内部実装を知らなくても簡単に使用できるようにします。

@property (nonatomic) LDImageVerticalAlignment imageVerticalAlignment;
@property (nonatomic) LDImageHorizontalAlignment imageHorizontalAlignment;
@property (nonatomic) LDImageContentMode imageContentMode;

ここで確認できます: https://github.com/LucasssD/LDAlignmentImageView

1
Łukasz Duda

画像が画面サイズよりも大きい場合、画像は常に「押し上げられる」ため、UIImageViewの高さに制約を設定することにより、Interface Builderでこれをネイティブに解決しました。

具体的には、UIImageViewを(高さの制約を介して)ビューと同じ高さに設定し、UIImageViewをIBの間隔制約で配置しました。これにより、UIImageViewには「アスペクトフィット」が設定され、IBで設定した上部の間隔の制約が引き続き考慮されます。

0
David T