怎么关闭ios 的位置通知蓝条

2015-12-08 18:42:36 jia12216 阅读数 2873

Xib控件和代码实现的页面,个人热点蓝条出现和消失时,页面下压和恢复导致页面混乱及蓝条下压页面底部控件看不到的问题
问苹果客服,他让我买解决问题次数,哥终于自己搞定它了,后续会更新答案。
当手机开启共享个人热点,一台设备连接这个热点就会出现,个人热点蓝条。
经过测试,无论是否蓝条,对应控件的frame都一样,屏幕实际尺寸也没有变,状态栏也无任何有蓝条的信息,没有找到这种屏幕变化的事件。真让人纠结。
最好解决方式,识别出个人热点蓝条(使用期间开启定位时有时候也会出现定位蓝条)出现和消失的响应事件来调整控件的frame来达到适配蓝条下压和消失的问题。问题是我找不到蓝条出现和消失的通知事件。
在蓝条启动时打印的控件绝对坐标和没有蓝条时的坐标一样:
_[fg214,57,30;2015-12-08 17:42:06:263 caocao[1569:970455] SingleAsyncSocket.m:__36-[SingleAsyncSocket socketBSDThread]_block_invoke:2129 Err:bottomView.frame.origin.x:0.000000, bottomView.frame.origin.y:513.000000,[UIScreen mainScreen].bounds.size.height:568.000000
正常的没有开启个人热点的页面
这里写图片描述
当开启应用时没有个人热点蓝条,当开启个人热点时,xib实现的控件和代码实现的控件分离,xib实现的那个紫色控件被下压,那个白色的代码实现控件保持在原来的位置。
这里写图片描述
当开启应用时有个人热点蓝条,xib实现的那个紫色控件和那个白色的代码实现控件都被下压。
这里写图片描述
当开启应用时有个人热点蓝条后关闭个人热点,xib实现的那个紫色控件上浮,那个白色的代码实现控件位置不变导致,两个控件重叠。
这里写图片描述
当开启应用时有个人热点蓝条,整个页面被下压,导致部分控件看不到,无法操作该控件。

无共享热点蓝条的页面:
这里写图片描述
有共享热点蓝条的页面:
这里写图片描述
开启个人热点:
这里写图片描述

这个是苹果给我回复的让我买咨询次数的邮件,看来老外没有免费的晚餐啊!


您好:

这里是 Apple 开发者计划支援,我是 Selena,很高兴能为您服务。

由于我们是帐号的申请与管理团队,因此,无法直接提供协助。

Apple 开发者计划的参与者可以从开发者技术支持 (DTS)团队获取代码级及电路级技术支持。开发者可以使用年度计划会籍中的 Technical Support Incident(技术支持事件)获取该支持。

要确认您的帐户是否有可用的 Technical Support Incident(技术支持事件),或要购买一个 Technical Support Incident(技术支持事件),请访问 Member Center (会员中心),网址为 :

https://developer.apple.com/membercenter

Apple 开发者技术支持团队会通过英文电子邮件提供支持。请使用以下形式提交您的支持请求

Apple app、Mac app 和 Safari 扩展
https://developer.apple.com/membercenter/index.action#requestTechSupport

有关开发者技术支持的更多信息,请访问:

https://developer.apple.com/cn/support/technical

若您还有其他任何帐号管理的疑问,请不吝与我们联系並请在致电时提供您的 Case Number:1005369896。我们的电话号码是 4006-701-855。

各地区 Apple 开发者计划支援电话号码:
https://developer.apple.com/contact/phone.php

谢谢您的合作。

Selena 
Apple Inc. 

解决方案见:蓝条下压和消失页面错乱问题解决方案(http://write.blog.csdn.net/postedit/50394240)。

2016-01-29 10:06:53 jia12216 阅读数 1747

蓝条下压产生问题的本质原因是,热点出现时系统状态栏高度增加了20个像素,当热点消失时,系统状态栏高度变为标准的20个高度。
理论上只需捕获到系统状态栏变更通知(UIApplicationWillChangeStatusBarFrameNotification这个通知我找了20多天才无意找到),找到自定义工具栏(手写实现或xib自动布局实现)的中心点上移和下移20个像素就可以解决。至于ios7.0以下的蓝条下压产生的页面布局变化就不再适配了,现在的应用最低支持的都是ios7.0了。
实际上解决起来没有那么简单,这只能保证部分全手写控件的蓝条下压和消失控件位置错乱,自动布局控件会有很多莫明奇妙的问题。
修改的方法:
1. 在系统状态栏改变后,调整自定义手写工具栏的中心点(注意若有热点,用户去设置页面并返回原页面,原页面控制器会收到两次状态栏改变通知,第一次带热点的状态栏消失通知,第二次是带热点的状态栏出现通知)。
2. 在系统状态栏改变后,有些调整自定义xib自动布局实现的工具栏中心点,注意有的自动布局控件的最外层承载view上线留有空白,调整的高度可能大于或小于20个像素。
3. 控件出现时要根据是否有蓝条来,对控件frame进行调整。
4. 对于自定义xib自动布局控件,并且是在控件的代理函数调整高度的,注意要在页面首次加载是根据是否有蓝条,调整默认初始化位置(若是自定义xib自动布局控件在viewDidAppear或viewWillAppear显示的加载一次,在用时才加载,那么在iphone6上会显示不出来。)。
蓝条下压导致控件异常的共有四种类型:
1. 普通代码实现控件,车辆类型选择控件。
2. 代码实现控件通过页面种的滑动事件触发调整控件frame,紫色的滑动工具条。
3. 自定义xib控件通过在主页面的滑动按钮事件调整控件frame,白色费用显示工具条。
4. 自定义xib控件通过控件自身的代理函数调整frame,黄色可下拉新的预约单列表显示页面。
测试用例共有六个,不同的场景测试出来的结果截然不同,只有搞定这四个测试用例才能算修改好。
1, 启动应用时有蓝条,在本页面蓝条消失和再次出现,控件位置不能变。
2, 启动应用时没有蓝条,在本页面蓝条出现和消失,控件位置不能变。
3, 启动应用时有蓝条,经过本页面条道其它页面,蓝条消失,再回到本页面,控件位置不能变。
4, 启动应用时没有蓝条,经过本页面条道其它页面,蓝条出现后跳到本页面,控件位置不能变。
5,启动应用时没有蓝条,启动成功,出现蓝条,然后又新的自定义的工具条(xib实现的工具条)出现在顶部。
6,启动应用时有蓝条,控件位置根据是否有蓝条自动调整。
对于车辆类型选择控件,是标准的手写控件,当点击是修改控件的中心点来实现类型的选择按钮的出现和消失。这个控件对栏条下压的适配很简单。
注意若是启动应用时有蓝条,那么你就不能立刻收到状态栏改变通知,需要在加载控件时根据是否有蓝条直接调整控件的初始高度。

- (void)initSelectView
{
    selectView.frame = CGRectMake(0, kControlHeight - 26 - [self handleUIApplicationStatusBarFrame], kControlWidth, 100);
    [self.view addSubview:selectView];

}

-(NSInteger)handleUIApplicationStatusBarFrame
{
    FLDDLogDebug(@"函数");
    // 根据系统状态栏高判断热点栏的变动
    BOOL bPersonalHotspotConnected = ([UIApplication sharedApplication].statusBarFrame.size.height==(SYS_STATUSBAR_HEIGHT+HOTSPOT_STATUSBAR_HEIGHT)?YES:NO);
    if(bPersonalHotspotConnected)
    {
        return HOTSPOT_STATUSBAR_HEIGHT;
    }
    else
    {
        return 0;
    }
}

只需要处理当控件是调整控件的初始位置,处理当收到系统状态改变通知时太哦正控件的中心点就可以,对点击按钮时改变控件位置时适配蓝条就可以。
代码:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleUIApplicationWillChangeStatusBarFrameNotification:)
name:UIApplicationWillChangeStatusBarFrameNotification
object:nil]; //清除车辆选择信息通知

  • (void)ShowBottomSlideView:(UIButton *)sender
    {
    FLDDLogDebug(@”函数”);
    if (NO == _isShow)
    {

    [UIView animateWithDuration:0.5 animations:^{
        CGRect rectView = _slideView.frame;
        if(IS_HOTSPOT_CONNECTED)
        {
            rectView.origin.y = kControlHeight - 110 - HOTSPOT_STATUSBAR_HEIGHT;
            _slideView.frame = rectView;
        }
        else
        {
            rectView.origin.y = kControlHeight - 110;
            _slideView.frame = rectView;
        }
    
    } completion:^(BOOL finished) {
    
          [sender setBackgroundImage:[UIImage imageNamed:@"接单下拉条"] forState:UIControlStateNormal];
    }];
    

    }
    else
    {
    [UIView animateWithDuration:0.5 animations:^{
    CGRect rectView = _slideView.frame;
    if(IS_HOTSPOT_CONNECTED)
    {
    rectView.origin.y = kControlHeight -26 - HOTSPOT_STATUSBAR_HEIGHT;
    _slideView.frame = rectView;
    }
    else
    {
    rectView.origin.y = kControlHeight -26;
    _slideView.frame = rectView;
    }
    } completion:^(BOOL finished) {

        [sender setBackgroundImage:[UIImage imageNamed:@"接单上拉条"] forState:UIControlStateNormal];
    }];
    

    }
    _isShow = !_isShow;
    }

  • (void)handleUIApplicationWillChangeStatusBarFrameNotification:(NSNotification*)notification
    {
    FLDDLogDebug(@”函数”);
    CGRect newStatusBarFrame = [(NSValue*)[notification.userInfo objectForKey:UIApplicationStatusBarFrameUserInfoKey] CGRectValue];
    // 根据系统状态栏高判断热点栏的变动
    BOOL bPersonalHotspotConnected = (CGRectGetHeight(newStatusBarFrame)==(SYS_STATUSBAR_HEIGHT+HOTSPOT_STATUSBAR_HEIGHT)?YES:NO);

    CGPoint newCenter = CGPointZero;
    CGFloat OffsetY = bPersonalHotspotConnected?+HOTSPOT_STATUSBAR_HEIGHT:-HOTSPOT_STATUSBAR_HEIGHT;
    newCenter = _slideView.center;
    if(bPersonalHotspotConnected)
    {
    if(kControlHeight - 26 + 110/2 == newCenter.y)
    {
    OffsetY = HOTSPOT_STATUSBAR_HEIGHT;
    newCenter.y -= OffsetY;
    _slideView.center = newCenter;
    }
    else if(kControlHeight - 110 + 110/2 == newCenter.y)
    {
    OffsetY = HOTSPOT_STATUSBAR_HEIGHT;
    newCenter.y -= OffsetY;
    _slideView.center = newCenter;
    }
    }
    else
    {
    if(kControlHeight - 110 - HOTSPOT_STATUSBAR_HEIGHT + 110/2 == newCenter.y)
    {
    newCenter.y = kControlHeight - 110 + 110/2;
    _slideView.center = newCenter;
    }
    else if(kControlHeight - 26 + 110/2 != newCenter.y)
    {
    newCenter.y = kControlHeight - 26 + 110/2;
    _slideView.center = newCenter;
    }

    }
    }

  • (void)hiddenBottomSlideView:(UIButton *)sender
    {
    FLDDLogDebug(@”函数”);
    if (_isShow)
    {
    CGRect rectView = _slideView.frame;
    rectView.origin.y = kControlHeight -26;
    _slideView.frame = rectView;
    [sender setBackgroundImage:[UIImage imageNamed:@”接单上拉条”] forState:UIControlStateNormal];

// [UIView animateWithDuration:0.5 animations:^{
// CGRect rectView = _slideView.frame;
// rectView.origin.y = kControlHeight -26;
// _slideView.frame = rectView;
// } completion:^(BOOL finished) {
//
// [sender setBackgroundImage:[UIImage imageNamed:@”接单上拉条”] forState:UIControlStateNormal];
// }];
_isShow = !_isShow;
}
}

  • (void)initWithBottomSlideView
    {
    FLDDLogDebug(@”函数”);

    if(IS_HOTSPOT_CONNECTED)
    {
    _slideView = [[UIView alloc] initWithFrame:CGRectMake(0, kControlHeight - 26 - HOTSPOT_STATUSBAR_HEIGHT, kControlWidth, 110)];
    }
    else
    {
    _slideView = [[UIView alloc] initWithFrame:CGRectMake(0, kControlHeight - 26, kControlWidth, 110)];
    }

}

对于自定义xib代理实现控件页面位置改变的控件,对蓝条适配要多一个在加载时的控件初始化位置适配:

  • (void)viewDidAppear:(BOOL)animated
    {
    FLDDLogDebug(@”函数”);
    [super viewDidAppear:YES];

    _mapView.frame = CGRectMake(0, 0, kControlWidth, kControlHeight);
    _mapView.delegate = self;
    _mapView.customizeUserLocationAccuracyCircleRepresentation = NO;
    _mapView.showsUserLocation = YES;

    if (!_viewDidApper)
    {
    _viewDidApper = YES;

    noticecarview.frame = CGRectMake(0, 0, kControlWidth, kControlHeight);
    servicingView.frame=CGRectMake(0, -200, kControlWidth, 178);
    if(IS_HOTSPOT_CONNECTED)
    {
        _bookingButtonView.frame = CGRectMake(0, 0, kControlWidth, 40);
    

    // self.center = CGPointMake(kControlWidth / 2, 84 - HOTSPOT_STATUSBAR_HEIGHT + 20);
    }
    else
    {
    _bookingButtonView.frame = CGRectMake(0, HOTSPOT_STATUSBAR_HEIGHT, kControlWidth, 40);
    // self.center = CGPointMake(kControlWidth / 2, 84);
    }
    _priceView.frame = CGRectMake(0, kControlHeight + 100, kControlWidth, 50);
    }
    }
    其它的自定位手写紫色工具条和自动定义xib自动布局白色费用工具条,黄色预约单工具条对蓝条匹配的代码:
    [[NSNotificationCenter defaultCenter] addObserver:self
    selector:@selector(handleUIApplicationWillChangeStatusBarFrameNotification:)
    name:UIApplicationWillChangeStatusBarFrameNotification
    object:nil];

  • (void)handleUIApplicationWillChangeStatusBarFrameNotification:(NSNotification*)notification
    {
    FLDDLogDebug(@”函数”);
    CGRect newStatusBarFrame = [(NSValue*)[notification.userInfo objectForKey:UIApplicationStatusBarFrameUserInfoKey] CGRectValue];
    // 根据系统状态栏高判断热点栏的变动
    BOOL bPersonalHotspotConnected = (CGRectGetHeight(newStatusBarFrame)==(SYS_STATUSBAR_HEIGHT+HOTSPOT_STATUSBAR_HEIGHT)?YES:NO);

    CGPoint newCenter = CGPointZero;
    CGFloat OffsetY = bPersonalHotspotConnected?+HOTSPOT_STATUSBAR_HEIGHT:-HOTSPOT_STATUSBAR_HEIGHT;
    newCenter = bottomView.center;
    if(bPersonalHotspotConnected)
    {
    if(kControlHeight-27.5 == newCenter.y)
    {
    OffsetY = HOTSPOT_STATUSBAR_HEIGHT;
    newCenter.y -= OffsetY;
    bottomView.center = newCenter;
    }

    newCenter = _priceView.center;
    if(!(_priceView.hidden))
    {
        if(MAP_VIEW_MAIN == [Singleton sharedInstance].mapViewPage)
        {
            newCenter.y = kControlHeight - 75 - HOTSPOT_STATUSBAR_HEIGHT + 14;
        }
        else
        {
            newCenter.y = kControlHeight - 75 - HOTSPOT_STATUSBAR_HEIGHT;
        }
    
        _priceView.center = newCenter;
    }
    

// if(_bookingButtonView.getBookShowFlag)
// {
// if(MAP_VIEW_MAIN == [Singleton sharedInstance].mapViewPage)
// {
// _bookingButtonView.center = CGPointMake(kControlWidth / 2, 84 - HOTSPOT_STATUSBAR_HEIGHT + 20);
// }
// else
// {
// _bookingButtonView.center = CGPointMake(kControlWidth / 2, 84);
// }
// }
}
else
{
if(kControlHeight-27.5 - HOTSPOT_STATUSBAR_HEIGHT == newCenter.y)
{
newCenter.y = kControlHeight-27.5;
bottomView.center = newCenter;
}
newCenter = _priceView.center;
if(!(_priceView.hidden))
{
newCenter.y = kControlHeight-75 - HOTSPOT_STATUSBAR_HEIGHT;
_priceView.center = newCenter;
}

// if(_bookingButtonView.getBookShowFlag)
// {
// _bookingButtonView.center = CGPointMake(kControlWidth / 2, 84);
// }
}
}

