在新的iOS7 Facebook iPhone应用程序中,当用户向上滚动时,navigationBar
逐渐隐藏到完全消失的程度。然后,当用户向下滚动时,navigationBar
逐渐显示出来。
您将如何自己实施此行为?我知道以下解决方案,但它立即消失,并没有与用户的滚动手势的速度挂钩。
[navigationController setNavigationBarHidden: YES animated:YES];
我希望这不是重复,因为我不确定如何最好地描述“扩展/收缩”行为。
答案 0 :(得分:161)
@peerless给出的解决方案是一个很好的开始,但它只会在拖动开始时启动动画,而不考虑滚动的速度。这会导致比Facebook应用程序更糟糕的体验。为了匹配Facebook的行为,我们需要:
首先,您需要以下属性:
@property (nonatomic) CGFloat previousScrollViewYOffset;
以下是UIScrollViewDelegate
方法:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGRect frame = self.navigationController.navigationBar.frame;
CGFloat size = frame.size.height - 21;
CGFloat framePercentageHidden = ((20 - frame.origin.y) / (frame.size.height - 1));
CGFloat scrollOffset = scrollView.contentOffset.y;
CGFloat scrollDiff = scrollOffset - self.previousScrollViewYOffset;
CGFloat scrollHeight = scrollView.frame.size.height;
CGFloat scrollContentSizeHeight = scrollView.contentSize.height + scrollView.contentInset.bottom;
if (scrollOffset <= -scrollView.contentInset.top) {
frame.origin.y = 20;
} else if ((scrollOffset + scrollHeight) >= scrollContentSizeHeight) {
frame.origin.y = -size;
} else {
frame.origin.y = MIN(20, MAX(-size, frame.origin.y - scrollDiff));
}
[self.navigationController.navigationBar setFrame:frame];
[self updateBarButtonItems:(1 - framePercentageHidden)];
self.previousScrollViewYOffset = scrollOffset;
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
[self stoppedScrolling];
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView
willDecelerate:(BOOL)decelerate
{
if (!decelerate) {
[self stoppedScrolling];
}
}
您还需要这些辅助方法:
- (void)stoppedScrolling
{
CGRect frame = self.navigationController.navigationBar.frame;
if (frame.origin.y < 20) {
[self animateNavBarTo:-(frame.size.height - 21)];
}
}
- (void)updateBarButtonItems:(CGFloat)alpha
{
[self.navigationItem.leftBarButtonItems enumerateObjectsUsingBlock:^(UIBarButtonItem* item, NSUInteger i, BOOL *stop) {
item.customView.alpha = alpha;
}];
[self.navigationItem.rightBarButtonItems enumerateObjectsUsingBlock:^(UIBarButtonItem* item, NSUInteger i, BOOL *stop) {
item.customView.alpha = alpha;
}];
self.navigationItem.titleView.alpha = alpha;
self.navigationController.navigationBar.tintColor = [self.navigationController.navigationBar.tintColor colorWithAlphaComponent:alpha];
}
- (void)animateNavBarTo:(CGFloat)y
{
[UIView animateWithDuration:0.2 animations:^{
CGRect frame = self.navigationController.navigationBar.frame;
CGFloat alpha = (frame.origin.y >= y ? 0 : 1);
frame.origin.y = y;
[self.navigationController.navigationBar setFrame:frame];
[self updateBarButtonItems:alpha];
}];
}
对于略有不同的行为,请将滚动时重新定位栏的行(else
中的scrollViewDidScroll
块)替换为此行:
frame.origin.y = MIN(20,
MAX(-size, frame.origin.y -
(frame.size.height * (scrollDiff / scrollHeight))));
这会根据最后一个滚动百分比定位条形,而不是绝对数量,这会导致淡入淡出。原来的行为更像Facebook,但我也喜欢这个。
注意:此解决方案仅适用于iOS 7+。如果您支持旧版iOS,请务必添加必要的检查。
答案 1 :(得分:52)
编辑:仅适用于iOS 8及更高版本。
您可以尝试使用
self.navigationController.hidesBarsOnSwipe = YES;
适合我。
如果您使用swift进行编码,则必须使用这种方式(来自https://stackoverflow.com/a/27662702/2283308)
navigationController?.hidesBarsOnSwipe = true
答案 2 :(得分:43)
以下是另一项实施: TLYShyNavBar v1.0.0发布!
我决定在尝试所提供的解决方案之后自己制作,对我而言,他们要么表现不佳,要么具有较高的进入障碍和锅炉板代码,要么缺少导航栏下方的扩展视图。要使用此组件,您所要做的就是:
self.shyNavBarManager.scrollView = self.scrollView;
哦,它在我们自己的应用程序中进行了战斗测试。
答案 3 :(得分:33)
您可以查看我的GTScrollNavigationBar。我已经将UINavigationBar子类化,使其基于UIScrollView的滚动进行滚动。
注意:如果您有一个OPAQUE导航栏,则滚动视图必须EXPAND,因为导航栏会获得HIDDEN。这正是GTScrollNavigationBar所做的。 (就像在iOS上的Safari一样。)
答案 4 :(得分:25)
iOS8包含可以免费隐藏导航栏的属性。有一个WWDC视频演示它,搜索“iOS 8中的View Controller Advancements”。
示例强>:
class QuotesTableViewController: UITableViewController {
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
navigationController?.hidesBarsOnSwipe = true
}
}
其他属性:
class UINavigationController : UIViewController {
//... truncated
/// When the keyboard appears, the navigation controller's navigationBar toolbar will be hidden. The bars will remain hidden when the keyboard dismisses, but a tap in the content area will show them.
@availability(iOS, introduced=8.0)
var hidesBarsWhenKeyboardAppears: Bool
/// When the user swipes, the navigation controller's navigationBar & toolbar will be hidden (on a swipe up) or shown (on a swipe down). The toolbar only participates if it has items.
@availability(iOS, introduced=8.0)
var hidesBarsOnSwipe: Bool
/// The gesture recognizer that triggers if the bars will hide or show due to a swipe. Do not change the delegate or attempt to replace this gesture by overriding this method.
@availability(iOS, introduced=8.0)
var barHideOnSwipeGestureRecognizer: UIPanGestureRecognizer { get }
/// When the UINavigationController's vertical size class is compact, hide the UINavigationBar and UIToolbar. Unhandled taps in the regions that would normally be occupied by these bars will reveal the bars.
@availability(iOS, introduced=8.0)
var hidesBarsWhenVerticallyCompact: Bool
/// When the user taps, the navigation controller's navigationBar & toolbar will be hidden or shown, depending on the hidden state of the navigationBar. The toolbar will only be shown if it has items to display.
@availability(iOS, introduced=8.0)
var hidesBarsOnTap: Bool
/// The gesture recognizer used to recognize if the bars will hide or show due to a tap in content. Do not change the delegate or attempt to replace this gesture by overriding this method.
@availability(iOS, introduced=8.0)
unowned(unsafe) var barHideOnTapGestureRecognizer: UITapGestureRecognizer { get }
}
通过http://natashatherobot.com/navigation-bar-interactions-ios8/
找到答案 5 :(得分:12)
我有一种快速而肮脏的解决方案。没有进行任何深入的测试,但这里的想法是:
该属性将保留导航栏中所有项目的UITableViewController类
@property (strong, nonatomic) NSArray *navBarItems;
在同一个UITableViewController类中,我有:
-(void)scrollViewDidScrollToTop:(UIScrollView *)scrollView
{
if([[[UIDevice currentDevice] systemVersion] floatValue] < 7.0f){
return;
}
CGRect frame = self.navigationController.navigationBar.frame;
frame.origin.y = 20;
if(self.navBarItems.count > 0){
[self.navigationController.navigationBar setItems:self.navBarItems];
}
[self.navigationController.navigationBar setFrame:frame];
}
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if([[[UIDevice currentDevice] systemVersion] floatValue] < 7.0f){
return;
}
CGRect frame = self.navigationController.navigationBar.frame;
CGFloat size = frame.size.height - 21;
if([scrollView.panGestureRecognizer translationInView:self.view].y < 0)
{
frame.origin.y = -size;
if(self.navigationController.navigationBar.items.count > 0){
self.navBarItems = [self.navigationController.navigationBar.items copy];
[self.navigationController.navigationBar setItems:nil];
}
}
else if([scrollView.panGestureRecognizer translationInView:self.view].y > 0)
{
frame.origin.y = 20;
if(self.navBarItems.count > 0){
[self.navigationController.navigationBar setItems:self.navBarItems];
}
}
[UIView beginAnimations:@"toggleNavBar" context:nil];
[UIView setAnimationDuration:0.2];
[self.navigationController.navigationBar setFrame:frame];
[UIView commitAnimations];
}
这仅适用于ios&gt; = 7,我知道这很丑,但实现这一目标很快。欢迎任何意见/建议:)
答案 6 :(得分:12)
这适用于iOS 8及更高版本,并确保状态栏仍保留其背景
self.navigationController.hidesBarsOnSwipe = YES;
CGRect statuBarFrame = [UIApplication sharedApplication].statusBarFrame;
UIView *statusbarBg = [[UIView alloc] initWithFrame:statuBarFrame];
statusbarBg.backgroundColor = [UIColor blackColor];
[self.navigationController.view addSubview:statusbarBg];
如果您想在点击状态栏时显示导航栏,则可以执行以下操作:
- (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView {
self.navigationController.navigationBarHidden = NO;
}
答案 7 :(得分:10)
以下是我的实施:SherginScrollableNavigationBar。
在我的方法中,我使用KVO
来观察UIScrollView
的状态,因此没有必要使用委托(并且您可以将此委托用于您需要的任何其他内容)
答案 8 :(得分:7)
请尝试我的解决方案,让我知道为什么这个问题与以前的答案一样好。
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
{
if (fabs(velocity.y) > 1)
[self hideTopBar:(velocity.y > 0)];
}
- (void)hideTopBar:(BOOL)hide
{
[self.navigationController setNavigationBarHidden:hide animated:YES];
[[UIApplication sharedApplication] setStatusBarHidden:hide withAnimation:UIStatusBarAnimationSlide];
}
答案 9 :(得分:6)
我完成此任务的一种方法如下。
例如,将您的视图控制器注册为UIScrollViewDelegate
的{{1}}。
UITableView
在de - (void)scrollViewDidScroll:(UIScrollView *)scrollView;
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView;
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate;
方法中,您可以获取新的contentOffset并相应地向上或向下翻译UIScrollViewDelegate
。
也可以根据您可以设置和计算的某些阈值和因子来设置子视图的alpha值。
希望它有所帮助!
答案 10 :(得分:4)
除了Iwburk的回答,我添加了以下内容来修复非自定义导航栏上的alpha问题并重置viewWillDisappear方法中的导航栏:
- (void)updateBarButtonItems:(CGFloat)alpha
{
for (UIView *view in self.navigationController.navigationBar.subviews) {
NSString *className = NSStringFromClass([view class]);
if ( ![className isEqualToString:@"_UINavigationBarBackground"] ) {
view.alpha = alpha;
}
}
}
- (void)resetNavigationBar {
CGRect frame = self.navigationController.navigationBar.frame;
frame.origin.y = 20;
[self.navigationController.navigationBar setFrame:frame];
[self updateBarButtonItems:1.0f];
}
答案 11 :(得分:4)
我一直在寻找一种允许任何风格和行为的解决方案。您会注意到许多不同的应用程序中的条形冷凝行为是不同的。当然,酒吧看起来的方式在应用程序之间完全不同。
我使用https://github.com/bryankeller/BLKFlexibleHeightBar/
为此问题创建了一个解决方案您可以定义自己的行为规则,以控制条形缩小和增长的方式和时间,并且您可以准确定义条形图的子视图对条形冷凝或增长的反应。
如果你想要有很多灵活性来制作你能想到的任何类型的标题栏,请查看我的项目。
答案 12 :(得分:3)
我试图在我需要一个围绕UITableView的自定义标头的情况下模拟这种行为。我推出了自己的导航&#34;吧,因为它位于页面上的一堆其他内容之下,我希望节标题符合默认值&#34; dock&#34;行为。我想我找到了一种非常聪明和简洁的方法来调整UITableView / UIScrollView和另一个对象,其风格类似于Facebook / Instagram / Chrome /等。应用。
在我的.xib文件中,我将我的组件加载到自由格式视图中:http://imgur.com/0z9yebJ(抱歉,没有内嵌图像的代表)
请注意,在左侧边栏中,表格在主标题视图后面排序。您无法通过屏幕截图判断,但它也与主标题视图具有相同的y位置。由于它超出了视线范围,因此UITableView上的contentInset属性设置为76(主标题视图的高度)。
为了使主标题视图与UIScrollView一起向上滑动,我使用UIScrollViewDelegate的scrollViewDidScroll方法执行一些计算并更改UIScrollView的contentInset以及主标题视图&#39;框架。
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
UIEdgeInsets insets = scrollView.contentInset;
//tableViewInsetDelta and tableViewOriginalInsetValue are NSInteger variables that I set to 0 and 76, respectively, in viewDidLoad
tableViewInsetDelta = tableViewOriginalInsetValue + scrollView.contentOffset.y;
insets.top = tableViewOriginalInsetValue - tableViewInsetDelta;
if (scrollView.contentOffset.y > -76 && scrollView.contentOffset.y < 0) {
[scrollView setContentInset:insets];
self.pathTitleContainer.frame = CGRectMake(self.pathTitleContainer.frame.origin.x, 44 - tableViewInsetDelta, self.pathTitleContainer.frame.size.width, self.pathTitleContainer.frame.size.height);
} else if (scrollView.contentOffset.y > 0) {
insets.top = 0;
[scrollView setContentInset:insets];
self.pathTitleContainer.frame = CGRectMake(self.pathTitleContainer.frame.origin.x, -32, self.pathTitleContainer.frame.size.width, self.pathTitleContainer.frame.size.height);
} else if (scrollView.contentOffset.y < -76) {
insets.top = 76;
[scrollView setContentInset:insets];
self.pathTitleContainer.frame = CGRectMake(self.pathTitleContainer.frame.origin.x, 44, self.pathTitleContainer.frame.size.width, self.pathTitleContainer.frame.size.height);
}
}
第一个 if 语句完成了大部分繁重工作,但我必须包含其他两个来处理用户强行拖动的情况,并且发送到scrollViewDidScroll的初始contentOffset值超出了第一个 if 语句的范围。
最终,这对我来说非常有用。我讨厌用一堆膨胀的子类加载我的项目。我无法说明这是否是性能方面的最佳解决方案(我总是犹豫不决将任何代码放在scrollViewDidScroll中,因为它一直被调用),但代码占用空间是最小的我&# 39;已经在任何解决方案中看到了这个问题,它并没有涉及在UIScrollView中嵌套UITableView(Apple在文档和触摸事件中提出建议,最终在UITableView上有点时髦)。希望这有助于某人!
答案 13 :(得分:3)
HidingNavigationBar 如果您愿意,可以隐藏导航栏和标签栏的精彩项目。
HidingNavigationBar支持隐藏/显示以下视图 元素:
UINavigationBar的
UINavigationBar和扩展UIView
UINavigationBar和UIToolbar
UINavigationBar和UITabBar
答案 14 :(得分:2)
我尝试实现GTScrollNavigationBar,但我的应用程序要求我修改自动布局约束。我决定在GitHub上添加一个我的实现示例,以防其他任何人必须使用自动布局执行此操作。我与大多数其他实现的另一个问题是,人们不设置滚动视图的边界,以避免在滚动和同时调整滚动视图的大小时创建的视差滚动效果。
如果您需要使用自动布局,请查看JSCollapsingNavBarViewController。我有两个版本,一个只有导航栏,另一个带导航栏下方的子栏,在折叠导航栏之前会折叠。
答案 15 :(得分:1)
我用这种方式尝试过,我希望它会有所帮助。 只需在委托方法中实现代码并设置为所需的视图/子视图
-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
CGRect frame=self.view.frame;
CGRect resultFrame=CGRectZero;
if(scrollView.contentOffset.y==0 || scrollView.contentOffset.y<0){
self.lastContentOffset=0;
self.offset=0;
resultFrame=CGRectMake(0, frame.size.height-(40-self.offset.intValue), frame.size.width, 40-self.offset.intValue);
// Pass the resultFrame
[self showHide:YES withFrame:resultFrame];
}else if (self.lastContentOffset > scrollView.contentOffset.y){
NSNumber *temp=[NSNumber numberWithDouble:self.lastContentOffset-scrollView.contentOffset.y];
if(temp.intValue>40 || self.offset.intValue<temp.intValue){
self.offset=[NSNumber numberWithInt:0];
resultFrame=CGRectMake(0, frame.size.height-(40-self.offset.intValue), frame.size.width, 40-self.offset.intValue);
// Pass the resultFrame
[self showHide:YES withFrame:resultFrame];
}else{
if(temp.intValue>0){
self.offset=[NSNumber numberWithInt:self.offset.intValue-temp.intValue];
resultFrame=CGRectMake(0, frame.size.height-(40-self.offset.intValue), frame.size.width, 40-self.offset.intValue);
// Pass the resultFrame
[self showHide:YES withFrame:resultFrame];
}
}
}else if (self.lastContentOffset < scrollView.contentOffset.y){
NSNumber *temp=[NSNumber numberWithDouble:scrollView.contentOffset.y-self.lastContentOffset];
if(self.offset.intValue>40 || (self.offset.intValue+temp.intValue)>40){
self.offset=[NSNumber numberWithInt:40];
// Pass the resultFrame
[self showHide:NO withFrame:resultFrame];
}else{
self.offset=[NSNumber numberWithInt:self.offset.intValue+temp.intValue];
resultFrame=CGRectMake(0, frame.size.height-(40-self.offset.intValue), frame.size.width, 40-self.offset.intValue);
// Pass the resultFrame
[self showHide:YES withFrame:resultFrame];
}
}
self.lastContentOffset = scrollView.contentOffset.y;
}
-(void)showHide:(Boolean)boolView withFrame:(CGRect)frame{
if(showSRPFilter){
//Assign value of "frame"to any view on which you wan to to perform animation
}else{
//Assign value of "frame"to any view on which you wan to to perform animation
}
}
答案 16 :(得分:1)
@Iwburk答案的扩展......我需要扩展/缩小导航栏的大小,而不是更改导航栏的原点。
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGRect frame = self.previousRect; // a property set in the init method to hold the initial size of the uinavigationbar
CGFloat size = frame.size.height;
CGFloat framePercentageHidden = ((MINIMUMNAVBARHEIGHT - frame.origin.y) / (frame.size.height - 1));
CGFloat scrollOffset = scrollView.contentOffset.y;
CGFloat scrollDiff = scrollOffset - self.previousScrollViewYOffset;
CGFloat scrollHeight = scrollView.frame.size.height;
CGFloat scrollContentSizeHeight = scrollView.contentSize.height + scrollView.contentInset.bottom;
if (scrollOffset <= -scrollView.contentInset.top) {
frame.origin.y = -MINIMUMNAVBARHEIGHT;
} else if ((scrollOffset + scrollHeight) >= scrollContentSizeHeight) {
frame.origin.y = -size;
} else {
frame.origin.y = MIN(-MINIMUMNAVBARHEIGHT, MAX(-size, frame.origin.y - scrollDiff));
}
self.previousRect = CGRectMake(0, frame.origin.y, self.jsExtendedBarView.frame.size.width, 155);
self.layoutConstraintExtendedViewHeight.constant = MAXIMUMNAVBARHEIGHT + frame.origin.y + MINIMUMNAVBARHEIGHT;
[self updateBarButtonItems:(1 - framePercentageHidden)];
self.previousScrollViewYOffset = scrollOffset;
}
它不适用于stoppedScrolling
方法,但是当我拥有它时发布更新
答案 17 :(得分:0)
所有这些方法看起来都过于复杂......很自然地,我建立了自己的方法:
class ViewController: UIViewController, UIScrollViewDelegate {
var originalNavbarHeight:CGFloat = 0.0
var minimumNavbarHeight:CGFloat = 0
weak var scrollView:UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// setup delegates
scrollView.delegate = self
// save the original nav bar height
originalNavbarHeight = navigationController!.navigationBar.height
}
func scrollViewDidScroll(scrollView: UIScrollView) {
// will relayout subviews
view.setNeedsLayout() // calls viewDidLayoutSubviews
}
override func viewDidLayoutSubviews() {
var percentageScrolled = min(scrollView.contentOffset.y / originalNavbarHeight, 1)
navigationController?.navigationBar.height = min(max((1 - percentageScrolled) * originalNavbarHeight, minimumNavbarHeight), originalNavbarHeight)
// re-position and scale scrollview
scrollView.y = navigationController!.navigationBar.height + UIApplication.sharedApplication().statusBarFrame.height
scrollView.height = view.height - scrollView.y
}
override func viewWillDisappear(animated: Bool) {
navigationController?.navigationBar.height = originalNavbarHeight
}
}
答案 18 :(得分:0)
我找到了Objective-C中给出的所有答案。这是我在Swift 3中的答案。这是非常通用的代码,可以直接使用。它适用于UIScrollView和UITableView。
var lastContentOffset: CGPoint? = nil
var maxMinus: CGFloat = -24.0
var maxPlus: CGFloat = 20.0
var initial: CGFloat = 0.0
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Alarm Details"
self.lastContentOffset = self.alarmDetailsTableView.contentOffset
initial = maxPlus
}
func scrollViewDidScroll(_ scrollView: UIScrollView)
{
var navigationBarFrame: CGRect = self.navigationController!.navigationBar.frame
let currentOffset = scrollView.contentOffset
if (currentOffset.y > (self.lastContentOffset?.y)!) {
if currentOffset.y > 0 {
initial = initial - fabs(CGFloat(currentOffset.y - self.lastContentOffset!.y))
}
else if scrollView.contentSize.height < scrollView.frame.size.height {
initial = initial + fabs(CGFloat(currentOffset.y - self.lastContentOffset!.y))
}
}
else {
if currentOffset.y < scrollView.contentSize.height - scrollView.frame.size.height {
initial = initial + fabs(CGFloat(currentOffset.y - self.lastContentOffset!.y))
}
else if scrollView.contentSize.height < scrollView.frame.size.height && initial < maxPlus {
initial = initial - fabs(CGFloat(currentOffset.y - self.lastContentOffset!.y))
}
}
initial = (initial <= maxMinus) ? maxMinus : initial
initial = (initial >= maxPlus) ? maxPlus : initial
navigationBarFrame.origin.y = initial
self.navigationController!.navigationBar.frame = navigationBarFrame
scrollView.frame = CGRect(x: 0.0, y: initial + navigationBarFrame.size.height , width: navigationBarFrame.size.width, height: self.view.frame.size.height - (initial + navigationBarFrame.size.height))
let framePercentageHidden: CGFloat = ((20 - navigationBarFrame.origin.y) / (navigationBarFrame.size.height));
self.lastContentOffset = currentOffset;
self.updateBarButtonItems(alpha: 1 - framePercentageHidden)
}
func updateBarButtonItems(alpha: CGFloat)
{
self.navigationController?.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName: UIColor.darkGray.withAlphaComponent(alpha)]
self.navigationController?.navigationBar.isUserInteractionEnabled = (alpha < 1) ? false: true
guard (self.navigationItem.leftBarButtonItems?.count) != nil else { return }
for (_, value) in self.navigationItem.leftBarButtonItems!.enumerated() {
value.customView?.alpha = alpha
}
guard (self.navigationItem.rightBarButtonItems?.count) != nil else { return }
for (_, value) in (self.navigationItem.rightBarButtonItems?.enumerated())! {
value.customView?.alpha = alpha
}
}
将alpha设置为导航项的逻辑从@ WayneBurkett回复复制并在Swift 3中重写。
答案 19 :(得分:0)
对于Swift 4,5-iOS 11及更高版本
private var previousScrollViewYOffset: CGFloat = 0
private var firstLoad = true
// to avoid scrollViewDidScroll called when first time view controller load
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
firstLoad = false
}
// MARK: - UIScrollViewDelegate
extension ViewController: UIScrollViewDelegate {
func stoppedScrolling() {
let frame = self.navigationController?.navigationBar.frame ?? .zero
if frame.origin.y < UIView.statusBarFrame.size.height {
self.animateNavBar(to: -frame.size.height + UIView.statusBarFrame.size.height)
}
}
func updateBarButtonItems(alpha: CGFloat) {
self.navigationItem.leftBarButtonItems?.forEach{ item in
item.customView?.alpha = alpha
}
self.navigationItem.rightBarButtonItems?.forEach{ item in
item.customView?.alpha = alpha
}
self.navigationItem.titleView?.alpha = alpha
self.navigationController?.navigationBar.tintColor = self.navigationController?.navigationBar.tintColor.withAlphaComponent(alpha)
}
func animateNavBar(to y: CGFloat) {
UIView.animate(withDuration: 0.2) {[weak self] in
var frame: CGRect = self?.navigationController?.navigationBar.frame ?? .zero
let alpha: CGFloat = frame.origin.y >= y ? 0 : 1
frame.origin.y = y
self?.navigationController?.navigationBar.frame = frame
self?.updateBarButtonItems(alpha: alpha)
}
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if firstLoad { return }
var frame = self.navigationController?.navigationBar.frame ?? .zero
let size = frame.size.height - UIView.statusBarFrame.size.height
let framePercentageHidden = (UIView.statusBarFrame.size.height - frame.origin.y) / (frame.size.height - 1)
let scrollOffset = scrollView.contentOffset.y
let scrollDiff = scrollOffset - previousScrollViewYOffset
let scrollHeight = scrollView.frame.size.height
let scrollContentSizeHeight = scrollView.contentSize.height + scrollView.contentInset.bottom
if scrollOffset <= -scrollView.contentInset.top {
frame.origin.y = UIView.statusBarFrame.size.height
} else if ((scrollOffset + scrollHeight) >= scrollContentSizeHeight) {
frame.origin.y = -size
} else {
frame.origin.y = min(UIView.statusBarFrame.size.height, max(-size, frame.origin.y - scrollDiff))
}
self.navigationController?.navigationBar.frame = frame
self.updateBarButtonItems(alpha: 1 - framePercentageHidden)
self.previousScrollViewYOffset = scrollOffset
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
self.stoppedScrolling()
}
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
if(!decelerate) {
self.stoppedScrolling()
}
}
}
UIView扩展
extension UIView {
public static var statusBarFrame: CGRect {
get {
return UIApplication.shared.statusBarFrame
}
}
}
您应自定义navigationItem.titleView
以应用集合alpha