jessesquires / JSQMessagesViewController

An elegant messages UI library for iOS

Home Page:https://www.jessesquires.com/blog/officially-deprecating-jsqmessagesviewcontroller/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Scrolling perfomance on iOS8

etolstoy opened this issue · comments

I've encountered a strange behaviour on iOS8. When I scroll the collectionView on the device with iOS8 GM, it performs with some lags, while on another device with iOS 7.1 everything is fine.
Is it a well-known issue?

thanks @igrekde - no one has reported this yet. there are other GM bugs too. hopefully this will resolve itself.

@jessesquires I've tested one of the apps using your library - Parse chat and noticed the same issue.

I too experience this performance issue

Yep, I saw that too.

BTW, this problem can be seen even in the demo application.

Hey guys -- thanks for the updates!

We addressed scrolling performance issues awhile back in v5.0.3, but it looks like the issue is back. I'm not 100% sure how to resolve this, so any help is much appreciated!

Also, the release-6.0 branch now has a stable demo. I've made some changes here that should help with scrolling. It feels like it has improved to me, but it still doesn't seem as smooth as iOS 7.0.
Let me know what you think!

@jessesquires I've tested the demo app in release-6 branch, but I still experience the same perfomance issues - maybe a slightly better than in the master.

@jessesquires FYI, I just spent a couple hours trying to find where the problem is. All I can say right now is that if you comment out this line:

https://github.com/jessesquires/JSQMessagesViewController/blob/release-6.0/JSQMessagesViewController/Controllers/JSQMessagesViewController.m#L433

everything begins to run more smoothly on my iPhone5. Obviously, this is not an option :-) That said, it seems to rule out problems with rendering the bubbles or the avatars images, at least.

In my test, I simply added 50 outgoing messages in a loop to populate the conversation. The profile shows a lot of layouting activity for the UICollectionView, so my guess is simply that finding a solution for the constraint system of the cell takes too much time, which happens only when the constraints have changed (so, in my test case, when the text has changed).

Hope this helps.

I thought that this SO answer might help too, if you did not see it already: http://stackoverflow.com/questions/18746929/using-auto-layout-in-uitableview-for-dynamic-cell-layouts-variable-row-heights#comment35309097_18746930

For the record, I just spent a few days trying to get a smooth UITableview running with cells layed out using Autolayout, and I never managed to get something satisfactory. I'm guessing that autolayout is still a bit too CPU intensive right now. Doing it "manually" in LayoutSubviews, in the other hands, did give excellent results. Just a data point.

Thanks so much for the investigation @madewulf ! Very interesting.

What makes this worse is that there are lots of optimizations already in place:

  1. we cache computed bubble sizes for use later
  2. we have a custom invalidation context
  3. we prevent constraint updates unless they have changed

These yielded great scrolling results under iOS 7. So something has changed with iOS 8.

Good to know that is it not the bubble or avatar images. That would have been my first guess.

I wonder if the issue is with data detectors? We should try turning them off.

I profiled the app, and it seems the issue here is the use of AutoLayout. Once everything related to auto layout is turned off, performance is suddenly very smooth.

You should consider dropping auto layout completely. There is nothing that seems difficult to layout manually, especially in cells, where you already have all the heights (or can calculate them using framework methods.

Thanks @LeoNatan !

To reiterate, this was not an issue on iOS 7. This was introduced in iOS 8. 😢

Dropping auto-layout is not an option — with size classes and iPhone 6, it should be clear that manual layouts will not suffice. There's a reason Apple has introduced these frameworks — this is the best way to implement adaptive UI. The previous version (v4.0.x) of this library did not use auto-layout — a decision that was the source of countless bugs, limitations, and one reason why v5.0 was a complete re-write. Auto-layout is the reason why this library still "just works" on both iPhone 6 and iPhone 6 Plus in portrait and landscape. (All all other devices 😄 )

However! I think (hope) we can fix this. Don't worry, this is high-priority.

Also — everyone's comments here are a huge help! Thanks so much! Keep them coming. 👍

I don't really see why in this case. The project is iOS7 and above, so TextKit can give you a very precise estimation of text size. For other things, the API asks the user for precise sizes. You know the container size, you know the layout size and you hear when these sizes change. All this amounts to actually quite a simple (but perhaps a little tedious) layoutSubviews. When the cell is sized correctly by the layout, performing internal layout is just as simple as pinning manually the avatar image and top and bottom to edges, and resizing the text area to fit the rest. For the bottom bar, the Auto Layout can remain, I doubt it has any real performance penalty.

I can help with this if you are interested.

@LeoNatan -- any/all help is much appreciated!

However, we should continue using auto-layout for the reasons above, but also because the 6.0 release will be finished soon. There are already significant changes in the library for 6.0 — overhauling all of the layout code is too risky this close to release.

For anyone wanting to submit a PR for this issue, please submit to branch release-6.0.

Release 6 is shaping up to be a very good release. Very cool additions.

The library is not really usable at this point. Devices such as 4S, iPad 2 and mini (non retina) really are not able to push the CPU power needed to layout the scene. I will comment only on iOS8 because I did not test on below, nor is it a consolation. With media attachments, the problem becomes even more so relevant.

In my project, I am still in a technology research phase and POC, so I don't mind the performance hit right now. But I cannot start work with this project with the current performance limitations.

If nothing comes up regarding Auto Layout performance, I will either write a lib of my own or help rewrite this one if timetable permits. Please let me know when possible.

Hello all:

After some investigation with Instruments, I've found some issues regarding scrolling performance and fixed them. Still not 100% but I think it is much better. Please pull the latest from develop and let me know how it goes.

Profiling data from Instruments Timer Profiler:

  1. Lots of time spent in collectionView: cellForItemAtIndexPath:
    • 44.8% dequeueReusableCellWithReuseIdentifier: forIndexPath:
    • 16.5% cell.textView.text = messageText;
    • 30.6% cell.cellTopLabel.attributedText = ...
  2. Lots of time spent in prepareForReuse / applyLayoutAttributes:
    • 51.4% self.textViewFrameInsets = customAttributes.textViewFrameInsets;
  3. Lots of time spent in messageBubbleSizeForItemAtIndexPath:
    • 90.1% boundingRectWithSize:

Clearly, there are some additional areas to optimize. Right now, I'm thinking applyLayoutAttributes: could be better.

If anyone else has additional information, please report back here. 👍

Hi, @jessesquires. I've tested the latest version of the library - it seems that the scrolling perfomance has improved a little - but it still works badly, especially with a fast scrolling. Anyway, thanks a lot for your effort. I hope, that you'll completely fix the issue.

For me on 5S it didn't improve sadly.

@LeoNatan - really? I'm testing mostly on an iPhone 5 and performance is not that bad. It's noticeably not as smooth as before, but it isn't terrible.

Hello all: a bit more progress here. Please checkout the latest on develop and let me know how this is working now.

@jessesquires I've tested the latest version - the perfomance is great!

Now I'm having expectations ^^

I can't say I'm happy with the performance. Scrolling has lags and janks even on a 5S which is troublesome considering I'd like to support devices back to 4S (as iOS 8 does).

Do you plan to improve scrolling after 6.0 is released (I'm guess you won't delay the launch just for this)?

Or is the "ok" performance on a 5S acceptable, cause it's not a priority?

@dereck009 - I'll continue making as much progress as I can on this issue (any help is appreciated! 😄) until 6.0 is ready (which it almost is). I will likely release 6.0 without this issue completely resolved, then keep working to fix it.

@dereck009 -- just to confirm, you tested with the latest on develop?

I merged the latest develop into my own and scrolling still stutters sadly.

@LeoNatan - but it's better than before? 🙏

Sorry, not really. I saw your edits. They don't really improve things, because constraint update and layout passes are performed out of current run loop, so calling setNeedsLayout or setNeedsUpdateConstraints many times does not really hinder performance.

Let's do it together, let's remove auto layout from cells! 😆

Yes it was develop branch. I appreciate your work, it's really a great project.

Unfortunately I only have access to iPhone 4S with iOS 7 (not my phone), so I can't really test the problems iOS 8 caused there, I can only deduct from the 5S performance.

The 5.x.x wasn't smooth on the 4S either so I had some custom optimizations (like using TTTAttributedLabel instead of UITextView inside the bubbles) which made it 60 FPS.
I don't have the time to deep dive into the new develop branch right now.

Almost as an aside (I haven't tested the speed of the new branch at all), I was considering TTTAttributedLabel instead of UITextView inside the bubbles as well, since I believe it simplifies layout. If that also improves speed, maybe that's a good direction?

Yes, I did some extensive testing & profiling and UITextView turned out to be a bottleneck (in v5.x.x). TTTAttributedLabel performed considerably faster.

I didn't run a full circle with compatibility testing, that's why I didn't create a PR, but it worked perfectly in my project.

TTTAttributedLabel is a good idea. That might be worth investigating.

Checked scrolling on iPhone 4S and 5S — it looks pretty bad. I haven't really tested previous builds but this is coming straight after JSQMVC5.x. Further, the performance seems okay on iPhone 6 but it isn't as smooth as in other apps for example the Facebook Messages app on iOS 8 on iPhone 6.

I don't think "pretty bad" is fair. I am testing the demo on my 5 and it is definitely acceptable. I run messenger and the demo as sidexside as one can, and the average person is not even going to notice.

Can it be better? Yes. Is it bad? No.

Damnit, I thought I edited that when I appended to my commented. It's definitely not 'pretty bad' — apologies for that. However it's definitely noticeable coming from 5.x. I just haven't tested previous builds so I don't know exactly how much better it has gotten in the last few days.

"demo as sidexside as one can, and the average person is not even going to notice" — I am not sure given I am running the example code straight off of the source. I think I need to run some tests.

Thanks for the feedback guys!

I'm strongly considering TTTAttributedLabel as it also solves other issues (namely, it doesn't have the problems associated with selectable). Also, I trust Matttt's code.

More code changes here to address this. Please let me know if this helps!

Looks like the layer rasterization provided only some minor improvements. I'm still seeing a bit of stuttering, particularly with data detectors

Did you have a chance to try TTTAttributedLabel?

Doing some profiling of my own I kept coming back to boundingRectWithSize: as the main issue for me. I looked into alternatives (Core Text, etc), but seems like this is a particularly troublesome area on iOS and none seemed worth pursuing. There is some caching of the message bubble sizes, but the cache gets cleared pretty regularly in normal chat view usage as to limit its usefulness in my opinion. Instead of caching via indexPath, I'm exploring caching using the message text as the dictionary key, or potentially a hash of the message text, if size becomes an issue. I'm invalidating the cache in shouldInvalidateLayoutForBoundsChange:. You obviously lose caching on media cell types, which I'm not using at this point so I haven't bothered addressing. I'm currently keeping this all local to the flow layout, but am considering keeping external to JSQ.

Just had another idea that seems like it should work generically. Most, if not all of us, probably have a unique identifier for each chat message. Pretty much just gets you around storing arbitrarily long messages as keys, but also makes the solution work for media messages as well.

@chadpod that looks easy. messageData.text.hash does seem like it would be better than the message text itself. We insert using performBatchUpdates, and that causes invalidateLayoutWithContext to be called for every insert, which wipes out the cache. Switching off indexPath based caching (and removing the cache clearing from invalidateLayoutWithContext and inserting it in shouldInvalidateLayoutForBoundsChange as you said) would fix that, too.

@chadpod / @ghazel - That is an excellent idea. Using the message hash as the key. I'll see how this works out.

Hello all - #584 and #322 have been resolved. Scrolling feels slightly more improved. Please pull the latest from develop and let me know how things go on your end.

All - I just implemented better caching for bubble sizes using message item hashes and NSCache.

Please pull from develop and let me know how this works for you.

It's pretty bad for me (iPhone 5S). I think we need to try TTTAttributedLabel.

It's not a constant low FPS scrolling, but big hangs like GC pauses out of nowhere
(obviously not because of GC, but they look like it).

Unfortunately, no change on 5S or 6 Plus. It's bad. The lack of smooth scroll is jarring.

I ran the following experiment - I changed the <textView>...</textView> to <label>...</label> in the two xib files, changed the outlet to UILabel, commented out some code that was not found for text views and set the number of lines to 0 on the labels. I must say, there is no noticeable performance increase, so hoping TTTAttributedLabel will fix the issue is false hope I fear.

Removing auto layout from the two xibs (incoming + outgoing), albeit without any other attempts to layout, makes scrolling butter smooth.

@LeoNatan / @galambalazs - I think scrolling have been greatly improved since this issue was first opened. (But still not perfect.) Can either of you post a demo project that shows the problems you're having?

I'm mostly testing on a 5S and iPad4 and it's not that bad.

Unfortunately, my POC code is tied to a much larger app which I cannot share, I think even the demo project with "Load many messages" option turned on, gives the same stuttery performance. I am very sensitive to stuttering and I notice it a lot. The interesting fact is that the scrolling stutters in a similar capacity on iPhone 5C, 5S and 6 Plus.

I did the testing that Leo suggested.
It does seem to be caused by AutoLayout.

The demo project shows it perfectly.

Steps to reproduce:

Scroll slowly like you were searching for something in your messages, but not really reading them just scanning. e.g.: with a speed of around 3-5 seconds for a full screen scroll.

The reason it is so annoying is because your eyes are concentrating on the messages and as they flow from top to button they occasionally jump, then go a little more, then a little stuttering again.

Also what Leo said about having it everywhere. This is what makes it strange and funny.

If it was only say on the 4S/5, you could argue it will be solved by waiting a bit, but we can see this on a 5S and 6 Plus.

I think the problem is in the constraint equation solver on Apple's part. In my two years of use of auto layout, I've seen it save me hours of work of layout, while taking hours of my time debugging slow views. I think it's great for some stuff and less for other.

I this case here, I don't even think it is necessary. All the size information is either provided by the user or calculated from the content. It can be removed with "ease" and still have the same exact look.

Yes it could be that the AutoLayout solver is more out of sync in iOS 8 somehow.
It can't finish on certain frames so they get dropped.

@galambalazs - Yes, autolayout performance has regressed with iOS 8 and Xcode 6.

Sad to see things regressing, you would expect Apple to improve how their recommended methods work, not the other way around.

Interesting how Facebook banned AutoLayout in their app Paper and for other FB teams too.
https://www.youtube.com/watch?v=h4QDbgB7RLo&feature=youtu.be&t=51m51s

So do you think it's absolutely necessary to use auto layout?

It's perfectly reasonable if you decide to stick to it, we just expect to know what's coming.
If this doesn't get resolved we may have to roll our own framework.

User experience is no.1 priority.

@galambalazs This video is very interesting. Thanks for sharing.

Thanks @galambalazs — I'm aware of the cost of AutoLayout (which is why so many optimizations have been implemented in this library, and performance was great until iOS 8 and Xcode 6), however I'm hesitant to drop it completely. I think it is "the future" of UI layout (current issues aside), and moving away from it will introduce its own set of complexities and bugs. I'm not suggesting those will be any better or worse than our current situation, but they will be there. Also, it would be a major change in the library — of which there have been many for the 6.0 release already. So there's a lot to consider.

I know @LeoNatan really wants auto layout gone, too. 😄

So.... I've just pushed a new branch: kill_autolayout.

@galambalazs / @LeoNatan — if you all, or anyone else here is set on removing AutoLayout, I would love it if we could do it on this branch. I'll be happy to accept PRs. At the very least, I can help maintain this official branch for anyone who wishes to use it. We can see how things go, and possibly merge to develop if all is well. I've probably made as much progress here as I can for the final 6.0 release. If we do merge any changes, they'll be set for 6.1.

Scrolling has noticeably improved for me with the HEAD of develop. The one that really stood out previously was when you tapped the status bar to scroll to top. Previously it would stutter a good bit during the animation. Now it is smooth.

Seems like most of the improvement is from the caching, as when my chat gets really long, I have the choppiness again. Couple thoughts on it. First, increase the cache limit. 100 seems unnecessarily small given what we are storing. I'd say bump it up to 1000. Second, does the cache need to be cleared in jsq_resetLayout? Seems excessive and unnecessary, and wouldn't just clearing it in shouldInvalidateLayoutForBoundsChange: be more appropriate? Unless the bounds change, those cached sizes should be valid.

Don't believe the underlying issues can be solved at this point without either moving to manual layout or Apple fixing it. Having said that, it is not that big of an issue that I can't live with it till Apple cleans up their side. iOS 8 introduced more bugs and performance issues than any major release to date!

I actually took the time to do a quick demo. I'm still not satisfied.

In case anyone wants to try it, there are two steps to do:

1.) Turn off AutoLayout for
JSQMessagesCollectionViewCellIncoming.xib &
JSQMessagesCollectionViewCellOutgoing.xib
(right hand side, first tab, File Inspector)

2.) Paste this version of JSQMessagesCollectionViewCell.m into the project.
https://gist.github.com/galambalazs/8d70309e5a112e9a138f

It's not a complete implementation, just for testing purposes, the cell top labels don't get centered all the time for some reason. But it serves its purpose.

As @LeoNatan said simply turning off Auto Layout (1. step) showed great performance, but after actually implementing the manual layout (2. step) it didn't perform all that good.

Not sure where to take it from here.

Can you run instruments and see what is causing this? If it is something else, there may still be hope for AL.

I ran it briefly, there's nothing that stands out at first glance. System stuff mostly.
Of course there is an exact reason somewhere but right now it just seems like a ghost.
Also I didn't see any scrolling problems after installing iOS 8 with any of my installed apps.

Oh and I tried it on 8.1.1 beta as well (because it promised performance optimizations, although mostly for older devices). No change.

But it's certainly doable because Apple's Messages app keeps a consistent 60 FPS with data detectors, dynamic gradient background, springy bubble physics, translucent navbar. There's a ton of computation going on and it still performs (even on a 4S).

Any new discoveries aside, I think more robust caching on message bubble sizes is our best bet (and I would be extremely surprised if Apple isn't doing this internally given the cost of computing them). We need a two stage cache (i.e. memory and disk). A quick search turns up Tumblr's general purpose two stage cache which I think would be a pretty safe bet:

https://github.com/tumblr/TMCache

As long as chat view bounds are not changing, computed sizes can be cached based on message and stored via message hash. Also, the same message across chats does not need to be computed twice. 'Hi' in chat one has the exact same size as 'Hi' in chat two, again given the chat view bounds are constant.

-[NSAttributedString boundingRectWithSize:options:context:] is the enemy :)

As far as I am aware, iMessage is actually a web view implementation. At least on the Mac side it is.

I would bet big bucks it's not a WebView on iOS. The Mac version looks and feels different (although I don't have a retina display).

If someone has a jailbroken device, resign debugserver with the proper entitlement and attach the Messages process and see. 😄

https://github.com/nst/iOS-Runtime-Headers/tree/master/PrivateFrameworks/ChatKit.framework

Hmm, so seems at least in iOS it is not web. Also, text is a text view.

@LeoNatan / @galambalazs / @chadpod - Thanks for all the investigation and information! 👍 Lots to digest here.

@chadpod RE: cache limit and clearing

See 5627228. New cacheLimit property exposed for customizing.

  1. 1000 seems quite large to me, I decided to up the default value here to 200. I checked iOS messages, and it looks like about ~50 messages are displayed at a time before you can "load earlier messages". Based on this, 200 seems like a sensible default. But again, you can set to whatever you like.
  2. The cache is only explicitly cleared on memory warnings and device rotations.

It seems I spoke too soon. Manual layout of cells does seem to significantly improve performance, it just didn't remove the stuttering completely on its own.

What I had to do to achieve about acceptable performance on a 5S (in order of their importance):

  • turn off auto layout for cells (manual layout)
  • turn off the top date labels (e.g. JSQMessagesTimestampFormatter is kinda slow)
  • turn off data detectors (or at least limit them to 1-2 types)
  • for the demo: use properties on self instead of querying NSUserDefaults for incoming/outgoingAvatarSetting inside avatarImageDataForItemAtIndexPath
  • (for an extra push turning off translucency on the navbar helps as well)

