2013-03-26 2 views
2

Хорошо, есть тонкая грань между взломом слишком далеко в глубокую пропасть, которая является частным классом iOS, и крестовый поход для окончательного многоразового кода.Как добавить представление под UINavigationBar

Я хочу иметь произвольный UIView высотой около 40 пикселей ниже UINavigationBar. Теперь, как правило, это может быть брошено в закрытом представлении, но я хочу, чтобы этот UIView оставался с UINavigationBar. Поэтому, когда пользователь разворачивается в разные виды, UIView под навигационной панелью останется.

Я попытался создать контейнер UIViewController, который будет размещен как RootViewController в UINavigationController, но это не помогает, так как когда пользователь нажимает на новое представление, мой контейнерный вид выходит из строя, и мой упорный бар идет с ним.

Если я положил UINavigationController внутри UIViewController, то я могу наложить панель UIView, которую я хочу, но внутренние представления, похоже, не изменят их кадр должным образом, а также когда изображение выталкивается или выскакивает в навигационном стеке, представления ненадлежащим образом изменяются (изменяя их положение Y, а также перемещаясь от экрана, из-за того, что UINavigationController не знает о своей новой позиции Y дальше по экрану.)

Итак, я просмотрел подклассификацию UINavigationController. Я знаю, он говорит: «Этот класс не предназначен для подкласса», но всегда есть исключения. Этот подкласс просто добавит устойчивый UIView под панелью навигации, изменит размер раздела содержимого (где появится нормальное представление), и все будет работать.

Но после игры с классами времени выполнения ObjC я не могу найти волшебные переменные, чтобы настроить их на работу (_containerView из UINavigationController была одной из таких переменных).

Каков наилучший способ для этого? Есть ли лучший способ, чем копирование и вставка кода, чтобы сделать этот бар во всех ViewControllers, который отобразит его?

+0

Здесь можно изменить панель навигации. Возможно, вы сможете изменить его размер и добавить свое мнение. http://sebastiancelis.com/2012/03/05/subclassing-hard-to-reach-classes/ –

ответ

5

Я предполагаю, что вы хотите установить индикатор выполнения загрузки или что-то подобное, что отображается под панелью навигации и остается с вами независимо от того, к какому виду вы идете. Я только что реализовал это пару недель назад с дополнительным предостережением - мы используем 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, которая «следует» навигационному контроллеру. Реализовать КВО где-то - я использую одноэлементный класс «Менеджер загрузки», и я положил его в инициализации этого класса, но это до дизайна вашего приложения:

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 ответ в мире, напрасно лол. Надеюсь, это поможет и сообщит мне, если какая-либо часть нуждается в разъяснении!

Смежные вопросы