簡體   English   中英

如何在UINavigationBar下面添加視圖

[英]How to add a view below UINavigationBar

好吧,在深入深入iOS私有類的深淵與對最終可重用代碼進行的探索之間有一條很好的界線。

我想在UINavigationBar下面有一個大約40像素高的任意UIView。 現在,通常這會扔在封閉的視圖,但我想這個UIView的在UINavigationBar的。 因此,當用戶向下鑽取不同的視圖時,導航欄下方的UIView將保持存在。

我嘗試構建一個容器UIViewController,該容器將作為RootViewController放置在UINavigationController中,但這無濟於事,因為當用戶按入新視圖時,我的容器視圖會滑出,而我的持久性欄隨它去。

如果我將UINavigationController放在UIViewController內,則可以覆蓋所需的UIView欄,但是內部視圖似乎仍無法正確更改其框架,以及在將視圖從導航堆棧中推入或彈出時,視圖的動畫效果很差(由於UINavigationController不知道屏幕下方的新Y位置,因此更改了它們的Y位置以及移離屏幕)。

因此,我研究了UINavigationController的子類化。 我知道,它說“ 該類不打算用於子類化。 ”,但是總是有例外。 該子類將簡單地在導航欄下方添加持久性UIView,調整內容部分的大小(將在其中顯示普通視圖),然后一切正常。

但是在玩了ObjC運行時類之后,我找不到要調整的魔術變量來使它起作用(UINavigationController的_containerView是這樣的變量)。

最好的方法是什么? 除了復制和粘貼代碼以使此Bar進入將顯示它的所有ViewController之外,還有更好的方法嗎?

我猜您正在尋找將上傳進度條或類似的內容顯示在導航條下方的方法,並且無論您使用哪種視圖,它都可以陪伴您。 我在幾周前剛剛實現了這一點,但需要額外注意:我們正在使用IIViewDeck,因此我們也必須能夠處理整個UINavigationController的更改。

我所做的是將視圖添加為導航欄的子視圖。 我的導航欄和導航控制器都是子類的(並且應用程序已經提交給App Store而沒有發生任何情況)。 起初我這樣做是因為我需要在沒有UIAppearance的幫助下自定義導航欄,但是現在它似乎一直在饋贈,因為它為諸如此類的任務提供了很多靈活性。

  1. 創建自定義的UINavigationController和UINavigationBar
  2. 如果需要處理觸摸,則必須實現hitTest:withEvent我也在下面包含了該代碼。
  3. 如果您有一個具有更改UINavigationController視圖的應用程序(例如選項卡欄應用程序,或者具有允許您更改位於中心的UINavigationController的側面菜單),則可以實施KVO來監視中心視圖控制器,以便進度視圖可以“跟隨它。 代碼也在下面。

這是我創建自定義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部分。 在某處實現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答案,但沒用。 希望對您有所幫助,並讓我知道是否需要澄清!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM