如何在UINavigationBar下面添加一个视图

好的,在iOS私密类深入深渊和最终可重用代码的讨伐之间有一条细微的界限。

我想在UINavigationBar下面有一个大约40像素高的任意UIView。 现在,通常这可以扔在封闭的视图,但我想这个UIView 在UINavigationBar。 所以当用户钻入不同的视图时,导航栏下方的UIView将保持不变。

我已经尝试构build一个容器UIViewController,它将作为RootViewController放置在UINavigationController中,但这不起作用,因为当用户按下一个新视图时,我的容器视图被滑出,并且我的持续性栏随它而去。

如果我把一个UINavigationController放进一个UIViewController里面,比我能覆盖的UIView栏,但内部的意见似乎仍然不会改变他们的框架的正确,以及当一个视图被推或popup导航堆栈,由于UINavigationController不知道他们在屏幕下方的新的Y位置,所以animation效果不佳(改变它们的Y位置以及离开屏幕)。

所以,我研究了UINavigationController的子类。 我知道,它说“ 这个类不适用于子类 ”,但总是有例外。 这个子类可以简单地在导航栏下面添加持久的UIView,调整内容部分的大小(正常视图将出现在哪里),一切都将正常工作。

但是在玩了ObjC运行时类之后,我找不到需要调整的魔术variables(UINavigationController的_containerView就是这样一个variables)。

什么是最好的方法来做到这一点? 有没有比复制和粘贴代码更好的方法,使这个酒吧进入所有的ViewControllers将显示它?

我猜你正在寻找一个上传进度栏或类似的东西,出现在导航栏下面,并与你保持在一起,不pipe你去哪个视图。 几个星期前,我刚刚实现了这个额外的警告 – 我们使用IIViewDeck,所以我们必须能够处理整个UINavigationController的变化。

我做的是我添加视图作为导航栏的子视图。 我的导航栏和导航控制器都是分类的(并且应用程序已经提交到App Store而不会发生)。 起初,我这样做是因为我需要在没有UIAppearance的帮助下自定义导航栏,但现在它似乎是继续给予的礼物,因为它给了我相当多的灵活性,这样的任务。

  1. 创build自定义的UINavigationController和UINavigationBar
  2. 如果你需要处理触摸,你将不得不实施hitTest:withEvent – 我已经包含了下面的代码。
  3. 如果你有一个改变UINavigationController视图的应用程序(比如一个标签栏应用程序,或者一个允许你改变中央UINavigationController的侧面菜单),我实现了KVO来监视中央视图控制器,这样进度视图可以“按照'周围。 代码也在下面。

