自定义 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,是否与此有关?还是有其他方法可以做到这一点?
因为没有足够的数据来确切确定您遇到的问题是什么,我刚刚创建了一个代码片段,它正在运行并且完全符合您的要求。
关于约束我认为问题是缺少的高约束(除非你在别处确定它),
试着记住,当您添加约束时,为编译器提供足够的数据,以了解如何根据它的超级视图调整子视图的大小和位置,在您的情况下,它不知道高度是什么,因为您既没有提供,也没有提供底部或高度约束来确定它。
关于委托方法,你没有提供足够的数据来准确确定问题所在,所以我写了一些我认为正在做你想要得到的东西。
此代码片段已经过测试并且可以正常工作:
子视图:
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];
}
我在为自定义 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,是否与此有关?还是有其他方法可以做到这一点?
因为没有足够的数据来确切确定您遇到的问题是什么,我刚刚创建了一个代码片段,它正在运行并且完全符合您的要求。
关于约束我认为问题是缺少的高约束(除非你在别处确定它), 试着记住,当您添加约束时,为编译器提供足够的数据,以了解如何根据它的超级视图调整子视图的大小和位置,在您的情况下,它不知道高度是什么,因为您既没有提供,也没有提供底部或高度约束来确定它。
关于委托方法,你没有提供足够的数据来准确确定问题所在,所以我写了一些我认为正在做你想要得到的东西。
此代码片段已经过测试并且可以正常工作:
子视图: 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];
}