CoreAnimation doesn't work with -setAlphaValue on QTMovieView - cocoa

I have some basic code for adding a QTMovieView. I want it to fade in, so I add an animation on setAlphaValue. Only issue is that it doesn't work. The alpha value is instantly set to whatever it was supposed to animate to. If I try animating e.g. setFrame: it works fine. You'll find my code below. This is on 10.5.7 with the Mac OS X 10.5 SDK.
- (void)addMovie:(NSString *)aFile
{
QTMovieView *aMovieView;
QTMovie *aMovie;
NSRect contentFrame;
contentFrame = [[self contentView] frame];
aMovieView = [[QTMovieView alloc]
initWithFrame:contentFrame];
[aMovieView setWantsLayer:YES];
[aMovieView setControllerVisible:NO];
[aMovieView setPreservesAspectRatio:YES];
aMovie = [[QTMovie alloc]
initWithFile:aFile error:nil];
[aMovieView setMovie:aMovie];
[aMovieView setAlphaValue:0.4];
[[self contentView] addSubview:aMovieView];
[NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext] setDuration:2.0];
[[aMovieView animator] setAlphaValue:0.9];
[NSAnimationContext endGrouping];
}
Any ideas?

I don't know for sure but it sounds like QTMovieView may not support alpha compositing. One further test I would try would be to call setWantsLayer on the superview of the QTMovieView to see if that affects the QTMovieViews ability to composite correctly. Probably will not work.
You could try using QTMovieLayer instead. One way to incorporate QTMovieLayer would be to make your own NSView subclass that created and managed a QTMovieLayer that was added inside its root layer. Be aware though that when mixing layer backed views with non layer backed views in the same window you may get funny ordering if the layer backed views are not all in front or are behind and overlapping.

I suggest NSViewAnimation. It's a subclass of NSAnimation that will do the fading in and out for you.

I began on QTMovieLayer, but being less powerfull (of course) than QTMovieView it opened another box of issues. The solution was to use NSAnimation on the QTMovieView. I have a NSAnimation class looking somewhat like this:
AlphaAnimation.h
#import <Cocoa/Cocoa.h>
NSString * const AAFadeIn;
NSString * const AAFadeOut;
#interface AlphaAnimation : NSAnimation {
NSView *animatedObject;
NSString *effect;
}
- (id)initWithDuration:(NSTimeInterval)duration effect:(NSString *)effect object:(NSView *)object;
#end
AlphaAnimation.m
#import "AlphaAnimation.h"
NSString * const AAFadeIn = #"AAFadeIn";
NSString * const AAFadeOut = #"AAFadeOut";
#implementation AlphaAnimation
- (id)initWithDuration:(NSTimeInterval)aDuration effect:(NSString *)anEffect object:(NSView *)anObject
{
self = [super initWithDuration:aDuration animationCurve:0];
if (self) {
animatedObject = anObject;
effect = anEffect;
}
return self;
}
- (void)setCurrentProgress:(NSAnimationProgress)progress
{
if ([effect isEqual:AAFadeIn])
[animatedObject setAlphaValue:progress];
else
[animatedObject setAlphaValue:1 - progress];
}
#end
Which can then be used like this:
animation = [[AlphaAnimation alloc] initWithDuration:0.5 effect:AAFadeIn object:movieView];
[animation setAnimationBlockingMode:NSAnimationNonblocking];
[animation startAnimation];
If your QTMovieViews are in full screen it isn't very smooth, though.

Related

subclass a programmatically created NSImage

