欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

iOS实现导航栏透明示例代码

程序员文章站 2023-12-20 12:20:10
在最近一个项目中碰到这样一个场景,在被push进来的一个页面设置导航栏透明,且要求控制对tableview组的头视图进行悬停显示,nav随着tableview偏移量改变透明...

在最近一个项目中碰到这样一个场景,在被push进来的一个页面设置导航栏透明,且要求控制对tableview组的头视图进行悬停显示,nav随着tableview偏移量改变透明度,当然这样的需求确实不是什么难事,但是如果当前页面继续push一个不需要此类效果的页面,当在返回当前页面的时候就会出现一个坑,nav的展示很突兀,下面是直接上解决方法...ps:假设a页面为需要设置透明,b页面被apush且不需要设置透明

首先在需要设置导航栏透明的页面的viewdidload中写上

self.title = @"title";
[self.navigationcontroller.navigationbar setbackgroundimage:[uiimage new] forbarmetrics:uibarmetricsdefault];
  self.navigationcontroller.navigationbar.shadowimage = [uiimage new];
  self.barimageview = self.navigationcontroller.navigationbar.subviews.firstobject;
  self.barimageview.alpha = 0;
  //设置状态栏
  [[uiapplication sharedapplication] setstatusbarstyle:uistatusbarstylelightcontent];
  //设置标题颜色
  self.navigationcontroller.navigationbar.titletextattributes = @{nsforegroundcolorattributename : [uicolor clearcolor]};

在scrollviewdidscroll代理方法中

-(void)scrollviewdidscroll:(uiscrollview *)scrollview {

  cgfloat offset = scrollview.contentoffset.y;
  //根据自己需要设置(136)的大小
  cgfloat alpha = offset / 136;
  _barimageview.alpha = alpha;
  //记录下当前的透明度,在返回当前页面时需要
  _alpha = alpha;
  [[nsuserdefaults standarduserdefaults] setobject:[nsnumber numberwithfloat:alpha] forkey:@"_alpha"];
  //设置标题的透明度
  self.navigationcontroller.navigationbar.titletextattributes = @{nsforegroundcolorattributename : [uicolor colorwithwhite:0 alpha:alpha]};
}

当前页的viewwillappear, viewdidappear, viewwilldisappear

-(void)viewwillappear:(bool)animated
{

  [super viewwillappear:animated];
  self.table.delegate = self;

}

-(void)viewdidappear:(bool)animated {
  bool isgesturepop = [[[nsuserdefaults standarduserdefaults] objectforkey:@"isgesturepop"] boolvalue];
  if (!isgesturepop) {
    _barimageview.alpha = _alpha;
    self.navigationcontroller.navigationbar.titletextattributes = @{nsforegroundcolorattributename : [uicolor colorwithwhite:0 alpha:_alpha]};
  }
  [super viewdidappear:animated];
}

-(void)viewwilldisappear:(bool)animated
{
  [super viewwilldisappear:animated];
  self.table.delegate = nil;
  self.navigationcontroller.navigationbar.titletextattributes = @{nsforegroundcolorattributename : [uicolor blackcolor]};

  _barimageview.alpha = 1;
  [[nsuserdefaults standarduserdefaults] setobject:[nsnumber numberwithbool:no] forkey:@"isgesturepop"];
}

那么在我们需要push的下一个页面需要什么操作呢,我们需要在这个页面显示正常的nav并且禁掉系统的手势pop,自己写一个pop手势,以方便我们拿到pop滑动时的偏移量,在做的时候使用了两个类,在最后会有源码贴出

b.m 须遵守uigesturerecognizerdelegate,并导入navigationinteractivetransition.h

全局变量

@property (nonatomic, strong) navigationinteractivetransition *navt;

viewdidload

