Objective-C的UINavigationController学习笔记

UINavigationController - 导航控制器

UINavigationController导航控制器,派生自UIViewController,是一种容器视图控制器,它定义了一种基于堆栈的方案,用于导航分层内容

UINavigationController是在导航界面中管理一个或多个子视图控制器的容器视图控制器。在这种类型的界面中,一次只能看到一个子视图控制器。在视图控制器中选择一个项目会使用动画在屏幕上推送一个新的视图控制器,从而隐藏以前的视图控制器。点击界面顶部导航栏中的“后退”按钮,将删除顶部控制器,从而显示下面的视图控制器。

UINavigationController对象使用称为导航堆栈的有序数组管理其子视图控制器。数组中的第一个视图控制器是根视图控制器,表示堆栈的底部。数组中的最后一个视图控制器是堆栈中最上面的项,表示当前显示的视图控制器。可以使用segues或此类的方法从堆栈中添加和移除视图控制器。用户还可以使用导航栏中的“后退”按钮或使用左边缘滑动手势删除最顶部的视图控制器。

UINavigationController管理界面顶部的导航栏(UINavigationBar)和界面底部的可选工具栏(UIToolbar)。导航栏始终存在并只有一个,由UINavigationController本身管理,UINavigationController使用其子视图控制器提供的内容更新导航栏(通常修改导航栏的子视图控制器同时也负责还原导航栏)。当toolbarHidden属性为NO时,UINavigationController类似地用最顶层视图控制器提供的内容更新工具栏。

导航控制器用它的代理对象协调它的行为。代理对象可以覆盖视图控制器的推送或弹出,提供自定义动画过渡,并为导航界面指定首选方向。提供的代理对象必须符合UINavigationControllerDelegate协议。

UINavigationController常用属性
@property(nullable, nonatomic,readonly,strong) UIViewController *topViewController;

函数描述:位于导航堆栈顶部的视图控制器。

@property(nullable, nonatomic,readonly,strong) UIViewController *visibleViewController;

属性描述与导航界面中当前可见视图关联的视图控制器。当前可见的视图可以属于导航堆栈顶部的视图控制器,也可以属于以模态方式呈现在导航控制器本身顶部的视图控制器。

@property(nonatomic,copy) NSArray<__kindof UIViewController *> *viewControllers;

属性描述当前在导航堆栈上的视图控制器。根视图控制器位于数组的索引0处,要返回的视图控制器位于索引n-2的位置,而顶部控制器位于索引n-1的位置,其中n是数组中的项数。为该属性分配新的视图控制器数组等效于调用将动画参数设置为NO的setViewControllers:animated:方法。

\color{red}{例如获取导航控制器堆栈中当前控制器的上一个控制器:}

- (UIViewController *)backViewController{
    NSInteger myIndex = [self.navigationController.viewControllers indexOfObject:self];
    if( myIndex !=0&& myIndex !=NSNotFound) {
        return [self.navigationController.viewControllers objectAtIndex:myIndex-1];
    }else{
        return nil;
    }
}
@property(nonatomic,getter=isNavigationBarHidden) BOOL navigationBarHidden;

属性描述一个布尔值,指示导航栏是否隐藏。如果为YES,则隐藏导航栏。 默认值为 NO。设置此属性会更改导航栏的可见性,但不会为更改设置动画。 如果要为改导航栏的可见性的更改设置动画,需要使用 setNavigationBarHidden:animated:函数。

@property(nonatomic,readonly) UINavigationBar *navigationBar; 

属性描述导航控制器(UINavigationController)管理的导航栏。允许使用UINavigationBar类的方法和属性自定义导航栏的外观,但决不能更改其frame、bounds或alpha值或直接修改其视图层次结构。要显示或隐藏导航栏,应始终通过导航控制器更改其navigationBarHidden属性或调用setNavigationBarHidden:animated:方法来显示或隐藏导航栏。

@property(nonatomic,getter=isToolbarHidden) BOOL toolbarHidden API_AVAILABLE(ios(3.0)) API_UNAVAILABLE(tvos);

属性描述一个布尔值,指示导航控制器的内置工具栏(UIToolbar)是否可见。如果此属性设置为 YES,则工具栏不可见。 此属性的默认值为 YES。

@property(null_resettable,nonatomic,readonly) UIToolbar *toolbar API_AVAILABLE(ios(3.0)) API_UNAVAILABLE(tvos);

属性描述与导航控制器(UINavigationController)关联的自定义工具栏。此属性包含对导航控制器管理的内置工具栏的引用。对该工具栏的访问仅提供给希望从工具栏显示操作表的客户端,不应该直接修改UIToolbar对象。这个工具栏的内容是通过与这个导航控制器关联的自定义视图控制器来管理的。对于导航堆栈上的每个视图控制器,可以使用UIViewController的setToolbarItems:animated:方法分配一组自定义的工具栏项。

此工具栏的可见性由 toolbarHidden 属性控制。 工具栏还遵循当前可见视图控制器的 hidesBottomBarWhenPushed 属性,并根据需要自动隐藏和显示自身。

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

属性描述导航控制器的代理对象。可以使用导航代理来执行其他操作以响应导航界面中的更改。

@property(nullable, nonatomic, readonly) UIGestureRecognizer *interactivePopGestureRecognizer API_AVAILABLE(ios(7.0)) API_UNAVAILABLE(tvOS);

属性描述负责从导航堆栈弹出顶视图控制器的手势识别器。导航控制器在其视图上安装此手势识别器,并使用它将最上面的视图控制器弹出导航堆栈。可以使用此属性检索手势识别器,并将其与用户界面中其他手势识别器的行为关联。将手势识别器连接在一起时,确保它们同时识别它们的手势,以确保手势识别器有机会处理事件。

@property (nonatomic, readwrite, assign) BOOL hidesBarsWhenKeyboardAppears API_AVAILABLE(ios(8.0)) API_UNAVAILABLE(tvos);

属性描述 : 一个布尔值,当此属性设置为 YES 时,当键盘出现时会导致导航控制器隐藏其导航栏和工具栏。 此属性的默认值为 NO。

@property (nonatomic, readwrite, assign) BOOL hidesBarsOnSwipe API_AVAILABLE(ios(8.0)) API_UNAVAILABLE(tvos);

属性描述 : 一个布尔值,指示导航栏是否隐藏其栏以响应滑动手势。当此属性设置为 YES 时,向上滑动会隐藏导航栏和工具栏。 向下滑动再次显示导航栏和工具栏。此属性的默认值为 NO。

@property (nonatomic, readonly, strong) UIPanGestureRecognizer *barHideOnSwipeGestureRecognizer API_AVAILABLE(ios(8.0)) API_UNAVAILABLE(tvos);

属性描述用于隐藏导航栏和工具栏的手势识别器。此属性包含用于隐藏和显示导航栏和工具栏的手势识别器。除非hidesBarsOnSwipe属性为YES,否则手势识别器是不活动的。可以根据需要对手势识别器进行更改,但不得更改其代理,并且不得删除随其配置的默认目标对象和操作。不要尝试通过覆盖该属性来替换此手势识别器。如果将此手势识别器与自己的一个手势识别器绑定,请确保两者同时识别他们的手势,以确保每个手势识别器都有机会处理事件。