这是我如何创build一个自定义的UINavigationController和UINavigationBar:

 + (ZNavigationController *)customizedNavigationController { //This is just a regular UINavigationBar subclass - doesn't have to have any code but I personally override the pop/push methods to call my own flavor of viewWill/Did/Appear/Disappear methods - iOS has always been a bit finicky about what gets called when ZNavigationController *navController = [[ZNavigationController alloc] initWithNibName:nil bundle:nil]; // Ensure the UINavigationBar is created so that it can be archived. If we do not access the // navigation bar then it will not be allocated, and thus, it will not be archived by the // NSKeyedArchvier. [navController navigationBar]; // Archive the navigation controller. NSMutableData *data = [NSMutableData data]; NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data]; [archiver encodeObject:navController forKey:@"root"]; [archiver finishEncoding]; // Unarchive the navigation controller and ensure that our UINavigationBar subclass is used. NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data]; [unarchiver setClass:[ZNavigationBar class] forClassName:@"UINavigationBar"]; ZNavigationController *customizedNavController = [unarchiver decodeObjectForKey:@"root"]; [unarchiver finishDecoding]; // Modify the navigation bar to have a background image. ZNavigationBar *navBar = (ZNavigationBar *)[customizedNavController navigationBar]; [navBar setTintColor:kZodioColorBlue]; [navBar setBackgroundImage:[UIImage imageNamed:@"Home_TopBar_RoundCorner_withLogo"] forBarMetrics:UIBarMetricsDefault]; return customizedNavController; } 

现在你已经有了自己定制的ZNavigationBar (这些是我的代码中的实际名称 – 是一个痛苦的经历和改变他们所有)你实现触摸处理 – variables名称是非常自我解释 – 我想检测触摸“左侧配件视图“,”主要区域“或”右侧配件视图“

 - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { DDLogVerbose(@"Got a touch at point X: %f Y: %f", point.x, point.y); //First check if the progress view is visible and touch is within that frame if ([[JGUploadManager sharedManager] progressViewVisible] && CGRectContainsPoint([[JGUploadManager sharedManager] uploadProgressView].frame, point)) { //Modified frame - have to compensate for actual position of buttons in the view from the standpoint of the UINavigationBar. Can probably use another/smarter method to do this. CGRect modifiedCancelButtonFrame = [[JGUploadManager sharedManager] uploadProgressView].leftAccessoryFrame; modifiedCancelButtonFrame.origin.y += [[JGUploadManager sharedManager] uploadProgressView].leftAccessoryView.superview.frame.origin.y; //Do the same thing for the right view CGRect modifiedRetryButtonFrame = [[JGUploadManager sharedManager] uploadProgressView].rightAccessoryFrame; modifiedRetryButtonFrame.origin.y += [[JGUploadManager sharedManager] uploadProgressView].rightAccessoryView.superview.frame.origin.y; //Touch is on the left button if ([[JGUploadManager sharedManager] progressViewVisible] && [[JGUploadManager sharedManager] uploadProgressView].leftAccessoryView && CGRectContainsPoint(modifiedCancelButtonFrame, point)) { return [[JGUploadManager sharedManager] uploadProgressView].leftAccessoryView; } //Touch is on the right button else if ([[JGUploadManager sharedManager] progressViewVisible] && [[JGUploadManager sharedManager] uploadProgressView].rightAccessoryView && CGRectContainsPoint(modifiedRetryButtonFrame, point)) { return [[JGUploadManager sharedManager] uploadProgressView].rightAccessoryView; } //Touch is in the main/center area else { return [[JGUploadManager sharedManager] uploadProgressView]; } } //Touch is not in the progress view, pass to super else { return [super hitTest:point withEvent:event]; } } 

现在,“跟随”导航控制器的KVO部分。 在某处实现KVO – 我使用了一个单独的“上传pipe理器”类,并将其放在该类的init中,但这取决于您的应用程序的devise:

 ZRootViewController *rootViewController = ((JGAppDelegate*)[UIApplication sharedApplication].delegate).rootViewDeckController; [rootViewController addObserver:sharedManager forKeyPath:@"centerController" options:NSKeyValueObservingOptionNew context:nil]; 

那么当然实现这个:

 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { DDLogVerbose(@"KVO shows something changed: %@", keyPath); if ([keyPath isEqualToString:@"centerController"]) { DDLogVerbose(@"Center controller changed to: %@", object); [self centerViewControllerChanged]; } } 

最后是centerViewControllerChanged函数:

 - (void)centerViewControllerChanged { ZRootViewController *rootViewController = ((JGAppDelegate*)[UIApplication sharedApplication].delegate).rootViewDeckController; ZNavigationController *centerController = (ZNavigationController *)rootViewController.centerController; //Check if the upload progress view is visible and/or active if (self.uploadProgressView.frame.origin.y > 0 && self.uploadProgressView.activityIndicatorView.isAnimating) { [centerController.navigationBar addSubview:self.uploadProgressView]; } //Keep weak pointer to center controller for other stuff DDLogVerbose(@"Setting center view controller: %@", centerController); self.centerViewController = centerController; } 

如果我误解了你的问题,那么我只是打了世界上最长的答案,徒劳无功。 我希望这有助于让我知道,如果有任何部分需要澄清!