以编程方式从XIB调整自定义UIView的大小

15
我有以下问题:我创建了一个自定义的UIView类,并在XIB文件中对其进行布局。假设我的自定义视图在XIB中的大小为150 x 50。我已经启用了sizeClasses(wAny hAny)和AutoLayout。在模拟度量中,我设置了Size = FreeformStatus Bar = None,所有其他项都是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,我已经创建了一个虚拟的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对象添加到我的UIViewController的self.view中,并使用以下方式设置其宽度和高度:
  • autoLayout约束
  • 在初始化我的自定义UIView时使用initWithFrame
但是,还是没有成功。自定义视图始终为150 x 50。
有人能否解释一下如何通过autoLayout约束以编程方式添加我的自定义XIB中制作的UIView并调整其大小
非常感谢。 编辑#1:当我尝试在我的CustomView上运行Andrea的代码时出现的错误
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 的帮助。


在您更改约束后,是否调用了layoutIfNeeded?您能否发布更改宽度和高度的代码?此外,如果视图在故事板中具有高度和宽度约束,则将约束添加到“contentView”将导致冲突,因为它们将发生冲突。 - Catalina T.
@CatalinaT。感谢您的回答。我没有调用layoutIfNeeded,但现在我已经尝试过了,它没有任何变化。_vContainer是一个虚拟视图,位于我的屏幕中心。我的想法是将我的CustomView添加到该虚拟视图中,并且我的CustomView具有与虚拟视图相同的大小。这就是为什么我写了4个约束条件,其中我说我的CustomView应与虚拟视图的四个边对齐。间接地,我以这种方式设置了CustomView的宽度和高度。但是,没有成功。CustomView始终具有其XIB的相同大小(150 x 50)。 - uerceg
3个回答

19

我猜主要问题是您在将xib添加到内容视图中时未使用自动布局。
在添加子视图后,请调用该方法,并传递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];
    }

}

[编辑]
你的设置代码应该像这样:

- (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;


[EDIT 2]为满足要求,我将尝试详细解释。使用自动布局时,如果您添加一个没有设置约束的视图,则自动布局会自动将自动调整大小掩模转换为约束,默认属性是UIViewAutoresizingNone。这意味着不要自动调整大小。
您的XIB视图被添加到其父视图中而没有约束和自动调整大小的可能性,因此保持其原始大小。由于您希望您的视图能够相应地调整大小以适应您的视图,因此有两种选择:

  • 更改xib视图的自动调整大小掩模为宽度和高度可变,但它们需要匹配父项大小,否则您将无法完全覆盖
  • 添加一些约束来约束两个视图相应地更改以适应父项更改。并且您可以通过在XIB视图和其父视图之间说后面的空间为0来实现该约束。就像在它们的边界上放置一些胶水一样。为此,您需要将autotranslate调整大小掩码设置为NO,否则您可能会遇到一些冲突,就像您在日志中发布的那样。

稍后您将添加约束到该视图(托管XIB视图的视图)以及其父视图,但是在XIB和其父项之间没有约束,因此自动布局不知道如何调整大小XIB视图。
视图层次结构中的每个视图都应具有自己的约束。
希望这有助于更好地理解。


我在将CustomView添加到虚拟容器之前写了_vCustomView.translatesAutoresizingMaskIntoConstraints = NO;。这样就足够让我在程序中对CustomView对象使用自动布局约束了吗?然后,我尝试使用这4个约束使我的CustomView适应虚拟容器视图,但是像我说的那样-没有成功。我已经尝试在将CustomView对象添加到虚拟容器并设置其四个约束后调用您的方法,但是没有成功-结果相同。CustomView仍然是150 x 50,而不是像虚拟容器一样大(我认为它应该是)。 - uerceg
我在谈论添加xib作为子视图后调用“-(void)setup”方法。你的xib视图缺少约束。 - Andrea
你确定在添加视图之前将xib视图的translatesAutoresizingMaskIntoConstraints设置为NO吗?因为我在编辑后添加了它。 - Andrea
stretchToSuperView 很棒。 - Victor
3
这个答案可能会达到预期效果,但是在您的自定义XIB中将self.bounds = self.view.bounds;更改为view.frame = self.bounds将产生相同的效果,而无需做其他任何事情。这里有一个用Swift演示这个想法的视频:https://www.youtube.com/watch?v=H-55qZYc9qI - maganap
显示剩余2条评论

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

注意:只需使用视图作为参数来调用函数。
这有助于我解决Swift 4.0中.xib重设问题。

1
在你的代码中,应该有类似这样的内容:
首先,在视图控制器或自定义视图中,你需要有一个指向要更改的宽度约束的 IBOutlet(我不知道你在哪里计算约束值的逻辑)。
@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
}

希望这对你有用。祝你好运!

Andrea通过他的回答帮助了我,但也非常感谢您愿意提供帮助。最好的问候。 - uerceg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接