フレームがすべてのサブビューを囲むために必要なサイズになるように、サブビューを追加した後、UIViewのフレームのサイズを変更する方法はありませんか?サブビューが動的に追加される場合、コンテナビューのフレームに必要なサイズをどのように決定できますか?これは機能しません:
[myView sizeToFit];
チェックアウト IView sizeToFitで意味のあることをするのに苦労しています
要点は、sizeToFit
はサブクラスがオーバーライドするためのものであり、UIViewでは何もしないということです。 UILabel
によって呼び出されるsizeThatFits:
をオーバーライドするため、sizeToFit
の処理を行います。
次のコードを追加して、サブビューの位置を計算することもできます。
[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
サブビューをフィットさせる必要がありました。Originポイントは負でした。CGRectUnion
は、ObjCによる正直な方法です。まず、どのように機能するかを見てみましょう。
以下に示すように、いくつかのサブビューが外側にあると想定しているため、サブビューの位置に影響を与えずに、これを適切に表示するために2つのことを行う必要があります。
写真は千の言葉に値します。
コードは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) }
ケニーの答え のように見えます- 参照された質問 の正しい解決策を示していますが、間違った概念を取り除いているかもしれません。 UIView
クラス参照 は、カスタムビューに関連する sizeToFit
を作成するシステムを明確に示唆しています。
sizeThatFits
ではなく、sizeToFit
をオーバーライドしますカスタムUIView
sは、 sizeThatFits
をオーバーライドして「レシーバーのサブビューに適合する新しいサイズ」を返す必要がありますが、これを計算する場合は。 別の答え の数学を使用して、新しいサイズを決定することもできます(ただし、組み込みのsizeToFit
システムを再作成する必要はありません)。
sizeThatFits
がその状態に関連する数値を返した後、カスタムビューでsizeToFit
を呼び出すと、予想されるサイズ変更が開始されます。
sizeThatFits
の仕組みオーバーライドなしでは、sizeThatFits
は単に渡されたサイズパラメータを返します(デフォルトはself.bounds.size
sizeToFit
からの呼び出し用。私は couplesources しか持っていませんが、渡されたサイズは厳密な要求とは見なされないようです。
[
sizeThatFits
]は、渡された制約に適合するコントロールの「最適な」サイズを返します。メソッドcan(それらを強調する)は、制約が満たされない場合、制約を無視することを決定します。
@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
}
}
古い質問ですが、再帰関数でこれを行うこともできます。
サブビューやサブサブビューの数に関係なく常に機能するソリューションが必要な場合があります。
更新:前のコードにはゲッター関数しかなく、現在はセッターもありました。
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)
}
}
}
独自の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*/);
}
}
}
この返されるサイズは、設計に完全に依存します。たとえば、ラベルを追加したばかりの場合、幅と高さはそのラベルの幅と高さになります。より複雑なビューがある場合は、すべてのサブビューに適合するサイズを返す必要があります。
以下は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)
}
これらのすべてのサブビューを動的に追加したモジュールは、それらをどこに配置するかを知る必要がありました(したがって、適切に関連するか、重複しないようになります)。サブビューでは、外側のビューを変更する必要があるかどうかを判断するために必要なものがすべて揃っています。