Программно изменить пользовательский UIView из XIB

У меня есть следующая проблема: я создал собственный UIView класс и его макет в файле XIB. Скажем, что размер моего пользовательского представления в XIB равен 150 x 50. Я включил sizeClasses (wAny hAny) и AutoLayout. В Simulated Metrics я установил Size = Freeform, Status Bar = None, all other = Inferred.

Код из моего пользовательского класса UIView выглядит следующим образом:

#import "CustomView.h"

@implementation CustomView

#pragma mark - Object lifecycle

- (id)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];

    if (self) {
        [self setup];
    }

    return self;
}

- (id)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];

    if (self) {
        [self setup];
    }

    return self;
}

#pragma mark - Private & helper methods

- (void)setup {
    if (self.subviews.count == 0) {
        [[NSBundle mainBundle] loadNibNamed:@"CustomView" owner:self options:nil];
        self.bounds = self.view.bounds;
        [self addSubview:self.view];
    }
}

@end

В моем UIViewController, где я хочу добавить этот пользовательский UIView, я сделал dummy UIView (какой размер я установил в Interface Builder), где я хочу добавить свои CustomView и , которые мне бы хотелось что мой CustomView соответствует границам моего представления контейнера.

Я пытаюсь сделать это вот так:

_vCustomView = [[CustomView alloc] init];
_vCustomView.translatesAutoresizingMaskIntoConstraints = NO;
[_vContainer addSubview:_vCustomView];

[_vContainer addConstraint:[NSLayoutConstraint constraintWithItem:_vCustomView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:_vContainer attribute:NSLayoutAttributeLeft multiplier:1.0 constant:0.0]];
[_vContainer addConstraint:[NSLayoutConstraint constraintWithItem:_vCustomView attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:_vContainer attribute:NSLayoutAttributeRight multiplier:1.0 constant:0.0]];
[_vContainer addConstraint:[NSLayoutConstraint constraintWithItem:_vCustomView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:_vContainer attribute:NSLayoutAttributeTop multiplier:1.0 constant:0.0]];
[_vContainer addConstraint:[NSLayoutConstraint constraintWithItem:_vCustomView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:_vContainer attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0.0]];

Но не повезло. Скажем, что мой контейнерный вид 300 x 100, когда я добавляю к нему свой собственный UIView, мой пользовательский вид все еще 150 x 50.

Я попытался работать без представления контейнера - добавить свой пользовательский объект UIView непосредственно в self.view моего UIViewController и установить его ширину и высоту с помощью:

  • AutoLayout
  • с помощью initWithFrame при инициализации моего пользовательского UIView

но опять же - не повезло. Пользовательский вид всегда 150 х 50.

Может кто-нибудь объяснить мне, как я могу программно добавить свой пользовательский UIView, сделанный в XIB, и изменить его размер с помощью AutoLayout ограничений?

Большое спасибо заранее.

Изменить # 1: Ошибка при попытке запустить код Andrea в моем CustomView

Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    "<NSLayoutConstraint:0x17489b760 V:[UIView:0x17418d820(91)]>",
    "<NSLayoutConstraint:0x170c9bf80 V:|-(0)-[CustomView:0x1741da7c0]   (Names: '|':CustomView:0x1741da8b0 )>",
    "<NSLayoutConstraint:0x170c9bfd0 V:[CustomView:0x1741da7c0]-(0)-|   (Names: '|':CustomView:0x1741da8b0 )>",
    "<NSLayoutConstraint:0x170c9be90 V:|-(0)-[CustomView:0x1741da8b0]   (Names: '|':UIView:0x17418d820 )>",
    "<NSLayoutConstraint:0x170c9bee0 CustomView:0x1741da8b0.bottom == UIView:0x17418d820.bottom>",
    "<NSAutoresizingMaskLayoutConstraint:0x170c9dba0 h=--& v=--& CustomView:0x1741da7c0.midY == + 25.0>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x170c9bfd0 V:[CustomView:0x1741da7c0]-(0)-|   (Names: '|':CustomView:0x1741da8b0 )>

Изменить # 2: он работает!

После добавления @Andrea:

view.translatesAutoresizingMaskIntoConstraints = NO;