self.navigationcontroller.interactivepopgesturerecognizer.enabled = no;

  uigesturerecognizer *gesture = self.navigationcontroller.interactivepopgesturerecognizer;
  gesture.enabled = no;
  uiview *gestureview = gesture.view;

  uipangesturerecognizer *poprecognizer = [[uipangesturerecognizer alloc] init];
  poprecognizer.delegate = self;
  poprecognizer.maximumnumberoftouches = 1;
  [gestureview addgesturerecognizer:poprecognizer];

  _navt = [[navigationinteractivetransition alloc] initwithviewcontroller:self.navigationcontroller];
  [poprecognizer addtarget:_navt action:@selector(handlecontrollerpop:)];

uigesturerecognizerdelegate 代理方法gesturerecognizershouldbegin

- (bool)gesturerecognizershouldbegin:(uigesturerecognizer *)gesturerecognizer {
  //记录当前是是否是通过手势滑动回去
  [[nsuserdefaults standarduserdefaults] setobject:[nsnumber numberwithbool:yes] forkey:@"isgesturepop"];
  /**
   * 这里有两个条件不允许手势执行,1、当前控制器为根控制器;2、如果这个push、pop动画正在执行(私有属性)
   */
  return self.navigationcontroller.viewcontrollers.count != 1 && ![[self.navigationcontroller valueforkey:@"_istransitioning"] boolvalue];
}

需要依赖的两个类源码

navigationinteractivetransition.h

#import <uikit/uikit.h>

@class uiviewcontroller, uipercentdriveninteractivetransition;
@interface navigationinteractivetransition : nsobject <uinavigationcontrollerdelegate>
- (instancetype)initwithviewcontroller:(uiviewcontroller *)vc;
- (void)handlecontrollerpop:(uipangesturerecognizer *)recognizer;
- (uipercentdriveninteractivetransition *)interactivepoptransition;
@end

navigationinteractivetransition.m

#import "navigationinteractivetransition.h"
#import "popanimation.h"

@interface navigationinteractivetransition ()
@property (nonatomic, weak) uinavigationcontroller *vc;
@property (nonatomic, strong) uipercentdriveninteractivetransition *interactivepoptransition;
@property(nonatomic, strong) uiimageview *barimageview;
@end

@implementation navigationinteractivetransition

- (instancetype)initwithviewcontroller:(uiviewcontroller *)vc
{
  self = [super init];
  if (self) {
    self.vc = (uinavigationcontroller *)vc;
    self.vc.delegate = self;
  }
  return self;
}

/**
 * 我们把用户的每次pan手势操作作为一次pop动画的执行
 */
- (void)handlecontrollerpop:(uipangesturerecognizer *)recognizer {
  /**
   * interactivepoptransition就是我们说的方法2返回的对象,我们需要更新它的进度来控制pop动画的流程,我们用手指在视图中的位置与视图宽度比例作为它的进度。
   */
  cgfloat progress = [recognizer translationinview:recognizer.view].x / recognizer.view.bounds.size.width;
  [self.vc.navigationbar setbackgroundimage:[uiimage new] forbarmetrics:uibarmetricsdefault];
  self.vc.navigationbar.shadowimage = [uiimage new];
  self.barimageview = self.vc.navigationbar.subviews.firstobject;

  cgfloat alpha = [[[nsuserdefaults standarduserdefaults] objectforkey:@"_alpha"] floatvalue];
  self.barimageview.alpha = 1 - progress > alpha ? alpha : 1 - progress;
//  nslog(@"===progress==%.2f",progress);
  /**
   * 稳定进度区间,让它在0.0(未完成)~1.0(已完成)之间
   */
  progress = min(1.0, max(0.0, progress));
  if (recognizer.state == uigesturerecognizerstatebegan) {
    /**
     * 手势开始,新建一个监控对象
     */
    self.interactivepoptransition = [[uipercentdriveninteractivetransition alloc] init];
    /**
     * 告诉控制器开始执行pop的动画
     */
    [self.vc popviewcontrolleranimated:yes];
  }
  else if (recognizer.state == uigesturerecognizerstatechanged) {

    /**
     * 更新手势的完成进度
     */
    [self.interactivepoptransition updateinteractivetransition:progress];
  }
  else if (recognizer.state == uigesturerecognizerstateended || recognizer.state == uigesturerecognizerstatecancelled) {

    /**
     * 手势结束时如果进度大于一半,那么就完成pop操作,否则重新来过。
     */
    if (progress > 0.5) {
      [self.interactivepoptransition finishinteractivetransition];
      self.barimageview.alpha = 0;;
    }
    else {
      [self.interactivepoptransition cancelinteractivetransition];
    }

    self.interactivepoptransition = nil;
  }

}

