web-dev-qa-db-ja.com

iPhone UIView-サブビューに合わせてフレームのサイズを変更

フレームがすべてのサブビューを囲むために必要なサイズになるように、サブビューを追加した後、UIViewのフレームのサイズを変更する方法はありませんか?サブビューが動的に追加される場合、コンテナビューのフレームに必要なサイズをどのように決定できますか?これは機能しません:

[myView sizeToFit];
40
sol

チェックアウト IView sizeToFitで意味のあることをするのに苦労しています

要点は、sizeToFitはサブクラスがオーバーライドするためのものであり、UIViewでは何もしないということです。 UILabelによって呼び出されるsizeThatFits:をオーバーライドするため、sizeToFitの処理を行います。

16
Kenny Winker

次のコードを追加して、サブビューの位置を計算することもできます。

[myView resizeToFitSubviews]

UIViewUtils.h

#import <UIKit/UIKit.h>

@interface UIView (UIView_Expanded)

-(void)resizeToFitSubviews;

@end

UIViewUtils.m

#import "UIViewUtils.h"

@implementation UIView (UIView_Expanded)

-(void)resizeToFitSubviews
{
    float w = 0;
    float h = 0;

    for (UIView *v in [self subviews]) {
        float fw = v.frame.Origin.x + v.frame.size.width;
        float fh = v.frame.Origin.y + v.frame.size.height;
        w = MAX(fw, w);
        h = MAX(fh, h);
    }
    [self setFrame:CGRectMake(self.frame.Origin.x, self.frame.Origin.y, w, h)];
}

@end
43
user1204936

サブビューをフィットさせる必要がありました。Originポイントは負でした。CGRectUnionは、ObjCによる正直な方法です。まず、どのように機能するかを見てみましょう。

以下に示すように、いくつかのサブビューが外側にあると想定しているため、サブビューの位置に影響を与えずに、これを適切に表示するために2つのことを行う必要があります。

  1. ビューのフレームを左上の位置に移動します
  2. サブビューを反対方向に移動して、効果を無効にします。

写真は千の言葉に値します。

demonstration

コードは10億語の価値があります。解決策は次のとおりです。

@interface UIView (UIView_Expanded)

- (void)resizeToFitSubviews;

@end

@implementation UIView (UIView_Expanded)

- (void)resizeToFitSubviews
{
    // 1 - calculate size
    CGRect r = CGRectZero;
    for (UIView *v in [self subviews])
    {
        r = CGRectUnion(r, v.frame);
    }

    // 2 - move all subviews inside
    CGPoint fix = r.Origin;
    for (UIView *v in [self subviews])
    {
        v.frame = CGRectOffset(v.frame, -fix.x, -fix.y);
    }

    // 3 - move frame to negate the previous movement
    CGRect newFrame = CGRectOffset(self.frame, fix.x, fix.y);
    newFrame.size = r.size;

    [self setFrame:newFrame];
}

@end

Swift 2.0 ..私は正しかった!

extension UIView {

    func resizeToFitSubviews() {

        let subviewsRect = subviews.reduce(CGRect.zero) {
            $0.union($1.frame)
        }

        let fix = subviewsRect.Origin
        subviews.forEach {
            $0.frame.offsetInPlace(dx: -fix.x, dy: -fix.y)
        }

        frame.offsetInPlace(dx: fix.x, dy: fix.y)
        frame.size = subviewsRect.size
    }
}

そして遊び場の証拠:

visualAidViewは移動せず、サブビューの位置を維持しながらsuperviewがどのようにサイズ変更されるかを確認するのに役立ちます。

let canvas = UIView(frame: CGRect(x: 0, y: 0, width: 80, height: 80))
canvas.backgroundColor = UIColor.whiteColor()

let visualAidView = UIView(frame: CGRect(x: 5, y: 5, width: 70, height: 70))
visualAidView.backgroundColor = UIColor(white: 0.8, alpha: 1)
canvas.addSubview(visualAidView)

let superview = UIView(frame: CGRect(x: 15, y: 5, width: 50, height: 50))
superview.backgroundColor = UIColor.purpleColor()
superview.clipsToBounds = false
canvas.addSubview(superview)

[
    {
        let view = UIView(frame: CGRect(x: -10, y: 0, width: 15, height: 15))
        view.backgroundColor = UIColor.greenColor()
        return view
    }(),
    {
        let view = UIView(frame: CGRect(x: -10, y: 40, width: 35, height: 15))
        view.backgroundColor = UIColor.cyanColor()
        return view
    }(),
    {
        let view = UIView(frame: CGRect(x: 45, y: 40, width: 15, height: 30))
        view.backgroundColor = UIColor.redColor()
        return view
    }(),

].forEach { superview.addSubview($0) }

playground image

24
Mazyod

ケニーの答え のように見えます- 参照された質問 の正しい解決策を示していますが、間違った概念を取り除いているかもしれません。 UIViewクラス参照 は、カスタムビューに関連する sizeToFit を作成するシステムを明確に示唆しています。

sizeThatFitsではなく、sizeToFitをオーバーライドします