//滑动动画
-(void)startSlideAnination
{
FLDDLogDebug(@”函数”);
[UIView animateWithDuration:0.5 animations:^{
// bottomView.frame = CGRectMake(bottomView.frame.origin.x, kControlHeight-100, bottomView.frame.size.width, bottomView.frame.size.height);
if(IS_HOTSPOT_CONNECTED)
{
bottomView.center = CGPointMake(kControlWidth/2,kControlHeight-27.5 - HOTSPOT_STATUSBAR_HEIGHT);
_priceView.center=CGPointMake(kControlWidth/2,kControlHeight-75 - HOTSPOT_STATUSBAR_HEIGHT);
// _priceView.center=CGPointMake(kControlWidth/2,kControlHeight-75);
}
else
{
bottomView.center = CGPointMake(kControlWidth/2,kControlHeight-27.5);
_priceView.center=CGPointMake(kControlWidth/2,kControlHeight-75);
// _priceView.center=CGPointMake(kControlWidth/2,kControlHeight);
}
if((-1 != [GlobalShare getGlobalShare].user.servicingOrderId) && (GETIN == [_orderDict[@”orderStatus”] integerValue]))
{
_priceView.hidden = NO;
}

        bottomView.hidden = NO;

// bottomV iew.center=CGPointMake(kControlWidth/2,kControlHeight-27.5);
// _priceView.center=CGPointMake(kControlWidth/2,kControlHeight-75);

        [slideImageView startAnimating];
    } completion:^(BOOL finished) {

// if(NETWORK_TYPE_WIFI == [self getNetworkTypeFromStatusBar])
// {
// [SVProgressHUD showErrorWithStatus:@”手机自动连接WiFi网络时,会导致行程费用计算不准确,为减少损失,请关闭WiFi”];
// }
// else
// {
// [SVProgressHUD dismiss];
// }

    }];

}

