I have an example of animations fully working for OS X, start, pause, change speed etc.
Now I started porting this to iOS and can't even start the animation. I simplified the code to a minimum - it works in OS X, but not on iOS.
What I do is
Show a scene with a (animated) character in it (idle animation) - works both in OS X and iOS
Start a run animation on the character. This works on OS X, run animation starts and loops. On iOS the character is positioned at the start of run animation. But it doesn't run...
If I start with e.g. run scene instead of idle, it works - character runs. The problem is when I start an animation (any) after the scene is loaded. It loads the model with the animation but doesn't play.
After detailed comparison between the OS X and iOS version I found 2 differences, which probably are related but I can't figure out how fix them:
In the OS X version the character is not animated until I start an animation. In the iOS version when I attach the nodes from the idle (or whatever other) scene to the root, it's animated. I don't know how to change this.
The OS X version has the scene.dae attached to the Scene View in the Storyboard - this is also the case in iOS. But in iOS for some reason this attachment is not working, ´self.scene´ is nil. This is why I have to instantiate and assign the scene programmatically. I couldn't fix it, tried re-adding the Scene View, assigning the outlet etc.
The scene kit view is added using storyboard. idle and run are .dae files. Each of them contains a full model with the character, and the animation. I just double checked that the animation identifiers are the same as in the .dae files. The models are actually provided in an example from Apple and work perfectly on OS X...
This is the code:
View controller:
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)viewDidAppear:(BOOL)animated {
[self.sceneView loadScene];
}
#end
Scene kit view header:
// ASCView.h
#import <UIKit/UIKit.h>
#import <SceneKit/SceneKit.h>
#interface ASCView : SCNView
- (void)loadScene;
#end
Scene kit view implementation:
//
// ASCView.m
// anim_test
//
//
#import "ASCView.h"
#implementation ASCView
- (void)loadScene {
self.backgroundColor = [UIColor blueColor];
self.allowsCameraControl = YES;
[self loadSceneAndAnimations];
}
#pragma mark - Animation loading
- (void)loadSceneAndAnimations {
// Load the character from one of our dae documents, for instance "idle.dae"
NSURL *idleURL = [[NSBundle mainBundle] URLForResource:#"idle" withExtension:#"dae"];
SCNScene *idleScene = [SCNScene sceneWithURL:idleURL options:nil error:nil];
SCNScene *scene = [SCNScene sceneNamed:#"scene.dae"];
self.scene = scene;
NSLog(#"scene: %#", self.scene);
// Merge the loaded scene into our main scene in order to
// place the character in our own scene
for (SCNNode *child in idleScene.rootNode.childNodes)
[self.scene.rootNode addChildNode:child];
// Load and start run animation
// The animation identifier can be found in the Node Properties inspector of the Scene Kit editor integrated into Xcode
[self loadAndStartAnimation:#"run" withIdentifier:#"RunID"];
}
- (void)loadAndStartAnimation:(NSString *)sceneName withIdentifier:(NSString *)animationIdentifier {
NSURL *sceneURL = [[NSBundle mainBundle] URLForResource:sceneName withExtension:#"dae"];
SCNSceneSource *sceneSource = [SCNSceneSource sceneSourceWithURL:sceneURL options:nil];
CAAnimation *animationObject = [sceneSource entryWithIdentifier:animationIdentifier withClass:[CAAnimation class]];
NSLog(#"duration: %f", [animationObject duration]); //0.9
animationObject.duration = 1.0;
animationObject.repeatCount = INFINITY;
[self.scene.rootNode addAnimation:animationObject forKey:#"foofoofoo"];
NSLog(#"animation: %#",[self.scene.rootNode animationForKey: #"foofoofoo"]);
NSLog(#"is paused: %#",[self.scene.rootNode isAnimationForKeyPaused: #"foofoofoo"] ? #"yes" : #"no"); //NO
}
#end
Ohh I found it. In iOS it seems I have to pass the options, in this case:
#{SCNSceneSourceAnimationImportPolicyKey:SCNSceneSourceAnimationImportPolicyPlayRepeatedly}
In OS X I don't - probably the defaults are different.
Related
My aim is to bundle a Quartz Composer file into Xcode and build a .saver file. I am currently using the Xcode pre-made template but having problems getting the screensaver to work. I am importing the .qtz file into the project and using QCView to render it on screen, however when I test the built .saver file all I see is a black screen.
- (instancetype)initWithFrame:(NSRect)frame isPreview:(BOOL)isPreview
{
self = [super initWithFrame:frame isPreview:isPreview];
if (self) {
[self setAnimationTimeInterval:1/30.0];
NSRect viewBounds = [self bounds];
//create the quartz composition view
qcView = [[QCView alloc] initWithFrame: NSMakeRect(0, 0, viewBounds.size.width, viewBounds.size.height)];
//make sure it resizes with the screensaver view
[qcView setAutoresizingMask:(NSViewWidthSizable|NSViewHeightSizable)];
//match its frame rate to your screensaver
[qcView setMaxRenderingFrameRate:30.0f];
//get the location of the quartz composition from the bundle
NSString* compositionPath = [[NSBundle mainBundle] pathForResource:#"QuartzComposerFileName" ofType:#"qtz"];
//load the composition
[qcView loadCompositionFromFile:compositionPath];
//add the quartz composition view
[self addSubview:qcView];
}
return self;
}
I have tried doing the same thing using Xcode 6.4 and it appears that QCView is not working with Xcode 6.4. I put an image in the view and set the size of my QCView to be smaller than the frame. I see a black box for the subview but it never renders. This is true for even the most basic QC composition.
You might be able to get it to work by rendering the composition yourself using a QCRenderer. I gave up before trying this, but here is the documentation: https://developer.apple.com/library/mac/documentation/GraphicsImaging/Reference/QuartzFramework/Classes/QCRenderer_Class/
So i am using xcode 6.2 and i am starting with making apps.
but i have a question: whenever i drag something in my storyboard like a navigation bar the map overlays it, it simply won't show up, please help.
Here is my code
#import "ViewController.h"
#interface ViewController () {
GMSMapView *mapView;
}
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:43.642510 longitude:-79.387038 zoom:12 bearing:0 viewingAngle:0];
mapView = [GMSMapView mapWithFrame:CGRectZero camera:camera];
mapView.myLocationEnabled = YES;
self.view = mapView;
GMSMarker *marker = [[GMSMarker alloc] init];
marker.position = CLLocationCoordinate2DMake(43.642510, -79.387038);
marker.title = #"CN-Tower";
marker.snippet = #"A beautifull tower";
marker.map = mapView;
marker.opacity = 0.7;
}
Thanks anyway,
JP
When you do self.view = mapView you're setting the root view of the view controller to the map - and so the map covers everything, replacing whatever you designed in your storyboard.
What you need to do is add the map as a subview of the root view, eg here's a few ways to do it:
Using the Google Maps SDK in views other than the main view
Google Maps iOS SDK - Add UIView/UIButton over a GMSMapView
Google Maps iOS SDK and storyboards
I am currently developing an app for the iPhone 3GS. The deployment target is set to 5.1 and I have created a rich storyboard with lots of segues and scenes. Last night I had the idea that I wanted to make the app available for the iPad, iPhone 4, and iPhone 5. I decided to create a separate storyboard for the different screen sizes / resolutions. Now I am not sure if this is the best practice, as I have just recently started reading about springs and struts on SO, so I don't know much information about it, but for my sake, I just wanted to launch a different storyboard when the application finished launching. However this desired effect is not happening.
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
// ViewControllerWelcome *viewControllerWelcome = (ViewControllerWelcome *)[[ViewControllerWelcome alloc]init];
// NSManagedObjectContext *context = (NSManagedObjectContext *) [self managedObjectContext];
// if (!context) {
// NSLog(#"\nCould not create *context for self");
// }
//[viewControllerWelcome setManagedObjectContext:context];
// Do I need to declare my view controllers here?
// Pass the managed object context to the view controller.
CGSize iOSDeviceScreenSize = [[UIScreen mainScreen] bounds].size;
if (iOSDeviceScreenSize.height == 480)
{
// Instantiate a new storyboard object using the storyboard file named iPhoneLegacy
UIStoryboard *iPhoneLegacy = [UIStoryboard storyboardWithName:#"iPhoneLegacy" bundle:nil];
// Instantiate the initial view controller object from the storyboard
UIViewController *ViewControllerWelcome = [iPhoneLegacy instantiateInitialViewController];
// Instantiate a UIWindow object and initialize it with the screen size of the iOS device
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Set the initial view controller to be the root view controller of the window object
self.window.rootViewController = ViewControllerWelcome;
// set the window object to be the key window and show it
[self.window makeKeyAndVisible];
}
if (iOSDeviceScreenSize.height == 968)
{
// Instantiate a new storyboard object using the storyboard file named iPhone4
UIStoryboard *iPhone4 = [UIStoryboard storyboardWithName:#"iPhone4" bundle:nil];
UIViewController *ViewControllerWelcome = [iPhone4 instantiateInitialViewController];
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.rootViewController = ViewControllerWelcome;
[self.window makeKeyAndVisible];
}
// iPhone 5 1136 x 640
// iPad Legacy 1024 x 768
return YES;
}
When I try testing to see if the different storyboard file are loading in the Simulator, Simulator just loads the iPhoneLegacy storyboard.
Does this code only work for the physical devices, and do I need separate code for the Simulator?
Fist of all, DELETE YOUR EXTRA STORYBOARDS! You only need one for the iPhone and one for the iPad.
There is a simple way to make a single storyboard for all iPhone/iPod Touch sizes.
Keep only ONE storyboard for the iPhone screen size (including iPhone 5).
Make a #2x file for all of your images.
To switch between 3.5 and 4 inch size, Apple provides a button in the bottom right that looks like a rectangle with arrows pointing in or out - that button will switch between the 3.5 and 4 inch screen sizes.
That's it! No code is actually needed to make a single storyboard for each iPhone/iPod Touch.
For the iPad, you are going to need to create a new storyboard that is made for the iPad and you are going to need to update your UI code to make sure it's compatible with both iPhone and iPad screen sizes. Again, make sure to make #2x image sizes for the iPad as well.
I have a Mac App that's been in the app store for a year or so now. It was first published with target SDK 10.7, Lion. Upon the update to Mountain Lion it no longer works.
The application displays large images in an IKImageView which is embedded in an NSScrollView. The purpose of putting it into a scrollview was to get two finger dragging working, rather than the user having to click to drag. Using ScrollViewWorkaround by Nicholas Riley, I was able to use two finger scrolling to show the clipped content after the user had zoomed in. Just like you see in the Preview app.
Nicholas Riley's Solution:
IKImageView and scroll bars
Now in Mountain Lion this doesn't work. After zooming in, pinch or zoom button, the image is locked in the lower left portion of the image. It won't scroll.
So the question is, what's the appropriate way to display a large image in IKImageView and have two finger dragging of the zoomed image?
Thank you,
Stateful
Well, Nicholas Riley's Solution is an ugly hack in that it addresses the wrong class; the issue isn't with NSClipView (which he subclassed, but which works just fine as is), but with IKImageView.
The issue with IKImageView is actually quite simple (God knows why Apple hasn't fixed this in what? … 7 years ...): Its size does not adjust to the size of the image it displays. Now, when you embed an IKImageView in an NSScrollView, the scroll view obviously can only adjust its scroll bars relative to the size of the embedded IKImageView, not to the image it contains. And since the size of the IKImageView always stays the same, the scroll bars won't work as expected.
The following code subclasses IKImageView and fixes this behavior. Alas, it won't fix the fact that IKImageView is crash-prone in Mountain Lion as soon as you zoom …
///////////////////// HEADER FILE - FixedIKImageView.h
#import <Quartz/Quartz.h>
#interface FixedIKImageView : IKImageView
#end
///////////////////// IMPLEMENTATION FILE - FixedIKImageView.m
#import "FixedIKImageView.h"
#implementation FixedIKImageView
- (void)awakeFromNib
{
[self setTranslatesAutoresizingMaskIntoConstraints:NO]; // compatibility with Auto Layout; without this, there could be Auto Layout error messages when we are resized (delete this line if your app does not use Auto Layout)
}
// FixedIKImageView must *only* be used embedded within an NSScrollView. This means that setFrame: should never be called explicitly from outside the scroll view. Instead, this method is overwritten here to provide the correct behavior within a scroll view. The new implementation ignores the frameRect parameter.
- (void)setFrame:(NSRect)frameRect
{
NSSize imageSize = [self imageSize];
CGFloat zoomFactor = [self zoomFactor];
NSSize clipViewSize = [[self superview] frame].size;
// The content of our scroll view (which is ourselves) should stay at least as large as the scroll clip view, so we make ourselves as large as the clip view in case our (zoomed) image is smaller. However, if our image is larger than the clip view, we make ourselves as large as the image, to make the scrollbars appear and scale appropriately.
CGFloat newWidth = (imageSize.width * zoomFactor < clipViewSize.width)? clipViewSize.width : imageSize.width * zoomFactor;
CGFloat newHeight = (imageSize.height * zoomFactor < clipViewSize.height)? clipViewSize.height : imageSize.height * zoomFactor;
[super setFrame:NSMakeRect(0, 0, newWidth - 2, newHeight - 2)]; // actually, the clip view is 1 pixel larger than the content view on each side, so we must take that into account
}
//// We forward size affecting messages to our superclass, but add [self setFrame:NSZeroRect] to update the scroll bars. We also add [self setAutoresizes:NO]. Since IKImageView, instead of using [self setAutoresizes:NO], seems to set the autoresizes instance variable to NO directly, the scrollers would not be activated again without invoking [self setAutoresizes:NO] ourselves when these methods are invoked.
- (void)setZoomFactor:(CGFloat)zoomFactor
{
[super setZoomFactor:zoomFactor];
[self setFrame:NSZeroRect];
[self setAutoresizes:NO];
}
- (void)zoomImageToRect:(NSRect)rect
{
[super zoomImageToRect:rect];
[self setFrame:NSZeroRect];
[self setAutoresizes:NO];
}
- (void)zoomIn:(id)sender
{
[super zoomIn:self];
[self setFrame:NSZeroRect];
[self setAutoresizes:NO];
}
- (void)zoomOut:(id)sender
{
[super zoomOut:self];
[self setFrame:NSZeroRect];
[self setAutoresizes:NO];
}
- (void)zoomImageToActualSize:(id)sender
{
[super zoomImageToActualSize:sender];
[self setFrame:NSZeroRect];
[self setAutoresizes:NO];
}
- (void)zoomImageToFit:(id)sender
{
[self setAutoresizes:YES]; // instead of invoking super's zoomImageToFit: method, which has problems of its own, we invoke setAutoresizes:YES, which does the same thing, but also makes sure the image stays zoomed to fit even if the scroll view is resized, which is the most intuitive behavior, anyway. Since there are no scroll bars in autoresize mode, we need not add [self setFrame:NSZeroRect].
}
- (void)setAutoresizes:(BOOL)autoresizes // As long as we autoresize, make sure that no scrollers flicker up occasionally during live update.
{
[self setHasHorizontalScroller:!autoresizes];
[self setHasVerticalScroller:!autoresizes];
[super setAutoresizes:autoresizes];
}
#end
I am trying to write a simple DVD player using Mac OS X's DVDPlayback.framework. I can get the DVD to play within a window, but ultimately I want this to run as a full-screen app.
I'm having difficulty adding a sub-view to display media controls whilst the DVD is playing (pause / play, progress slider to scroll through the movie, change chapter etc).
It seems that if I create a sub-view (NSView) within the window being used by the DVD framework, it always seems to fall behind the DVD content, even if I tell the NSView to be at the top-most level.
Here's the simplified code, which just tries to create a white sub-view within a region of the window:
(I've tried the code on 10.6 and 10.7 with the same results).
const BOOL PLAY_DVD = YES;
#interface ControlsView : NSView {
}
#end
#implementation ControlsView
- (void)drawRect:(NSRect)rect {
[[NSColor whiteColor] set];
NSRectFill(rect);
}
#end
#implementation AppDelegate
#synthesize window = _window;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSView *view = self.window.contentView;
if (PLAY_DVD) {
NSLog(#"%# Playing DVD video", [self class]);
// Register error handler
OSStatus err;
err = DVDInitialize();
if (err != noErr) {
NSLog(#"DVDInitialise failed with error code %d", err);
[NSApp terminate:self];
}
// Set window to be the main window
err = DVDSetVideoWindowID([self.window windowNumber]);
// Set the display...
CGDirectDisplayID display = (CGDirectDisplayID) [[[[self.window screen] deviceDescription] valueForKey:#"NSScreenNumber"] intValue];
Boolean isSupported;
err = DVDSwitchToDisplay(display, &isSupported);
// Set video bounds
NSRect frame = [self.window frame];
CGRect rect = CGRectMake(0, 0, frame.size.width, frame.size.height);
err = DVDSetVideoCGBounds(&rect);
FSRef ref;
DVDOpenMediaFileWithURL([NSURL URLWithString:#"file:///Path/to/my/TestDVD/VIDEO_TS"]);
DVDOpenMediaFile(&ref);
DVDPlay();
}
// Attempt to add a subview to show the controls...
ControlsView *controls = [[ControlsView alloc] initWithFrame:NSMakeRect(20, 20, 100, 50)];
[view addSubview:controls positioned:NSWindowAbove relativeTo:nil];
}
#end
If PLAY_DVD is NO, the subview is correctly rendered (and I can create other sub-views and show that the ordering is correct).
If PLAY_DVD is YES, the media starts playing, but the sub-view is never visible because it always seems to fall behind the video.
The only examples of DVD playback I've been able to find have had the controls in a second window, but for a full-screen application I'd like the controls to be part of the full-screen view and to fade in/out when required.
Does anyone know how best to do this? Do my full-screen controls have to be in a separate window which floats above the full-screen window? I've not been able to find an example which has the controls and the DVD playback in the same window.
Thanks in advance for your help!