@property (nonatomic, readwrite, assign) BOOL hidesBarsWhenVerticallyCompact API_AVAILABLE(ios(8.0)) API_UNAVAILABLE(tvos);

属性描述一个布尔值,指示导航控制器是否在垂直紧凑的环境中隐藏其栏。当此属性的值为 YES 时,导航控制器在转换到垂直紧凑环境时隐藏其导航栏和工具栏。当退出在垂直紧凑环境后,导航控制器会自动再次显示这两个栏。此外在内容区未处理的点击会导致导航控制器再次显示两个栏。该属性的默认值是NO。

@property (nonatomic, readwrite, assign) BOOL hidesBarsOnTap API_AVAILABLE(ios(8.0)) API_UNAVAILABLE(tvos);

属性描述 :一个布尔值,指示导航控制器是否允许使用点击手势隐藏其栏。当此属性的值为 YES 时,导航控制器会切换其导航栏和工具栏的隐藏和显示,以响应内容区域中未处理的点击。 此属性的默认值为 NO。

@property (nonatomic, readonly, assign) UITapGestureRecognizer *barHideOnTapGestureRecognizer API_AVAILABLE(ios(8.0)) API_UNAVAILABLE(tvos);

属性描述此属性包含用于隐藏或显示栏的手势识别器。除非hidesBarsOnTap属性为YES,否则手势识别器是不活动的。可以根据需要对手势识别器进行更改,但不得更改其代理,并且不得删除随其配置的默认目标对象和操作。不要尝试通过覆盖该属性来替换此手势识别器。如果将此手势识别器与自己的一个手势识别器绑定,请确保两者同时识别他们的手势,以确保每个手势识别器都有机会处理事件。

UIViewController (UINavigationControllerItem) - 导航控制器导航项对视图控制器的扩展属性
@property(nullable, nonatomic,readonly,strong) UINavigationController *navigationController;

属性描述视图控制器层次结构中最近的父控制器,即导航控制器(UINavigationController)。如果视图控制器或其父控制器之一是导航控制器的子控制器,则此属性包含所属的导航控制器。如果视图控制器未嵌入导航控制器,则此属性为nil。

@property(nonatomic,readonly,strong) UINavigationItem *navigationItem;

属性描述用于在父导航栏中表示视图控制器的导航项。这是UINavigationItem的唯一实例,创建该实例是为了在视图控制器被推送到导航控制器(UINavigationController)上时表示该视图控制器。第一次访问属性时,将创建UINavigationItem对象。因此如果不使用导航控制器来显示视图控制器,则不应访问此属性。为确保配置了导航项,可以重写此属性并添加代码以在首次访问时创建条形按钮项,或者在视图控制器的初始化代码中创建项。

避免在导航项中创建条形按钮项的创建与视图控制器视图的创建相关联。视图控制器的导航项可以独立于视图控制器的视图进行检索。例如当将两个视图控制器推到导航堆栈上时,最上面的视图控制器变为可见,但是可以检索另一个视图控制器的导航项以显示其后退按钮。

默认行为是创建一个导航项,显示视图控制器的标题。

@property(nonatomic) BOOL hidesBottomBarWhenPushed API_UNAVAILABLE(tvOS);

属性描述一个布尔值,指示将视图控制器推到导航控制器上时是否隐藏屏幕底部的工具栏。作为导航控制器的子级添加的视图控制器可以在屏幕底部显示可选工具栏。最顶部视图控制器上此属性的值决定工具栏是否可见。如果此属性的值为“YES”,则工具栏将隐藏。如果此属性的值为“NO”,则工具栏可见。

UIViewController (UINavigationControllerContextualToolbarItems) - 导航控制器上下文工具栏项对视图控制器的扩展属性与函数
@property (nullable, nonatomic, strong) NSArray<__kindof UIBarButtonItem *> *toolbarItems API_AVAILABLE(ios(3.0)) API_UNAVAILABLE(tvos);

属性描述与视图控制器关联的工具栏项。该属性包含一个UIBarButtonItem对象数组,并与UINavigationController对象一起工作。如果这个视图控制器被嵌入到导航控制器界面中,并且导航控制器显示一个工具栏,此属性标识要在该工具栏中显示的项。可以显式设置此属性的值,或者使用setToolbarItems:animated:方法对可见的工具栏项集进行动画化更改。

- (void)setToolbarItems:(nullable NSArray<UIBarButtonItem *> *)toolbarItems animated:(BOOL)animated API_AVAILABLE(ios(3.0)) API_UNAVAILABLE(tvos);

函数描述设置要与视图控制器一起显示的工具栏项。由导航控制器(UINavigationController)管理的视图控制器可以使用此方法为导航控制器的内置工具栏指定工具栏项。 可以在显示视图控制器之前或在它已经可见之后为视图控制器设置工具栏项。

参数 :

toolbarItems : 要在内置工具栏中显示的工具栏项目。

animated : 如果YES,则为工具栏中的项目更改设置动画。

UINavigationController常用函数
- (instancetype)initWithRootViewController:(UIViewController *)rootViewController NS_DESIGNATED_INITIALIZER; 

函数描述初始化导航控制器并将根视图控制器推送到导航堆栈上。每个导航堆栈必须至少有一个视图控制器作为根视图控制器。

参数:

rootViewController:位于导航堆栈底部的视图控制器,此对象不能是UITabBarController类的实例。

返回值:初始化的导航控制器对象,如果初始化对象时出现问题,则返回nil。

