自定义 UIView(使用 xib)自动布局宽度并从委托中推送 viewcontroller

Custom UIView (with xib) autolayout width and pushing viewcontroller from delegate

我在为自定义 UIView 设置约束并在我点击视图时通过委托推送新 ViewController 时遇到问题。我试图找到解决这些问题的方法,但找不到能回答这些问题的方法。

自动布局问题

我有一个自定义的 xib 文件,它被设置为大小:自由形式,宽度:600 和高度:25,它还包括一个标签和一个按钮,在这个视图中有约束。我已经在我想要的导航栏下方成功添加了这个视图。问题是,它没有做任何事情来使它的宽度与导航栏/window 大小相等(我尝试了多种选择,例如,为宽度为 window/导航栏的视图制作新框架).无论我尝试什么,它似乎始终只有静态 600 宽度。

前两个约束起作用了,它出现在导航栏下方 25 点并居中。但是最后一个什么也做不了。

我应该如何正确执行此操作?到目前为止有这个:

[self.subView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addSubview:self.subView];

[self.view addConstraint:
 [NSLayoutConstraint constraintWithItem:self.subView
                              attribute:NSLayoutAttributeBottom
                              relatedBy:NSLayoutRelationEqual
                              toItem:self.navBar
                              attribute:NSLayoutAttributeBottom
                              multiplier:1
                              constant:25.0]];

[self.view addConstraint:
 [NSLayoutConstraint constraintWithItem:self.subView
                              attribute:NSLayoutAttributeCenterX
                              relatedBy:NSLayoutRelationEqual
                                 toItem:self.view
                              attribute:NSLayoutAttributeCenterX
                             multiplier:1.0
                               constant:0.0]];

[self.view addConstraint:
 [NSLayoutConstraint constraintWithItem:self.subView
                              attribute:NSLayoutAttributeWidth
                              relatedBy:NSLayoutRelationEqual
                                 toItem:self.view
                              attribute:NSLayoutAttributeWidth
                             multiplier:1
                               constant:0]];

我是否应该对 xib 文件做更多的事情,使这个宽度适合它的父视图?我还在自定义视图中实现了 initWithFrame、initWithCoder 和 intrinsicContentSize。


解决方案

我最终为我的子视图制作了 containerView,并将其垂直和水平居中,并找到了正确的宽度约束。我也忘记更新我的子视图的视图框架以匹配导航栏宽度。这是我最后的结果(如果有更好的方法,我很高兴批评):

self.containerView = [[UIView alloc] initWithFrame:CGRectMake(0, 62, self.navBar.frame.size.width, 25)];
[self.view addSubview:self.containerView];

self.subView = [[SubView alloc]init];
[self.subView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.containerView addSubview:self.subView];

self.subView.view.frame = CGRectMake(0, 0, self.containerView.frame.size.width, self.containerView.frame.size.height);

[self.containerView addConstraint:
 [NSLayoutConstraint constraintWithItem:self.subView
                              attribute:NSLayoutAttributeCenterY
                              relatedBy:NSLayoutRelationEqual
                                 toItem:self.containerView
                              attribute:NSLayoutAttributeCenterY
                             multiplier:1.0
                               constant:0.0]];

[self.containerView addConstraint:
 [NSLayoutConstraint constraintWithItem:self.subView
                              attribute:NSLayoutAttributeCenterX
                              relatedBy:NSLayoutRelationEqual
                                 toItem:self.containerView
                              attribute:NSLayoutAttributeCenterX
                             multiplier:1.0
                               constant:0.0]];

[self.containerView addConstraint:
 [NSLayoutConstraint constraintWithItem:self.subView
                              attribute:NSLayoutAttributeWidth
                              relatedBy:NSLayoutRelationEqual
                                 toItem:self.containerView
                              attribute:NSLayoutAttributeWidth
                             multiplier:1.0f
                               constant:0]];

委托问题(已解决)

这个问题的答案:查看下面 MisterGreen 的答案。

当我在我的自定义视图中使用委托创建 UITapGestureRecognizer 时出现了另一个问题。我想要的是当我点击视图时,它会打开另一个 ViewController。委托函数是这样的,我实现了我的自定义视图:

-(void)pushViewControllerUsingDelegate
{
    NSLog(@"DELEGATE WAS : %@", self.subView.delegate);
    [self pushViewController:self.anotherViewController animated:YES];
}

现在当我点击视图时出现异常:

DELEGATE WAS : <MasterViewController: 0x7fc96132e7d0> <-- Delegate is OK
*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<AnotherViewController 0x7fc961248230> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key subViewButton.'

这到底是什么意思?我有一个弱 属性 的 subViewButton IBOutlet,是否与此有关?还是有其他方法可以做到这一点?

我遵循的教程:https://www.youtube.com/watch?v=TfKv1MYxnA4

因为没有足够的数据来确切确定您遇到的问题是什么,我刚刚创建了一个代码片段,它正在运行并且完全符合您的要求。

关于约束我认为问题是缺少的高约束(除非你在别处确定它), 试着记住,当您添加约束时,为编译器提供足够的数据,以了解如何根据它的超级视图调整子视图的大小和位置,在您的情况下,它不知道高度是什么,因为您既没有提供,也没有提供底部或高度约束来确定它。

关于委托方法,你没有提供足够的数据来准确确定问题所在,所以我写了一些我认为正在做你想要得到的东西。

此代码片段已经过测试并且可以正常工作:

子视图: View.h

@protocol viewManager <NSObject>
@optional
- (void)subviewWasTapped;
@end
@interface View : UIView
@property (nonatomic, strong) id<viewManager>delegate;
@end

View.m

@implementation View

- (void)awakeFromNib{
    [super awakeFromNib];
    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(viewWasTapped:)];
    [self addGestureRecognizer:tap];
}

- (void)viewWasTapped:(NSNotification *)notification
{
    [self sendViewWasTappedToDelegate];
}

- (void)sendViewWasTappedToDelegate
{
    @synchronized(_delegate)
    {
        if([_delegate respondsToSelector:@selector(subviewWasTapped)])
        {
            [_delegate subviewWasTapped];
        }
    }
}

@end

第一视图控制器:

@interface ViewController () <viewManager>
@property (nonatomic, strong) View *subview;

@end

@implementation ViewController
@synthesize subview;
- (void)viewDidLoad {
    [super viewDidLoad];

    NSArray *subviewArray = [[NSBundle mainBundle] loadNibNamed:@"View" owner:self options:nil];
    subview = [subviewArray objectAtIndex:0];
    [subview setTranslatesAutoresizingMaskIntoConstraints:NO];
    [self.view addSubview:subview];

    [self.view addConstraint:
     [NSLayoutConstraint constraintWithItem:subview
                                  attribute:NSLayoutAttributeTop
                                  relatedBy:NSLayoutRelationEqual
                                     toItem:self.topLayoutGuide
                                  attribute:NSLayoutAttributeBottom
                                 multiplier:1.0
                                   constant:0.0]];


     [self.view addConstraint:
      [NSLayoutConstraint constraintWithItem:subview
                                  attribute:NSLayoutAttributeCenterX
                                  relatedBy:NSLayoutRelationEqual
                                     toItem:self.view
                                  attribute:NSLayoutAttributeCenterX
                                 multiplier:1.0
                                   constant:0.0]];

     [self.view addConstraint:
      [NSLayoutConstraint constraintWithItem:subview
                                  attribute:NSLayoutAttributeWidth
                                  relatedBy:NSLayoutRelationEqual
                                     toItem:self.view
                                  attribute:NSLayoutAttributeWidth
                                 multiplier:1
                                   constant:0.0]];

    // Height constraint to determine the
    [self.view addConstraint:
     [NSLayoutConstraint constraintWithItem:subview
                                  attribute:NSLayoutAttributeHeight
                                  relatedBy:NSLayoutRelationEqual
                                     toItem:nil
                                  attribute:NSLayoutAttributeNotAnAttribute
                                 multiplier:1
                                   constant:25.0]];

    [self.view layoutIfNeeded];

    [subview setDelegate:self];
}

#pragma mark - viewManager delegate method 

- (void)subviewWasTapped{
    SecondeViewController *secondeVC = [self.storyboard instantiateViewControllerWithIdentifier:@"SecondeViewController"];
    [self.navigationController pushViewController:secondeVC animated:YES];
}