Now this means stripped down functionality (no dates) and trade-off in looks (no translucency), but the current state of this library makes these mandatory to keep scrolling close to acceptable.

Not all of these steps are related to the library's development, but they are relevant for using it in real world applications.

As for the manual layout and top labels, I had to comment out this line:

// [self setTranslatesAutoresizingMaskIntoConstraints:NO];

...inside JSQMessagesLabel.m / jsq_configureLabel (in order to completely get rid of auto layout for those labels). Now they work as well.

https://github.com/jessesquires/JSQMessagesViewController/blob/develop/JSQMessagesViewController/Views/JSQMessagesLabel.m#L35

Thanks @galambalazs 👍

After more Instruments exploration, it does seem like ditching auto layout is the only way out of this. Having said that, I did go ahead with my two stage caching idea (using TMCache), via a subclass of JSQMessagesCollectionViewFlowLayout. The main part of interest would be:

- (CGSize)messageBubbleSizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
    id<JSQMessageData> messageItem = [self.collectionView.dataSource collectionView:self.collectionView messageDataForItemAtIndexPath:indexPath];

    CGSize finalSize = CGSizeZero;

    if ([messageItem isMediaMessage]) {
        finalSize = [[messageItem media] mediaViewDisplaySize];
    }
    else {
        TMCache *messageBubbleCache = [TMCache sharedCache];
        NSString *md5Hash = [[messageItem text] sam_MD5Digest];
        NSValue *cachedSize = [messageBubbleCache objectForKey:md5Hash];
        if (cachedSize != nil) {
            return [cachedSize CGSizeValue];
        }
        else
            NSLog(@"***************************** cache miss ****************************");

        CGSize avatarSize = [self jsq_avatarSizeForIndexPath:indexPath];

        //  from the cell xibs, there is a 2 point space between avatar and bubble
        CGFloat spacingBetweenAvatarAndBubble = 2.0f;
        CGFloat horizontalContainerInsets = self.messageBubbleTextViewTextContainerInsets.left + self.messageBubbleTextViewTextContainerInsets.right;
        CGFloat horizontalFrameInsets = self.messageBubbleTextViewFrameInsets.left + self.messageBubbleTextViewFrameInsets.right;

        CGFloat horizontalInsetsTotal = horizontalContainerInsets + horizontalFrameInsets + spacingBetweenAvatarAndBubble;
        CGFloat maximumTextWidth = self.itemWidth - avatarSize.width - self.messageBubbleLeftRightMargin - horizontalInsetsTotal;

        CGRect stringRect = [[messageItem text] boundingRectWithSize:CGSizeMake(maximumTextWidth, CGFLOAT_MAX)
                                                             options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading)
                                                          attributes:@{ NSFontAttributeName : self.messageBubbleFont }
                                                             context:nil];

        CGSize stringSize = CGRectIntegral(stringRect).size;

        CGFloat verticalContainerInsets = self.messageBubbleTextViewTextContainerInsets.top + self.messageBubbleTextViewTextContainerInsets.bottom;
        CGFloat verticalFrameInsets = self.messageBubbleTextViewFrameInsets.top + self.messageBubbleTextViewFrameInsets.bottom;

        //  add extra 2 points of space, because `boundingRectWithSize:` is slightly off
        //  not sure why. magix. (shrug) if you know, submit a PR
        CGFloat verticalInsets = verticalContainerInsets + verticalFrameInsets + 2.0f;

        //  same as above, an extra 2 points of magix
        CGFloat finalWidth = MAX(stringSize.width + horizontalInsetsTotal, self.bubbleImageAssetWidth) + 2.0f;

        finalSize = CGSizeMake(finalWidth, stringSize.height + verticalInsets);

        [messageBubbleCache setObject:[NSValue valueWithCGSize:finalSize] forKey:md5Hash];
    }

    return finalSize;
}

