diff --git a/JSQMessagesViewController/Controllers/JSQMessagesViewController.m b/JSQMessagesViewController/Controllers/JSQMessagesViewController.m index 4a04c1544..b9d0c3b0b 100644 --- a/JSQMessagesViewController/Controllers/JSQMessagesViewController.m +++ b/JSQMessagesViewController/Controllers/JSQMessagesViewController.m @@ -42,6 +42,7 @@ #import "UIColor+JSQMessages.h" #import "UIDevice+JSQMessages.h" #import "NSBundle+JSQMessages.h" +#include static void * kJSQMessagesKeyValueObservingContext = &kJSQMessagesKeyValueObservingContext; @@ -446,19 +447,23 @@ - (void)scrollToBottomAnimated:(BOOL)animated // possibly a UIKit bug, see #480 on GitHub NSUInteger finalRow = MAX(0, [self.collectionView numberOfItemsInSection:0] - 1); NSIndexPath *finalIndexPath = [NSIndexPath indexPathForItem:finalRow inSection:0]; + + /* + * Chat used to determine whether or not to scroll to the top or bottom based on cell size, but scrolling to "top" was causing messages to show up behind the keyboard + * so now we always scroll to bottom, leaving previous code here for now + * CGSize finalCellSize = [self.collectionView.collectionViewLayout sizeForItemAtIndexPath:finalIndexPath]; - CGFloat maxHeightForVisibleMessage = CGRectGetHeight(self.collectionView.bounds) - self.collectionView.contentInset.top - CGRectGetHeight(self.inputToolbar.bounds); - UICollectionViewScrollPosition scrollPosition = (finalCellSize.height > maxHeightForVisibleMessage) ? UICollectionViewScrollPositionBottom : UICollectionViewScrollPositionTop; + */ if (animated) { [self.collectionView scrollToItemAtIndexPath:finalIndexPath - atScrollPosition:scrollPosition + atScrollPosition:UICollectionViewScrollPositionBottom//scrollPosition animated:animated]; } else { - [self.collectionView setContentOffset:CGPointMake(0, 44+collectionViewContentHeight-(self.collectionView.bounds.size.height))]; + [self.collectionView setContentOffset:CGPointMake(0, self.bottomLayoutGuide.length+44+collectionViewContentHeight-(self.collectionView.bounds.size.height))]; } } @@ -540,12 +545,18 @@ - (UICollectionViewCell *)collectionView:(JSQMessagesCollectionView *)collection if ([messageItem conformsToProtocol:@protocol(JSQMessageAttributedData)]) { id attributedMessageItem = (id ) messageItem; - cell.textView.attributedText = [attributedMessageItem attributedText]; + if (![self isPointNaN:cell.textView.layer.position]) + { + cell.textView.attributedText = [attributedMessageItem attributedText]; + } } else { - cell.textView.text = nil; - cell.textView.attributedText = [[NSAttributedString alloc] initWithString:[messageItem text] attributes:@{ NSFontAttributeName : collectionView.collectionViewLayout.messageBubbleFont }]; + if (![self isPointNaN:cell.textView.layer.position]) + { + cell.textView.text = nil; + cell.textView.attributedText = [[NSAttributedString alloc] initWithString:([messageItem text] ?: @"") attributes:@{ NSFontAttributeName : collectionView.collectionViewLayout.messageBubbleFont }]; + } } NSParameterAssert(cell.textView.text != nil); @@ -915,14 +926,11 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N - (void)keyboardController:(JSQMessagesKeyboardController *)keyboardController keyboardDidChangeFrame:(CGRect)keyboardFrame { - if (![self.inputToolbar.contentView.textView isFirstResponder] && self.toolbarBottomLayoutGuide.constant == 0.0f) { - return; + CGFloat heightFromBottom = MAX(0.0f, CGRectGetMaxY(self.collectionView.frame) - CGRectGetMinY(keyboardFrame)); + if (![self.inputToolbar.contentView.textView isFirstResponder]) { + heightFromBottom = 0.0f; } - CGFloat heightFromBottom = CGRectGetMaxY(self.collectionView.frame) - CGRectGetMinY(keyboardFrame); - - heightFromBottom = MAX(0.0f, heightFromBottom); - [self jsq_setToolbarBottomLayoutGuideConstant:heightFromBottom]; } @@ -1164,4 +1172,9 @@ - (void)jsq_addActionToInteractivePopGestureRecognizer:(BOOL)addAction } } +-(BOOL)isPointNaN:(CGPoint)point +{ + return (isnan(point.x)) || (isnan(point.y)); +} + @end diff --git a/JSQMessagesViewController/Controllers/JSQMessagesViewController.xib b/JSQMessagesViewController/Controllers/JSQMessagesViewController.xib index bdf8b03a5..11fa7ca6a 100644 --- a/JSQMessagesViewController/Controllers/JSQMessagesViewController.xib +++ b/JSQMessagesViewController/Controllers/JSQMessagesViewController.xib @@ -1,8 +1,13 @@ - + + + + - - + + + + @@ -19,42 +24,41 @@ - + - + - - + + - - + + - - + + - - + - + @@ -65,22 +69,19 @@ + - + + - + - - - - - diff --git a/JSQMessagesViewController/Factories/JSQMessagesMediaViewBubbleImageMasker.m b/JSQMessagesViewController/Factories/JSQMessagesMediaViewBubbleImageMasker.m index 831804dd8..ed12c698a 100644 --- a/JSQMessagesViewController/Factories/JSQMessagesMediaViewBubbleImageMasker.m +++ b/JSQMessagesViewController/Factories/JSQMessagesMediaViewBubbleImageMasker.m @@ -81,10 +81,37 @@ - (void)jsq_maskView:(UIView *)view withImage:(UIImage *)image NSParameterAssert(view != nil); NSParameterAssert(image != nil); + /* UIImageView *imageViewMask = [[UIImageView alloc] initWithImage:image]; imageViewMask.frame = CGRectInset(view.frame, 2.0f, 2.0f); view.layer.mask = imageViewMask.layer; + */ + + //the original method of doing this is broken in Xcode 12/iOS 14; masking with UIImageView layers doesn't work anymore + //we can get around this by creating the mask layer by hand, but it's a little tricky: + + //first we need to get a new UIImage with the .orientation property reset to .up + //if we use the image as-is we lose the orientation of the UIImage when we grab its GCImage below + UIGraphicsBeginImageContextWithOptions(image.size, NO, image.scale); + [image drawAtPoint:CGPointMake(0, 0)]; + UIImage *img = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + //create the layer that will be used as the mask + CALayer *layer = [[CALayer alloc] init]; + layer.contents = (__bridge id _Nullable)(img.CGImage); + layer.contentsScale = img.scale; + + //the following line essentially nine-slices the message bubble shape so that it can be resized + layer.contentsCenter = CGRectMake((img.size.width / 2.0f - 1.0f) / img.size.width, + (img.size.height / 2.0f - 1.0f) / img.size.height, + 1.0f / img.size.width, + 1.0f / img.size.height); + + layer.frame = CGRectInset(view.frame, 2.0f, 2.0f); + + view.layer.mask = layer; } @end diff --git a/JSQMessagesViewController/Layout/JSQMessagesCollectionViewFlowLayout.m b/JSQMessagesViewController/Layout/JSQMessagesCollectionViewFlowLayout.m index 595dae6e0..629c980dc 100644 --- a/JSQMessagesViewController/Layout/JSQMessagesCollectionViewFlowLayout.m +++ b/JSQMessagesViewController/Layout/JSQMessagesCollectionViewFlowLayout.m @@ -313,6 +313,11 @@ - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect { NSArray *attributesInRect = [super layoutAttributesForElementsInRect:rect]; + // This function is available in iOS 10. Disable it for dynamic position of `SupplementaryView` + if ([self.collectionView respondsToSelector:@selector(setPrefetchingEnabled:)]) { + self.collectionView.prefetchingEnabled = false; + } + if (self.springinessEnabled) { NSMutableArray *attributesInRectCopy = [attributesInRect mutableCopy]; NSArray *dynamicAttributes = [self.dynamicAnimator itemsInRect:rect]; @@ -335,14 +340,22 @@ - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect attributesInRect = attributesInRectCopy; } - [attributesInRect enumerateObjectsUsingBlock:^(JSQMessagesCollectionViewLayoutAttributes *attributesItem, NSUInteger idx, BOOL *stop) { - if (attributesItem.representedElementCategory == UICollectionElementCategoryCell) { - [self jsq_configureMessageCellLayoutAttributes:attributesItem]; - } - else { - attributesItem.zIndex = -1; - } - }]; + @try + { + [attributesInRect enumerateObjectsUsingBlock:^(JSQMessagesCollectionViewLayoutAttributes *attributesItem, NSUInteger idx, BOOL *stop) { + if (attributesItem.representedElementCategory == UICollectionElementCategoryCell) { + [self jsq_configureMessageCellLayoutAttributes:attributesItem]; + } + else { + attributesItem.zIndex = -1; + } + }]; + } + @catch (NSException *exception) + { + NSLog(@"JSQMessagesCollectionViewFlowLayout:: failed to layout elements in rect %@: %@", + NSStringFromCGRect(rect), exception.description); + } return attributesInRect; } diff --git a/JSQMessagesViewController/Views/JSQMessagesCollectionViewCellIncoming.xib b/JSQMessagesViewController/Views/JSQMessagesCollectionViewCellIncoming.xib index f69b5404d..1b14cd607 100644 --- a/JSQMessagesViewController/Views/JSQMessagesCollectionViewCellIncoming.xib +++ b/JSQMessagesViewController/Views/JSQMessagesCollectionViewCellIncoming.xib @@ -1,8 +1,10 @@ - + + - - + + + @@ -16,7 +18,7 @@ - + @@ -67,7 +69,7 @@ - + @@ -79,7 +81,7 @@ - - - - - diff --git a/JSQMessagesViewController/Views/JSQMessagesCollectionViewCellOutgoing.xib b/JSQMessagesViewController/Views/JSQMessagesCollectionViewCellOutgoing.xib index 2bfdb2044..2952f0b0b 100644 --- a/JSQMessagesViewController/Views/JSQMessagesCollectionViewCellOutgoing.xib +++ b/JSQMessagesViewController/Views/JSQMessagesCollectionViewCellOutgoing.xib @@ -1,8 +1,10 @@ - + + - - + + + @@ -16,7 +18,7 @@ - + @@ -67,7 +69,7 @@ - + @@ -79,7 +81,7 @@ - - - - - diff --git a/JSQMessagesViewController/Views/JSQMessagesComposerTextView.m b/JSQMessagesViewController/Views/JSQMessagesComposerTextView.m index e46c95992..d22995252 100644 --- a/JSQMessagesViewController/Views/JSQMessagesComposerTextView.m +++ b/JSQMessagesViewController/Views/JSQMessagesComposerTextView.m @@ -73,7 +73,6 @@ - (void)jsq_configureTextView self.keyboardAppearance = UIKeyboardAppearanceDefault; self.keyboardType = UIKeyboardTypeDefault; self.returnKeyType = UIReturnKeyDefault; - self.allowsEditingTextAttributes = YES; self.text = nil; self.attributedText = nil; @@ -164,31 +163,7 @@ - (void)setTextAlignment:(NSTextAlignment)textAlignment - (void)paste:(id)sender { if (!self.pasteDelegate || [self.pasteDelegate composerTextView:self shouldPasteWithSender:sender]) { - if (!shouldPasteRTF) { - [self setText:[UIPasteboard generalPasteboard].string]; - [self.pasteDelegate composerTextView:self didPasteWithSender:sender]; - return; - } - UIPasteboard *pasteboard = [UIPasteboard generalPasteboard]; - NSData* data = nil; - NSMutableArray *types = [[NSMutableArray alloc]init]; - for (NSDictionary* item in pasteboard.items) { - for (NSString* type in item.allKeys) - [types addObject:type]; - } - if ([types containsObject:@"com.apple.flat-rtfd"]) - { - for (NSDictionary* item in pasteboard.items) { - if (item[@"com.apple.flat-rtfd"]) { - data = item[@"com.apple.flat-rtfd"]; - NSDictionary *options = @{NSDocumentTypeDocumentAttribute: NSRTFDTextDocumentType}; - [self setAttributedText: [[NSMutableAttributedString alloc]initWithData:data options:options documentAttributes:nil error:nil]]; - break; - } - } - } - else [super paste:sender]; - [self.pasteDelegate composerTextView:self didPasteWithSender:sender]; + [super paste:sender]; } } diff --git a/JSQMessagesViewController/Views/JSQMessagesTopBannerView.h b/JSQMessagesViewController/Views/JSQMessagesTopBannerView.h index a197c382b..c3dbd4f52 100644 --- a/JSQMessagesViewController/Views/JSQMessagesTopBannerView.h +++ b/JSQMessagesViewController/Views/JSQMessagesTopBannerView.h @@ -2,8 +2,8 @@ // JSQMessagesTopBannerView.h // Dollarama // -// Created by besat@morgiij.com on 2017-03-10. -// Copyright © 2017 binnj. All rights reserved. +// Created by binnj, inc. on 2017-03-10. +// Copyright © 2017 binnj, inc. All rights reserved. // #import diff --git a/JSQMessagesViewController/Views/JSQMessagesTopBannerView.m b/JSQMessagesViewController/Views/JSQMessagesTopBannerView.m index d5969ec7d..37849dd4e 100644 --- a/JSQMessagesViewController/Views/JSQMessagesTopBannerView.m +++ b/JSQMessagesViewController/Views/JSQMessagesTopBannerView.m @@ -2,8 +2,8 @@ // JSQMessagesTopBannerView.m // Dollarama // -// Created by besat@morgiij.com on 2017-03-10. -// Copyright © 2017 binnj. All rights reserved. +// Created by binnj, inc. on 2017-03-10. +// Copyright © 2017 binnj, inc. All rights reserved. // #import "JSQMessagesTopBannerView.h"