Error with custom Stickerpack iMessages Application - Unbalanced calls to begin/end appearance transitions for...

  • I'm creating a custom stickerpack using an iMessages application
  • I have 28 stickers in the pack, all apng, all < 500kb
  • Sticker were created in photoshop, exported as image sequences, compressed using imageOptim, compiled into APNG using PNG Animator (os x)
  • I put my stickers in a Stickers folder within MessagesExtension but the problem still occurs when images are within the MessagesExtension as well
  • I can run the app, build is fine, but after a few moments I get the following errors:
  • The problem is intermittent—happens when I start dragging a sticker, if I expand/collapse the browser, if I send stickers as attachment—seems random
  • I thought it was my PNG files so I replaced them with GIFS, same problem

    I replaced the images with static PNG and it works fine

  • I also have a separate app that uses an embedded viewController as recommended by Apple, the same behavior occurs
  • I've tested the app on a 7+, 6, both with iOS 10.2
  • Here is my code:


import Messages
import UIKit
class MessagesViewController: MSMessagesAppViewController, MSStickerBrowserViewDataSource {

    var stickers = [MSSticker]()
  var numStickers = 28

  override func viewDidLoad() {
        super.viewDidLoad()
        loadStickers()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    override func willBecomeActive(with conversation: MSConversation) {
        super.willBecomeActive(with: conversation)
        presentViewController(with: presentationStyle)
    }

    private func presentViewController(with presentationStyle:
        MSMessagesAppPresentationStyle) {
        for child in childViewControllers {
            child.willMove(toParentViewController: nil)
            child.view.removeFromSuperview()
            child.removeFromParentViewController()
        }
        let controller = MSStickerBrowserViewController(stickerSize: .small)
        addChildViewController(controller)
        controller.view.frame = view.bounds
        controller.view.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(controller.view)
        controller.view.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
        controller.view.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
        controller.view.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
        controller.view.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
        controller.didMove(toParentViewController: self)
        controller.stickerBrowserView.dataSource = self
    }

    func loadStickers() {
        for index in 1...numStickers{
         
            if let url = Bundle.main.url(forResource: ("\(index)"), withExtension: "png", subdirectory: "Stickers") {
                print(url)
                do {
                    let sticker = try MSSticker(contentsOfFileURL: url, localizedDescription: "")
                    stickers.append(sticker)
                } catch {
                    print(error)
                }
            }
        }
    }


    func numberOfStickers(in stickerBrowserView: MSStickerBrowserView) -> Int {
        return stickers.count
    }

    func stickerBrowserView(_ stickerBrowserView: MSStickerBrowserView, stickerAt index: Int) -> MSSticker {
        return stickers[index]
    }
}

Replies

Whats actually happening in the app when that Output runs. Are you getting a crash? Or are you just seeing all that in the Output window?


Your loadStickers function looks fine (nearly identical to mine).

You might want to try losing the private func presentViewController and replace it with this...

func createTheStickerBrowser() {

            addChildViewController(controller)
            view.addSubview(controller.view)
            
            controller.stickerBrowserView.backgroundColor = UIColor.clear
            controller.stickerBrowserView.dataSource = self

            view.topAnchor.constraint(equalTo: controller.view.topAnchor).isActive = true
            view.bottomAnchor.constraint(equalTo: controller.view.bottomAnchor).isActive = true
            view.leftAnchor.constraint(equalTo: controller.view.leftAnchor).isActive = true
            view.rightAnchor.constraint(equalTo: controller.view.rightAnchor).isActive = true

    }


I call createTheStickerBrowser at the end of my viewDidLoad after the loadStickers func

Also then remove everything in the wellBecomeActive statement. Mine is blank.

Basically two things happen, I get that kind of output in my console and I get a gray screen on the app that says "Unable to load StickerPackName" Where stickerpackname is the name of my app.


Here's the updated code:


import Messages
import UIKit
class MessagesViewController: MSMessagesAppViewController, MSStickerBrowserViewDataSource {
   var stickers = [MSSticker]()
   var numStickers = 28

   override func viewDidLoad() {
        super.viewDidLoad()
        loadStickers()
        createTheStickerBrowser()
    }
  
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
  