\color{red}{例如在窗口中设置第一个控制器为导航控制器时:}

    self.window = [[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds];
    self.window.backgroundColor = [UIColor whiteColor];
    ViewController *viewController = [[ViewController alloc]init];
    UINavigationController * navigationController = [[UINavigationController alloc]initWithRootViewController:viewController];
    self.window.rootViewController = navigationController;
    [self.window makeKeyAndVisible];
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated;

函数描述将视图控制器推到调用方堆栈上并更新显示。viewController参数中的对象成为导航堆栈上的顶视图控制器。如果已设置动画的参数为“YES”,则视图将设置动画到相应位置,否则视图将仅显示在其最终位置。如果要推送视图控制器已经在导航堆栈上,则此方法引发异常

参数:

viewController :要推到堆栈上的视图控制器。此对象不能是UITabBarController的实例。

animated:指定“YES”设置过渡的动画,如果不希望设置过渡的动画,则指定“NO”。如果在启动时设置导航控制器,则可以指定“NO”。

- (nullable UIViewController *)popViewControllerAnimated:(BOOL)animated; 

函数描述从导航堆栈弹出顶端视图控制器并更新显示。此方法从堆栈中移除顶部视图控制器,并使堆栈的新顶部成为活动视图控制器。如果堆栈顶部的视图控制器是根视图控制器,则此方法不执行任何操作。换句话说,不能弹出堆栈上的最后一个项。除了在堆栈顶部显示与新视图控制器关联的视图外,此方法还相应地更新导航栏和工具栏。

参数 :

animated : 将此值设置为YES可设置转换的动画。如果要在显示导航控制器的视图之前设置该控制器,请设置NO。

返回值 : 从堆栈中弹出的视图控制器。

\color{red}{例如在推出控制器后延迟1秒弹出控制器 :}

[self.navigationController performSelector:@selector(popViewControllerAnimated:) withObject:@(YES) afterDelay:1.0];
- (nullable NSArray<__kindof UIViewController *> *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated; 

函数描述弹出视图控制器,直到指定的视图控制器位于导航堆栈的顶部

参数 :

viewController : 要位于堆栈顶部的视图控制器。此视图控制器当前必须位于导航堆栈上。

animated : 将此值设置为YES可设置转换的动画。如果要在显示导航控制器的视图之前设置该控制器,请设置NO。

返回值 : 包含从堆栈中弹出的视图控制器的数组。

\color{red}{弹到指定的控制器,通常会判断该控制器是否在控制器栈中,例如 :}

    UIViewController *targetViewController = nil;
    for (UIViewController *viewController in self.navigationController.viewControllers) {
        if ([viewController isMemberOfClass:NSClassFromString(RefundListViewController)]) {
            targetViewController = viewController;
            break;
        }
    }
    if (targetViewController) {
        [self.navigationController popToViewController:targetViewController animated:YES];
    }
- (nullable NSArray<__kindof UIViewController *> *)popToRootViewControllerAnimated:(BOOL)animated; 

函数描述 :弹出堆栈上除根视图控制器之外的所有视图控制器并更新显示

参数 :

animated :将此值设置为YES可设置转换的动画。如果要在显示导航控制器的视图之前设置该控制器,请设置NO。

返回值 :从堆栈中弹出的项的视图控制器数组。

- (void)setViewControllers:(NSArray<UIViewController *> *)viewControllers animated:(BOOL)animated API_AVAILABLE(ios(3.0));

函数描述用指定的项目替换当前由导航控制器管理的视图控制器。使用此方法更新或替换当前视图控制器堆栈,而无需显式推送或弹出每个控制器。此外,此方法允许您在不为更改设置动画的情况下更新控制器集。

如果启用了动画,此方法会根据 items 数组中的最后一项是否已经在导航堆栈中来决定执行哪种类型的转换。如果视图控制器当前在堆栈中,但不是最顶部的项目,则此方法使用弹出过渡;如果它是最顶层的项目,则不执行转换。如果视图控制器不在堆栈上,则此方法使用推送转换。只执行一次转换,但当该转换完成时,堆栈的全部内容将被新的视图控制器替换。

参数 :

viewControllers : 放置在堆栈中的视图控制器。此数组中控制器的从前到后的顺序表示导航堆栈中控制器的新的从下到上的顺序。因此,添加到数组的最后一项成为导航堆栈的顶部项。

animated : 如果YES,动画顶视图控制器的推送或弹出。如果NO,则替换没有任何动画的视图控制器。

- (void)setToolbarHidden:(BOOL)hidden animated:(BOOL)animated API_AVAILABLE(ios(3.0)) API_UNAVAILABLE(tvos);

函数描述更改导航控制器内置工具栏的可见性。可以使用此方法对内置工具栏的可见性进行动画更改。在动画参数设置为 NO 的情况下调用此方法相当于直接设置 toolbarHidden 属性的值。 工具栏只是根据隐藏参数中的值出现或消失。

参数 :

hidden : 指定YES以隐藏工具栏或指定NO以显示工具栏。

animated : 如果希望工具栏在屏幕上隐藏或显示有动画,请指定YES。

- (void)setNavigationBarHidden:(BOOL)hidden animated:(BOOL)animated; 

函数描述设置导航栏是否隐藏。对于动画过渡,动画的持续时间由UINavigationControllerHideShowBarDuration常量中的值指定。

参数 :

hidden : 指定“YES”隐藏导航栏,或指定“NO”显示导航栏。

animated : 如果要设置可见性更改的动画,请指定“YES”;如果要立即显示导航栏,请指定“NO”。

- (void)showViewController:(UIViewController *)vc sender:(nullable id)sender API_AVAILABLE(ios(8.0)); 

函数描述此方法以与 pushViewController:animated: 方法类似的方式将视图控制器推送到导航堆栈。 如果需要,可以直接调用此方法,但通常当需要显示新的视图控制器时,会从视图控制器层次结构中的其他位置调用此方法。Show segue 使用这个方法来显示一个新的视图控制器。

参数 :

vc : 要显示的视图控制器。

sender : 请求显示视图控制器的对象。

关于模态显示控制器与导航控制器推送控制器混合显示的问题

在开发中,经常有这样的情景,从一个控制器A模态到另一个控制器B,再从B控制器push到另一个C控制器,但是按照通常的方法,模态到B控制器之后,就push不到C控制器了,这个是因为B控制器不是导航控制器的根控制器或子控制器。只有当前控制器在导航控制器栈中才可以使用push到导航其它视图,所你必须把B控制器加入到导航控制器中,才能用来push 其他视图。

//在A的控制器里模态到B的控制器:
UINavigationController* navigationController = [[UINavigationController alloc] initWithRootViewController:A];
[self presentViewController:navigationController animated:YES completion:nil];
//再从B控制器push 到C控制器
[self.navigationController pushViewController:C animated:YES];

UINavigationBar - 导航控制器管理的导航栏

UINavigationBar派生自UIView,UINavigationBar对象是一个栏状视图,通常显示在窗口的顶部,包含用于在屏幕层次结构中导航的按钮。主要组件是一个左(后退)按钮、一个中间标题和一个可选的右按钮。可以将导航栏作为独立对象使用,也可以将其与导航控制器对象结合使用。

导航栏最常用于导航控制器中。UINavigationController对象创建、显示和管理其关联的导航栏,并使用添加的视图控制器的属性来控制导航栏中显示的内容

要在使用导航控制器时控制导航栏,需要执行以下步骤:

1.在界面生成器或代码中创建导航控制器。
2.使用UINavigationController对象上的navigationBar属性配置导航栏的外观。
3.通过在推送到导航控制器堆栈上的每个UIViewController上设置title和navigationItem属性来控制导航栏的内容。

也可以使用独立的导航栏,而不使用导航控制器。要向界面添加导航栏,需要执行以下步骤:

1.设置自动布局规则以控制导航栏在界面中的位置。
2.创建根导航项以提供初始标题。
3.配置委托对象以处理用户与导航栏的交互。
4.自定义导航栏的外观。
5.配置应用程序,以便在用户浏览分层屏幕时推送和弹出相关导航项。

UINavigationBar常用属性
@property(nonatomic,assign) UIBarStyle barStyle UI_APPEARANCE_SELECTOR API_UNAVAILABLE(tvos);

属性描述指定其外观的导航栏样式。当导航栏由导航控制器(UINavigationController)对象管理时,可以设置此属性的值。默认值为 UIBarStyleDefault。

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

属性描述导航栏的代理对象。代理必须符合 UINavigationBarDelegate 协议, 默认值为nil。如果导航栏由导航控制器(UINavigationController)创建并由该对象管理,则不得更改此属性的值,导航控制器充当它创建的导航栏的代理。

@property(nonatomic,assign,getter=isTranslucent) BOOL translucent API_AVAILABLE(ios(3.0)) UI_APPEARANCE_SELECTOR;

属性描述一个布尔值,指示导航栏是否半透明,当导航栏是半透明的时,将视图控制器的edgesForExtendedLayout和extendedLayoutIncludesOpaqueBars属性配置为在导航栏下显示内容。

如果导航栏没有自定义背景图像,或者背景图像的任何像素的alpha值小于1.0,则此属性的默认值为YES。如果背景图像是完全不透明的,则此属性的默认值为NO。如果将此属性设置为YES,并且自定义背景图像是完全不透明的,则UIKit将对图像应用小于1.0的系统定义不透明度。如果将此属性设置为NO,并且背景图像不透明,则UIKit将添加不透明背景。

@property(nullable, nonatomic,readonly,strong) UINavigationItem *topItem;

属性描述导航栏堆栈顶部的导航项

@property(nullable, nonatomic,readonly,strong) UINavigationItem *backItem;

属性描述导航栏堆栈上最顶部导航项下方的导航项。如果最顶部导航项的 leftBarButtonItem 属性为 nil,则导航栏显示一个后退按钮,其标题派生自该属性中的项。如果导航栏的堆栈中只有一项,则此属性的值为 nil。

@property(nullable,nonatomic,copy) NSArray<UINavigationItem *> *items;

属性描述由导航栏(UINavigationBar)管理的导航项(UINavigationItem)数组。底部项目在索引 0 处,后面项目在索引 n-2 处,顶部项目在索引 n-1 处,其中 n 是数组中的项目数。

@property (nonatomic, readwrite, assign) BOOL prefersLargeTitles UI_APPEARANCE_SELECTOR API_AVAILABLE(ios(11.0)) API_UNAVAILABLE(tvos);

属性描述一个布尔值,指示标题是否以大格式显示。当此属性设置为 YES 时,导航栏允许标题显示在行外并使用更大的字体。 用于构建栏的导航项必须指定是否希望以大格式或小格式显示其标题。 使用 largeTitleDisplayMode 属性配置标题的外观。当该属性设置为 NO 时,导航栏显示与其他栏按钮项内联的标题。

@property(nullable, nonatomic,strong) UIColor *barTintColor API_AVAILABLE(ios(7.0)) UI_APPEARANCE_SELECTOR;

属性描述应用于导航栏背景的半透明色。默认情况下,此颜色是半透明的,除非将“ translucent”属性设置为“NO”。

@property(null_resettable, nonatomic,strong) UIColor *tintColor;

属性描述应用于导航项和栏按钮项的色调颜色

@property(nullable, nonatomic,strong) UIImage *shadowImage API_AVAILABLE(ios(6.0)) UI_APPEARANCE_SELECTOR;

属性描述用于导航栏的阴影图像。默认值为nil,与默认阴影图像相对应。非nil时,此属性表示要显示的自定义阴影图像。要显示自定义阴影图像,还必须使用setBackgroundImage:forBarMetrics:方法设置自定义背景图像。如果使用默认背景图像,则无论此属性的值如何,都将使用默认阴影图像。

@property(nullable,nonatomic,copy) NSDictionary<NSAttributedStringKey, id> *titleTextAttributes API_AVAILABLE(ios(5.0)) UI_APPEARANCE_SELECTOR;

属性描述显示栏标题文本的属性。可以使用NSAttributedStringKey中描述的文本属性键在文本属性字典中指定标题的字体、文本颜色、文本阴影颜色和文本阴影偏移量。

@property(nullable,nonatomic,strong) UIImage *backIndicatorImage API_AVAILABLE(ios(7.0)) UI_APPEARANCE_SELECTOR API_UNAVAILABLE(tvos);

属性描述后退按钮旁边显示的图像。如果要自定义返回指示器图像,还必须设置 backIndicatorTransitionMaskImage。

@property(nullable,nonatomic,strong) UIImage *backIndicatorTransitionMaskImage API_AVAILABLE(ios(7.0)) UI_APPEARANCE_SELECTOR API_UNAVAILABLE(tvos);

属性描述在推送和弹出转换期间用作内容掩码的图像。如果要自定义返回指示器图像,还必须设置 backIndicatorImage。

UINavigationBar - 13.0后设置导航栏外观常用属性
@property (nonatomic, readwrite, copy) UINavigationBarAppearance *standardAppearance UI_APPEARANCE_SELECTOR API_AVAILABLE(ios(13.0), tvos(13.0));

属性描述标准高度导航栏的外观设置。该属性的默认值是一个包含系统默认外观设置的外观对象。

@property (nonatomic, readwrite, copy, nullable) UINavigationBarAppearance *compactAppearance UI_APPEARANCE_SELECTOR API_AVAILABLE(ios(13.0));

属性描述高度紧凑的导航栏的外观设置。如果此属性的值为 nil,UIKit 使用存储在 topItem 属性中的导航项的标准外观。

@property (nonatomic, readwrite, copy, nullable) UINavigationBarAppearance *scrollEdgeAppearance UI_APPEARANCE_SELECTOR API_AVAILABLE(ios(13.0));

属性描述当可滚动内容的边缘与导航栏的边缘对齐时导航栏的外观设置。当导航控制器包含导航栏和滚动视图时,滚动视图的部分内容会出现在导航栏下方。 如果滚动内容的边缘到达该栏,UIKit 将应用此属性中的外观设置。

如果此属性的值为 nil,UIKit 将使用 standardAppearance 属性中的设置,修改为使用透明背景。 如果没有导航控制器管理您的导航栏,UIKit 会忽略此属性并使用导航栏的标准外观

在使用 iOS 14 或更早版本的应用程序上运行时,此属性适用于具有大标题的导航栏。 在 iOS 15 中,此属性适用于所有导航栏。

@property(nonatomic,readwrite, copy, nullable) UINavigationBarAppearance *compactScrollEdgeAppearance UI_APPEARANCE_SELECTOR API_AVAILABLE(ios(15.0));

属性描述当可滚动内容的边缘与导航栏的边缘对齐时,高度紧凑的导航栏的外观设置。当导航控制器包含导航栏和滚动视图时,滚动视图的部分内容会出现在导航栏下方。 如果滚动内容的边缘到达该栏,UIKit 将应用此属性中的外观设置。

此属性适用于紧凑高度的导航栏。 如果这个属性的值为 nil,UIKit 使用导航栏的 scrollEdgeAppearance 的值。 如果没有导航控制器管理您的导航栏,UIKit 会忽略此属性并使用导航栏的 compactAppearance 的值

\color{red}{例如设置一个透明的导航栏代码片段:}

- (void)setNavigationBarAttributes {
    
    self.navigationController.navigationBar.barTintColor = [UIColor colorWithRed:54.0f/255.0f green:53.0f/255.0f blue:58.0f/255.0f alpha:0.5f];
    self.navigationController.navigationBar.titleTextAttributes = @{NSForegroundColorAttributeName: [UIColor whiteColor]};
    //去掉透明后导航栏下边的黑边
    self.navigationController.navigationBar.shadowImage = [[UIImage alloc] init];
    //设置导航栏背景图片为一个空的image,这样就透明了
    [self.navigationController.navigationBar setBackgroundImage:[[UIImage alloc] init] forBarMetrics:UIBarMetricsDefault];
    self.navigationController.navigationBar.translucent = YES;
}
截屏2020-02-16下午12.08.13.png
UINavigationBarAppearance - iOS13.0及以后自定义导航栏外观的对象

继承自 UIBarAppearance,用于自定义导航栏外观的对象。创建 UINavigationBarAppearance 对象后,使用此类的方法和属性来指定导航栏中项目所需的外观。 使用从 UIBarAppearance 继承的属性来配置导航栏本身的背景和阴影属性。

@property (nonatomic, readwrite, copy) NSDictionary<NSAttributedStringKey, id> *titleTextAttributes;

属性描述应用于标准大小标题文本的字符串属性。如果没有为文本指定字体或颜色属性,UIKit 会提供适当的默认值。

@property (nonatomic, readwrite, assign) UIOffset titlePositionAdjustment;

属性描述水平和垂直偏移标题的距离(以点为单位)。正值将标题向下和向右移动。 负值将标题向上和向左移动。

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

属性描述应用于大标题文本的字符串属性。如果没有为文本指定字体或颜色属性,UIKit 会应用默认的字体和颜色值。

@property (nonatomic, readonly, strong) UIImage *backIndicatorImage;

属性描述显示在后退按钮前沿的图像

@property (nonatomic, readonly, strong) UIImage *backIndicatorTransitionMaskImage;

属性描述:用于在推送和弹出转换期间屏蔽在后退指示符图像下方流动的内容的图像

@property (nonatomic, readwrite, copy) UIBarButtonItemAppearance *buttonAppearance;

属性描述导航栏中普通按钮项的外观属性。要分别配置后退或完成按钮的外观,请改用 backButtonAppearance 和 doneButtonAppearance 属性。

@property (nonatomic, readwrite, copy) UIBarButtonItemAppearance *doneButtonAppearance;

属性描述导航栏中完成按钮的外观属性。使用此属性可在适当时配置使用 UIBarButtonItemStyleDone 样式的导航栏按钮项的外观。 如果导航栏没有完成按钮,则设置此属性的值无效。

@property (nonatomic, readwrite, copy) UIBarButtonItemAppearance *backButtonAppearance;

属性描述导航栏中后退按钮的外观属性。如果不更改此属性的值,导航栏将应用 buttonAppearance 属性中的属性。

- (void)setBackIndicatorImage:(nullable UIImage *)backIndicatorImage transitionMaskImage:(nullable UIImage *)backIndicatorTransitionMaskImage;

函数描述设置后退按钮指示器的图像及其过渡蒙版。如果为 backIndicatorImage参数 或 backIndicatorTransitionMaskImage参数 指定 nil,则此方法会将两个图像重置为其默认值。

参数 :

backIndicatorImage : 显示在后退按钮前沿的图像。

backIndicatorTransitionMaskImage : 用于在推送和弹出转换期间屏蔽在后退指示符图像下方流动的内容的图像。

UINavigationBar常用函数
- (void)pushNavigationItem:(UINavigationItem *)item animated:(BOOL)animated;

函数描述将给定的导航项(UINavigationItem)推送到导航栏(UINavigationBar)的堆栈并更新UI。推出一个导航项会在导航栏的中心显示该项的标题。该导航项如果存在上一个顶部导航项,上一个顶部导航项显示为导航栏左侧的后退按钮。如果新推出的顶部项目具有左侧自定义视图,则显示自定义视图而不是后退按钮。

参数 :

item : 要压入堆栈的导航项。

animated : 如果为YES有动画效果,NO则没有。

- (nullable UINavigationItem *)popNavigationItemAnimated:(BOOL)animated;

函数描述从导航栏(UINavigationBar)的堆栈中弹出顶部导航项(UINavigationItem)并更新 UI。弹出一个导航项会从堆栈中移除顶部的导航项并将其替换为后面的导航项。 后面的导航项的标题以导航栏为中心,并显示其他属性。

参数:

animated : 如果为YES有动画效果,NO则没有。

返回值 : 弹出的顶部项目。

- (void)setItems:(nullable NSArray<UINavigationItem *> *)items animated:(BOOL)animated;

函数描述将当前由导航栏(UINavigationBar)管理的导航项(UINavigationItem)替换为指定导航项。可以使用此方法更新或替换导航栏管理的堆栈中的导航项,而无需显式推送或弹出每个导航项。此外此方法允许您在不为更改设置动画的情况下更新导航栏管理的堆栈。

如果启用了动画,此方法会根据 items 数组中的最后一个导航项是否已经在当前导航栏堆栈上来决定执行哪种类型的转换。如果该导航项当前在堆栈上,但不是最顶部的导航项,则此方法使用弹出过渡;如果它是最顶层的导航项,则不执行转换。如果导航项不在堆栈上,则此方法使用推出转换。只执行一个转换,但当该转换完成时,堆栈的全部内容都将替换为新导航项。

参数 :

items :要放置在堆栈中的 UINavigationItem 对象。此数组中导航项的从前到后的顺序表示导航栏(UINavigationBar)堆栈中新的导航项从下到上的顺序。因此添加到数组的最后一个导航项成为导航堆栈的顶部导航项。

animated :如果为YES,导航栏(UINavigationBar)堆栈中顶部导航项带动画的推入或弹出。如果为NO,则没有任何动画的替换导航栏(UINavigationBar)堆栈中的导航项。

- (void)setBackgroundImage:(nullable UIImage *)backgroundImage forBarPosition:(UIBarPosition)barPosition barMetrics:(UIBarMetrics)barMetrics API_AVAILABLE(ios(7.0)) UI_APPEARANCE_SELECTOR;

函数描述设置用于导航栏中给定位置和指示常数的背景图像。如有必要可调整大小的图像将垂直拉伸到 UIBarPositionTopAttached 的位置。

参数 :

backgroundImage : 用于指定位置和指标的图像。

barPosition :导航栏的位置。

barMetrics : 导航栏的指示常数。

- (nullable UIImage *)backgroundImageForBarPosition:(UIBarPosition)barPosition barMetrics:(UIBarMetrics)barMetrics API_AVAILABLE(ios(7.0)) UI_APPEARANCE_SELECTOR;

函数描述返回用于导航栏中给定位置和指示常数的背景图像

参数 :

barPosition : 导航栏的位置。

barMetrics : 导航栏的指示常数。

返回值 : 用于指定位置和指标的图像。

- (void)setBackgroundImage:(nullable UIImage *)backgroundImage forBarMetrics:(UIBarMetrics)barMetrics API_AVAILABLE(ios(5.0)) UI_APPEARANCE_SELECTOR;

函数描述设置给定导航栏的背景图像

参数 :

backgroundImage : 用于barMetrics的背景图像。

barMetrics : 一个导航栏指示常数。

- (nullable UIImage *)backgroundImageForBarMetrics:(UIBarMetrics)barMetrics API_AVAILABLE(ios(5.0)) UI_APPEARANCE_SELECTOR;

函数描述返回给定导航栏的背景图像。等效于使用 backgroundImageForBarPosition:barMetrics:函数位置参数传递UIBarPositionAny。

参数 :

barMetrics : 一个导航栏指示常数。

返回值 : 给定导航栏指示常数的的背景图像。

- (void)setTitleVerticalPositionAdjustment:(CGFloat)adjustment forBarMetrics:(UIBarMetrics)barMetrics API_AVAILABLE(ios(5.0)) UI_APPEARANCE_SELECTOR;

函数描述为导航栏设置在给定指示常数下设置标题的垂直位置调整

参数 :

adjustment : 标题垂直位置调整值。

barMetrics : 一个导航栏指示常数。

- (CGFloat)titleVerticalPositionAdjustmentForBarMetrics:(UIBarMetrics)barMetrics API_AVAILABLE(ios(5.0)) UI_APPEARANCE_SELECTOR;

函数描述返回导航栏给定指示常数下的标题的垂直位置调整值

参数 :

barMetrics : 一个导航栏指示常数。

UINavigationItem - 导航栏的导航项

UINavigationItem派生自NSObject,当关联的视图控制器可见时,导航栏将显示的导航项。构建导航界面时,推送到导航堆栈上的每个视图控制器都必须有一个UINavigationItem对象,该对象包含要在导航栏中显示的按钮和视图。管理UINavigationController对象使用最上面两个视图控制器的导航项向导航栏填充内容。

导航项始终反映与其关联的视图控制器的信息。当视图控制器位于导航堆栈的顶部时,导航项必须提供要显示的标题。此外,该项可能包含要显示在导航栏右侧(或尾部)的其他按钮。可以使用leftbarbuttonems属性指定要在工具栏左侧(或前导)显示的按钮和视图,但导航控制器仅在有可用空间时才显示这些按钮。

导航项的backBarButtonElem属性反映当前视图控制器正好位于最顶部视图控制器下方时要显示的后退按钮。换句话说,当当前视图控制器位于最上面时,不使用后退按钮。

为导航项指定按钮时,必须使用UIBarButtonItem对象。如果要在导航栏中显示自定义视图,则必须在将这些视图添加到导航项之前将其包装在UIBarButtonItem对象中。

UINavigationItem常用属性
@property(nullable, nonatomic,copy)   NSString        *title;  

属性描述导航栏中显示的导航项标题,默认值为nil。当调用方位于导航项堆栈上,并且位于顶部的第二个位置时,换句话说,它的视图控制器管理用户将导航回的视图,此属性中的值用于最顶部导航栏上的back按钮。如果此属性的值为nil,则系统使用字符串“Back”作为Back按钮的文本。在ios11及以后版本中,标题的大小和位置由导航栏的preferslargetitle属性和导航项的largeTitleDisplayMode属性决定。

例如给导航栏设置一个标题:

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor redColor];
    self.navigationItem.title = @"测试代码控制器";
}
截屏2020-02-16下午5.19.32.png
@property(nullable, nonatomic,strong) UIView          *titleView; 