カスタムUIViewsは、 sizeThatFits をオーバーライドして「レシーバーのサブビューに適合する新しいサイズ」を返す必要がありますが、これを計算する場合は。 別の答え の数学を使用して、新しいサイズを決定することもできます(ただし、組み込みのsizeToFitシステムを再作成する必要はありません)。

sizeThatFitsがその状態に関連する数値を返した後、カスタムビューでsizeToFitを呼び出すと、予想されるサイズ変更が開始されます。

sizeThatFitsの仕組み

オーバーライドなしでは、sizeThatFitsは単に渡されたサイズパラメータを返します(デフォルトはself.bounds.sizesizeToFitからの呼び出し用。私は couplesources しか持っていませんが、渡されたサイズは厳密な要求とは見なされないようです。

[sizeThatFits]は、渡された制約に適合するコントロールの「最適な」サイズを返します。メソッドcan(それらを強調する)は、制約が満たされない場合、制約を無視することを決定します。

18
patridge

@Mazyodの答えをSwift 3.0に更新しました。

extension UIView {

func resizeToFitSubviews() {

    let subviewsRect = subviews.reduce(CGRect.zero) {
        $0.union($1.frame)
    }

    let fix = subviewsRect.Origin
    subviews.forEach {
        $0.frame.offsetBy(dx: -fix.x, dy: -fix.y)
        }

        frame.offsetBy(dx: fix.x, dy: fix.y)
        frame.size = subviewsRect.size
    }
}
2
mr. sudo

古い質問ですが、再帰関数でこれを行うこともできます。

サブビューやサブサブビューの数に関係なく常に機能するソリューションが必要な場合があります。

更新:前のコードにはゲッター関数しかなく、現在はセッターもありました。

extension UIView {

    func setCGRectUnionWithSubviews() {
        frame = getCGRectUnionWithNestedSubviews(subviews: subviews, frame: frame)
        fixPositionOfSubviews(subviews, frame: frame)
    }

    func getCGRectUnionWithSubviews() -> CGRect {
        return getCGRectUnionWithNestedSubviews(subviews: subviews, frame: frame)
    }

    private func getCGRectUnionWithNestedSubviews(subviews subviews_I: [UIView], frame frame_I: CGRect) -> CGRect {

        var rectUnion : CGRect = frame_I
        for subview in subviews_I {
            rectUnion = CGRectUnion(rectUnion, getCGRectUnionWithNestedSubviews(subviews: subview.subviews, frame: subview.frame))
        }
        return rectUnion
    }

    private func fixPositionOfSubviews(subviews: [UIView], frame frame_I: CGRect) {

        let frameFix : CGPoint = frame_I.Origin
        for subview in subviews {
            subview.frame = CGRectOffset(subview.frame, -frameFix.x, -frameFix.y)
        }
    }
}
1
R Menke

独自のUIViewクラスを作成する場合、サブクラスでIntrinsicContentSizeをオーバーライドすることを検討してください。このプロパティは、ビューの推奨サイズを知るためにOSによって呼び出されます。 C#(Xamarin)のコードスニペットを配置しますが、考え方は同じです。

[Register("MyView")]
public class MyView : UIView
{
    public MyView()
    {
        Initialize();
    }

    public MyView(RectangleF bounds) : base(bounds)
    {
        Initialize();
    }

    Initialize()
    {
        // add your subviews here.
    }

    public override CGSize IntrinsicContentSize
    {
        get
        {
            return new CGSize(/*The width as per your design*/,/*The height as per your design*/);
        }
    }
}

この返されるサイズは、設計に完全に依存します。たとえば、ラベルを追加したばかりの場合、幅と高さはそのラベルの幅と高さになります。より複雑なビューがある場合は、すべてのサブビューに適合するサイズを返す必要があります。

0
Ibrahim

以下はSwift受け入れられた回答のバージョンで、小さな変更でもあります。これを拡張する代わりに、このメソッドはビューを変数として取得して返します。

func resizeToFitSubviews(#view: UIView) -> UIView {
    var width: CGFloat = 0
    var height: CGFloat = 0

    for someView in view.subviews {
        var aView = someView as UIView
        var newWidth = aView.frame.Origin.x + aView.frame.width
        var newHeight = aView.frame.Origin.y + aView.frame.height
        width = max(width, newWidth)
        height = max(height, newHeight)
    }

    view.frame = CGRect(x: view.frame.Origin.x, y: view.frame.Origin.y, width: width, height: height)
    return view
}

拡張バージョンについてはこちら:

/// Extension, resizes this view so it fits the largest subview
func resizeToFitSubviews() {
    var width: CGFloat = 0
    var height: CGFloat = 0
    for someView in self.subviews {
        var aView = someView as! UIView
        var newWidth = aView.frame.Origin.x + aView.frame.width
        var newHeight = aView.frame.Origin.y + aView.frame.height
        width = max(width, newWidth)
        height = max(height, newHeight)
    }

    frame = CGRect(x: frame.Origin.x, y: frame.Origin.y, width: width, height: height)
}
0
Esqarrouth

これらのすべてのサブビューを動的に追加したモジュールは、それらをどこに配置するかを知る必要がありました(したがって、適切に関連するか、重複しないようになります)。サブビューでは、外側のビューを変更する必要があるかどうかを判断するために必要なものがすべて揃っています。

0
hotpaw2