I don't even bother to cache non text cell sizes. Text cell sizes I cache by the MD5 hash of the text.

I've tried this but only to find that TTTAttributedLabel doesn't support NSTextAttachments and therefor it is futile to go through this if you want to have something like smilies in your text.
Read more: TTTAttributedLabel/TTTAttributedLabel/issues#447

Gotta admit, opting out of auto-layout improves the scrolling performance so much it seems the only way for now. Although current implementation of manual layout suggested by https://gist.github.com/galambalazs/8d70309e5a112e9a138f isn't complete (especially cellBottomLabel sizing and whatnots), but it's nothing compared to the length we have to go to improve performance with auto-layout!

Thanks for the input @mohpor !

Regarding TTTAttributedLabel's lack of support for NSTextAttachment — doesn't this not matter, given the media message framework?

of course it matters, media message is one thing but the lack of inline UIImage is something else. Without inline images, there will be no smilies!


Sent from Mailbox

On Mon, Feb 2, 2015 at 8:25 PM, Jesse Squires notifications@github.com
wrote:

Thanks for the input @mohpor !

Regarding TTTAttributedLabel's lack of support for NSTextAttachment — doesn't this not matter, given the media message framework?

Reply to this email directly or view it on GitHub:
#492 (comment)

Usually smilies are emoji characters, implemented using Unicode and the drawing system renders it's ownnicons there, with no need for text attachments. While the problem is there, it's sideline issue. I'd say performance is more important.

I wish it was that simple, if you were to support multi-platform messaging and wanted to implement your unique Smilies, you’d need those attachments. I’m not all against TTTAttributedLabel, I’m just pointing out its caveats.

Plus, I might have found a solution to improve performance so much  that may resolve all these problems  around performance. I have achieved near silk smooth performance even with text attachments in UITextView now, but I’m going to push it to the limits and then share it with you guys too.


Sent from Mailbox

On Mon, Feb 2, 2015 at 8:44 PM, Leo Natan notifications@github.com
wrote:

Usually smilies are emoji characters, implemented using Unicode and the drawing system renders it's ownnicons there, with no need for text attachments. While the problem is there, it's sideline issue. I'd say performance is more important.

Reply to this email directly or view it on GitHub:
#492 (comment)

Cool. Did you remove autolayout? =]

Of course I did! It was my first step. It helped so much I was going to cry out of happiness! :D


Sent from Mailbox

On Mon, Feb 2, 2015 at 8:51 PM, Leo Natan notifications@github.com
wrote:

Cool. Did you remove autolayout? =]

Reply to this email directly or view it on GitHub:
#492 (comment)

any updates on the new versions?

@jessesquires
I was a believer of autolayout but recently I've seen a tech-talk by a Facebook dev and learnt that autolayout is banned at Facebook. After encountering multiple issues here related to it and also to give AsyncDisplayKit (doesn't support autolayout) a try, I decided to remove all autolayout stuff and nibs from JSQMessagesViewController.
And guess what? I can enforce the very same constraints without actually using autolayout.

Here is an example for JSQMessagesToolbarContentView:

CGRect frame = self.bounds;
CGRect leftContainerFrame = CGRectMake(8.0f, 6.0f, self.leftBarButtonContainerViewWidthConstraint, 32.0f);
self.leftBarButtonContainerView.frame = leftContainerFrame;

CGRect rightContainerFrame = CGRectMake(frame.size.width - (8.0 + self.rightBarButtonContainerViewWidthConstraint), 6.0f, self.rightBarButtonContainerViewWidthConstraint, 32.0f);
self.rightBarButtonContainerView.frame = rightContainerFrame;