属性描述当调用方是顶部项目时,显示在导航栏中心的自定义视图。如果此属性值为nil,则当调用方为顶部项目时,导航项目的标题将显示在导航栏的中心。如果将此属性设置为自定义视图,则将显示它而不是标题。自定义视图可以包含按钮。使用UIButton类中的buttonWithType:方法,以导航栏的样式将按钮添加到自定义视图中。自定义标题视图以导航栏为中心,可以根据需要调整大小。默认值为nil。

@property(nullable, nonatomic,strong) UIBarButtonItem *leftBarButtonItem;

属性描述当调用方是顶部导航项时,显示在导航栏左侧(或前缘)的自定义栏按钮项。此属性的内容始终引用leftBarButtonItems数组中的第一个栏按钮项。将新值赋给此属性将用新值替换leftBarButtonItems数组中的第一项。将此属性设置为nil将删除数组中的第一项。如果bar按钮项已经在数组中,则将其从当前位置移动到数组的前面。在从右向左的用户界面中,左侧栏按钮项的位置将自动翻转。

@property(nullable, nonatomic,strong) UIBarButtonItem *rightBarButtonItem;

属性描述当调用方是顶部导航项时,显示在导航栏右(或后缘)的自定义栏按钮项。此属性的内容始终引用rightBarButtonItems数组中的第一个栏按钮项。将新值赋给此属性将用新值替换rightBarButtonItems数组中的第一项。将此属性设置为nil将删除数组中的第一项。如果bar按钮项已经在数组中,则将其从当前位置移动到数组的前面。在从右向左的用户界面中,右栏按钮项的位置将自动翻转。

