3

Adding empty SKView to UICollectionView cell makes the scrolling almost impossible on iPhone 6 (iOS 9.x). Lets say collection view contains 6 items of which first 3 are visible, scrolling horizontally for next 3 items takes 3sec with jerkiness.

Here's the relating part of code:

func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
    {
        let cell = collectionView.dequeueReusableCellWithReuseIdentifier(ACDFilterCollectionViewCellConstants.CellIdentifier, forIndexPath: indexPath) as! ACDFilterCollectionViewCell

        let filterType = filters[indexPath.row]

        cell.titleLabel.text = filterType.rawValue

        let skview = SKView(frame: cell.frame)
        cell.filterView.addSubview(skview)

        return cell
    }

What should I do to have a smooth scrolling with SKViews inside my UICollectionViewCells?

On iOS 8.x I'm getting the exception on rendering this view:

enter image description here

manikal
  • 953
  • 7
  • 30
  • I've seen similar (or I can say, the same) question on Developer forum... Obviously something is causing fps drops... But actually, why do you use UICollectionView like that (mixing UIKit with SpriteKit)? Are you sure that you can't make everything you need by using just SpriteKit? – Whirlwind Oct 19 '15 at 20:17
  • I'm using SpriteKit only to draw UIBezierPath with custom shaders, then I'm adding this to my UIView/UICollectionViewCell. Rendering of shapes which are in cells or in other views outside UICollectionView looks pretty much normal until I start scrolling. – manikal Oct 20 '15 at 07:35
  • 1
    Right now Caleb's answer is the best you will get unless we get more information. What exactly are you trying to achieve? Can these be pre-rendered and added as PNGs to your app or rendered offscreen and cached like Caleb stated? – DR Haus Oct 21 '15 at 21:52
  • I am confused you say it is an iOS 9 issue but you mention you get an exception thrown on iOS 8. I take it this doesn't work on iOS 8 either? – Skyler Lauren Oct 22 '15 at 00:33
  • @DRHaus I trying to have smooth scrolling of collection view with SKView on both iOS 8 and iOS 9. I did tried using image approach just out of curiosity and it didn't work, but thats not what I want anyway, I want the shapes to be animatable. – manikal Oct 22 '15 at 05:47
  • @SkylerLauren Yes, it does not work on iOS 8, I'm getting the exception in screenshot in question. – manikal Oct 22 '15 at 05:49
  • Can a CAShapeLayer settle your need? – MatthewLuiHK Oct 26 '15 at 13:02

3 Answers3

4

I don't know what the specific answer is w.r.t. the cause of the poor performance, but I can give you some pointers as to how to proceed.

The first thing to do is to profile the code. Use Instruments to figure out where the app is spending the most time when you start scrolling. Is setting up the SKViews as they come onto the screen the expensive part? Or is it drawing all those sprite views over and over? Is it something in the sprite views that's taking a long time to draw? I hear that SKShapeNode can be a drag on performance. What happens if you remove all the subnodes from the SKViews?

Once you know where the bottleneck is, you can decide how to proceed. From your comment it sounds like you're only using SKView to render some paths, and maybe you don't need those to animate once they're drawn? If so, perhaps you can use a single offscreen SKView to render your path into an image, and then insert that image into the collection cell. People use collections full of images all the time, and they scroll beautifully.

Community
  • 1
  • 1
Caleb
  • 124,013
  • 19
  • 183
  • 272
  • The issue is the same whether I'm using SKView SKShapeNode w/wo shaders, or, just empty SKView. I would like to have my paths animated all the time, and I did try generating and adding just image from SKView with drawViewHierarchyInRect asynchronously, but it didn't help. – manikal Oct 22 '15 at 05:41
3

My initial suggestion would be simply this: Scale back your expectations of what's possible in terms of framework interactions and relationship building between them.

Look into CAShapeLayer and see if it can do enough of what you're attempting to do with SKShapeNode and shaders. There is a "shouldRasterize" feature of of CAShapeLayers, and you can then apply things like Core Image filters, which might get you close to the desired result without using SKViews, SKShapeNode and SKShaders.


The problem you're up against is artificially inflated expectations of quality and performance within Apple's newer APIs and Frameworks, and their ability to interact and contain one another.

This is not your fault.

Apple has mastered the art of Showmanship and Salesmanship, long ago.

For a very long time these arts were reserved for physical product releases - the pitching and promoting of the devices people buy.

Sometime around the ailing and death of the late great Mr Jobs (may he RIP) there was a transition underway to promote far more actively the rapid development and early release of software to the App Store.

Most of this was part of the war on Android and Google. There must have been some consensus (probably amongst the supplier focused channels of the company that now lead) that getting as many apps into the store as possible was a way to nullify and/or beat Android.

To this end iOS 7 was created, UICollectionViews, AutoLayout and all manner of other "wonderful" goodies designed to remove the need (and concern) for design and designers for most app creation.