UIEdgeInsets textViewInsets = UIEdgeInsetsMake(7.0f, CGRectGetMaxX(leftContainerFrame) + 8.0f, 7.0f, frame.size.width - CGRectGetMinX(rightContainerFrame) + 8.0f);
self.textView.frame = UIEdgeInsetsInsetRect(frame, textViewInsets);

This is equivalent to what autolayout was doing. One problem might arise if some constraints have inequalities but that can be solved too with simple math. None of the constraints are too complex and even if there was a really complex one, I guess its performance would have been horrible.

My point is, maybe autolayout can really be dropped.

@anilanar is right, but for me it's not a theory anymore, I have successfully re-implemented everything without auto layout ,using ASDK and batch updates instead of collection view's reload and the performance is much much better now.

I agree, and I'm aware of all of the above.

But here's what's most frustrating:
We had great performance with auto-layout. This is a regression in iOS 8. 😢

As the author and maintainer, I need to deeply consider the kinds of fundamental changes suggested here, and plan accordingly (build out a roadmap). Major changes, or any breaking API changes, mean new major version releases (i.e. v6.x to v7.0). And then we have to live with those changes until the next major release (i.e. v8.0). Introducing new dependencies increases complexity in general and for devs using this library. Also, a lot of people are using this library — so it's also good to consider the most user-friendly approach to any major change.