I programatically create an NSImage like this:
NSImageView *IconBox = [[NSImageView alloc] initWithFrame:CGRectMake(0, 0, 350, 300)];
NSImage *capper = [[NSImage alloc] initWithData:[self.superview dataWithPDFInsideRect:[self.superview bounds]]];
IconBox.image = capper;
[self addSubview:IconBox];
This Image gets main Part of my Window and i want to make this window draggable anywhere, i know i need to set mouseDownCanMoveWindow, but this doesn't work, while this applies to the Windoow, but while my Image is the main visible part of the Window you cannot access its background, so i need to tell the Image to drag the Window,
i read i need to subclass the NSImage and override this Method, but how can i subclass a programatically created NSImage?
I already created the Class Files in my Project, but how can i tell the newly created Image to use this custom class?
Figured it out:
Class snap = NSClassFromString(#"snapper");
object_setClass(IconBox, [snap class]);
This does the trick, also ithe default Method moveWindowByMouseDown didn't work either, but this snippet did:
In the NSImage Subclass:
-(void)mouseDragged:(NSEvent *)theEvent
{
CGFloat deltax = [theEvent deltaX];
CGFloat deltay = [theEvent deltaY];
NSRect frame = [[self window] frame];
frame.origin.x += deltax;
frame.origin.y -= deltay;
[[self window] setFrameOrigin:frame.origin];
}

initWithCoder is returning 0 values for frame property

I have sub-classed uiscrollview and used initWithCoder to prepare my uiscrollview with all the subviews. I have used below code to set it up:
- (id)initWithCoder:(NSCoder *)aDecoder {
if ((self = [super initWithCoder:aDecoder])) {
NSLog(#"%f", self.frame.size.height);
self.contentSize = CGSizeMake(5000, self.frame.size.height);
labelContainerView = [[UIView alloc] init];
labelContainerView.frame = CGRectMake(0, 0, self.contentSize.width, self.contentSize.height/2);
labelContainerView.backgroundColor = [UIColor yellowColor];
[self addSubview:labelContainerView];
}
But this keeps failing because the self.frame property always return 0 values. Its super important for me to use self.frame property because I want to use storyboard to do any height adjustments for the frame of the scrollview.
I have tried the same thing using xib files instead of using the storyboard and it works fine. But in my project I dont want to use xib files.
It'll be really great if anyone can explain me why I get 0 values in initWithCoder: method when I use storyboard? and if using storyboard how to achieve this?
PS: I notice layoutSubviews method return correct frame information but I cannot create my subviews here since it get called for each frame change (when I scroll)
Are you using Autolayout in your Storyboard?
If you are really not taking advantage of it, you can disable it and your -initWithCoder: method will return your frame again.
If you do need Autolayout, try the following:
-(void)awakeFromNib
{
[super awakeFromNib];
[self setNeedsLayout];
[self layoutIfNeeded];
// self.frame will now return something :)
}

How to change color of divider in NSSplitView?

Can we change the color of the divider?
Apple documentations says, that we can override -dividerColor in subclass of NSSplitView for this, but it doesn't works for me, or my understanding isn't correct. Also I've try create color layer over divider, e.g.:
colorLayer = [CALayer layer];
NSRect dividerFrame = NSMakeRect([[self.subviews objectAtIndex:0] frame].size.width, [[self.subviews objectAtIndex:0] frame].origin.y, [self dividerThickness], self.frame.size.height);
[colorLayer setBackgroundColor:[color coreGraphicsColorWithAlfa:1]];
[colorLayer setFrame:NSRectToCGRect(dividerFrame)];
[self.layer addSublayer:colorLayer];
Not works.
This answer may be late but:
If you are using Interface Builder, it is possible to change the property by going to the Identity Inspector of the NSSplitView (cmd+alt+3) and adding a User Defined Runtime Attribute for dividerColor of the type Color.
Actually, simply subclassing NSSplitView and overriding -(void)dividerColor works, but works only for thin or thick divider.
I've created simple configurable split view like this:
#interface CustomSplitView : NSSplitView
#property NSColor* DividerColor
#end
#implementation CustomSplitView
- (NSColor*)dividerColor {
return (self.DividerColor == nil) ? [super dividerColor] : self.DividerColor;
}
#end
Then in Interface Builder specify custom class for your split view to be CustomSplitView and add new user defined runtime attribute with key path = DividerColor, type = Color and select desired splitter color.
I've tried subclassing - (void)dividerColor too and I'm not sure why it doesn't work even though I know it's being called (and it's in the documentation).
One way to change the color of the divider is to subclass - (void)drawDividerInRect:(NSRect)aRect. However, for some reason, this method isn't called and I've checked all over the web for answers, but couldn't find anything, so I ended up calling it from drawRect. Here is the code for the subclassed NSSplitView:
-(void) drawRect {
id topView = [[self subviews] objectAtIndex:0];
NSRect topViewFrameRect = [topView frame];
[self drawDividerInRect:NSMakeRect(topViewFrameRect.origin.x, topViewFrameRect.size.height, topViewFrameRect.size.width, [self dividerThickness] )];
}
-(void) drawDividerInRect:(NSRect)aRect {
[[NSColor redColor] set];
NSRectFill(aRect);
}
Based on Palle's answer, but with the possibility to change the color dynamically in code, I'm currently using this solution (Swift 4):
splitView.setValue(NSColor.red, forKey: "dividerColor")
If your splitview control is part of a NSSplitViewController, you should use something like this:
splitViewController?.splitView.setValue(NSColor.red, forKey: "dividerColor")
In Swift and on macOS 11 I was able to achieve this by simply subclassing the NSSPlitView and only override drawDivider()
import Foundation
import AppKit
class MainSplitView: NSSplitView {
override func drawDivider(in rect: NSRect) {
NSColor(named: "separatorLinesColor")?.setFill()
rect.fill()
}
}
I had previously tried some of the other way, listed in here and what used to work stopped working with macOS 11... but it seems that this works.
One important point I haven't seen mentioned anywhere is that if you are overriding drawRect in a split view then you must call super -- otherwise drawDividerInRect: is never called. So, it should go something like this:
- (void)drawRect:(NSRect)dirtyRect {
// your other custom drawing
// call super last to draw the divider on top
[super drawRect:dirtyRect];
}
- (void)drawDividerInRect:(NSRect)aRect {
[[NSColor blueColor] set];
NSRectFill(aRect);
}

Drag window with NSImage as handle?

I am writing a mac app and have used this code to hide the window handle,
[self.window setStyleMask:NSBorderlessWindowMask];
Well this is great, but i still need a handle to move around the window, so is there a way to move around the window from another object like an NSImage, acting like the window handle?
The code for my image is in the .h,
#interface WOWAppDelegate : NSObject <NSApplicationDelegate> {
NSWindow *window;
IBOutlet NSImageView* winView;
}
and in the .m file,
- (void)awakeFromNib {
NSString *inFilePathwin = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"win.png"];
NSImage *winImage = [[[NSImage alloc] initWithContentsOfFile:inFilePathwin] autorelease];
[winView setImage:winImage];
}
So the image is working and showing so how do i link that function?
Hook the mouseDragged event on a NSView subview.
-(void)mouseDragged:(NSEvent *)theEvent
{
CGFloat deltax = [theEvent deltaX];
CGFloat deltay = [theEvent deltaY];
NSRect frame = [[self window] frame];
frame.origin.x += deltax;
frame.origin.y -= deltay;
[[self window] setFrameOrigin:frame.origin];
}
So
subclass NSView as XView
place your handle image in an instance of this view container.
Place the container where you want in the windows main view.
Use this snippet in XView to drag the window around.
Adjust the behaviour with the mouseDown and MouseUp events.

Custom NSScroller issues

I'm trying to subclass NSScroller in order to draw my own scroller knob. To do this, I've subclassex NSScrollView and usex the following code to instantiate my custom NSScrollers:
- (void)awakeFromNib;
{
NSRect horizontalScrollerFrame = [[self horizontalScroller] frame];
NSRect verticalScrollerFrame = [[self verticalScroller] frame];
NSString *scrollBarVariant = [[[NSUserDefaults standardUserDefaults] persistentDomainForName:NSGlobalDomain] valueForKey:#"AppleScrollBarVariant"];
if (![scrollBarVariant isEqualToString:#"DoubleBoth"]) {
[self setVerticalScroller:[[[TRScroller alloc] initWithFrame:verticalScrollerFrame] autorelease]];
[self setHorizontalScroller:[[[TRScroller alloc] initWithFrame:horizontalScrollerFrame] autorelease]];
}
}
This works and my NSScrollers display correctly. But I'm occasionally seeing rendering issues upon first loading my application. Within Interface Builder I have laid out a number of NSScrollViews with their scrollbars set to hide automatically. The issue I'm seeing is that when the application first loads, the scrollbar backgrounds are rendered across the NSScrollViews contents.
alt text http://www.freeimagehosting.net/uploads/1d3fc75db8.png
I believe this is because I instantiate my NSScroll subclass (TRSubclass) via awakeFromNib, which means that the scrollbars are given the frame of the NSScrollView before it is automatically resized to meet the windows saved location and size (in other words, it's using the frame that's assigned by default within Interface Builder). What's the best way around this?
I've tried forcing the NSScrollView to redisplay (using setNeedsDisplay: and display:) but with no luck. Has anyone else come across a similar issue?
I'm using the same schema in my applications and I fighted this issues a lot. I use the same trick: scrollers are substituted in [scrollView awakeFromNib] methods, but I don't face such rendering issues at the moment. You can try to play with "draws background" property of the NSScrollView - it really helps sometimes
- (void)changeSubs
{
// change clip view
// ...
// change scrollers
NSRect horizontalScrollerFrame = [[self horizontalScroller] frame];
NSRect verticalScrollerFrame = [[self verticalScroller] frame];
if (![[self verticalScroller] isKindOfClass:[CRScroller class]])
[self setVerticalScroller:[[[CRScroller alloc] initWithFrame:verticalScrollerFrame] autorelease]];
if (![[self horizontalScroller] isKindOfClass:[CRScroller class]])
[self setHorizontalScroller:[[[CRScroller alloc] initWithFrame:horizontalScrollerFrame] autorelease]];
}
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
[self changeSubs];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
NSKeyedUnarchiver* unpacker = (id)aDecoder;
[unpacker setClass:[CRClipView class] forClassName:[NSClipView className]];
[unpacker setClass:[CRScroller class] forClassName:[NSScroller className]];
self = [super initWithCoder:aDecoder];
if (self)
{
}
return self;
}
- (void)awakeFromNib
{
[self changeSubs];
}
There are few tricks here, they work depending on a way NSScrollView is created. 'isKindOfClass' check helps to avoid double-swap.

Resources