iOS in-Call 指示器正在下推视图/内容,修改根视图`frame`

Posted

技术标签:

【中文标题】iOS in-Call 指示器正在下推视图/内容,修改根视图`frame`【英文标题】:iOS in-Call indicator is pushing down view/content, modifying root view `frame` 【发布时间】:2018-02-01 17:07:26 【问题描述】:

我有一个问题,我的根视图(UIViewController 视图)被通话中指示器下推:window.rootViewController.view.frame 正在被修改(Y 设置为 20)。当我自己回复did/willStatusBarFrameChange 时,我不想要这种行为。

我正在寻找阻止修改框架以响应通话状态栏的属性或设置。我使用其他 API 来响应顶部/底部框架和 iPhone X 安全区域的变化。

我已经尝试过autoResizingMaskextendedLayoutIncludesOpaqueBarsedgesForExtendedLayoutviewRespectsSystemMinimumLayoutMargins 之类的方法,但没有任何效果。

如果相关,视图也会向下动画,表明它不是某种副作用,而是某处的预期行为。

我已经阅读了许多类似行为的报告,但还没有弄清楚他们是否真的解决了这个问题和/或解决方案实际上是什么(每个解决方案似乎解决了一个略有不同的问题)。

相关问题:Prevent In-Call Status Bar from Affecting View(答案不够详细)、Auto Layout and in-call status bar(不清楚如何改编)

--

我无法提供简单的再现,但设置视图的代码部分如下所示:

窗口设置:

uWindow* window = [[uContext sharedContext] window];
window.rootViewController = (UIViewController*)[[UIApplication sharedApplication] delegate];
[window makeKeyAndVisible];

我们的 AppDelegate 实现(相关部分)

@interface uAppDelegate : UIViewController<@(AppDelegate.Implements:Join(', '))>

...

@implementation uAppDelegate
- (id)init

    CGRect screenBounds = [UIScreen mainScreen].bounds;
    uWindow* window = [[uWindow alloc] initWithFrame:screenBounds];
    return self;

我们将根视图分配给上述委托,即 UIViewController 的 .view 属性。

@interface OurRootView : UIControl<UIKeyInput>

UIControl* root = [[::OurRootView alloc] init];
[root setUserInteractionEnabled: true];
[root setMultipleTouchEnabled: true];
[root setOpaque: false];
[[root layer] setAnchorPoint:  0.0f, 0.0f ];
// some roundabout calls that make `root` the `rootViewController.view = root`
[root sizeToFit];

目标是OurRootView 始终占据整个屏幕空间,而不管调整了哪些帧/控件/边距。我正在使用其他 API 来检测这些帧并相应地调整内容。我没有使用任何其他控制器、视图或布局。

【问题讨论】:

你没有使用自动布局?您从代码中实例化了视图? 是的,这都是在 ObjC 中手工编码的。我们需要控制根视图。 我遇到了同样的问题。如果我理解得很好,我会把解决方案发给你。 “问题是我的视图本身在控制器中被向下移动,所以我不能在它下面绘制。”那有什么意思?控制器不是视图,因此视图不能在其中移动。什么是“画在下面”?当 in call bar 出现时,您的应用程序向下移动是正常的和预期的。有什么问题? "打个电话给rootViewController.view = root" 但是你知道那是完全违法的吗?您不能将视图分配给视图控制器(当然,除了在它的loadView 实现中)。视图控制器有多种方式获取它的视图,直接赋值不是其中之一。你所做的一切都如此另类,如此完全地在苍白之外,我很惊讶它竟然能奏效。这让人很难同情。 【参考方案1】:

目前尚不清楚是否有禁用此行为的标志。然而,我确实找到了一种否定效果的方法。

通过修改根视图的frame 来实现框架下移的原因。可以覆盖此设置器并阻止移动。在我们的例子中,根视图是固定的,因此我这样做了:

@implementation OurRootView

- (void)setFrame:(CGRect)frame;

    frame.origin.y = 0;
    [super setFrame:frame];

@endf

当显示通话显示时,这会将视图保持在固定位置(我们通过更改 statusBarFrame 和/或 safeAreaInsets 自己处理新大小)。 我不知道为什么这也避免了帧的动画,但确实如此。

如果由于某种原因您无法覆盖setFrame,您可以通过覆盖应用程序委托的didChangeStatusBarFrame 并修改根视图的框架(将原点设置回0)来获得几乎相似的效果。动画仍然在这条路线上播放。

【讨论】:

不幸的是,我们似乎没有“api 正确”的答案,这是最好的解决方法。 :(【参考方案2】:

我希望我能理解您的问题:如果您有诸如 incall 之类的指示器,或者在我的情况下使用地图的位置。您需要在启动应用程序时检测到有一些指示器并重新设置整个窗口的框架。我的解决方案:

didFinishLaunchingWithOptions 中检查状态栏的框架,因为 incall 是状态栏的一部分。

CGFloat height = [UIApplication sharedApplication].statusBarFrame.size.height;
    if (height == 20) 
        self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    
    else 
        CGRect frame = [[UIScreen mainScreen] bounds];
        frame.size.height = frame.size.height - height +20;
        frame.origin.y = height-20;
        self.window = [[UIWindow alloc] initWithFrame:frame];
    

【讨论】:

我希望有一个 API 或标志可以禁用该行为。如果操作系统行为发生变化,以这种方式使用 statusBarFrame 可能会导致大小错误。 好吧,使用故事板将解决这个问题。这是唯一不被黑客攻击的方法。【参考方案3】:

您可以在视图控制器中收听通知UIApplicationDidChangeStatusBarFrameNotification 以捕捉状态栏何时发生变化。然后调整视图控制器的主视图矩形,使其始终覆盖整个屏幕。

// Declare in your class
@property (strong, nonatomic) id<NSObject> observer;

- (void)viewDidLoad 
    [super viewDidLoad];

    _observer = [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidChangeStatusBarFrameNotification object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) 
        CGFloat newHeight = self.view.frame.size.height + self.view.frame.origin.y;
        self.view.frame = CGRectMake(0.0, 0.0, self.view.frame.size.width, newHeight);
    ];


-(void)dealloc 
    [[NSNotificationCenter defaultCenter] removeObserver:_observer];

据我所知,我在各种型号上都试过了,效果很好。在 iPhone X 上,通知不会发布,因为它不会改变通话状态栏的高度。

还有一个对应的UIApplicationWillChangeStatusBarFrameNotification 在状态栏改变之前被触发,以防你想以某种方式准备你的视图。

【讨论】:

我同时发的。我已经弄清楚了这一点,并设法进一步退后一步,完全避免修改框架矩形。 是的,你的解决方案很好!

以上是关于iOS in-Call 指示器正在下推视图/内容,修改根视图`frame`的主要内容,如果未能解决你的问题,请参考以下文章

ios7所有搜索视图和表格视图关闭20像素

MVVMCross:如何制作无限滚动视图并下推刷新视图?

iOS7 - 搜索结果 tableview 下推 20px

嵌入在 NavigationView 中的 TabView 将内容下推

下推所有视图并添加自定义视图

使用烧瓶闪光灯下推所有内容