蓝条下压问题相关宏定义:
//系统状态栏相关声明
// iOS系统版本

define SYSTEM_VERSION [[[UIDevice currentDevice] systemVersion] doubleValue]

// 标准系统状态栏高度

define SYS_STATUSBAR_HEIGHT 20

// 热点栏高度

define HOTSPOT_STATUSBAR_HEIGHT 20

// 导航栏(UINavigationController.UINavigationBar)高度

define NAVIGATIONBAR_HEIGHT 44

// 工具栏(UINavigationController.UIToolbar)高度

define TOOLBAR_HEIGHT 44

// 标签栏(UITabBarController.UITabBar)高度

define TABBAR_HEIGHT 44

// APP_STATUSBAR_HEIGHT=SYS_STATUSBAR_HEIGHT+[HOTSPOT_STATUSBAR_HEIGHT]

define APP_STATUSBAR_HEIGHT (CGRectGetHeight([UIApplication sharedApplication].statusBarFrame))

// 根据APP_STATUSBAR_HEIGHT判断是否存在热点栏

define IS_HOTSPOT_CONNECTED (APP_STATUSBAR_HEIGHT==(SYS_STATUSBAR_HEIGHT+HOTSPOT_STATUSBAR_HEIGHT)?YES:NO)

// 无热点栏时,标准系统状态栏高度+导航栏高度

define NORMAL_STATUS_AND_NAV_BAR_HEIGHT (SYS_STATUSBAR_HEIGHT+NAVIGATIONBAR_HEIGHT)

// 实时系统状态栏高度+导航栏高度,如有热点栏,其高度包含在APP_STATUSBAR_HEIGHT中。

define STATUS_AND_NAV_BAR_HEIGHT (APP_STATUSBAR_HEIGHT+NAVIGATIONBAR_HEIGHT)

正常的带预约单黄条页面:

这里写图片描述
当因为没有适配首次出现黄条的初始位置时,当测试那4个测试用例时出现的异常页面:
这里写图片描述