These facilities give the impression that anything and everything can be done with APIs, and that there's little to no need to consider design, nor even technical design.

So Developers plod into the frameworks, bright eyed, bushy tailed, and optimistic about their chances or realising anything they conceive... if they simply use a blend of the frameworks and APIs available to them, via the relationships they perceive to exist between frameworks from their understanding of the WWDC videos and other Apple promotional materials.

Unfortunately the aforementioned salesmanship and showmanship means that what you perceive to be possible is actually limited to the literal demonstrations you're shown. The inferred and suggested possible potential of the frameworks for interwoven relationships and success are just that... POSSIBLE POTENTIAL future functionality.

Most everything other than what is literally demonstrated is broken, or in some other way massively constrained and ill-performing. In some cases that which is demonstrated is also subsequently broken or doesn't make it to the release.

So if you're taking the time to imagine something like combining SKViews within UICollectionViews, and that this relationship should work, that assumption is your problem.

This gets far worse when you start thinking about SKShapeNodes and Shaders, both of which have known issues within SceneKit. Obviously you've found that the issue is that SKViews inside UICollectionViews aren't performant. That is the first problem. You're going to have far more problems gleaning performance from SKShapeNodes and your shaders later, and then the issue of immutability of the SKShapeNode is going to crop up for your animations, too.

It's not an incorrect assumption, you've been lead to believe this modularity of frameworks is a thing, and a huge feature of the massive frameworks of iOS.

However it's all early days, and that's not mentioned. Sprite Kit is fundamentally broken in iOS 9, you can read more about many of its problems here:

https://forums.developer.apple.com/thread/14487

here:

https://forums.developer.apple.com/thread/20758

here:

https://forums.developer.apple.com/thread/17463

There has been no formal communication about the cause and/or nature of the issues blighting those using Scene Kit in iOS 9 that I'm aware of, despite the noise about its many issues.

One enormous issue that's plagued many is that any use of UIKit and SKViews (in either way they can inter-relate) causes huge performance problems in iOS 9. At this point there are no apparent ways around this problem other than keeping these two frameworks separate.

Confused
  • 6,048
  • 6
  • 34
  • 75
  • I think I'll give CAShapeLayer a whirl. CIFilters are usually using CIImage as inputImage, it doesn't make sense if I would need to create CIImage from CAShapeLayer, use it as inputImage and then add that filter to CAShapeLayer.filters. – manikal Oct 26 '15 at 21:10
  • Yeah, I know what you mean. We did live thumbnails for layers in an illustrative app, and had to make sure all sorts of things didn't happen at times when the user might be interacting with the screen, and do all manner of tricks to make the sequence of events happen in a way that maintained user perception of performance. – Confused Oct 26 '15 at 21:42
  • And that's the real trick. We worked on the perception of performance rather than actual performance, basically because it was impossible to achieve performant, truly dynamic updates. Which is what I meant by scaling back expectations. If you can do that, then you can begin to think of ways to present a hacky version of the ideal experience. – Confused Oct 26 '15 at 21:43
  • Just to confirm, there is no other way around with applying CIFilter to CAShapeLayer other then as I described above? How can I efficiently convert CAShapeLayer to CIImage? – manikal Oct 27 '15 at 06:21
  • CALayers have a "contents", which you can extract/send/make a CIImage, in this manner: http://stackoverflow.com/questions/9701358/applying-a-cifilter-to-a-calayer – Confused Oct 27 '15 at 06:49
  • The best thing about CIImage and filters is the operation isn't done until you need the image. So you can use this "feature" to control when the operations happen and focus on giving that perception of performance to the user by shuffling work off to when they're likely not interacting with your app. – Confused Oct 27 '15 at 06:50
  • contents is nil in my case, I created CAShapeLayer and set path, strokeColor and lineWidth properties – manikal Oct 27 '15 at 07:15
  • You might need to use renderInContext, then. Which is uglier... but works. – Confused Oct 27 '15 at 07:16
  • Here's something you should read about contents... it might help: http://blog.spacemanlabs.com/2011/08/calayer-internals-contents/ – Confused Oct 27 '15 at 07:20
  • I tried with renderInContext but no luck, maybe I'm missing something: https://gist.github.com/manikal/00b54787701e389e648d – manikal Oct 27 '15 at 11:40
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/93487/discussion-between-jackiedigital-and-confused). – manikal Oct 27 '15 at 12:05
0

One big change on iOS 9 vs. iOS 8 is the switch from OpenGL to Metal for SpriteKit and SceneKit. I suggest you try overriding that default, and switch back to OpenGL, by adding the key/value pair PrefersOpenGL/YES to your Info.plist. See Technical Q&A QA1904, Specifying the renderer for SpriteKit and SceneKit.

Hal Mueller
  • 7,019
  • 2
  • 24
  • 42