it-swarm.com.de

iPhone UIView - Bildgröße für Unteransichten anpassen

Sollte es nicht möglich sein, den Frame einer UIView nach dem Hinzufügen von Subviews so zu skalieren, dass der Frame die Größe hat, die zum Einschließen aller Subviews erforderlich ist? Wenn Ihre Unteransichten dynamisch hinzugefügt werden, wie können Sie dann bestimmen, wie groß der Rahmen der Containeransicht sein muss? Das funktioniert nicht:

[myView sizeToFit];
40
sol

Check out Probleme mit UIView sizeToFit, etwas Sinnvolles zu tun

Das Wesentliche ist, dass sizeToFit für Unterklassen gilt, die außer Kraft gesetzt werden, und in UIView nichts tut. Es macht Sachen für UILabel, da es sizeThatFits: überschreibt, der von sizeToFit aufgerufen wird.

16
Kenny Winker

Sie können auch den folgenden Code hinzufügen, um die Position der Unteransichten zu berechnen.

[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

Ich musste die Unteransichten anpassen, die einen negativen Ursprungspunkt hatten, und CGRectUnion ist die Art und Weise von ObjC, ehrlich gesagt, wie jemand in den Kommentaren erwähnt. Zuerst wollen wir sehen, wie es funktioniert:

Wie Sie unten sehen können, gehen wir davon aus, dass einige Unteransichten außerhalb liegen. Daher müssen wir zwei Dinge tun, damit dies gut aussieht, ohne die Positionierung der Unteransichten zu beeinträchtigen:

  1. Verschieben Sie den Rahmen der Ansicht ganz nach links oben
  2. Verschieben Sie die Unteransichten in die entgegengesetzte Richtung, um den Effekt zu annullieren.

Ein Bild sagt mehr als tausend Worte.

demonstration

Code ist eine Milliarde Wörter wert. Hier ist die Lösung:

@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

Ich dachte, es würde Spaß machen, in Swift 2.0 zu schreiben. Ich hatte recht!

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
    }
}

Und der Spielplatz Beweis:

Beachten Sie, dass sich die Variable visualAidView nicht bewegt, und zeigt Ihnen, wie die Größe der superview geändert wird, während die Positionen der Unteransichten beibehalten werden.

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

23
Mazyod

Es sieht so aus, als ob die Antwort von Kenny auf die richtige Lösung in der verwiesenen Frage verweist, aber möglicherweise das falsche Konzept entfernt hat. Die UIView-Klassenreferenz schlägt definitiv ein System vor, um sizeToFit für Ihre benutzerdefinierten Ansichten relevant zu machen.

Überschreiben Sie sizeThatFits, nicht sizeToFit

Ihre benutzerdefinierten UIViews müssen sizeThatFits überschreiben, um "eine neue Größe zurückzugeben, die zu den Unteransichten des Empfängers passt". Dies möchten Sie jedoch berechnen. Sie können sogar die Mathematik aus einer anderen Antwort verwenden, um Ihre neue Größe zu bestimmen (jedoch ohne das eingebaute sizeToFit-System neu zu erstellen).

Nachdem sizeThatFits für seinen Status relevante Zahlen zurückgegeben hat, werden Aufrufe von sizeToFit in Ihren benutzerdefinierten Ansichten mit den erwarteten Größenänderungen ausgelöst.

Wie sizeThatFits funktioniert

Ohne Überschreibung gibt sizeThatFits einfach den übergebenen Größenparameter zurück (standardmäßig self.bounds.size für Aufrufe von sizeToFit... Während ich nur ein couplesources habe, scheint die übergebene Größe zu sein nicht als strikte Forderung zu sehen.

[sizeThatFits] gibt die "am besten geeignete" Größe für das Steuerelement zurück, das den übergebenen Einschränkungen entspricht. Die Methode can (Hervorhebung ihrer) kann die Einschränkungen ignorieren, wenn sie nicht erfüllt werden können.

18
patridge

@ Mazyods Antwort auf Swift 3.0 aktualisiert, funktionierte wie ein Zauber!

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

Alte Frage, aber Sie könnten dies auch mit einer rekursiven Funktion tun.

Möglicherweise möchten Sie eine Lösung, die immer funktioniert, unabhängig von der Anzahl der Unteransichten und Unteransichten. 

Update: Vorheriges Stück Code hatte nur eine Getter-Funktion, jetzt auch einen Setter.

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

Wenn Sie Ihre eigene UIView-Klasse erstellen, sollten Sie IntrinsicContentSize in der Unterklasse überschreiben. Diese Eigenschaft wird vom Betriebssystem aufgerufen, um die empfohlene Größe Ihrer Ansicht zu ermitteln. Ich werde Code-Snippet für C # (Xamarin) einfügen, aber die Idee ist die gleiche:

[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*/);
        }
    }
}

Diese zurückgegebene Größe hängt vollständig von Ihrem Design ab. Wenn Sie beispielsweise gerade ein Etikett hinzugefügt haben, entspricht Breite und Höhe nur der Breite und Höhe dieses Etiketts. Wenn Sie eine komplexere Ansicht haben, müssen Sie die Größe zurückgeben, die für alle Ihre Unteransichten passt.

0
Ibrahim

Hier ist eine schnelle Version der akzeptierten Antwort, auch eine kleine Änderung. Statt sie zu erweitern, ruft diese Methode die Ansicht als Variable ab und gibt sie zurück.

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
}

Hier ist die Erweiterungsversion:

/// 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

Unabhängig von dem Modul, das alle diese Unteransichten dynamisch hinzufügte, musste es wissen, wo sie platziert werden sollten (damit sie sich richtig beziehen, oder dass sie sich nicht überlappen usw.) Wenn Sie das wissen, plus die Größe der aktuellen Ansicht plus die Größe der Unteransicht haben Sie alles, um festzustellen, ob die umschließende Ansicht geändert werden muss.

0
hotpaw2