iPhone UIView - изменить размер кадра, чтобы подогнать подвиды - PullRequest
40 голосов
/ 31 августа 2010

Не должен ли быть способ изменить размер фрейма UIView после того, как вы добавили подпредставления, чтобы размер фрейма соответствовал всем подпредставлениям?Если ваши подпредставления добавляются динамически, как вы можете определить, каким должен быть размер фрейма представления контейнера?Это не работает:

[myView sizeToFit];

Ответы [ 10 ]

43 голосов
/ 12 февраля 2012

Вы также можете добавить следующий код для расчета позиции подпредставлений.

[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
24 голосов
/ 14 января 2014

Мне нужно, чтобы у подвидов была отрицательная исходная точка, а CGRectUnion - это честный способ ObjC, как кто-то упоминал в комментариях. Для начала посмотрим, как это работает:

Как вы можете видеть ниже, мы предполагаем, что некоторые подпредставления лежат снаружи, поэтому нам нужно сделать две вещи, чтобы это выглядело хорошо, не затрагивая расположение подпредставлений:

  1. Переместить рамку вида в крайнее верхнее левое положение
  2. Переместите подпредставления в противоположном направлении, чтобы свести на нет эффект.

Картинка стоит тысячи слов.

demonstration

Код стоит миллиард слов. Вот решение:

@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

18 голосов
/ 12 декабря 2012

Похоже, ответ Кенни выше указывает на правильное решение в указанном вопросе , но, возможно, отбросило неверную концепцию. Ссылка класса UIView определенно предлагает систему для настройки sizeToFit в соответствии с вашими пользовательскими представлениями.

Переопределить sizeThatFits, а не sizeToFit

Ваши пользовательские UIView s должны переопределить sizeThatFits, чтобы вернуть "новый размер, который соответствует подпредставлениям получателя", однако вы хотите рассчитать это. Вы даже можете использовать математику из другого ответа , чтобы определить свой новый размер (но без воссоздания встроенной системы sizeToFit).

После того, как sizeThatFits вернет номера, соответствующие его состоянию, вызовы sizeToFit в ваших пользовательских представлениях начнут вызывать ожидаемые изменения размера.

Как sizeThatFits работает

Без переопределения, sizeThatFits просто возвращает переданный параметр размера (по умолчанию self.bounds.size для вызовов из sizeToFit. Пока у меня есть только пара источников по этому вопросу выясняется, что передаваемый размер не следует рассматривать как строгое требование.

[sizeThatFits] возвращает «наиболее подходящий» размер для элемента управления, который соответствует переданным ему ограничениям. Метод может (выделение их) решает игнорировать ограничения, если они не могут быть выполнены.

16 голосов
/ 21 ноября 2010

Проверьте Не можете заставить UIView sizeToFit сделать что-нибудь значимое

Суть в том, что sizeToFit предназначена для переопределения подклассов и ничего не делает в UIView. Он делает вещи для UILabel, потому что он переопределяет sizeThatFits:, который вызывается sizeToFit

2 голосов
/ 22 апреля 2017

Обновлен ответ @ 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
    }
}
1 голос
/ 23 сентября 2015

Старый вопрос, но вы также можете сделать это с помощью рекурсивной функции.

Возможно, вам понадобится решение, которое всегда работает независимо от того, сколько подпредставлений и подподозрений ...

Обновление: предыдущий фрагмент кода имел только функцию получения, теперь также установщик.

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)
        }
    }
}
0 голосов
/ 15 августа 2018

Когда вы создаете свой собственный класс UIView, рассмотрите возможность переопределения IntrinsicContentSize в подклассе. Это свойство вызывается ОС, чтобы узнать рекомендуемый размер вашего представления. Я поставлю фрагмент кода для 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 голосов
/ 29 марта 2015

Вот быстрая версия принятого ответа, также небольшое изменение, вместо расширения, этот метод получает представление в виде переменной и возвращает его.

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 голосов
/ 31 августа 2010
[myView sizeToFit];

Должно работать, почему бы вам не проверить CGRect до и после?

0 голосов
/ 31 августа 2010

Любой модуль, который динамически добавил все эти подпредставления, должен был знать, где их разместить (чтобы они правильно соотносились, или чтобы они не перекрывались и т. Д.). Как только вы это узнаете, плюс размер текущего представления плюс Размер подпредставления, у вас есть все, что вам нужно, чтобы определить, нужно ли модифицировать окружающее представление.

...