@property(nullable,nonatomic,copy) NSArray<UIBarButtonItem *> *leftBarButtonItems API_AVAILABLE(ios(5.0));

属性描述当调用方是顶部导航项时,显示在导航栏左侧(或前导)的自定义栏按钮项数组。此数组可以包含0个或多个要显示在导航栏左侧(或前导)的导航栏按钮项。项目可以包括固定宽度和灵活的宽度空间。如果leftItemsSupplementBackButton属性为YES,则项目将显示在“后退”按钮的右侧(或尾部),否则项目将替换“后退”按钮并从栏的左侧(或前缘)开始。项目按在数组中显示的相同顺序从左到右显示。在从右到左的用户界面中,项目将自动翻转。

如果没有足够的空间显示数组中的所有项,与标题视图(如果存在)或栏右侧按钮重叠的项则不会显示。数组中的第一项也可以使用leftBarButtonItem属性设置。

@property(nullable,nonatomic,copy) NSArray<UIBarButtonItem *> *rightBarButtonItems API_AVAILABLE(ios(5.0));

属性描述当调用方是顶部导航项时,显示在导航栏右侧(或尾部)的自定义栏按钮项数组。此数组可以包含0个或多个要显示在导航栏右侧(或尾随)的导航栏按钮项。项目按在数组中显示的相同顺序从右到左显示。因此,数组中的第一个项是最右边的项,其他项被添加到前一个项的左边。在从右到左的用户界面中,项目将自动翻转。

