Xummer

用系统控件实现 “你的名字” 中的 NavigationBar

随着“你的名字”在中国的热映, 各种仿 My Diary 的 App 层出不穷。大部分在 UI 上还都是比较粗糙的。本文将使用 iOS 私有 api 实现顶部 NavigationBar 的效果。如果使用将无法通过 AppStore 审核,但不是还有 JSPatch 嘛(笑。

先上一个图

大家都知道 iOS 7 以上 UINavigatinBar 默认的高度是 64 (44 NavigationBar + 20 statusBar), 系统也没有给出可以自定义 NavigationBar 的方法,如果要实现这个效果基本上就是再自己重写一个空间了。但是我们能看到在 AppStore 中是有类似实现的,具体如下图。

也就是说 Apple 是有这个控件的,只是藏起来而已,用 Reveal 看了下后发现是一个 _UINavigationControllerPalette 类,RuntimeHeader 还是在 UIKit 里的,好了到这里就知道我们也可以调用这些私有 api 来实现这个效果。

Talk is cheap, show me the code.

主要用到的私有 API 有以下这些:

@interface _UINavigationControllerPalette : UIView

- (id)_initWithNavigationController:(UINavigationController *)navigationCtrl forEdge:(unsigned int)edge;

- (void)_setPinningBar:(UINavigationBar *)navigationBar;

@end

@interface UINavigationController ()

- (void)attachPalette:(_UINavigationControllerPalette *)palette isPinned:(BOOL)isPinned;

- (void)_detachPalette:(_UINavigationControllerPalette *)palette;

@end

关键代码 Github

_UINavigationControllerPalette *palette = [[_UINavigationControllerPalette alloc] _initWithNavigationController:self.navigationController forEdge:0];
[palette _setPinningBar:self.navigationController.navigationBar];
    
palette.frame = (CGRect){
   .origin.x = 0,
   .origin.y = 64,
   .size.width = CGRectGetWidth(self.navigationController.view.frame),
   .size.height = 38
};
    
UILabel *titleLabel = [[UILabel alloc] initWithFrame:palette.bounds];
titleLabel.textAlignment = NSTextAlignmentCenter;
titleLabel.text = @"Diary";
titleLabel.textColor = MAIN_TINTCOLOR;
titleLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    
[palette addSubview:titleLabel];
    
[self.navigationController setValue:palette forKey:@"_topPalette"];
    
self.navigationItem.titleView = [[UISegmentedControl alloc] initWithItems:@[@"Entries", @"Calendar", @"Dairy"]];

// 使其显示
[self.navigationController attachPalette:palette isPinned:YES];

// 取消显示
[self.navigationController _detachPalette:palette];

最终效果图

-以上-