diff --git a/Signal/src/ViewControllers/ConversationView/ConversationInputToolbar.h b/Signal/src/ViewControllers/ConversationView/ConversationInputToolbar.h index dc1d0754f..adebf8e83 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationInputToolbar.h +++ b/Signal/src/ViewControllers/ConversationView/ConversationInputToolbar.h @@ -54,6 +54,8 @@ NS_ASSUME_NONNULL_BEGIN - (void)updateFontSizes; +- (void)updateLayoutWithSafeAreaInsets:(UIEdgeInsets)safeAreaInsets; + #pragma mark - Voice Memo - (void)ensureTextViewHeight; diff --git a/Signal/src/ViewControllers/ConversationView/ConversationInputToolbar.m b/Signal/src/ViewControllers/ConversationView/ConversationInputToolbar.m index 75015ce8e..64a9d9b31 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationInputToolbar.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationInputToolbar.m @@ -69,6 +69,8 @@ const CGFloat kMaxTextViewHeight = 98; @property (nonatomic, nullable) UILabel *recordingLabel; @property (nonatomic) BOOL isRecordingVoiceMemo; @property (nonatomic) CGPoint voiceMemoGestureStartLocation; +@property (nonatomic, nullable) NSArray *layoutContraints; +@property (nonatomic) UIEdgeInsets receivedSafeAreaInsets; @property (nonatomic, nullable) InputLinkPreview *inputLinkPreview; @property (nonatomic) BOOL wasLinkPreviewCancelled; @property (nonatomic, nullable, weak) LinkPreviewView *linkPreviewView; @@ -84,6 +86,7 @@ const CGFloat kMaxTextViewHeight = 98; self = [super initWithFrame:CGRectZero]; _conversationStyle = conversationStyle; + _receivedSafeAreaInsets = UIEdgeInsetsZero; if (self) { [self createContents]; @@ -199,8 +202,6 @@ const CGFloat kMaxTextViewHeight = 98; [self addSubview:self.hStack]; [self.hStack autoPinEdgeToSuperviewEdge:ALEdgeTop]; [self.hStack autoPinEdgeToSuperviewSafeArea:ALEdgeBottom]; - [self.hStack autoPinEdgeToSuperviewSafeArea:ALEdgeLeading]; - [self.hStack autoPinEdgeToSuperviewSafeArea:ALEdgeTrailing]; [self.hStack setContentHuggingHorizontalLow]; [self.hStack setCompressionResistanceHorizontalLow]; @@ -370,6 +371,35 @@ const CGFloat kMaxTextViewHeight = 98; } } +// iOS doesn't always update the safeAreaInsets correctly & in a timely +// way for the inputAccessoryView after a orientation change. The best +// workaround appears to be to use the safeAreaInsets from +// ConversationViewController's view. ConversationViewController updates +// this input toolbar using updateLayoutWithIsLandscape:. +- (void)updateContentLayout +{ + if (self.layoutContraints) { + [NSLayoutConstraint deactivateConstraints:self.layoutContraints]; + } + + self.layoutContraints = @[ + [self.hStack autoPinEdgeToSuperviewEdge:ALEdgeLeft withInset:self.receivedSafeAreaInsets.left], + [self.hStack autoPinEdgeToSuperviewEdge:ALEdgeRight withInset:self.receivedSafeAreaInsets.right], + ]; +} + +- (void)updateLayoutWithSafeAreaInsets:(UIEdgeInsets)safeAreaInsets +{ + BOOL didChange = !UIEdgeInsetsEqualToEdgeInsets(self.receivedSafeAreaInsets, safeAreaInsets); + BOOL hasLayout = self.layoutContraints != nil; + + self.receivedSafeAreaInsets = safeAreaInsets; + + if (didChange || !hasLayout) { + [self updateContentLayout]; + } +} + - (void)handleLongPress:(UIGestureRecognizer *)sender { switch (sender.state) { diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index bcd06a9a2..2c3d14173 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -747,6 +747,7 @@ typedef enum : NSUInteger { NSTimeInterval appearenceDuration = CACurrentMediaTime() - self.viewControllerCreatedAt; OWSLogVerbose(@"First viewWillAppear took: %.2fms", appearenceDuration * 1000); } + [self updateInputToolbarLayout]; } - (NSArray> *)viewItems @@ -1231,6 +1232,8 @@ typedef enum : NSUInteger { // Clear the "on open" state after the view has been presented. self.actionOnOpen = ConversationViewActionNone; + + [self updateInputToolbarLayout]; } // `viewWillDisappear` is called whenever the view *starts* to disappear, @@ -4841,6 +4844,8 @@ typedef enum : NSUInteger { // new size. [strongSelf resetForSizeOrOrientationChange]; + [strongSelf updateInputToolbarLayout]; + if (lastVisibleIndexPath) { [strongSelf.collectionView scrollToItemAtIndexPath:lastVisibleIndexPath atScrollPosition:UICollectionViewScrollPositionBottom @@ -4873,6 +4878,23 @@ typedef enum : NSUInteger { // Try to update the lastKnownDistanceFromBottom; the content size may have changed. [self updateLastKnownDistanceFromBottom]; } + [self updateInputToolbarLayout]; +} + +- (void)viewSafeAreaInsetsDidChange +{ + [super viewSafeAreaInsetsDidChange]; + + [self updateInputToolbarLayout]; +} + +- (void)updateInputToolbarLayout +{ + UIEdgeInsets safeAreaInsets = UIEdgeInsetsZero; + if (@available(iOS 11, *)) { + safeAreaInsets = self.view.safeAreaInsets; + } + [self.inputToolbar updateLayoutWithSafeAreaInsets:safeAreaInsets]; } @end