如果没有足够的空间显示数组中的所有项,与标题视图(如果存在)或栏左侧按钮重叠的项则不会显示。数组中的第一项也可以使用rightBarButtonItem属性设置。

Push到下级界面后再移除当前控制器的代码片段
NSMutableArray *controllerArray = [viewController.navigationController.viewControllers mutableCopy];
for (UIViewController *newViewController in controllerArray) {
    if (newViewController == viewController) {
        [controllerArray removeObject:newViewController];
        break;
    }
}
[viewController.navigationController setViewControllers:controllerArray animated:NO];

自定义导航栏练习代码片段

//
//  WGUITextField.h
#import <UIKit/UIKit.h>
@interface WGUITextField : UITextField
@end
//
//  WGUITextField.m
#import "WGUITextField.h"
@implementation WGUITextField

/**
 返回接收器左侧覆盖视图的绘制矩形。不应该直接调用这个方法。如果想将左侧覆盖视图放置在不同的位置,可以覆盖此方法并返回新矩形。注意,在从右到左的用户界面中,绘制矩形保持不变。
 
 参数:
 bounds : 接收器的边框。
 返回值 : 要在其中绘制左覆盖视图的矩形。
 */

- (CGRect)leftViewRectForBounds:(CGRect)bounds {
    CGRect iconRect = [super leftViewRectForBounds:bounds];
    iconRect.origin.x += 10;
    return iconRect;
}