    func createTheStickerBrowser() {
        let controller = MSStickerBrowserViewController(stickerSize: .small)


        addChildViewController(controller)
        view.addSubview(controller.view)
        controller.stickerBrowserView.backgroundColor = UIColor.clear
        controller.stickerBrowserView.dataSource = self
        view.topAnchor.constraint(equalTo: controller.view.topAnchor).isActive = true
        view.bottomAnchor.constraint(equalTo: controller.view.bottomAnchor).isActive = true
        view.leftAnchor.constraint(equalTo: controller.view.leftAnchor).isActive = true
        view.rightAnchor.constraint(equalTo: controller.view.rightAnchor).isActive = true
      
    }

    func loadStickers() {
        for index in 1...numStickers{
          
            if let url = Bundle.main.url(forResource: ("\(index)"), withExtension: "png", subdirectory: "Stickers") {
                print(url)
                do {
                    let sticker = try MSSticker(contentsOfFileURL: url, localizedDescription: "")
                    stickers.append(sticker)
                } catch {
                    print(error)
                }
            }
        }
    }
  
    func numberOfStickers(in stickerBrowserView: MSStickerBrowserView) -> Int {
        return stickers.count
    }
  
    func stickerBrowserView(_ stickerBrowserView: MSStickerBrowserView, stickerAt index: Int) -> MSSticker {
        return stickers[index]
    }
}



Sadly still getting the same error:


https://gist.github.com/mydudechris/651ac58609c15eea1488ccfd85e1e9dc

How do you create your animated stickers? PS > PNG Animator? Online tool? Do you have the poster frame enabled?

And are you using 2 view controllers as recommended by Apple or generating one programmtically?

How many frames do your animated stickers have? I'm wondering if there is something wrong with my png files. I just tried adding them to a standard stickerpack, no frills, and the same behavior occurs. I've tried adding the images as sticker sequences which causes xcode to crash, too many frames, and as compiled apng. Max number of frames = 50


Here's my process:


  • Create animated sequence in Photoshop
  • File > Export > Render Video: Photoshop Image Sequence as PNG with default settings:
    • Compression - smallest, Interlace - none
    • Frame Rate: 30fps
    • Render Options: Alpha Channel - Straight - Unmatted
    • 618px x 618px
  • Run PNGs through ImageOptim to reduce the filesize

    Although I've done this without ImageOptim and the results are the same

  • Create APNG using PNG Animator with default settings

All images are < 500kb and 618x618 when they are added to Xcode


I tried exporting them using the Scripts flow through photoshop as PNG8, thought this would help. It doesn't. I'm at my wits end.

I'm using PNG Animator from the Mac App Store. Its a buck or two. Well worth it. If there's a Photoshop plugin with the same name, I'm not aware of it.

I'm using the iMessage app template to start. I have the main class...


class MessagesViewController: MSMessagesAppViewController, MSStickerBrowserViewDataSource


And then I declare the sticker browser like so...


let controller = MSStickerBrowserViewController(stickerSize: .regular)


Looks like you're doing the same thing, but the main difference I see is you're calling presentViewController, and I'm not. I'm adding mine from a func call at the end of viewDidLoad. It doesn't seem lik that would make much difference but hey, who knows.

Re-read your first post, You're just saying you made them in Photoshop then used PNG Animator. When I read PS > PNG Animator, I thought maybe there was a plugin for Photoshop.

I export my frames from Adobe Animate. PNG's 408 by 408, 32Bit. There's very few options.

And no I don't set any kind of Poster frame. I didn't see that option.

Thanks for all your help!It's gotta be something with my images. I've tried 4 different application types: Custom (above), 2 controllers, StickerPack with apng, and StickerPack with sticker sequences and all produce the same results. In fact, the StickerPack with sequences wouldn't even build. Too many frames I think.


Do you change any settings in PNG Animator?

You mentioned that you provide Regular sized stickers, maybe that's what's breaking things on my end. I include large stickers, 618 x 618, but scale them down with code. I wonder if there's some conversion error that's not being logged because the scaling isn't actually happening? Really strange. This is what I'm going to try and do next.

Hmm, you shouldn't need to do any of your own scaling with code. If you're going to include 618 by 618 size stickers change this...


let controller = MSStickerBrowserViewController(stickerSize: .small)


to..


let controller = MSStickerBrowserViewController(stickerSize: .large)

And no, I used the PNG Animator settings right out of the box. Compression is zlib. I definitely don't think you should try to show your 618 images at the small size. Apple was pretty specific about those sizes.

Ugh, that might have fixed it. Fingers crossed but I replaced my stickers with 300x300 and it appears to work as expected. Not going to sign off on it yet as I need to do a little more testing but lawdamercy I hope that's all it is.

"You're all clear kid, lets **** this thing and go home!"