UINavigationController:对所有弹出视图控制器的视图应用通用填充/边距
Posted
技术标签:
【中文标题】UINavigationController:对所有弹出视图控制器的视图应用通用填充/边距【英文标题】:UINavigationController: Apply Common Padding/Margin to all Popped View Controller's Views 【发布时间】:2010-12-28 15:32:06 【问题描述】:我有一个UINavigationController
,并且我希望弹出到堆栈中的每个视图控制器的视图都具有一个共同的填充/边距(例如,所有边均为 25 像素)。实现这一目标的最佳方法是什么?
我原本以为我可以实现UINavigationControllerDelegate
,并在navigationController:didShowViewController:animated
或navigationController:willShowViewController:animated
方法中,只需更改即将显示的视图控制器的框架。不过,这似乎没有效果。
我尝试在视图控制器的 viewDidAppear
和 viewWillAppear
方法中做同样的事情,但这也没有用。理想情况下,无论如何我都不想在控制器中添加任何逻辑,因为它们可能并不总是在导航控制器中使用。
我还没有尝试过的最后一个想法是创建一个“包装器”UIViewController
,它实际上会被推送到这个堆栈上。这个包装器会将真实视图控制器的视图添加为带有可提供所需边距的框架的子视图。这里的缺点是我需要继承UINavigationController
并覆盖pushViewController:animated
,包装器将被初始化和推送。 Apple 的文档表明 UINavigationController
不应该被子类化。
提前致谢。
【问题讨论】:
试图实现上面提到的“最后一个想法”,但遇到了障碍。由于正在显示的视图控制器实际上是由另一个视图控制器“包装”的,并且包装器实际上被压入堆栈,因此正在显示的视图控制器上的navigationController
属性是nil
。这意味着我无法获得导航控制器的句柄以便将另一个视图控制器推到顶部。
另一个数据点:在navigationController:didShowViewController:animated
中更改视图控制器的视图框架确实有效,但视图控制器首先出现在原始框架中,然后在事后调整大小,这看起来很糟糕。更改 navigationController:willShowViewController:animated
中的框架没有效果。
【参考方案1】:
我通过在UIViewController
的view
周围放置一个“包装器”UIView
而不是UIViewController
本身解决了这个问题。然后包装视图通过在layoutSubviews
方法中设置子视图的框架来填充子视图。
为了方便起见,我附上了我使用的代码。要使用,请将您的UINavigationController
替换为PaddedNavigationController
,并设置PaddedNavigationController
的insets
属性。
PaddedNavigationController.h
:
#import <Foundation/Foundation.h>
@interface PaddedNavigationController : UINavigationController
UIEdgeInsets _insets;
@property (nonatomic, assign) UIEdgeInsets insets;
@end
PaddedNavigationController.m
:
#import "PaddedNavigationController.h"
@interface PaddedView : UIView
UIView *_view;
UIEdgeInsets _insets;
@property (nonatomic, assign) UIEdgeInsets insets;
+ (PaddedView *) wrapView:(UIView *)view withInsets:(UIEdgeInsets)insets;
- (id) initWithView:(UIView *)view insets:(UIEdgeInsets)insets;
@end
@implementation PaddedNavigationController
@synthesize insets = _insets;
- (void) pushViewController:(UIViewController *)viewController animated:(BOOL)animated
//check if the UIViewController's view has already been wrapped by the PaddedView; don't want to wrap it twice
if(![viewController.view isKindOfClass:[PaddedView class]])
viewController.view = [PaddedView wrapView:viewController.view withInsets:self.insets];
[super pushViewController:viewController animated:animated];
- (void) setInsets:(UIEdgeInsets)insets
_insets = insets;
//loop through this navigation controller's view controllers and set the new insets on any PaddedViews
for(UIViewController *viewController in self.viewControllers)
if([viewController.view isKindOfClass:[PaddedView class]])
PaddedView *padded = (PaddedView *)viewController.view;
padded.insets = insets;
@end
@implementation PaddedView
@synthesize insets = _insets;
+ (PaddedView *) wrapView:(UIView *)view withInsets:(UIEdgeInsets)insets
return [[[PaddedView alloc] initWithView:view insets:insets] autorelease];
- (id) initWithView:(UIView *)view insets:(UIEdgeInsets)insets
if(self = [super initWithFrame:view.frame])
_insets = insets;
self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_view = [view retain];
[self addSubview:view];
return self;
- (void) dealloc
[_view release];
[super dealloc];
- (void) layoutSubviews
//apply the insets to the subview
_view.frame = CGRectMake(self.insets.left, self.insets.top, self.frame.size.width - self.insets.left - self.insets.right, self.frame.size.height - self.insets.top - self.insets.bottom);
- (void) setInsets:(UIEdgeInsets)insets
_insets = insets;
//we need to re-layout the subviews as the insets have changed
[self layoutSubviews];
@end
【讨论】:
我还没有机会尝试这个,但我接受它,因为它看起来是一个非常好的解决方案(至少比我能想出的任何东西都好)。谢谢! 沿着这条路走下去,事实证明这实际上并没有那么好用。您的视图控制器真的不能不知道他们的视图被包装的事实。尝试添加子视图并使用视图控制器的视图边界来确定子视图的边界将导致不希望的结果。 此外,当内存警告发生并且视图控制器的视图被重新加载时,这种方法是有问题的,而不是包装在PaddedView
实例中。以上是关于UINavigationController:对所有弹出视图控制器的视图应用通用填充/边距的主要内容,如果未能解决你的问题,请参考以下文章
错误:访问限制:“数据源”类型不是 API(对所需库的限制 ..\rt.jar)[重复]
Android Studio 3.3,Advanced Profiling 对所选进程不可用