Others have noted that moving away from UITextView and using TTTAttributedLabel has resolved these issues. Currently, I'm leaning toward this solution for a few reasons.

  1. Aside from performance, there are other issues with UITextView (#747, #613, #438). Given these, I think moving to TTTAttributedLabel is inevitable.
  2. Swapping out a UITextView for a UILabel is a pretty benign change. We basically preserve all the current semantics and functionality.
  3. On the other hand, using AsyncDisplayKit requires some fundamental architectural changes.
  4. For current consumers of this API, TTTAttributedLabel will be easiest to understand and adapt to.

Now, for all the AsynDisplayKit advocates here 😄 , I'm not suggesting this will never be an option. But these are my current thoughts for the library's roadmap to version 7.0 8.0. Furthermore, there are some architectural changes I would like to make for v7.0 v8.0 (see #453). Integrating AsyncDisplayKit on top of all these changes would be too much. But, maybe AsyncDisplayKit would make the cut for v8.0 v9.0.

UPDATED: 16 Mar 2015

Thanks @jessesquires for the insight to the future.
I for one, have the utmost respect for you and your work and by no means I wanted to deny your right to decide the future of this wonderful framework, I just shared what I've found about a solution.
Now some notes from an AsyncDisplayKit advocate 😄 : Re-impleneting everything using AsyncDisplayKit would mean more than refactoring and tweaking, it means RE-IMPLEMENTING everything, because it is just not worth it otherwise and to be fair, I've started such thing and so far, it doesn't look good! 😞, it is unstable, it is perilous to reload or redraw, it flickers most of the time and many many many more shortcomings I haven't even faced yet. BUT, there are aspects that could mean a lot if we switch to, that may have a noticeable performance difference. e.g. changing some factories to use ASImageNode instead of UIImageView, because they are perfectly encapsulated and if developers have been using this framework correctly, they shouldn't feel anything! although it requires us to rethink the current reload mechanism 😖! In a real world application, the collection view may face around 2-10 reloads per second (in an active chat) that means a lot of performance if we reload the entire collection view!

One final though on this is that I think the pressure of maintaining this framework is unfair on just one person and we all should contribute more and improve this wonderful project.

I have been playing around with trying to fix the choppy scrolling, and one thing that fixed it in my own implementation is when configuring layout attributes:

layoutAttributes.cellTopLabelHeight = [self.collectionView.delegate collectionView:self.collectionView
                                                                                layout:self
                                                      heightForCellTopLabelAtIndexPath:indexPath];

layoutAttributes.messageBubbleTopLabelHeight = [self.collectionView.delegate collectionView:self.collectionView
                                                                                         layout:self
                                                      heightForMessageBubbleTopLabelAtIndexPath:indexPath];

layoutAttributes.cellBottomLabelHeight = [self.collectionView.delegate collectionView:self.collectionView
                                                                                   layout:self
                                                      heightForCellBottomLabelAtIndexPath:indexPath];`

For me what I was doing was in the implementation of these methods was calculating the stringRect size, which means that these calculations are not getting cached and are being calculated every time the layout is invalidated. Either changing these to a constant value or caching these values greatly improved my performace (not perfect but definitely better). Hope this helps anyone :)

@jessesquires at this point I wonder if it's worth using a TSI and getting some feedback from DTS.

So as it happens, I have been field testing the application a bit. Response is that the message view is really laggy overall. Some have also claimed that they feel they are using Android if they still in the message view for a while. Is there any progress on this? @jessesquires Please let me know how I can help as I am happy to spend some time (at most a weak) investigate a potential fix for iOS 8. Thanks. :)

Hi guys,

I currently have an iPhone 5c with iOS 7.1.2 and another iPhone 5c with iOS 8.1.3.
I just modified the sample project to add some more messages, and i ran timeProfiler on both. I just open the message view and do 12 back and forth.

You can have a quick look at the profile here, we clearly see the difference between iOS7 and 8 (it's smooth on iOS7):

iOS 7:
timeprofiler_ios7

iOS 8:
timeprofiler_ios8

In iOS8 there is like no difference between the first layout and the subsequent ones.
I am new to Instruments, and i am still trying to understand better. But what i can see is that there is a difference in the call tree in iOS8.

For example i can see this in iOS7

Running Time    Self     
1020.0ms   19.2%    0,0    -[UICollectionView _createPreparedCellForItemAtIndexPath:withLayoutAttributes:applyAttributes:]
1017.0ms   19.2%    1,0     -[DemoMessagesViewController collectionView:cellForItemAtIndexPath:]
3.0ms       0.0%    0,0     -[UICollectionView _addControlledSubview:atZIndex:]

and this in iOS8

Running Time    Self     
3620.0ms   37.8%    1,0    -[UICollectionView _createPreparedCellForItemAtIndexPath:withLayoutAttributes:applyAttributes:]
2593.0ms   27.0%    1,0     -[UICollectionReusableView preferredLayoutAttributesFittingAttributes:]
1011.0ms   10.5%    0,0     -[DemoMessagesViewController collectionView:cellForItemAtIndexPath:]
8.0ms       0.0%    0,0     -[UICollectionView _addControlledSubview:atZIndex:]
3.0ms       0.0%    1,0     -[JSQMessagesCollectionViewLayoutAttributes isEqual:]
1.0ms       0.0%    1,0     objc_getProperty_non_gc(objc_object*, objc_selector*, int, signed char)
1.0ms       0.0%    1,0     -[UICollectionReusableView _wasDequeued]
1.0ms       0.0%    1,0     -[UICollectionReusableView _layoutAttributes]
1.0ms       0.0%    1,0     objc_autoreleaseReturnValue

The new call to preferredLayoutAttributesFittingAttributes is taking a lot of time.
Does this talk to someone?
Are there any changes or known issues with autoLayout in iOS8?

I am trying to drill this down.

If i modify JSQMessagesCollectionViewCell.m and just return the given layoutAttributes, it's not chunky anymore (but not as fluid iOS7 though).

- (UICollectionViewLayoutAttributes *)preferredLayoutAttributesFittingAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes
{
    return layoutAttributes;
}

Before:

3620.0ms   37.8%    1,0     -[UICollectionView _createPreparedCellForItemAtIndexPath:withLayoutAttributes:applyAttributes:]
2593.0ms   27.0%    1,0      -[UICollectionReusableView preferredLayoutAttributesFittingAttributes:]
2591.0ms   27.0%    0,0       -[UIView(UIConstraintBasedLayout) systemLayoutSizeFittingSize:]
1.0ms      0.0% 1,0       -[JSQMessagesCollectionViewLayoutAttributes copyWithZone:]

After:

Running    Time Self
668.0ms    7.2% 0,0   -[UICollectionView _createPreparedCellForItemAtIndexPath:withLayoutAttributes:applyAttributes:]
478.0ms    5.2% 0,0    -[UICollectionReusableView preferredLayoutAttributesFittingAttributes:]
478.0ms    5.2% 0,0     -[UIView(UIConstraintBasedLayout) systemLayoutSizeFittingSize:]

@BillCarsonFr excellent work! thanks! 👍

These changes have to do with the new self-sizing cells in iOS 8. That's what preferredLayoutAttributesFittingAttributes: is for.

Great information. I'll be looking into this.

@jessesquires Is there any update regarding the inclusion of TTTAttributedLabel? Just wondering if this was investigated at some point?

@kartikthapar @jessesquires I changed the JSQMessagesCellTextView from UITextView to TTTAttributedLabel. Although I still feel lags, the scrolling performance greatly improved.

Hey @kartikthapar -- the plan is to eventually switch to TTTAttributedLabel see my comment above for why.

That sounds really solid. I am happy to contribute in whatever capacity is needed. Thanks @jessesquires. Great work.