- (id<uiviewcontrolleranimatedtransitioning>)navigationcontroller:(uinavigationcontroller *)navigationcontroller
                 animationcontrollerforoperation:(uinavigationcontrolleroperation)operation
                        fromviewcontroller:(uiviewcontroller *)fromvc
                         toviewcontroller:(uiviewcontroller *)tovc {
  /**
   * 方法1中判断如果当前执行的是pop操作,就返回我们自定义的pop动画对象。
   */
  if (operation == uinavigationcontrolleroperationpop)
    return [[popanimation alloc] init];

  return nil;
}

- (id<uiviewcontrollerinteractivetransitioning>)navigationcontroller:(uinavigationcontroller *)navigationcontroller
             interactioncontrollerforanimationcontroller:(id<uiviewcontrolleranimatedtransitioning>)animationcontroller {

  /**
   * 方法2会传给你当前的动画对象animationcontroller,判断如果是我们自定义的pop动画对象,那么就返回interactivepoptransition来监控动画完成度。
   */
  if ([animationcontroller iskindofclass:[popanimation class]])
    return self.interactivepoptransition;

  return nil;
}

@end

popanimation.h

#import <foundation/foundation.h>
#import <uikit/uikit.h>

@interface popanimation : nsobject <uiviewcontrolleranimatedtransitioning>

@end

popanimation.m

#import "popanimation.h"

@interface popanimation ()
@property (nonatomic, strong) id <uiviewcontrollercontexttransitioning> transitioncontext;
@end

@implementation popanimation

- (nstimeinterval)transitionduration:(id <uiviewcontrollercontexttransitioning>)transitioncontext {
  //这个方法返回动画执行的时间
  return 0.25;
}

/**
 * transitioncontext你可以看作是一个工具,用来获取一系列动画执行相关的对象,并且通知系统动画是否完成等功能。
 */
- (void)animatetransition:(id <uiviewcontrollercontexttransitioning>)transitioncontext {
  /**
   * 获取动画来自的那个控制器
   */
  uiviewcontroller *fromviewcontroller = [transitioncontext viewcontrollerforkey:uitransitioncontextfromviewcontrollerkey];
  /**
   * 获取转场到的那个控制器
   */
  uiviewcontroller *toviewcontroller = [transitioncontext viewcontrollerforkey:uitransitioncontexttoviewcontrollerkey];

  /**
   * 转场动画是两个控制器视图时间的动画,需要一个containerview来作为一个“舞台”,让动画执行。
   */
  uiview *containerview = [transitioncontext containerview];
  [containerview insertsubview:toviewcontroller.view belowsubview:fromviewcontroller.view];

  nstimeinterval duration = [self transitionduration:transitioncontext];

  /**
   * 执行动画,我们让fromvc的视图移动到屏幕最右侧
   */
  [uiview animatewithduration:duration animations:^{
    fromviewcontroller.view.transform = cgaffinetransformmaketranslation([uiscreen mainscreen].bounds.size.width, 0);
  }completion:^(bool finished) {
    /**
     * 当你的动画执行完成,这个方法必须要调用,否则系统会认为你的其余任何操作都在动画执行过程中。
     */
    [transitioncontext completetransition:!transitioncontext.transitionwascancelled];
  }];

}

- (void)animationdidstop:(catransition *)anim finished:(bool)flag {
  [_transitioncontext completetransition:!_transitioncontext.transitionwascancelled];
}
@end

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

上一篇:

下一篇: