Iphone uiview - изменить размер рамки для поддисков

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

[myView sizeToFit];

Ответ 2

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

[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

Ответ 3

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

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

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

Изображение стоит тысячи слов.

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

Ответ 4

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

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

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

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

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

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

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

Ответ 5

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

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

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

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

Ответ 6

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

Ответ 7

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

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

Ответ 8

Обновлен ответ @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
    }
}

Ответ 9

[myView sizeToFit];

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