в своем:

- (void) stretchToSuperView:(UIView*) view;

все работает так, как я ожидал.

Благодаря @Andrea.

Ответ 1

Я предполагаю, что основная проблема заключается в том, что вы не используете автоматический макет при добавлении xib в представление контента.
После добавления subview в ваши методы init, вызовите этот метод, передав представление xib:

- (void) stretchToSuperView:(UIView*) view {
    view.translatesAutoresizingMaskIntoConstraints = NO;
    NSDictionary *bindings = NSDictionaryOfVariableBindings(view);
    NSString *formatTemplate = @"%@:|[view]|";
    for (NSString * axis in @[@"H",@"V"]) {
        NSString * format = [NSString stringWithFormat:formatTemplate,axis];
        NSArray * constraints = [NSLayoutConstraint constraintsWithVisualFormat:format options:0 metrics:nil views:bindings];
        [view.superview addConstraints:constraints];
    }

}

[EDIT]
Ваш код установки должен выглядеть так:

- (void)setup {
    if (self.subviews.count == 0) {
        [[NSBundle mainBundle] loadNibNamed:@"CustomView" owner:self options:nil];
        self.bounds = self.view.bounds;
        [self addSubview:self.view];
        [self stretchToSuperView:self.view];
    }
}

Обратите внимание, что в режиме растяжения я добавил view.translatesAutoresizingMaskIntoConstraints = NO;
[ИЗМЕНИТЬ 2]
Я попробую уточнить свой ответ в соответствии с просьбой. Использование автоматического выключения при добавлении представления без ограничений по умолчанию автоотключение автоматически преобразует авторезистирующие маски в ограничения, по умолчанию их значение равно UIViewAutoresizingNone, что означает, что вы не выполняете автореализацию.
Ваше представление XIB было добавлено к его родительскому объекту без ограничений и без возможности автоматической сортировки, сохраняя при этом свой первоначальный размер. Поскольку вы хотите, чтобы ваше представление изменялось в соответствии с вашим представлением, у вас было два варианта:

  • Измените маски авторезистентности вашего xib на гибкую ширину и высоту, но они должны соответствовать размеру родителя, или у вас не будет полной обложки
  • Добавьте некоторые ограничения, которые ограничивают два представления для изменения в соответствии с родительскими изменениями. И вы достигаете этого высказывания между представлением XIB и его родительским представлением, пространство между трейлингом/ведущим и top/botton равно 0. Похоже, вы кладете некоторый клей на свои границы. Для этого вам нужно установить маску изменения размера автотрансляции в значение НЕТ, или вы можете столкнуться с некоторыми конфликтами, когда вы разместили в журнале.

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

Ответ 2

В вашем коде у вас должно быть что-то вроде этого:

Во-первых, a IBOutlet к ограничению ширины, которое вы хотите изменить, либо в viewController, либо в вашем CustomView (я действительно не знаю, где у вас есть логика для вычисления значения ограничения).

@property (nonatomic, weak) IBOutlet NSLayoutConstraint *widthConstraint;

Затем в методе, который вы хотите обновить постоянное значение, вы получите следующее:

-(void)updateWidth {
    [self.widthConstraint setConstant:newConstant];
    [self.view layoutIfNeeded]; // self.view must be an ancestor of the view

    //If you need the changes animated, call layoutIfNeeded in an animation block
}

Надеюсь, это сработает для вас. Удачи!

Ответ 3

Функция Swift 4.0 растягивается до супер-представления:

Фрагмент кода:

static public func stretchToSuperView(view:UIView)
    {
        view.translatesAutoresizingMaskIntoConstraints = false
        var d = Dictionary<String,UIView>()
        d["view"] = view
        for axis in ["H","V"] {
            let format = "\(axis):|[view]|"
            let constraints = NSLayoutConstraint.constraints(withVisualFormat: format, options: NSLayoutFormatOptions(rawValue: 0), metrics: [:], views: d)
            view.superview?.addConstraints(constraints)
        }
    }

Примечание. Просто вызовите функцию с аргументом в качестве подзаголовка.

Это помогает мне решить проблему удаления .xib в swift 4.0.