2013-03-26 42 views
2

好了一個觀點,有黑客太遠的深淵是iOS的私有類之間的細線,併爲最終的可重用代碼的討伐。如何添加以下UINavigationBar的

我想有一個任意的UIView,約40像素的高度,在UINavigationBar的下方。現在,通常這會扔在封閉的視圖,但我想這對UIView的留的UINavigationBar的。所以當用戶鑽入不同的視圖時,導航欄下方的UIView將保持存在。我試圖建立一個容器UIViewController,它將作爲RootViewController放置在UINavigationController中,但這沒有幫助,因爲當用戶按下新視圖時,我的容器視圖被滑出,並且我堅持不懈的酒吧與它同行。

如果我把一個UINavigationController放到一個UIViewController中,比我可以覆蓋UIView欄,但是內部視圖仍然不會改變它們的框架的正確,以及當一個視圖被推入或彈出由於UINavigationController不知道它們在屏幕下方的新Y位置,因此導航堆棧,視圖動畫效果較差(更改它們的Y位置以及從屏幕移開)。

因此,我研究了UINavigationController的子類。我知道,它說:「此類不適用於子類。」,但總有例外。這個子類將簡單的添加持續性的UIView導航欄下,調整​​內容部分(在普通視圖中會出現),一切都將只是工作。

但是在玩了ObjC運行時類之後,我找不到需要調整的魔術變量才能使它工作(UINavigationController的_containerView就是這樣一個變量)。

這樣做的最好方法是什麼?有沒有比複製和粘貼代碼更好的方法,使這個酒吧進入所有的ViewControllers將顯示它?

+0

以下是更改導航欄的方法。您可能可以調整其大小並添加您的視圖。 http://sebastiancelis.com/2012/03/05/subclassing-hard-to-reach-classes/ – 2013-03-27 00:40:55

回答

5

我猜你正在尋找一個上傳進度條或類似的東西,顯示在導航欄下方,並與你保持關係,無論你去哪個視圖。幾周前我剛剛實現了這個功能,並提出了額外的警告 - 我們使用IIViewDeck,因此我們必須能夠處理整個UINavigationController的更改。

我所做的是我添加的視圖導航欄的子視圖。我的導航欄和導航控制器都是分類的(並且應用程序已經無需事先提交到App Store)。起初,我這樣做是因爲我需要在沒有UIAppearance的幫助下自定義導航欄,但現在它似乎是繼續給予的禮物,因爲它給了我相當多的靈活性來完成這樣的任務。

  1. 創建自定義的UINavigationController和UINavigationBar的
  2. 如果你需要處理的觸摸,你將不得不實行hitTest:withEvent - 以下附上太多的代碼。
  3. 如果你有改變UINavigationController視圖(如標籤欄的應用程序,或用側面菜單,讓你改變UINavigationController這是在中心)的應用程序,我實現了志願監控中心視圖控制器,這樣的進展觀點可以'追隨'它周圍。代碼也在下面。

這是我如何創建一個自定義的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(這些都是從我的代碼的實際名稱 - 是一個痛苦的經歷和變化所有這些)你實現觸摸操作 - 變量名是相當自我解釋 - 我要檢測一個「左附件視圖」接觸,「主要區域」,或該進度條的「右附件視圖」:

- (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部分。實行志願的地方 - 我用一個單「上傳管理器」類,我把它放在類的初始化,但是這取決於你的應用程序的設計:

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

如果我誤解了你的問題,然後我剛鍵入的最長的SO回答世界,白白笑。我希望這有助於並讓我知道是否有任何部分需要澄清!

相關問題