/**
 返回文本字段的文本的绘制矩形。不应该直接调用这个方法。如果想为文本自定义绘图矩形,可以覆盖此方法并返回一个不同的矩形。此方法的默认实现返回一个矩形,该矩形派生自控件的原始边界,但不包括接收方的边框或覆盖视图占用的区域。
 
 参数:
 bounds : 接收器的边框。
 返回值 : 用于标签文本的计算绘图矩形。
 */

- (CGRect)textRectForBounds:(CGRect)bounds {
    bounds.size.width += 35.0;
    return CGRectInset(bounds, 35, 0);
}

/**
 返回文本字段的占位符文本的绘制矩形。不应该直接调用这个方法。如果想为占位符文本自定义绘图矩形,可以覆盖此方法并返回一个不同的矩形。如果占位符字符串为空或nil,则不调用此方法。
 
 参数:
 bounds : 接收器的边框。
 返回值 : 占位符文本的计算绘图矩形。
 */

- (CGRect)placeholderRectForBounds:(CGRect)bounds {
    return CGRectInset(bounds, 35, 0);
}

/**
 返回可编辑文本显示的矩形。不应直接调用此方法。如果要为文本提供不同的编辑矩形,可以重写此方法并返回该矩形。默认情况下,此方法返回文本字段中不被任何覆盖视图占用的区域。
 
 参数 :
 bounds : 接收器的边框。
 返回值 : 可编辑文本显示的矩形。
 
 */

- (CGRect)editingRectForBounds:(CGRect)bounds {
    bounds.size.width += 35.0;
    return CGRectInset(bounds, 35, 0);
}

/**
 返回接收器右覆盖视图的绘图位置。不应直接调用此方法。如果要将右覆盖视图放置在其他位置,可以重写此方法并返回新矩形。请注意,在从右到左的用户界面中,绘图矩形保持不变。
 
 参数:
 bounds : 接收器的边框。
 返回值 : 在其中绘制右覆盖视图的矩形。
 */

- (CGRect)rightViewRectForBounds:(CGRect)bounds {
    CGRect textRect = [super rightViewRectForBounds:bounds];
    textRect.origin.x -= 10 * 0.5;
    return textRect;
}
@end
//
//  TestNavigationController.h
#import <UIKit/UIKit.h>
@interface TestNavigationController : UIViewController

@end
//
//  TestNavigationController.m
#import "TestNavigationController.h"
#import "YSCUITextField.h"

@interface TestNavigationController ()<UITextFieldDelegate>

@property (nonatomic, strong) WGUITextField *searchTextField;
@property (nonatomic, strong) UIColor *backBarTintColor;
@property (nonatomic, copy) NSDictionary *backTitleAttributes;
@property (nonatomic, strong) UIImage *backShadowImage;
@property (nonatomic, assign) BOOL isHaveDian;//是否是小数

@end

@implementation TestNavigationController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor redColor];
}

- (void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
    //备份导航栏属性
    [self backupNavigationBarAttributes];
    //设置导航栏透明
    [self setNavigationBarAttributes];
}

- (void)viewDidAppear:(BOOL)animated{
    [super viewDidAppear:animated];
    //设置导航项左侧返回按钮
    [self setNavigationLeftBlockItem];
    //设置导航栏右侧按钮
    [self setNavigationRightBlockItem];
    //设置导航栏搜索视图
    [self setNavigationItemTitleView];
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    //重置导航栏属性
    [self resetNavigationBarAttributes];
    //将导航栏左侧返回按钮重置为黑色图标
    UIButton *leftButton = self.navigationItem.leftBarButtonItem.customView;
    [leftButton setImage:[UIImage imageNamed:@"btn_back_dark"] forState:UIControlStateNormal];
    [leftButton setImage:[UIImage imageNamed:@"btn_back_dark"] forState:UIControlStateHighlighted];
}


///---------------------------------------- UINavigationController代码测试区 -------------------------------

///备份导航栏属性
- (void)backupNavigationBarAttributes {
    _backBarTintColor = self.navigationController.navigationBar.barTintColor;
    _backTitleAttributes = self.navigationController.navigationBar.titleTextAttributes;
    _backShadowImage = self.navigationController.navigationBar.shadowImage;
}

///重置导航栏属性
- (void)resetNavigationBarAttributes {
    //栏标题文本的属性
    self.navigationController.navigationBar.titleTextAttributes = self.backTitleAttributes;
    //导航栏背景的色调颜色
    self.navigationController.navigationBar.barTintColor = self.backBarTintColor;
    //导航栏的阴影图像
    self.navigationController.navigationBar.shadowImage = self.backShadowImage;
    //导航栏是否半透明
    self.navigationController.navigationBar.translucent = NO;
}

///设置导航项左侧返回按钮
- (void)setNavigationLeftBlockItem{
    [((UIButton *)self.navigationItem.leftBarButtonItem.customView) setImageEdgeInsets:UIEdgeInsetsMake(0, -10, 0, 0)];
    [((UIButton *)self.navigationItem.leftBarButtonItem.customView) setImage:[UIImage imageNamed:@"btn_back_white"] forState:UIControlStateNormal];
    [((UIButton *)self.navigationItem.leftBarButtonItem.customView) setImage:[UIImage imageNamed:@"btn_back_white"] forState:UIControlStateHighlighted];
}

///设置导航栏透明
- (void)setNavigationBarAttributes {
    
    self.navigationController.navigationBar.barTintColor = [UIColor colorWithRed:54.0f/255.0f green:53.0f/255.0f blue:58.0f/255.0f alpha:0.5f];
    self.navigationController.navigationBar.titleTextAttributes = @{NSForegroundColorAttributeName: [UIColor whiteColor]};
    //去掉透明后导航栏下边的黑边
    self.navigationController.navigationBar.shadowImage = [[UIImage alloc] init];
    //设置导航栏背景图片为一个空的image,这样就透明了
    [self.navigationController.navigationBar setBackgroundImage:[[UIImage alloc] init] forBarMetrics:UIBarMetricsDefault];
    self.navigationController.navigationBar.translucent = YES;
}

