Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 26 additions & 13 deletions JSQMessagesViewController/Controllers/JSQMessagesViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
#import "UIColor+JSQMessages.h"
#import "UIDevice+JSQMessages.h"
#import "NSBundle+JSQMessages.h"
#include <math.h>


static void * kJSQMessagesKeyValueObservingContext = &kJSQMessagesKeyValueObservingContext;
Expand Down Expand Up @@ -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))];
}
}

Expand Down Expand Up @@ -540,12 +545,18 @@ - (UICollectionViewCell *)collectionView:(JSQMessagesCollectionView *)collection
if ([messageItem conformsToProtocol:@protocol(JSQMessageAttributedData)])
{
id <JSQMessageAttributedData> attributedMessageItem = (id <JSQMessageAttributedData>) 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);

Expand Down Expand Up @@ -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];
}

Expand Down Expand Up @@ -1164,4 +1172,9 @@ - (void)jsq_addActionToInteractivePopGestureRecognizer:(BOOL)addAction
}
}

-(BOOL)isPointNaN:(CGPoint)point
{
return (isnan(point.x)) || (isnan(point.y));
}

@end
43 changes: 22 additions & 21 deletions JSQMessagesViewController/Controllers/JSQMessagesViewController.xib
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="10117" systemVersion="16F73" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<development version="7000" identifier="xcode"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="JSQMessagesViewController">
Expand All @@ -19,42 +24,41 @@
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="mUa-cS-ru4">
<rect key="frame" x="0.0" y="0.0" width="320" height="480"/>
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Iiw-H7-6Z3">
<rect key="frame" x="0.0" y="0.0" width="320" height="480"/>
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
</imageView>
<view alpha="0.75" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="vkV-Xr-oCa" userLabel="Alpha View">
<rect key="frame" x="0.0" y="0.0" width="320" height="480"/>
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
<collectionView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" minimumZoomScale="0.0" maximumZoomScale="0.0" dataMode="none" translatesAutoresizingMaskIntoConstraints="NO" id="l9u-2b-4LK" customClass="JSQMessagesCollectionView">
<rect key="frame" x="0.0" y="0.0" width="320" height="480"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<collectionViewLayout key="collectionViewLayout" id="dZl-7C-LHR" customClass="JSQMessagesCollectionViewFlowLayout"/>
<cells/>
</collectionView>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Mha-tV-tTy" customClass="JSQMessagesTopBannerView">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<rect key="frame" x="0.0" y="44" width="414" height="44"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" constant="44" id="aNj-bg-a4G"/>
</constraints>
<connections>
<outlet property="topBannerViewHeightConstraint" destination="aNj-bg-a4G" id="QRJ-Ow-WOF"/>
<outlet property="topBannerViewTopConstraint" destination="vZM-5c-n3V" id="u0R-G7-ufr"/>
</connections>
</view>
<toolbar opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="BoD-Az-3DM" customClass="JSQMessagesInputToolbar">
<rect key="frame" x="0.0" y="436" width="320" height="44"/>
<rect key="frame" x="0.0" y="818" width="414" height="44"/>
<constraints>
<constraint firstAttribute="height" constant="44" id="HIk-02-qcW"/>
</constraints>
<items/>
</toolbar>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="Iiw-H7-6Z3" secondAttribute="trailing" id="15M-QN-Kvq"/>
<constraint firstItem="vkV-Xr-oCa" firstAttribute="top" secondItem="Iiw-H7-6Z3" secondAttribute="top" id="1TY-0t-DKw"/>
Expand All @@ -65,22 +69,19 @@
<constraint firstItem="Iiw-H7-6Z3" firstAttribute="top" secondItem="mUa-cS-ru4" secondAttribute="top" id="KZb-7G-4V3"/>
<constraint firstItem="l9u-2b-4LK" firstAttribute="leading" secondItem="mUa-cS-ru4" secondAttribute="leading" id="MmF-oh-Y75"/>
<constraint firstAttribute="trailing" secondItem="l9u-2b-4LK" secondAttribute="trailing" id="O9u-TA-A0e"/>
<constraint firstItem="Mha-tV-tTy" firstAttribute="top" secondItem="rR6-8r-nyl" secondAttribute="top" id="Rc8-zd-ffj"/>
<constraint firstAttribute="bottom" secondItem="l9u-2b-4LK" secondAttribute="bottom" id="Re7-WW-UmS"/>
<constraint firstAttribute="trailing" secondItem="Mha-tV-tTy" secondAttribute="trailing" id="Wkn-kr-Inm"/>
<constraint firstItem="vkV-Xr-oCa" firstAttribute="bottom" secondItem="Iiw-H7-6Z3" secondAttribute="bottom" id="Zt5-QX-KBO"/>
<constraint firstItem="Iiw-H7-6Z3" firstAttribute="leading" secondItem="mUa-cS-ru4" secondAttribute="leading" id="adR-JE-yp2"/>
<constraint firstAttribute="bottom" secondItem="BoD-Az-3DM" secondAttribute="bottom" id="rHs-6q-NX4"/>
<constraint firstItem="BoD-Az-3DM" firstAttribute="bottom" secondItem="rR6-8r-nyl" secondAttribute="bottom" priority="250" id="f9C-gi-Er8"/>
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="BoD-Az-3DM" secondAttribute="bottom" id="rHs-6q-NX4"/>
<constraint firstItem="BoD-Az-3DM" firstAttribute="leading" secondItem="mUa-cS-ru4" secondAttribute="leading" id="ts7-8f-0lH"/>
<constraint firstItem="Mha-tV-tTy" firstAttribute="leading" secondItem="mUa-cS-ru4" secondAttribute="leading" id="uPZ-Rm-oj0"/>
<constraint firstItem="vkV-Xr-oCa" firstAttribute="trailing" secondItem="Iiw-H7-6Z3" secondAttribute="trailing" id="uQi-Gp-tOQ"/>
<constraint firstItem="Mha-tV-tTy" firstAttribute="top" secondItem="mUa-cS-ru4" secondAttribute="top" id="vZM-5c-n3V"/>
</constraints>
<nil key="simulatedStatusBarMetrics"/>
<viewLayoutGuide key="safeArea" id="rR6-8r-nyl"/>
</view>
</objects>
<simulatedMetricsContainer key="defaultSimulatedMetrics">
<simulatedStatusBarMetrics key="statusBar"/>
<simulatedOrientationMetrics key="orientation"/>
<simulatedScreenMetrics key="destination"/>
</simulatedMetricsContainer>
</document>
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand All @@ -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;
}
Expand Down
Loading