按照以上方案当热点共享显示和消失时,仍有低概率出现底部按钮错乱的问题。主要是xib和手写带frame的控件难以控制。
可以统一使用SDAutoLayout这个第三方库写页面,请看我的新的完美解决方案:热点共享蓝条下压,导致页面底部按钮显示不全完美解决方案(http://blog.csdn.net/jia12216/article/details/53171647

2016-11-15 15:17:46 jia12216 阅读数 3528

手机共享了热点,另一部手机通过wifi连接连接上这个热点。手机顶部会出现一个蓝色的工具栏,并显示”个人热点:1个连接“,这个工具栏比正常的工具栏告20个像素。
当打开一个应用,若该页面不显示系统工具栏,看不到这个热点工具栏;若该页面显示了系统工具栏,那么就可以看到这个高度增加了一倍的工具栏,并且页面的所有空间下移20像素,导致在底部的按钮显示不全。想修改这个问题,主要是如何发现蓝条的显示和消失,来决定是否上移底部的按钮20个像素。系统会根据蓝条的显示和消失,重新布局页面,所以不要移动所有所有控件,一般只需要适配底层一到二个控件就可以。
判断应用状态栏是否显示蓝条可以通过三种方法:
// 标准系统状态栏高度

define SYS_STATUSBAR_HEIGHT 20

// 热点栏高度

define HOTSPOT_STATUSBAR_HEIGHT 20

(1).被动方式,当蓝条显示或消失,会发送状态栏变更通知(UIApplicationWillChangeStatusBarFrameNotification),通过读取通知中状态栏的高度来判断是否显示了栏条。

- (void)handleUIApplicationWillChangeStatusBarFrameNotification:(NSNotification*)notification
{
    [self logViewTreeForMainWindow];

    if(notification != nil)
    {
        CGRect newStatusBarFrame = [(NSValue*)[notification.userInfo objectForKey:UIApplicationStatusBarFrameUserInfoKey] CGRectValue];
        // 根据系统状态栏高判断热点栏的变动
        [SingleObject sharedInstance].bPersonalHotspotConnected = (CGRectGetHeight(newStatusBarFrame)==(SYS_STATUSBAR_HEIGHT+HOTSPOT_STATUSBAR_HEIGHT)?YES:NO);
    }
    else
    {
        [SingleObject sharedInstance].bPersonalHotspotConnected = ([UIApplication sharedApplication].statusBarFrame.size.height==(SYS_STATUSBAR_HEIGHT+HOTSPOT_STATUSBAR_HEIGHT)?YES:NO);
    }
    float hotspotConnectedOffsetY = 0;
    if(([SingleObject sharedInstance].bPersonalHotspotConnected) && (_listenOrderPanel != nil))
    {
        hotspotConnectedOffsetY = -HOTSPOT_STATUSBAR_HEIGHT;
}
//调整控件的frame
}

(2).主动方式,通过直接读取系统状态栏的高度([UIApplication sharedApplication].statusBarFrame.size.height)来判断是否显示了热点蓝条。

- (void)applicationDidBecomeActive:(UIApplication *)application {
    BOOL bPersonalHotspotConnected = ([UIApplication sharedApplication].statusBarFrame.size.height==(SYS_STATUSBAR_HEIGHT+HOTSPOT_STATUSBAR_HEIGHT)?YES:NO);
    [SingleObject sharedInstance].bPersonalHotspotConnected = bPersonalHotspotConnected;
    if([SingleObject sharedInstance].isInYXCenterViewController)
    {
        [[NSNotificationCenter defaultCenter] postNotificationName:CHANGE_STATUS_BAR_FRAME_NOTIFICATION object:nil];
    }
}

(3).主动获取,通过检查keyWindow的倒数第二个图层的初始y坐标是否为20像素来判断是否显示热点蓝条

+(BOOL)getPersonalHotspotConnected
{
    UIView *keyWindowView = [[UIApplication sharedApplication] keyWindow];
    if((keyWindowView != nil) && ([keyWindowView subviews].count > 1))
    {
        UIView *UILayoutContainerView0 = [keyWindowView subviews][1];
        if(UILayoutContainerView0.frame.origin.y == HOTSPOT_STATUSBAR_HEIGHT)
        {
            [SingleObject sharedInstance].bPersonalHotspotConnected = YES;
        }
        else
        {
            [SingleObject sharedInstance].bPersonalHotspotConnected = NO;
        }
    }
    return [SingleObject sharedInstance].bPersonalHotspotConnected;
}

既然知道了如何判断是否显示了热点蓝条,剩下的是如何捕获热点蓝条显示和消失的时机,进而进行实时调整底部的控件的偏移就可以了。热点蓝条显示和消失的情景如下:
1.应用在前台切换到后台时会收到状态栏变更通知,蓝色状态栏通知(状态栏高度为正常的20像素),就是应用在后台,你的应用是没有热点蓝条的。
2.应用在后台,操作系统热点蓝条消失,若应用非定位服务的应用可能你收不到状态栏变更通知,定位服务的应用会收到状态栏变更通知。
3.应用在后台,操作系统热点蓝条出现,若应用非定位服务的应用可能你收不到状态栏变更通知,定位服务的应用应该会收到状态栏变更通知。
4.当应用从后台切换到前台时,会收到状态栏改变通知(状态栏高度为40像素)。
5.当操作系统已经显示蓝条,应用开始启动,你收不到状态变更通知。你想正确调整底部控件,只能主动获取状态栏的高度进行是否显示了热点蓝条,进而调整底部控件。
6.启动时没有热点蓝条,当应用在前台,有人接入热点,显示热点蓝条,会收到状态栏变更通知。当应用在前台,显示热点蓝条,后蓝条消失,会收到状态栏变更通知。
5.当操作系统已经显示蓝条,应用开始启动后,有人接入热点,显示热点蓝条,会收到状态栏变更通知。当应用在前台,显示热点蓝条,后蓝条消失,会收到状态栏变更通知。
可以看到应用在前台时,都是能够收到系统的状态栏变更,只需要被动的接收系统的状态栏变更通知,再调整控件就可以了。
当应用在后台,经过多次热点蓝条的消失和显示,你可能漏掉正确的调整空间的时机。
真对这种情况,你只需要在应用进入前台时,通知相关需要调整的页面调整一下底部控件就可以了,如:(2)主动式获取状态栏的处理。
你处理了这两类情况,你就解决了绝大多数情况。但是还有一种异常情况,咱们看一下:
当然当应用启动前手机已经显示了蓝条,有可能(我的出租车司机端必现)出现整个页面下移动40像素。当蓝条消失时,显示顶部控件到手机顶部比期望的高20像素,工具栏显示的状态栏背景为黑色。当显示热点蓝条时,通过View UI Hierarchy,观察立体图层发现该种异常情况下,页面的全屏幕布局分了3个阶梯,每一个阶梯相差20像素;当不显示热点蓝条时,通过View UI Hierarchy,观察立体图层发现该种异常情况下,页面的全屏幕布局分了2个阶梯,每一个阶梯相差20像素。
当然当应用启动前手机不显示蓝条,进入首页面显示热点蓝条时,通过View UI Hierarchy,观察立体图层发现该种情况下,页面的全屏幕布局分了2个阶梯,每一个阶梯相差20像素;当热点蓝条消失后,通过View UI Hierarchy,观察立体图层发现该种异常情况下,页面的全屏幕布局都一个高度。
通过分析上面两种情况的全屏幕图层的frame高度和起始高度数据看到第6层及以后的名称为 UILayoutContainerView图层显然出现了明显的差异,如:
进入时有蓝条,及蓝条消失第7层的打印数据:
————[ 6] UILayoutContainerView, height:548.000000, origin.y:20.000000
————[ 6] UILayoutContainerView, height:548.000000, origin.y:20.000000
进入时没有蓝条,蓝条显示和消失,再显示时的第7层的打印数据:
————[ 6] UILayoutContainerView, height:568.000000, origin.y:0.000000
————[ 6] UILayoutContainerView, height:568.000000, origin.y:0.000000
————[ 6] UILayoutContainerView, height:568.000000, origin.y:0.000000
可以看到不同,这就是进入时有蓝条,当进入首页时顶部控件距离热点蓝条多20像素,当热点蓝条消失时显示的状态栏有20个像素的黑色区域并且首页的顶部控件到状态栏多20个像素的真正原因。
既然知道了原因,只需要判断出这这种情况,修改大于等于第6层的 UILayoutContainerView图层frame就可以了,看来还是需要修改系统底层的东西啊!应用的图层都是在第8层UINavigationTransitionView以后的图层,并且名称都不叫UILayoutContainerView。只要在加载首页面时判断出这种情况修改好就可以。可以通过遍历keyWindow的所有图层,找到异常的图层把他的frame修改成CGRectMake(0, 0, kScreenWidth, kScreenHeight)就可以。
具体需要修改的代码,在viewDidLoad调整就可以,代码如下:

- (void)dumpView:(UIView *)aView atIndent:(int)indent into:(NSMutableString *)outstring
{
    for (int i = 0; i < indent; i++) [outstring appendString:@"--"];
    [outstring appendFormat:@"[%2d] %@, height:%f, origin.y:%f\n", indent, [[aView class] description], aView.frame.size.height, aView.frame.origin.y];
    if((indent > 5) && (aView != nil) && ([[[aView class] description] isEqualToString:@"UILayoutContainerView"]))
    {
        if(aView.frame.origin.y == HOTSPOT_STATUSBAR_HEIGHT)
        {
            aView.frame = CGRectMake(0, 0, kScreenWidth, kScreenHeight);
        }
    } 
    for (UIView *view in [aView subviews])
        [self dumpView:view atIndent:indent + 1 into:outstring];
}
// Start the tree recursion at level 0 with the root view
- (NSMutableDictionary *) displayViews: (UIView *) aView
{
    NSMutableString *outstring = [[NSMutableString alloc] init];
    UIView *keyWindowView = [[UIApplication sharedApplication] keyWindow];
    [self dumpView: aView atIndent:0 into:outstring];
    if(outstring.length != 0)
    {
        if((keyWindowView != nil) && ([keyWindowView subviews].count > 1))
        {
            UIView *UILayoutContainerView0 = [keyWindowView subviews][1];
            if(UILayoutContainerView0.frame.origin.y == HOTSPOT_STATUSBAR_HEIGHT)
            {
                [SingleObject sharedInstance].bPersonalHotspotConnected = YES;
            }
            else
            {
                [SingleObject sharedInstance].bPersonalHotspotConnected = NO;
            }
        }
        NSString *personalHotspotConnectedValue = [SingleObject sharedInstance].bPersonalHotspotConnected ? @"1":@"0";
        NSMutableDictionary *dic = [NSMutableDictionary dictionaryWithObjectsAndKeys:outstring, @"outstring", personalHotspotConnectedValue, @"bPersonalHotspotConnected",  nil];
        return dic;
    }
    else
    {
        return nil;
    }
}
// Show the tree
- (void)logViewTreeForMainWindow
{
    NSMutableDictionary *dic = [self displayViews:[[UIApplication sharedApplication] keyWindow]];
    if((dic.count == 2) && ([dic[@"outstring"] isKindOfClass:[NSString class]]))
    {
        FLDDLogDebug(@"The view tree:\n%@", dic[@"outstring"]);
    }  
}
- (void)viewDidLoad {
    [super viewDidLoad];
    [self logViewTreeForMainWindow];
}

进入时有蓝条,及蓝条消失全部数据,第1层的数据都相同就不重复了:
[ 0] UIWindow, height:568.000000, origin.y:0.000000
–[ 1] UILayoutContainerView, height:548.000000, origin.y:20.000000
–[ 1] UILayoutContainerView, height:568.000000, origin.y:0.000000
—-[ 2] UINavigationTransitionView, height:548.000000, origin.y:0.000000
—-[ 2] UINavigationTransitionView, height:568.000000, origin.y:0.000000
——[ 3] UIViewControllerWrapperView, height:548.000000, origin.y:0.000000
——[ 3] UIViewControllerWrapperView, height:568.000000, origin.y:0.000000
——–[ 4] UIView, height:548.000000, origin.y:0.000000
——–[ 4] UIView, height:568.000000, origin.y:0.000000
———-[ 5] UIView, height:548.000000, origin.y:0.000000
———-[ 5] UIView, height:548.000000, origin.y:0.000000
————[ 6] UILayoutContainerView, height:548.000000, origin.y:20.000000
————[ 6] UILayoutContainerView, height:548.000000, origin.y:20.000000
————–[ 7] UINavigationTransitionView, height:548.000000, origin.y:0.000000
————–[ 7] UINavigationTransitionView, height:548.000000, origin.y:0.000000
—————-[ 8] UIViewControllerWrapperView, height:548.000000, origin.y:0.000000
—————-[ 8] UIViewControllerWrapperView, height:548.000000, origin.y:0.000000
进入时没有蓝条,蓝条显示和消失的全部数据,第1层的数据都相同就不重复了:
[ 0] UIWindow, height:568.000000, origin.y:0.000000
–[ 1] UILayoutContainerView, height:568.000000, origin.y:0.000000
–[ 1] UILayoutContainerView, height:548.000000, origin.y:20.000000
–[ 1] UILayoutContainerView, height:568.000000, origin.y:0.000000
—-[ 2] UINavigationTransitionView, height:568.000000, origin.y:0.000000
—-[ 2] UINavigationTransitionView, height:548.000000, origin.y:0.000000
—-[ 2] UINavigationTransitionView, height:568.000000, origin.y:0.000000
——[ 3] UIViewControllerWrapperView, height:568.000000, origin.y:0.000000
——[ 3] UIViewControllerWrapperView, height:548.000000, origin.y:0.000000
——[ 3] UIViewControllerWrapperView, height:568.000000, origin.y:0.000000
——–[ 4] UIView, height:568.000000, origin.y:0.000000
——–[ 4] UIView, height:548.000000, origin.y:0.000000
——–[ 4] UIView, height:568.000000, origin.y:0.000000
———-[ 5] UIView, height:568.000000, origin.y:0.000000
———-[ 5] UIView, height:568.000000, origin.y:0.000000
———-[ 5] UIView, height:568.000000, origin.y:0.000000
————[ 6] UILayoutContainerView, height:568.000000, origin.y:0.000000
————[ 6] UILayoutContainerView, height:568.000000, origin.y:0.000000
————[ 6] UILayoutContainerView, height:568.000000, origin.y:0.000000
————–[ 7] UINavigationTransitionView, height:568.000000, origin.y:0.000000
————–[ 7] UINavigationTransitionView, height:568.000000, origin.y:0.000000
————–[ 7] UINavigationTransitionView, height:568.000000, origin.y:0.000000
—————-[ 8] UIViewControllerWrapperView, height:568.000000, origin.y:0.000000
—————-[ 8] UIViewControllerWrapperView, height:568.000000, origin.y:0.000000
—————-[ 8] UIViewControllerWrapperView, height:568.000000, origin.y:0.000000
——————[ 9] UIView, height:568.000000, origin.y:0.000000
——————[ 9] UIView, height:568.000000, origin.y:0.000000
——————[ 9] UIView, height:568.000000, origin.y:0.000000
——————–[10] UIImageView, height:568.000000, origin.y:0.000000
——————–[10] UIImageView, height:568.000000, origin.y:0.000000
——————–[10] UIImageView, height:568.000000, origin.y:0.000000
进入时有蓝条的图层层次图(异常场景):
这里写图片描述
进入时有蓝条,热点共享蓝条消失后的图层层次图(异常场景):
这里写图片描述
进入时没有有蓝条,热点共享蓝条出现后的图层层次图(正常场景):
这里写图片描述
进入时没有热点共享蓝条的图层层次图(正常场景):
这里写图片描述
进入时没有热点蓝条,当热点蓝条消失时,状态栏显示为20个像素的黑色区域,顶部控件到顶部的距离也比预期的高20个像素的截图如下(异常场景):
这里写图片描述
进入时有热点蓝条,当热点蓝条消失时,修正后的正确截图如下:
这里写图片描述
进入时有热点蓝条,修正后的正确截图如下:
这里写图片描述
下面是微信没有完美解决热点共享蓝条下压后导致顶部工具栏异常的图片。可见只是靠接收UIApplicationWillChangeStatusBarFrameNotification通知来处理它引起的状态栏异常是不完美的。我也经过很长时间的修改探索才找到这个完美解决方案。对工作执着、对产品负责的态度,极度注重细节,不断追求完美,没有绝对完美的产品,我们都是在追求路上。
这里写图片描述

2015-11-05 16:16:20 sun2728 阅读数 1090


iOS8到iOS 9 变化笔记 

这里将介绍下我们日常开发一些从iOS8过度到iOS9给我们带来的一些变化,及解决方法。 

一、信息收集 

language:zh-Hans(iOS8) ——> zh-Hans-CN(iOS9) 所有的进程列表都获取不到了


二、App Transport Security 


iOS9和OS X El Capitan的一个新特性,App Transport Security 的目地是提高Apple 操作系统的安全性以及在此操作系统上运行的任何应用的安全性。ATS是苹果针对与 NSURL这一层做的封装,iOS9后ATS默认是开启的,即网络传输需要使用HTTPS。如果想在iOS9后继续使用HTTP的话,有两条路可以走: 


在Info.plist中添加 NSAppTransportSecurity类型Dictionary,

在NSAppTransportSecurity下添加NSAllowsArbitraryLoads,Boolean 为 YES。 


直接使用CFNetwork做网络请求,ASIHTTPRequest就是基于CFNetwotk做的封装,如果有需求的同学可以看看ASI里面           的源码,如果某个时间段你又想要使用HTTPS的话,ASI对SSL/TSL的证书验证有点问题,证书验证还得自己封装一下才行。刚才         我说道,ATS是苹果针对与NSURL这一层做的封装,所以我们使用CFNetwork或者更底层做网络请求的话是不受ATS限制的。

 

三、移除了discoveryd DNS解析服务 

iPhone升级到iOS8后WiFi有时候会有问题,特别是Mac升级到OS X Yosemite后,时而电脑休眠唤醒唬就连不上WiFi,有时候还突然掉线,经常要手动去关闭WiFi在重新连接,这是因为苹果到了OS X Yosemite系统后,把之前的mDNSResponder换成了discoveryd DNS。iOS9和OS X Yosemite10.4后mDNSResponder又回来了。 


四、App Thinning

是一个关于节省iOS设备存储空间的功能,它可以让iOS设备在安装、更新及运行App等场景中仅下载所需的资源,减少App的占用空间,从而节省设备的存储空间。 


App Thinning主要有三个机制: 


Slicing: 

开发者把App安装包上传到AppStore后,Apple服务会自动对安装包切割为不同的应用变体(App variant), 当用户下载安装包时,系统会根据设备型号下载安装对应的单个应用变体。 


On-Demand Resources:

 ORD(随需资源)是指开发者对资源添加标签上传后,系统会根据App运行的情况,动态下载并加载所需资源,而在存储空间不足时,自动删除这类资源。


Bitcode:

开启Bitcode编译后,可以使得开发者上传App时只需上传Intermediate Representation(中间件),而非最终的可执行二进制文件。 在用户下载App之前,AppStore会自动编译中间件,产生设备所需的执行文件供用户下载安装。 


启用Bitcode编译机制,需要注意以下几点:

如果应用开启Bitcode,那么其集成的其他第三方库也需要是Bitcode编译的包才能真正进行Bitcode编译

其中,Bitcode的机制可以支持动态的进行App Slicing,而对于Apple未来进行硬件升级的措施,此机制可以保证在开发者不重新发布版本的情况下而兼容新的设备。


Xcode7默认是开始了Bitcode,如果不想使用可以手动关闭Bitcode: 

选择项目——>点击Target——>点击Build Setttings——>搜索栏里搜bitcode——>把Enable Bitcode对应的Yes改成No。

 

开启Bitcode编译后,编译产生的.app体积会变大(中间代码,不是用户下载的包),且.dSYM文件不能用来崩溃日志的符号化(用户下载的包是Apple服务重新编译产生的,有产生新的符号文件),使用dSYM来收集Crash日志的同学得注意了。 

通过Archive方式上传AppStore的包,可以在Xcode的Organizer工具中下载对应安装包的新的符号文件 

五、后台定位 

iOS9后苹果为了对保障用户的地理位置的隐私对App请求后台定位有了权限设置,则需要多加一些代码。如果不适配iOS9,就不能偷偷在后台定位,如果没有后台定位的权限也是可以在后台定位的,只是会出现蓝条。 

开启后台定位功能:locationManager.allowsBackgroundLocationUpdates = YES; locationManager是CLLocationManager的对象,用来管理整个定位的。


重点 :


配置info.plist,添加一个Required background modes,Array类型的,然后在Required background modes里面Item 0对应的Value设置为App registers for location updates,这样就解决了iOS9后台定位出现蓝条的问题了。


六、 UI Testing 

Xcode7中苹果引入了一种新的方式在应用中进行测试——UI Testting,UI Testting允许我们找到UI元素与之交互,还能检查属性和状态。UI Testting已经完全集成进了Xcode7的测试报告,可以和单元测试一起执行。使用起来跟之前Xcode5出来的XCTest差不多,Xcode bots提供对此的支持,而且command line支持当UI测试失败时会立即发出通知。 可以参考Github上的Demo,

步骤 : 

在DemoTests.m里创建一个test开头的方法 在setUp()里启动应用 XCUIApplication().launch() 

新建一个方法test开头的,在里面获取应用let app = XCUIApplication() 的到let app = XCUIApplication(),app.buttons[“View Detail”].tap()?。

buttons是当前这个界面的所有按钮的集合,[]里面写按钮的名字,tap()就是执行这个按钮所对应的方法,可以是网络请求、界面跳转等等。 

七、URL scheme 

在iOS9中,如果使用URL scheme必须在"Info.plist"中将你要在外部调用的URL scheme列为白名单,否则不能使用。 

配置info.plist,添加一个LSApplicationQueriesSchemes,Array类型的,然后在LSApplicationQueriesSchemes的Item里面添加urlscheme就行了,urlscheme是任意一个字符串,就是你自己需要使用的urlscheme,iOS9 URL scheme白名单适配就完成了。 

八、出现大量的警告 

Xcode7后运行以前的项目后出现大量的警告如: (null): warning: /var/folders/p4/z7zy68r92hd3p5ry5g2v3k_8rlwzzr/C/org.llvm.clang.dalmo/ModuleCache/1TXZDLI9N2EMV/Foundation-3DFYNEBRQSXST.pcm: No such file or directory。 

作为一个有洁癖的我反正是不能忍,出现警告的大致原因跟我上面提到的开启Bitcode,.dSYM文件不能用来符号化有关,Xcode试图去创建dSYM文件,但是你又不需要。


解决方法 : 


Build Settings ——>Build Options——>Debug Information Format Debug下的DWARF with dsYM File改成DWARF 

Release下的还是之前默认的DWARF with dsYM File不变

 
2017-07-06 11:24:45 zhangzheng_CSDN 阅读数 2517


1、iOS开发中我们要使用GPS功能就要开启对应的权限,可以根据公司项目的需求在info.Plist中添加:

Privacy - Location When In Use Usage Description(在应用程序使用期间)

Privacy - Location Always Usage Description(始终)

2、应用程序设置中会有三个选项(1)、永不(2)、使用应用程序期间(3)、始终

3、当应用程序需要实现后台定位时候必须打开capabilities中的uplocation,

4、此时应用程序设置中选择“使用应用程序期间”的时候就会出现蓝条,选择“始终”的时候蓝条就不会出现

iOS9的新特性

阅读数 1812