Basil Al-Dajane
Basil Al-Dajane

Reputation: 427

Use UIMotionEffect with OpenGL ES

I'm using OpenGL ES (2.0) exclusively for my app. I'm trying to get UIMotionEffect to work with one of my OpenGL objects - where all I need are the "tilt" values.

UIInterpolatingMotionEffect can only be applied to a UIView, and a hack would be to apply UIInterpolatingMotionEffect to a UIView and grab the values from there per frame, while not rendering the supposed UIView. But it seems far too hack-ish to be the only solution.

I tried to subclass UIMotionEffect, but couldn't figure out how - (NSDictionary *)keyPathsAndRelativeValuesForViewerOffset:(UIOffset)viewerOffset worked (ie. what calls it and how to retrieve the values I need).

Any ideas on how could I use UIMotionEffect with OpenGL?

Upvotes: 1

Views: 655

Answers (2)

Basil Al-Dajane
Basil Al-Dajane

Reputation: 427

I finally tested the method suggested by @rickster: connect UIInterpolatingMotionEffect to my GLKView/GL View pass keys that are custom made and just retrieve them. Unfortunately, that idea royally fails.

First, the keys that are passed to UIInterpolatingMotionEffect must be UIView animatable properties, and can't be custom variables/properties. Considering most people wouldn't want their entire context to parallax in one way direction/intensity, this method will fail.

Second, you can't just retrieve the values. If you pass center.x as a key, UIInterpolatingMotionEffect won't update center.x since the changes are considered "animations". As such, you have you retrieve the animated center.x, for example [[view.layer presentationLayer] center];

Final Notes: Just create a separate UIView on the main thread, add it to your UIViewController and systematically retrieve values from it (also on the main thread). I found that to be the simplest solution by far.

Also, since I needed to retrieve the values on a separate thread, I'm generally 1 value behind, as I dispatch to the main thread to retrieve the current value, while at the same time retrieving the last value set (make sure the variable is thread-safe/atomic).

EDIT

Since its best to access the presentationLayer on the main thread, but my method is called for a thread that isn't main, I dispatch the main thread to retrieve the value and place it in an atomic property. I would then return the atomic property outside the thread. This makes sure my secondary thread doesn't get blocked by the main thread, and I can retrieve the values I need.

Here's the code I used to retrieve values from a thread that isn't the main thread:

@property (atomic) GLKVector2 currentOffset;
@property (atomic, readonly) GLKVector2 offset;

- (GLKVector2)offset
{
    dispatch_async(dispatch_get_main_queue(), ^{
        CGRect frame = [[self.view.layer presentationLayer] frame];
        self.currentOffset = (GLKVector2){-frame.origin.x, -frame.origin.y};
    });

    return self.currentOffset;
}

Upvotes: 5

rickster
rickster

Reputation: 126167

UIInterpolatingMotionEffect works by using key-value coding to set a value for any numeric property on a UIView. This can be any property you define, not just those built into the UIView class.

You need some kind of UIView to get your content on screen. If you're using OpenGL ES on iOS, you're (preferably) using GLKView, which is a UIView subclass that manages its own GL framebuffers and gives you a place to write GL drawing code to render into those framebuffers. (If you're not using GLKView, you're probably using some kind of custom UIView subclass that uses a CAEAGLLayer to render its contents.)

Either way, if you've got GL-rendered content on the screen already, you already have a UIView. If that view isn't your own custom subclass (whether a direct UIView subclass or GLKView subclass), you can make it one, and define your own custom properties on it. Then set up your UIInterpolatingMotionEffect to use those properties. In your view subclass, or whatever object is responsible for your GL rendering, read the values of those properties and use them to set up your scene. (For example, you could use them to set up a ModelView matrix, and pass that to a shader uniform.)

Upvotes: 1

Related Questions