UINavigationController的總結

一、UINavigationBar導航欄


一、UINavigationBar的簡介

  • UINavigationBar 是由 UINavigationController 管理的,要顯示或隱藏 UINavigationBar 必須通過 UINavigationController 來控制。
self.navigationController.navigationBarHidden = NO;
[self.navigationController setNavigationBarHidden:NO animated:YES];
  • 可以使用 UINavigationBar 類的方法和屬性來自定義導航欄的外觀,但是絕對不能更改其框架,邊界或alpha值或直接修改其視圖層次。

1、UINavigationBar的視圖層次

在這裏插入圖片描述

  • 1 表示UINavigationBar,它默認是透明的,劉海屏上的 frame = (0 44; 414 44);非劉海屏 frame = (0 20; 414 44)

  • 2 表示_UIBarBackground,它的背景默認爲透明,其包2個子視圖,其劉海屏frame = (0 -44; 414 88)

  • 3 表示UIVisualEffectView,它是 _UIBarBackground 的子視圖

  • 4 表示_UIVisualEffectBackdropView,他是 UIVisualEffectView 的子視圖。

  • 5 表示 _UINavigationBarContentView

其中_UIBarBackgroundShadowView和它自身的子視圖_UIBarBackgroundShadowContentImageView,代表下劃線,其frame = (0 0; 414 0.333333)。

在這裏插入圖片描述
在這裏插入圖片描述


UINavigationBar的默認效果

在這裏插入圖片描述
在這裏插入圖片描述

二、UINavigationBar導航欄的使用

UINavigationBar 是 UINavigationController 的一個屬性,

1、barStyle 指定其外觀的導航欄樣式

默認值爲 UIBarStyleDefault,即 採用白色樣式的導航欄。一般不對它進行其它修改,採用默認值。

self.navigationController.navigationBar.barStyle =  UIBarStyleBlack;

2、barTintColor修改導航欄的顏色

注意: barTintColor 無法設置爲透明的顏色

// 修改了導航欄的顏色,不可見
self.navigationController.navigationBar.backgroundColor = [UIColor yellowColor];
// 修改了UIVisualEffectView的顏色,即導航欄顯示時的顏色,常用
self.navigationController.navigationBar.barTintColor = [UIColor greenColor];

3、translucent導航欄透明

translucent 決定了導航欄是否是半透明(毛玻璃效果)。

self.navigationController.navigationBar.translucent = YES;

其默認值比較特別:

  • 如果導航欄沒有自定義背景圖像,或者背景圖像的任何像素的Alpha值小於1.0,則此屬性的默認值爲YES。
  • 如果背景圖像是完全不透明的,則此屬性的默認值爲NO。
// 如果爲yellow1,則translucent默認爲 YES
UIImage *yellow1 = [UIImage imageWithColor:[[UIColor yellowColor] colorWithAlphaComponent:0.3]];

// 如果爲yellow2,則translucent默認爲 NO
UIImage *yellow2 = [UIImage imageWithColor:[UIColor yellowColor]];

[self.navigationController.navigationBar setBackgroundImage:yellow forBarMetrics:UIBarMetricsDefault];
1、當 translucent 爲 YES 時

在這裏插入圖片描述

  • 當 translucent 爲 YES 時,導航欄的背景爲半透明,需要配置UIViewController的 edgesForExtendedLayout 和 extendedLayoutIncludesOpaqueBars 屬性,以使 ViewController 的 View 在 UINavigationBar 底部 開始佈局顯示內容。
// 爲ViewController擴展邊緣的方式,默認爲: UIRectEdgeAll,系統建議不要修改,在iOS11中可用 的safeAreaLayoutGuide和safeAreaInsets屬性替代
self.edgesForExtendedLayout = UIRectEdgeNone;
// 或 self.edgesForExtendedLayout = UIRectEdgeLeft | UIRectEdgeRight;

// 告訴控制器佈局時是否包含不透明導航條,默認值爲NO
self.extendedLayoutIncludesOpaqueBars = NO;
2、當 translucent 爲 NO 時

當 translucent 爲 NO 時,並且沒有設置導航欄的背景色時,系統會自動添加導航欄背景色爲白色,並且 ViewController 的佈局會從 UINavigationBar 的 底部 開始佈局。如下代碼:

在這裏插入圖片描述
如果此時需要VC延展到 UINavigationBar 的下方佈局時,需要進行如下設置:

self.navigationController.navigationBar.translucent = NO;
self.edgesForExtendedLayout = UIRectEdgeAll;
self.extendedLayoutIncludesOpaqueBars = YES;

在這裏插入圖片描述


4、shadowImage 設置導航欄底部下劃線的顏色

在這裏插入圖片描述

官方文檔:用於導航欄的陰影圖像。默認值爲nil,即採用系統自帶的陰影圖像。可以進行如下自定義:

UIImage *gray = [UIImage imageWithColor:[[UIColor grayColor] colorWithAlphaComponent:0.1]];
self.navigationController.navigationBar.shadowImage = gray;

注意: Apple文檔中表述,shadowImage 與 setBackgroundImage:forBarMetrics: 方法配套使用,否則你設置下劃線的顏色無效,它依然採用系統自帶的。

  • 所以我們經常寫隱藏導航欄下劃線會如下這樣寫:
self.navigationController.navigationBar.shadowImage = [UIImage new];
UIImage *white = [UIImage imageWithColor:[UIColor whiteColor]];
[self.navigationController.navigationBar setBackgroundImage:white forBarPosition:UIBarPositionAny barMetrics:UIBarMetricsDefault];
  • 如果你就是不想設置 [navigationBar setBackgroundImage:…],只想用 barTintColor 來改變顏色和控制導航欄下劃線的顯示顏色,可進行如下設置:
self.navigationController.navigationBar.translucent = NO;
self.navigationController.navigationBar.barTintColor = [UIColor whiteColor];

// 顯示不同的下劃線顏色
self.navigationController.navigationBar.shadowImage = clear or yellow ...;

// 使用默認的顏色
self.navigationController.navigationBar.shadowImage = nil;

5、setBackgroundImage:設置導航欄背景圖片

可以通過設置導航欄背景圖片,來達到設置它的顏色。

// 方式一:
// iOS 5.0
// 該方法等價於:setBackgroundImage:forBarPosition:UIBarPositionAny barMetrics:
UIImage *red = [UIImage imageWithColor:[UIColor redColor]];
[self.navigationController.navigationBar setBackgroundImage:red forBarMetrics:UIBarMetricsDefault];

// 方式二:
// ios 7.0
UIImage *blue = [UIImage imageWithColor:[UIColor whiteColor]];
[self.navigationController.navigationBar setBackgroundImage:blue forBarPosition:UIBarPositionAny barMetrics:UIBarMetricsDefault];

優先級問題:方式二 > 方式一 > barTintColor
設置了優先級高的,優先級低的無論怎麼設置都失效。


6、修改導航欄返回按鈕

導航控制的返回按鈕,默認採用前一個 VC 的 Title,作爲其 backTitle。

在這裏插入圖片描述

以下屬性在 A push B 時,在 A 中設置,改變B的返回按鈕

// 設置返回按鈕 titile 和 image 的顏色
self.navigationController.navigationBar.tintColor = [UIColor redColor];

// 後退按鈕旁邊顯示的圖片,它和backIndicatorTransitionMaskImage配套使用
self.navigationController.navigationBar.backIndicatorImage = [UIImage imageNamed:@"back_btn_clo_black"];
// 在 pop 和 push 過渡期間用作內容遮罩的圖像
self.navigationController.navigationBar.backIndicatorTransitionMaskImage = [UIImage imageNamed:@"nav_btn_back"];

從Apple 的官方文檔中,如果需要修改導航欄返回按鈕的 title,則通過設置 navigationItem.backBarButtonItem 即可。

 UIBarButtonItem *backItem = [[UIBarButtonItem alloc] initWithTitle:@"返回" style:UIBarButtonItemStyleBordered target:nil action:nil];
self.navigationItem.backBarButtonItem = backItem;

// 隱藏返回圖片
self.navigationController.navigationBar.backIndicatorImage = [UIImage new];
  • 如果設置了 navigationItem.leftBarButtonItem ,則 系統自帶的 BackItem 設置無效,同時 navigationBar.delegate 也無效,滑動返回手勢失效。

解決 navigationItem.leftBarButtonItem 導致側滑返回失效辦法:

方式一:在 viewController 中設置 側滑返回代理

navigationController?.interactivePopGestureRecognizer?.delegate = self

方式二:設置全屏滑動返回

