2

We are developing a messaging application and are using a collectionview that contains several types of cells. The cells have different types with dynamic sizes, one of the cells should show styled text with multicolors and formats received from webserver. The cell shows this correctly on first view but after scroll the app crash on HTML re-rendering with error:

Received unhandled ObjectiveC exception: NSRangeException

We implemented our collectionview based on this link UICollectionView insert cells above maintaining position (like Messages.app)

Below is the code:

We tried to add the NSAttributes calculated to cache but we are not able to retrieve them back as NSAttributes since they are saved as strings.

ChatCollectionViewSource.cs

[Export("collectionView:layout:sizeForItemAtIndexPath:"), CompilerGenerated]
public virtual CGSize GetSizeForItem(UICollectionView collectionView, UICollectionViewLayout layout, NSIndexPath indexPath)
{
    var msg = chatMessages[indexPath.Row];
    var estimatedFrame = new CGRect(0, 0, 220, 1000);

    if(msg.Message.IsHtml()){
        var ss = NSString ss = new NSString(msg.Message);
        var htmlAttributes = TextHelper.GetHtmlNSAttributes(ss);
        var estimatedFrame = TextHelper.GetEstimatedCGRectHtml(htmlAttributes); 
        return new CGSize(this.view.Frame.Width, estimatedFrame.Height + 24);
    }
    else {
        // return cgsize of nonhtml text cell
    }
}

public override UICollectionViewCell GetCell(UICollectionView collectionView, NSIndexPath indexPath)
{
    var msg = chatMessages[indexPath.Row];
    var textCell = collectionView.DequeueReusableCell("message_cell_identifier", indexPath) as MessageCell;

    textCell.Layer.ShouldRasterize = true;
    textCell.Layer.RasterizationScale = UIScreen.MainScreen.Scale;

    var estimatedFrame = new CGRect(0, 0, 220, 1000);

    if (msg.Message.IsHTML()) // html
    {
        NSString ss = new NSString(msg.Message);

        NSAttributedString NSAttributes = null;
        NSAttributes = TextHelper.GetHtmlNSAttributes(ss);

        textCell.SetHTML(NSAttributes, msg.Date, msg.Id.ToString());
        textCell.NSAttributedString = NSAttributes;
    }
    else
    {
        textCell.SetText(msg, msg.Date);
        estimatedFrame = TextHelper.GetEstimatedCGRect(msg.Message); // get estimated frame
    }

    textCell.SetFrame(estimatedFrame, chatMessages[indexPath.Row].IsSend, this.view, msg);

    return textCell;
}   

MessageCell.cs

public void SetHTML(NSAttributedString NSAttributes, DateTime dateTime, string Id = null)
{
    //ChatTextView.TextStorage.BeginEditing();
    try
    {
        ChatTextView.TextColor = null;

        if (NSAttributes != null)
        {
            DispatchQueue.MainQueue.DispatchAsync(() =>
            {
                ChatTextView.AttributedText = new NSMutableAttributedString(NSAttributes);
            });
        }
    }
    catch (Exception exception)
    {
        Debug.WriteLine(exception);
    }}

TextHelper.cs

    public static NSAttributedString GetHtmlNSAttributes(NSString HTMLtext)
        {
            var HTMLStyle = "<style>.body{font-family: '-apple-system', 'GothamMedium'; font-size:13; color:rgb(179, 178, 178);}</style>";
            var text = HTMLStyle + "<div class='body'>" + HTMLtext + "</div>";
            var ns = (NSString)text;

            var myHtmlData = ns.Encode(NSStringEncoding.Unicode);

            var options = new NSAttributedStringDocumentAttributes
            {
                DocumentType = NSDocumentType.HTML
            };

            NSError error = new NSError();
            error = null;
            NSDictionary dict = new NSDictionary();
            dict = null;

            try
            {
                var attrString = new NSAttributedString(myHtmlData, options, out dict, ref error);

                return attrString;
            }
            catch (Exception ex)
            {
                Console.WriteLine(myHtmlData);
                Console.WriteLine(ex.Message);
                Console.WriteLine(options);
                return null;
                //throw ex;
            }
        }

This actually works perfectly

We are trying to add feature: Get last 50 messages (Infinite Scroll) When the user scrolls to top of the list

ChatViewController.cs

#region scrolled to top observer
scrolledToTopObs = NSNotificationCenter.DefaultCenter.AddObserver((NSString)"scrolled_to_top",
async (notification) =>
{
    var moreMessages = await Static.DB.getChatsAsync(this.CaseId, this.UserType, ++page);

    if (moreMessages.Count == 0)
        return;

    list.InsertRange(0, moreMessages);

    Debug.WriteLine("items count in list: " + list.Count);

    // https://stackoverflow.com/questions/25548257/uicollectionview-insert-cells-above-maintaining-position-like-messages-app

    NSIndexPath[] insertedPaths = new NSIndexPath[moreMessages.Count];
    for (int i = 0; i < insertedPaths.Length; i++)
        insertedPaths[i] = NSIndexPath.FromItemSection(i, 0);

    DispatchQueue.MainQueue.DispatchAsync(() =>
    {

        var bottomOffset = ChatCollectionView.ContentSize.Height - ChatCollectionView.ContentOffset.Y;
        CATransaction.Begin();
        CATransaction.DisableActions = true;
        ChatCollectionView.PerformBatchUpdates(() =>
        {
            ChatCollectionView.InsertItems(insertedPaths);
        },
        (finished) =>
        {
            ChatCollectionView.SetContentOffset(new CGPoint(0, ChatCollectionView.ContentSize.Height - bottomOffset), false);
            CATransaction.Commit();
        });
    });
});

The app crashes after DispatchQueue.MainQueue.DispatchAsync() with this crash report:

2019-05-10 12:10:38.152 MyApp[1890:923892] Xamarin.iOS: Received unhandled ObjectiveC exception: NSRangeException * -[__NSArrayM objectAtIndexedSubscript:]: index 16 beyond bounds [0 .. 15] 2019-05-10 12:10:38.156 MyApp[1890:923892]....................... HTMLString of type NSAttributedString is null 2019-05-10 12:10:38.163 MyApp[1890:923892] Objective-C exception thrown. Name: NSRangeException Reason: * -[__NSArrayM objectAtIndexedSubscript:]: index 16 beyond bounds [0 .. 15] Native stack trace: 0
CoreFoundation 0x00000001bb829ebc + 252 1 libobjc.A.dylib 0x00000001ba9f9a50 objc_exception_throw + 56 2 CoreFoundation
0x00000001bb7a1384 _CFArgv + 0 3 CoreFoundation
0x00000001bb724b78 + 0 4 UIKitCore
0x00000001e82d4f5c + 1836 5 UIKitCore
0x00000001e82d69dc + 260 6 UIKitCore
0x00000001e82d0d48 + 44 7 UIKitCore
0x00000001e82b3d50 + 1648 8 UIKitCore
0x00000001e82b3580 + 92 9 Foundation
0x00000001bc2c9420 __NSFireDelayedPerform + 404 10 CoreFoundation
0x00000001bb7ba718 + 28 11 CoreFoundation
0x00000001bb7ba448 + 864 12 CoreFoundation
0x00000001bb7b9c7c + 248 13 CoreFoundation
0x00000001bb7b4b58 + 1880 14 CoreFoundation
0x00000001bb7b40e0 CFRunLoopRunSpecific + 436 15 UIFoundation
0x00000001c5e42368 + 1728 16 UIFoundation
0x00000001c5e4533c + 28 17 UIFoundation
0x00000001c5e923ec _NSReadAttributedStringFromURLOrData + 8120 18 UIFoundation 0x00000001c5e45278 + 136 19 MyApp 0x000000010318b498 MyApp + 5911704 20 MyApp 0x0000000103143104 MyApp + 5615876 21 MyApp
0x000000010314329c MyApp + 5616284 22 MyApp
0x0000000102ca95f4 MyApp + 792052 23 MyApp
0x0000000102c7594c MyApp + 579916 24 MyApp
0x000000010305b598 MyApp + 4666776 25 Mono
0x0000000105a38f08 mono_get_runtime_build_info + 1332 26 Mono
0x0000000105add8a4 mono_runtime_invoke_checked + 152 27 Mono
0x0000000105ae1214 mono_runtime_invoke + 160 28 MyApp
0x0000000102c40e98 MyApp + 364184 29 MyApp
0x0000000102c40af0 MyApp + 363248 30 UIKitCore
0x00000001e82d53ac + 2940 31 UIKitCore
0x00000001e82d69dc + 260 32 UIKitCore
0x00000001e82d0830 + 224 33 UIKitCore
0x00000001e82c9054 + 228 34 UIKitCore
0x00000001e82ac2d4 + 10232 35 UIKitCore
0x00000001e82b3f90 + 92 36 UIKitCore
0x00000001e82b42c8 + 388 37 UIKitCore
0x00000001e82b4124 + 96 38 UIKitCore
0x00000001e82b40a8 + 84 39 UIKitCore
0x00000001e82b3fe4 + 64 40 MyApp
0x00000001031868f8 MyApp + 5892344 41 MyApp
0x0000000103129e88 MyApp + 5512840 42 MyApp
0x0000000102cc1878 MyApp + 891000 43 MyApp
0x000000010315a830 MyApp + 5711920 44 MyApp
0x0000000103194a7c MyApp + 5950076 45 libdispatch.dylib
0x00000001bb262484 + 16 46 libdispatch.dylib
0x00000001bb20e9a4 + 1068 47 CoreFoundation
0x00000001bb7b9ce4 + 12 48 CoreFoundation
0x00000001bb7b4bac + 1964 49 CoreFoundation
0x00000001bb7b40e0 CFRunLoopRunSpecific + 436 2019-05-10 12:10:38.163 MyApp[1890:923892] 50 GraphicsServices
0x00000001bda2d584 GSEventRunModal + 100 51 UIKitCore
0x00000001e89c8c00 UIApplicationMain + 212 52 MyApp
0x00000001031914e4 MyApp + 5936356 53 MyApp
0x000000010312798c MyApp + 5503372 54 MyApp
0x000000010312794c MyApp + 5503308 55 MyApp
0x0000000102c591c0 MyApp + 463296 56 MyApp
0x000000010305b598 MyApp + 4666776 57 Mono
0x0000000105a38f08 mono_get_runtime_build_info + 1332 58 Mono
0x0000000105add8a4 mono_runtime_invoke_checked + 152 59 Mono
0x0000000105ae3b54 mono_runtime_exec_main_checked + 120 60 Mono
0x0000000105a16bfc mono_jit_exec + 316 61 MyApp
0x000000010548777c _Z9__isctypeim + 56712 62 MyApp
0x0000000102c590b8 MyApp + 463032 63 libdyld.dylib
0x00000001bb272bb4 + 4

