mirror of https://github.com/oxen-io/session-ios
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
236 lines
7.8 KiB
Objective-C
236 lines
7.8 KiB
Objective-C
//
|
|
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
|
//
|
|
|
|
#import "OWSViewController.h"
|
|
#import "UIView+OWS.h"
|
|
#import "AppContext.h"
|
|
#import <PureLayout/PureLayout.h>
|
|
#import <SessionUIKit/SessionUIKit.h>
|
|
#import <SignalCoreKit/OWSAsserts.h>
|
|
|
|
NS_ASSUME_NONNULL_BEGIN
|
|
|
|
BOOL IsLandscapeOrientationEnabled(void)
|
|
{
|
|
return UIDevice.currentDevice.isIPad;
|
|
}
|
|
|
|
UIInterfaceOrientationMask DefaultUIInterfaceOrientationMask(void)
|
|
{
|
|
return (IsLandscapeOrientationEnabled() ? UIInterfaceOrientationMaskAll
|
|
: UIInterfaceOrientationMaskPortrait);
|
|
}
|
|
|
|
@interface OWSViewController ()
|
|
|
|
@property (nonatomic, weak) UIView *bottomLayoutView;
|
|
@property (nonatomic) NSLayoutConstraint *bottomLayoutConstraint;
|
|
@property (nonatomic) BOOL shouldAnimateBottomLayout;
|
|
|
|
@end
|
|
|
|
#pragma mark -
|
|
|
|
@implementation OWSViewController
|
|
|
|
- (void)dealloc
|
|
{
|
|
// Surface memory leaks by logging the deallocation of view controllers.
|
|
OWSLogVerbose(@"Dealloc: %@", self.class);
|
|
|
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
|
}
|
|
|
|
- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil bundle:(nullable NSBundle *)nibBundleOrNil
|
|
{
|
|
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
|
|
if (!self) {
|
|
self.shouldUseTheme = YES;
|
|
return self;
|
|
}
|
|
|
|
[self observeActivation];
|
|
|
|
return self;
|
|
}
|
|
|
|
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder
|
|
{
|
|
self = [super initWithCoder:aDecoder];
|
|
if (!self) {
|
|
self.shouldUseTheme = YES;
|
|
return self;
|
|
}
|
|
|
|
[self observeActivation];
|
|
|
|
return self;
|
|
}
|
|
|
|
#pragma mark - View Lifecycle
|
|
|
|
- (void)viewDidAppear:(BOOL)animated
|
|
{
|
|
[super viewDidAppear:animated];
|
|
|
|
self.shouldAnimateBottomLayout = YES;
|
|
}
|
|
|
|
- (void)viewDidDisappear:(BOOL)animated
|
|
{
|
|
[super viewDidDisappear:animated];
|
|
|
|
self.shouldAnimateBottomLayout = NO;
|
|
}
|
|
|
|
#pragma mark -
|
|
|
|
- (void)autoPinViewToBottomOfViewControllerOrKeyboard:(UIView *)view avoidNotch:(BOOL)avoidNotch
|
|
{
|
|
OWSAssertDebug(view);
|
|
OWSAssertDebug(!self.bottomLayoutConstraint);
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(keyboardWillShow:)
|
|
name:UIKeyboardWillShowNotification
|
|
object:nil];
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(keyboardDidShow:)
|
|
name:UIKeyboardDidShowNotification
|
|
object:nil];
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(keyboardWillHide:)
|
|
name:UIKeyboardWillHideNotification
|
|
object:nil];
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(keyboardDidHide:)
|
|
name:UIKeyboardDidHideNotification
|
|
object:nil];
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(keyboardWillChangeFrame:)
|
|
name:UIKeyboardWillChangeFrameNotification
|
|
object:nil];
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(keyboardDidChangeFrame:)
|
|
name:UIKeyboardDidChangeFrameNotification
|
|
object:nil];
|
|
|
|
self.bottomLayoutView = view;
|
|
if (avoidNotch) {
|
|
NSLayoutConstraint *bottomLayoutConstraint = [view.bottomAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.bottomAnchor];
|
|
[bottomLayoutConstraint setActive:YES];
|
|
self.bottomLayoutConstraint = bottomLayoutConstraint;
|
|
} else {
|
|
self.bottomLayoutConstraint = [view autoPinEdge:ALEdgeBottom toEdge:ALEdgeBottom ofView:self.view];
|
|
}
|
|
}
|
|
|
|
- (void)observeActivation
|
|
{
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(owsViewControllerApplicationDidBecomeActive:)
|
|
name:UIApplicationDidBecomeActiveNotification
|
|
object:nil];
|
|
}
|
|
|
|
- (void)owsViewControllerApplicationDidBecomeActive:(NSNotification *)notification
|
|
{
|
|
[self setNeedsStatusBarAppearanceUpdate];
|
|
}
|
|
|
|
- (void)keyboardWillShow:(NSNotification *)notification
|
|
{
|
|
[self handleKeyboardNotificationBase:notification];
|
|
}
|
|
|
|
- (void)keyboardDidShow:(NSNotification *)notification
|
|
{
|
|
[self handleKeyboardNotificationBase:notification];
|
|
}
|
|
|
|
- (void)keyboardWillHide:(NSNotification *)notification
|
|
{
|
|
[self handleKeyboardNotificationBase:notification];
|
|
}
|
|
|
|
- (void)keyboardDidHide:(NSNotification *)notification
|
|
{
|
|
[self handleKeyboardNotificationBase:notification];
|
|
}
|
|
|
|
- (void)keyboardWillChangeFrame:(NSNotification *)notification
|
|
{
|
|
[self handleKeyboardNotificationBase:notification];
|
|
}
|
|
|
|
- (void)keyboardDidChangeFrame:(NSNotification *)notification
|
|
{
|
|
[self handleKeyboardNotificationBase:notification];
|
|
}
|
|
|
|
// We use the name `handleKeyboardNotificationBase` instead of
|
|
// `handleKeyboardNotification` to avoid accidentally
|
|
// calling similarly methods with that name in subclasses,
|
|
// e.g. ConversationViewController.
|
|
- (void)handleKeyboardNotificationBase:(NSNotification *)notification
|
|
{
|
|
OWSAssertIsOnMainThread();
|
|
|
|
if (self.shouldIgnoreKeyboardChanges) {
|
|
return;
|
|
}
|
|
|
|
NSDictionary *userInfo = [notification userInfo];
|
|
|
|
NSValue *_Nullable keyboardEndFrameValue = userInfo[UIKeyboardFrameEndUserInfoKey];
|
|
if (!keyboardEndFrameValue) {
|
|
OWSFailDebug(@"Missing keyboard end frame");
|
|
return;
|
|
}
|
|
|
|
CGRect keyboardEndFrame = [keyboardEndFrameValue CGRectValue];
|
|
CGRect keyboardEndFrameConverted = [self.view convertRect:keyboardEndFrame fromView:nil];
|
|
// Adjust the position of the bottom view to account for the keyboard's
|
|
// intrusion into the view.
|
|
//
|
|
// On iPhoneX, when no keyboard is present, we include a buffer at the bottom of the screen so the bottom view
|
|
// clears the floating "home button". But because the keyboard includes it's own buffer, we subtract the length
|
|
// (height) of the bottomLayoutGuide, else we'd have an unnecessary buffer between the popped keyboard and the input
|
|
// bar.
|
|
CGFloat offset = -MAX(0, (self.view.height - self.bottomLayoutGuide.length - keyboardEndFrameConverted.origin.y));
|
|
|
|
dispatch_block_t updateLayout = ^{
|
|
if (self.shouldBottomViewReserveSpaceForKeyboard && offset >= 0) {
|
|
// To avoid unnecessary animations / layout jitter,
|
|
// some views never reclaim layout space when the keyboard is dismissed.
|
|
//
|
|
// They _do_ need to relayout if the user switches keyboards.
|
|
return;
|
|
}
|
|
self.bottomLayoutConstraint.constant = offset;
|
|
[self.bottomLayoutView.superview layoutIfNeeded];
|
|
};
|
|
|
|
|
|
if (self.shouldAnimateBottomLayout && CurrentAppContext().isAppForegroundAndActive) {
|
|
updateLayout();
|
|
} else {
|
|
// UIKit by default animates all changes in response to keyboard events.
|
|
// We want to suppress those animations if the view isn't visible,
|
|
// otherwise presentation animations don't work properly.
|
|
[UIView performWithoutAnimation:updateLayout];
|
|
}
|
|
}
|
|
|
|
#pragma mark - Orientation
|
|
|
|
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
|
|
{
|
|
return DefaultUIInterfaceOrientationMask();
|
|
}
|
|
|
|
@end
|
|
|
|
NS_ASSUME_NONNULL_END
|