///设置导航栏搜索视图
- (void)setNavigationItemTitleView{
    //初始化文本框
    self.searchTextField = [[WGUITextField alloc] initWithFrame:CGRectMake(0, 0, UIScreen.mainScreen.bounds.size.width, 33.0)];
    //设置文本输入框样式
    self.searchTextField.clearButtonMode = UITextFieldViewModeWhileEditing;
    self.searchTextField.backgroundColor = [UIColor whiteColor];
    self.searchTextField.layer.cornerRadius = 16.5;
    self.searchTextField.leftViewMode = UITextFieldViewModeAlways;
    self.searchTextField.font = [UIFont systemFontOfSize:13];
    self.searchTextField.textColor = [UIColor blackColor];
    self.searchTextField.returnKeyType = UIReturnKeySearch;
    self.searchTextField.delegate = self;
    self.searchTextField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:@"输入搜索内容" attributes:@{NSForegroundColorAttributeName: [UIColor blackColor], NSFontAttributeName: [UIFont systemFontOfSize:13]}];
    //初始化放大镜按钮视图
    UIButton *searchButton = [UIButton buttonWithType:UIButtonTypeCustom];
    [searchButton setImage:[UIImage imageNamed:@"ic_search_black"] forState:UIControlStateNormal];
    searchButton.contentMode = UIViewContentModeScaleAspectFit;
    searchButton.frame = CGRectMake (7.5, 7.5, 18.0, 18.0);
    //放大镜图标视图添加点击事件
    UITapGestureRecognizer *searchImageViewTapGestureRecognizer = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(searchImageViewTouchUpInside)];
    [searchButton addGestureRecognizer:searchImageViewTapGestureRecognizer];
    //初始化文本清空按钮
    UIButton *clearButton = [UIButton buttonWithType:UIButtonTypeCustom];
    clearButton.frame = CGRectMake(0.0, 7, 19.0, 19.0);
    [clearButton setImage:[UIImage imageNamed:@"btn_clear_content_cricled"] forState:UIControlStateNormal];
    clearButton.imageEdgeInsets = UIEdgeInsetsMake(2.5, 2.5, 2.5, 2.5);
    [clearButton addTarget:self action:@selector(clearSearchText:) forControlEvents:UIControlEventTouchUpInside];
    //添加清空按钮到文本输入框右侧
    self.searchTextField.rightView = clearButton;
    //添加放大镜图标视图到文本输入框左侧
    self.searchTextField.leftView = searchButton;
    //将文本输入框作为导航栏视图
    self.navigationItem.titleView = self.searchTextField;
}

///设置导航栏右侧按钮
- (void)setNavigationRightBlockItem{
    
    //初始化更多按钮
    UIButton *moreButton = [UIButton buttonWithType:UIButtonTypeCustom];
    //设置按钮矩形框架
    moreButton.frame = CGRectMake(0, 0, 25.0, 25.0);
    //添加点击事件
    [moreButton addTarget:self action:@selector(navigationBarButtonItemOnclick:) forControlEvents:UIControlEventTouchUpInside];
    //设置图片内间距
    moreButton.imageEdgeInsets = UIEdgeInsetsMake(0, 0, 0, -6.0);
    //设置...图标
    [moreButton setImage:[UIImage imageNamed:@"btn_more_white"] forState:UIControlStateNormal];
    //初始化条形按钮项
    UIBarButtonItem *moreBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:moreButton];
    //添加标签
    moreButton.tag = 3000;
    
    //初始化扫描按钮
    UIButton *scanButton = [UIButton buttonWithType:UIButtonTypeCustom];
    //设置按钮矩形框架
    scanButton.frame = CGRectMake(0, 0, 25.0, 25.0);
    //添加点击事件
    [scanButton addTarget:self action:@selector(navigationBarButtonItemOnclick:) forControlEvents:UIControlEventTouchUpInside];
    //设置图片内间距
    scanButton.imageEdgeInsets = UIEdgeInsetsMake(0, 0, 0, -6.0);
    //设置扫描图标
    [scanButton setImage:[UIImage imageNamed:@"btn_scan_white"] forState:UIControlStateNormal];
    //初始化条形按钮项
    UIBarButtonItem *scanBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:scanButton];
    //添加标签
    scanButton.tag = 3001;
    //将条形按钮项数组添加到导航栏右侧按钮
    NSArray *rightBarButtonItems = @[moreBarButtonItem, scanBarButtonItem];
    self.navigationItem.rightBarButtonItems = rightBarButtonItems;
}

///清空输入的搜索内容
- (void)clearSearchText:(UIButton *)sender {
    self.searchTextField.text = nil;
}

///点击视图文本输入框以外内容时文本框放弃第一响应对象
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    if ([self.searchTextField isFirstResponder]) {
        [self.searchTextField resignFirstResponder];
    }
}

///导航栏右侧按钮点击事件
- (void)navigationBarButtonItemOnclick:(UIButton *)sender {
    if(sender.tag == 3000){
        NSLog(@"点击了更多按钮");
    }else if(sender.tag == 3001){
        NSLog(@"点击了扫描按钮");
    }
}

///放大镜图标视图点击事件
- (void)searchImageViewTouchUpInside{
    NSLog(@"放大镜点击了");
}

#pragma mark - Text field delegate

- (BOOL)textFieldShouldReturn:(UITextField *)textField {
    
    if (textField.isFirstResponder) {
        [textField resignFirstResponder];
    }
    NSLog(@"搜索%@",textField.text);
    return YES;
}

@end

效果如图 :

Jietu20200218-212943.gif

Bug记录

问题如下 : 在一个有导航栏的控制器push到另一个有导航栏的控制器,在另一个控制器中隐藏了导航栏,然后滑动屏幕返回上一控制器,控制器导航栏消失了,但在控制器滑动返回前,明明在viewWillDisappear:函数中设置了导航栏恢复的代码,但却没有起作用,代码如下:

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    self.disapper = YES;
    self.navigationController.navigationBar.translucent = NO;
    [self.navigationController setNavigationBarHidden:NO animated:YES];
    if ([self.searchTextField isFirstResponder]) {
        [self.searchTextField resignFirstResponder];
    }
}

Jietu20200911-163945.gif

通过对比发现,在隐藏导航栏时使用的是self.navigationController.navigationBar.hidden = YES;来隐藏的,索性将恢复的代码也改为self.navigationController.navigationBar.hidden = NO;进行修改,问题解决,代码如下:

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    self.disapper = YES;
    self.navigationController.navigationBar.translucent = NO;
    self.navigationController.navigationBar.hidden = NO;
    if ([self.searchTextField isFirstResponder]) {
        [self.searchTextField resignFirstResponder];
    }
}
Jietu20200911-165757.gif
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,012评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,589评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,819评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,652评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,954评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,381评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,687评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,404评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,082评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,355评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,880评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,249评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,864评论 3 232
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,007评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,760评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,394评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,281评论 2 259