override func viewDidLoad() {
    super.viewDidLoad()
    
    
    let target = self.interactivePopGestureRecognizer?.delegate
    let pan = UIPanGestureRecognizer(target: target, action: #selector(handleNavigationTransition(_:)))
    pan.delegate = self
    view.addGestureRecognizer(pan)
    self.interactivePopGestureRecognizer?.isEnabled = false
    
}

7、修改導航欄Title

self.navigationController.navigationBar.topItem.title = @"首頁";
// 或者:self.navigationItem.title = @"首頁";
// 調整導航欄 title 垂直方向的位置,一般不需要調整
[self.navigationController.navigationBar setTitleVerticalPositionAdjustment:3 forBarMetrics:UIBarMetricsDefault];
// 設置導航欄 title 的樣式
self.navigationController.navigationBar.titleTextAttributes = @{NSFontAttributeName: [UIFont systemFontOfSize:18], NSForegroundColorAttributeName: [UIColor blackColor]};

8、UINavigationBarDelegate

  • 如果 UINavigationBar 是由 UINavigationController 創建的,並且由該對象管理,則不得更改此屬性的值。 並且 UINavigationController 充當其創建的 UINavigationBar 的 delegate。

  • 我們可以在自定義的 UINavigationController 中,實現該代理方法。

@property(nonatomic, weak) id<UINavigationBarDelegate> delegate;

9、 ios11新增

@property (nonatomic, readwrite, assign) BOOL prefersLargeTitles;
@property(nullable, nonatomic, copy) NSDictionary<NSAttributedStringKey, id> *largeTitleTextAttributes;

10、ios13新增

@property (nonatomic, readwrite, copy) UINavigationBarAppearance *standardAppearance;
@property (nonatomic, readwrite, copy, nullable) UINavigationBarAppearance *compactAppearance;
@property (nonatomic, readwrite, copy, nullable) UINavigationBarAppearance *scrollEdgeAppearance;

三、UINavigationBar 和 UINavigationItem

UINavigationBar雖然繼承自UIView, 但是其並非通過addSubview:方法來添加子視圖, 而是通過其管理的UINavigationItem堆棧來決定展示在UINavigationBar中的內容。

  • UINavigationBar上面顯示什麼內容,取決於當前控制器的navigationItem屬性。
  • UINavigationBar是view, navigationItem是model。
@interface UINavigationBar : UIView <NSCoding, UIBarPositioning> 


@interface UINavigationItem : NSObject <NSCoding>

四、UINavigationBarDelegate 和 UINavigationControllerDelegate

  • UINavigatonController 會自動將包含的UINavigationBar 的 delegate 指向自己。所以我們不能在 UINavigationController 中設置它。
@implementation ZMNavigationController

- (void)viewDidLoad {
    [super viewDidLoad];
 // 這樣設置會崩潰
//    self.navigationBar.delegate = self;
}
  • UINavigationController 的 delegate 則需要我們手動設置。

二、UINavigationItem

  • UINavigationItem 是 UIViewController 的一個屬性,只有當把 UIViewController push 到導航棧中,纔可以訪問。並且第一次訪問該屬性時,將創建UINavigationItem對象。

  • 每個 push 到導航堆棧的 ViewController 都必須具有UINavigationItem對象,該對象包含要在 UINavigationBar 中顯示的按鈕和視圖。

// 設置返回按鈕title
// 設置 push 到 B 頁面的默認返回文字位置的內容
// 爲backBarButtonItem綁定事件會被忽略,UINavigatonController自動爲其綁定事件,只做POP動作。
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"返回" style:UIBarButtonItemStylePlain target:self action:@selector(clickBack)];

UINavigationItem的作用

UINavigationBar 是一個UIView,它屬於UINavigationController,所以說當修改它的屬性,會影響到導航控制器管理的所有視圖控制器。

UINavigationItem 它是 NSOject 類型,它屬於當前的 UIViewController,修改它只會影響到當前的控制器;

  • 可以設置 backBarButtonItem 的返回文字,而 返回圖片則需要通過 navigationBar 設置。

  • 主要用來操作:backBarButtonItem、leftBarButtonItem、rightBarButtonItem、titleView、title


三、UINavigationController 的 PopGestureRecognizer

self.interactivePopGestureRecognizer?.delegate = self

iOS 7.0 以後新增了 pop 手勢,從屏幕的左邊緣滑動 VC 的 View 會跟隨手指進行 pop 操作。

實際的開發中我們經常需要設置全屏滑動觸發 pop 操作,具體看利用Runtime自定義控制器POP手勢動畫


UINavigationController的Tips

修改導航欄的透明度

- (void)setNavigationBarBackgroundAlpha:(CGFloat)alpha {
    UIView *barBackgroundView = self.navigationBar.subviews.firstObject;
    if (@available(iOS 13.0, *)) {
        barBackgroundView.alpha = alpha;
        return;
    }
    // 處理底部分割線
    UIView *shadowView = [barBackgroundView valueForKey:@"_shadowView"];
    shadowView.hidden = (alpha < 1.0);
    // 處理背景
    if (self.navigationBar.isTranslucent) {
        if (@available(iOS 10.0, *)) {
            UIView *backgroundEffectView = [barBackgroundView valueForKey:@"_backgroundEffectView"];
            backgroundEffectView.alpha = alpha;
        } else {
            UIView *adaptiveBackdrop = [barBackgroundView valueForKey:@"_adaptiveBackdrop"];
            adaptiveBackdrop.alpha = alpha;
        }
    } else {
        barBackgroundView.alpha = alpha;
    }
}


參考博客:
HChong
簡書
掘金UINavigationController全屏側滑返回

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章