2019-05-10 12:10:38.163 MyApp[1890:923892] Foundation.NSAttributedStringDocumentAttributes reached top of collectionview 12:10:38 PM items count in list: 300 objc[1890]: Invalid or prematurely-freed autorelease pool 0x1057f33c8.

Native Crash Reporting

Got a SIGABRT while executing native code. This usually indicates a fatal error in the mono runtime or one of the native libraries used by your application.

Basic Fault Adddress Reporting

Memory around native instruction pointer...

Native stacktrace:

0x105a30fc8 - /private/var/containers/Bundle/Application/20685366-DEC9-4CB0-BA4E-3B41F073FEBD/MyApp.app/Frameworks/Mono.framework/Mono : mono_dump_native_crash_info 0x105a270c8 - /private/var/containers/Bundle/Application/20685366-DEC9-4CB0-BA4E-3B41F073FEBD/MyApp.app/Frameworks/Mono.framework/Mono : mono_handle_native_crash 0x1bb4359f0 - /usr/lib/system/libsystem_platform.dylib : 0x1bb3b780c - /usr/lib/system/libsystem_kernel.dylib : 0x1bb3b77a8 - /usr/lib/system/libsystem_kernel.dylib : 0x1ba9f94c4 - /usr/lib/libobjc.A.dylib : 0x1ba9f941c - /usr/lib/libobjc.A.dylib

H_H
  • 126
  • 2
  • 9
  • Index out of range, I would suggest you not testing with 50 messages, instead you can test with 5, 10 messages to find out the problem. And check the data if there is a null value. And check the if the insertedPaths is right. – nevermore May 13 '19 at 08:24
  • @JackHua-MSFT the issue appear when HTML cell is found in the list if the list doest contain this (HTML Cell) will scroll normally without any issues. – H_H May 13 '19 at 09:05
  • Are you getting null NSAttributedString in `NSAttributes = TextHelper.GetHtmlNSAttributes(ss);` ? – nevermore May 13 '19 at 09:11
  • @JackHua-MSFT yes we are getting null for the NSAttributes – H_H May 13 '19 at 12:47
  • So you should avoid these null strings in method `GetHtmlNSAttributes`. – nevermore May 14 '19 at 01:35
  • @JackHua-MSFT the calculated NSAttributes are returning null but the value of SS is not null .. i Updated the questuion and add the method GetHtmlNSAttributes code – H_H May 14 '19 at 06:51
  • I can't find out the real causes without debugging codes. You should add breakpoint in the HTML cell and find out the problem. Pay more attention to Arrays as the exception is NSRangeException. – nevermore May 15 '19 at 02:23

0 Answers0