Skip to content

Commit

Permalink
shadowPath bounds are now in sync with those of the view
Browse files Browse the repository at this point in the history
  • Loading branch information
omorandi committed Feb 26, 2012
1 parent 9041a76 commit 66ba62e
Show file tree
Hide file tree
Showing 6 changed files with 214 additions and 24 deletions.
11 changes: 11 additions & 0 deletions Classes/JRSwizzle/JRSwizzle.h
@@ -0,0 +1,11 @@
// Copyright (c) 2007-2011 Jonathan 'Wolf' Rentzsch: http://rentzsch.com
// Some rights reserved: http://opensource.org/licenses/mit-license.php

#import <Foundation/Foundation.h>

@interface NSObject (JRSwizzle)

+ (BOOL)jr_swizzleMethod:(SEL)origSel_ withMethod:(SEL)altSel_ error:(NSError**)error_;
+ (BOOL)jr_swizzleClassMethod:(SEL)origSel_ withClassMethod:(SEL)altSel_ error:(NSError**)error_;

@end
132 changes: 132 additions & 0 deletions Classes/JRSwizzle/JRSwizzle.m
@@ -0,0 +1,132 @@
// Copyright (c) 2007-2011 Jonathan 'Wolf' Rentzsch: http://rentzsch.com
// Some rights reserved: http://opensource.org/licenses/mit-license.php

#import "JRSwizzle.h"

#if TARGET_OS_IPHONE
#import <objc/runtime.h>
#import <objc/message.h>
#else
#import <objc/objc-class.h>
#endif

#define SetNSErrorFor(FUNC, ERROR_VAR, FORMAT,...) \
if (ERROR_VAR) { \
NSString *errStr = [NSString stringWithFormat:@"%s: " FORMAT,FUNC,##__VA_ARGS__]; \
*ERROR_VAR = [NSError errorWithDomain:@"NSCocoaErrorDomain" \
code:-1 \
userInfo:[NSDictionary dictionaryWithObject:errStr forKey:NSLocalizedDescriptionKey]]; \
}
#define SetNSError(ERROR_VAR, FORMAT,...) SetNSErrorFor(__func__, ERROR_VAR, FORMAT, ##__VA_ARGS__)

#if OBJC_API_VERSION >= 2
#define GetClass(obj) object_getClass(obj)
#else
#define GetClass(obj) (obj ? obj->isa : Nil)
#endif

@implementation NSObject (JRSwizzle)

+ (BOOL)jr_swizzleMethod:(SEL)origSel_ withMethod:(SEL)altSel_ error:(NSError**)error_ {
#if OBJC_API_VERSION >= 2
Method origMethod = class_getInstanceMethod(self, origSel_);
if (!origMethod) {
#if TARGET_OS_IPHONE
SetNSError(error_, @"original method %@ not found for class %@", NSStringFromSelector(origSel_), [self class]);
#else
SetNSError(error_, @"original method %@ not found for class %@", NSStringFromSelector(origSel_), [self className]);
#endif
return NO;
}

Method altMethod = class_getInstanceMethod(self, altSel_);
if (!altMethod) {
#if TARGET_OS_IPHONE
SetNSError(error_, @"alternate method %@ not found for class %@", NSStringFromSelector(altSel_), [self class]);
#else
SetNSError(error_, @"alternate method %@ not found for class %@", NSStringFromSelector(altSel_), [self className]);
#endif
return NO;
}

class_addMethod(self,
origSel_,
class_getMethodImplementation(self, origSel_),
method_getTypeEncoding(origMethod));
class_addMethod(self,
altSel_,
class_getMethodImplementation(self, altSel_),
method_getTypeEncoding(altMethod));

method_exchangeImplementations(class_getInstanceMethod(self, origSel_), class_getInstanceMethod(self, altSel_));
return YES;
#else
// Scan for non-inherited methods.
Method directOriginalMethod = NULL, directAlternateMethod = NULL;

void *iterator = NULL;
struct objc_method_list *mlist = class_nextMethodList(self, &iterator);
while (mlist) {
int method_index = 0;
for (; method_index < mlist->method_count; method_index++) {
if (mlist->method_list[method_index].method_name == origSel_) {
assert(!directOriginalMethod);
directOriginalMethod = &mlist->method_list[method_index];
}
if (mlist->method_list[method_index].method_name == altSel_) {
assert(!directAlternateMethod);
directAlternateMethod = &mlist->method_list[method_index];
}
}
mlist = class_nextMethodList(self, &iterator);
}

// If either method is inherited, copy it up to the target class to make it non-inherited.
if (!directOriginalMethod || !directAlternateMethod) {
Method inheritedOriginalMethod = NULL, inheritedAlternateMethod = NULL;
if (!directOriginalMethod) {
inheritedOriginalMethod = class_getInstanceMethod(self, origSel_);
if (!inheritedOriginalMethod) {
SetNSError(error_, @"original method %@ not found for class %@", NSStringFromSelector(origSel_), [self className]);
return NO;
}
}
if (!directAlternateMethod) {
inheritedAlternateMethod = class_getInstanceMethod(self, altSel_);
if (!inheritedAlternateMethod) {
SetNSError(error_, @"alternate method %@ not found for class %@", NSStringFromSelector(altSel_), [self className]);
return NO;
}
}

int hoisted_method_count = !directOriginalMethod && !directAlternateMethod ? 2 : 1;
struct objc_method_list *hoisted_method_list = malloc(sizeof(struct objc_method_list) + (sizeof(struct objc_method)*(hoisted_method_count-1)));
hoisted_method_list->obsolete = NULL; // soothe valgrind - apparently ObjC runtime accesses this value and it shows as uninitialized in valgrind
hoisted_method_list->method_count = hoisted_method_count;
Method hoisted_method = hoisted_method_list->method_list;

if (!directOriginalMethod) {
bcopy(inheritedOriginalMethod, hoisted_method, sizeof(struct objc_method));
directOriginalMethod = hoisted_method++;
}
if (!directAlternateMethod) {
bcopy(inheritedAlternateMethod, hoisted_method, sizeof(struct objc_method));
directAlternateMethod = hoisted_method;
}
class_addMethods(self, hoisted_method_list);
}

// Swizzle.
IMP temp = directOriginalMethod->method_imp;
directOriginalMethod->method_imp = directAlternateMethod->method_imp;
directAlternateMethod->method_imp = temp;

return YES;
#endif
}

+ (BOOL)jr_swizzleClassMethod:(SEL)origSel_ withClassMethod:(SEL)altSel_ error:(NSError**)error_ {
return [GetClass((id)self) jr_swizzleMethod:origSel_ withMethod:altSel_ error:error_];
}

@end
1 change: 0 additions & 1 deletion Classes/TiUIView+WithShadow.h
Expand Up @@ -3,7 +3,6 @@


@interface TiUIView (WithShadow) @interface TiUIView (WithShadow)



-(void)setShadow_:(id)args; -(void)setShadow_:(id)args;


@end @end
66 changes: 51 additions & 15 deletions Classes/TiUIView+WithShadow.m
@@ -1,18 +1,69 @@
#import "TiUIView+WithShadow.h" #import "TiUIView+WithShadow.h"
#import "TiProxy.h"
#import "JRSwizzle.h"


@interface TiUIView ()
-(void)checkBounds;
@end




@implementation TiUIView (WithShadow) @implementation TiUIView (WithShadow)




+(void)initialize
{
/*
HERE we extend the TiUIView checkBounds through the method swizzling technique described here:
http://www.cocoadev.com/index.pl?MethodSwizzling
https://github.com/rentzsch/jrswizzle
it works in this way:
* in this category we define a specialized checkBoundsAlt method
* in the initialize method we swap the original checkBounds with our checkBoundsAlt
it will happen that every call to [myView checkBounds] will be redirected to the implementation provided by
checkBoundsAlt here.
Again, in this specialized implementation we want to call the one originally provided by checkBounds, but once
the methods implementations have been swapped, this will be found with the name checkBoundsAlt.
This is why apparently checkBoundsAlt calls itself. Actually after method swizzling, a call to checkBoundsAlt will
be redirected to the original checkBounds implementation
Why overriding checkBounds instead of frameSizeChanged? because it's the method calling the frameSizeChanged
method, which is the hook used by TiUIView subclasses for being notified about frame & bounds changes.
Since we cannot be sure that subclasses of TiUIView call [super frameSizeChanged], we need to play safe.
*/

NSError *error = nil;
[TiUIView jr_swizzleMethod:@selector(checkBounds) withMethod:@selector(checkBoundsAlt) error:&error];
if (error != nil) {
NSLog(@"[ERROR] %@", [error localizedDescription]);
}
}

-(void)checkBoundsAlt
{
[self checkBoundsAlt];

if ([self.proxy valueForUndefinedKey:@"shadow"])
{
[self.layer setShadowPath:[[UIBezierPath bezierPathWithRect:self.bounds ] CGPath]];
}

}


//this code is by Javier Rayon and was originally posted in the Titanium Q&A forum: //this code is by Javier Rayon and was originally posted in the Titanium Q&A forum:
//http://developer.appcelerator.com/question/130784/trick-drop-real-shadows-in-titanium-ios //http://developer.appcelerator.com/question/130784/trick-drop-real-shadows-in-titanium-ios





-(void)setShadow_:(id)args -(void)setShadow_:(id)args
{ {
if(args != nil) if(args != nil)
{ {
self.layer.masksToBounds = NO; self.layer.masksToBounds = NO;
[self.layer setShouldRasterize:YES];


if ([args objectForKey:@"shadowOffset"] != nil) { if ([args objectForKey:@"shadowOffset"] != nil) {
CGPoint p = [TiUtils pointValue: [args objectForKey:@"shadowOffset"]]; CGPoint p = [TiUtils pointValue: [args objectForKey:@"shadowOffset"]];
Expand All @@ -31,21 +82,6 @@ -(void)setShadow_:(id)args
UIColor * shadowColor = [[TiUtils colorValue:[args objectForKey:@"shadowColor"]] _color]; UIColor * shadowColor = [[TiUtils colorValue:[args objectForKey:@"shadowColor"]] _color];
[self.layer setShadowColor:[shadowColor CGColor]]; [self.layer setShadowColor:[shadowColor CGColor]];
} }

// improve performance

// [self.layer setShadowPath:[[UIBezierPath bezierPathWithRect:[self bounds] ] CGPath ] ]; // not working yet


if ([args objectForKey:@"shadowPath"] != nil) {
CGRect shadowRect = [TiUtils rectValue:[args objectForKey:@"shadowPath"]] ;
[self.layer setShadowPath:[[UIBezierPath bezierPathWithRect:shadowRect ] CGPath]];
}

if ([args objectForKey:@"rasterize"] != nil) {
[self.layer setShouldRasterize: [TiUtils boolValue:[args objectForKey:@"rasterize"] ]];
}

} }
} }


Expand Down
12 changes: 4 additions & 8 deletions example/app.js
@@ -1,5 +1,3 @@
require('ti.viewshadow');

var win = Ti.UI.createWindow({backgroundColor:'#fff'}); var win = Ti.UI.createWindow({backgroundColor:'#fff'});


var view = Ti.UI.createView({ var view = Ti.UI.createView({
Expand All @@ -10,16 +8,14 @@ var view = Ti.UI.createView({
borderRadius:20, borderRadius:20,
backgroundColor:"green", backgroundColor:"green",
shadow:{ shadow:{
shadowRadius:10, shadowRadius:2,
shadowOpacity:0.5, shadowOpacity:0.5,
shadowOffset:{x:10, y:10}, shadowOffset:{x:4, y:4},
shadowPath:{x:0, y:0, width:100, height:100}, // this improves performance a lot, and is a temporary solution until we find how to get the element's boundaries
rasterize:true // this should improve performance but only if there is no animation that affect the element
} }
}) })


var btn = Ti.UI.createButton({ var btn = Ti.UI.createButton({
title:'Shadow from module', title:'Button with shadow',
height:60, height:60,
width:200, width:200,
left:60, left:60,
Expand Down Expand Up @@ -47,7 +43,7 @@ var lbl = Ti.UI.createLabel({
}); });


lbl.setShadow({ lbl.setShadow({
shadowRadius:5, shadowRadius:3,
shadowOpacity:1, shadowOpacity:1,
shadowOffset:{x:0, y:0}, shadowOffset:{x:0, y:0},
shadowColor:'#00f' shadowColor:'#00f'
Expand Down
16 changes: 16 additions & 0 deletions tiviewshadow.xcodeproj/project.pbxproj
Expand Up @@ -24,6 +24,8 @@
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
0918FC6B14C5BDA200513CDC /* TiUIView+WithShadow.h in Headers */ = {isa = PBXBuildFile; fileRef = 0918FC6914C5BDA200513CDC /* TiUIView+WithShadow.h */; }; 0918FC6B14C5BDA200513CDC /* TiUIView+WithShadow.h in Headers */ = {isa = PBXBuildFile; fileRef = 0918FC6914C5BDA200513CDC /* TiUIView+WithShadow.h */; };
0918FC6C14C5BDA200513CDC /* TiUIView+WithShadow.m in Sources */ = {isa = PBXBuildFile; fileRef = 0918FC6A14C5BDA200513CDC /* TiUIView+WithShadow.m */; }; 0918FC6C14C5BDA200513CDC /* TiUIView+WithShadow.m in Sources */ = {isa = PBXBuildFile; fileRef = 0918FC6A14C5BDA200513CDC /* TiUIView+WithShadow.m */; };
0988CDAF14FA3FDD00DB731F /* JRSwizzle.h in Headers */ = {isa = PBXBuildFile; fileRef = 0988CDAD14FA3FDD00DB731F /* JRSwizzle.h */; };
0988CDB014FA3FDD00DB731F /* JRSwizzle.m in Sources */ = {isa = PBXBuildFile; fileRef = 0988CDAE14FA3FDD00DB731F /* JRSwizzle.m */; };
24DD6CF91134B3F500162E58 /* TiViewshadowModule.h in Headers */ = {isa = PBXBuildFile; fileRef = 24DD6CF71134B3F500162E58 /* TiViewshadowModule.h */; }; 24DD6CF91134B3F500162E58 /* TiViewshadowModule.h in Headers */ = {isa = PBXBuildFile; fileRef = 24DD6CF71134B3F500162E58 /* TiViewshadowModule.h */; };
24DD6CFA1134B3F500162E58 /* TiViewshadowModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 24DD6CF81134B3F500162E58 /* TiViewshadowModule.m */; }; 24DD6CFA1134B3F500162E58 /* TiViewshadowModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 24DD6CF81134B3F500162E58 /* TiViewshadowModule.m */; };
24DE9E1111C5FE74003F90F6 /* TiViewshadowModuleAssets.h in Headers */ = {isa = PBXBuildFile; fileRef = 24DE9E0F11C5FE74003F90F6 /* TiViewshadowModuleAssets.h */; }; 24DE9E1111C5FE74003F90F6 /* TiViewshadowModuleAssets.h in Headers */ = {isa = PBXBuildFile; fileRef = 24DE9E0F11C5FE74003F90F6 /* TiViewshadowModuleAssets.h */; };
Expand All @@ -45,6 +47,8 @@
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
0918FC6914C5BDA200513CDC /* TiUIView+WithShadow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "TiUIView+WithShadow.h"; path = "Classes/TiUIView+WithShadow.h"; sourceTree = "<group>"; }; 0918FC6914C5BDA200513CDC /* TiUIView+WithShadow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "TiUIView+WithShadow.h"; path = "Classes/TiUIView+WithShadow.h"; sourceTree = "<group>"; };
0918FC6A14C5BDA200513CDC /* TiUIView+WithShadow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "TiUIView+WithShadow.m"; path = "Classes/TiUIView+WithShadow.m"; sourceTree = "<group>"; }; 0918FC6A14C5BDA200513CDC /* TiUIView+WithShadow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "TiUIView+WithShadow.m"; path = "Classes/TiUIView+WithShadow.m"; sourceTree = "<group>"; };
0988CDAD14FA3FDD00DB731F /* JRSwizzle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JRSwizzle.h; path = Classes/JRSwizzle/JRSwizzle.h; sourceTree = "<group>"; };
0988CDAE14FA3FDD00DB731F /* JRSwizzle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = JRSwizzle.m; path = Classes/JRSwizzle/JRSwizzle.m; sourceTree = "<group>"; };
24DD6CF71134B3F500162E58 /* TiViewshadowModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TiViewshadowModule.h; path = Classes/TiViewshadowModule.h; sourceTree = "<group>"; }; 24DD6CF71134B3F500162E58 /* TiViewshadowModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TiViewshadowModule.h; path = Classes/TiViewshadowModule.h; sourceTree = "<group>"; };
24DD6CF81134B3F500162E58 /* TiViewshadowModule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TiViewshadowModule.m; path = Classes/TiViewshadowModule.m; sourceTree = "<group>"; }; 24DD6CF81134B3F500162E58 /* TiViewshadowModule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TiViewshadowModule.m; path = Classes/TiViewshadowModule.m; sourceTree = "<group>"; };
24DD6D1B1134B66800162E58 /* titanium.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = titanium.xcconfig; sourceTree = "<group>"; }; 24DD6D1B1134B66800162E58 /* titanium.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = titanium.xcconfig; sourceTree = "<group>"; };
Expand Down Expand Up @@ -97,6 +101,7 @@
08FB77AEFE84172EC02AAC07 /* Classes */ = { 08FB77AEFE84172EC02AAC07 /* Classes */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
0988CDA714FA0B3F00DB731F /* JRSwizzle */,
24DE9E0F11C5FE74003F90F6 /* TiViewshadowModuleAssets.h */, 24DE9E0F11C5FE74003F90F6 /* TiViewshadowModuleAssets.h */,
24DE9E1011C5FE74003F90F6 /* TiViewshadowModuleAssets.m */, 24DE9E1011C5FE74003F90F6 /* TiViewshadowModuleAssets.m */,
24DD6CF71134B3F500162E58 /* TiViewshadowModule.h */, 24DD6CF71134B3F500162E58 /* TiViewshadowModule.h */,
Expand All @@ -107,6 +112,15 @@
name = Classes; name = Classes;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
0988CDA714FA0B3F00DB731F /* JRSwizzle */ = {
isa = PBXGroup;
children = (
0988CDAD14FA3FDD00DB731F /* JRSwizzle.h */,
0988CDAE14FA3FDD00DB731F /* JRSwizzle.m */,
);
name = JRSwizzle;
sourceTree = "<group>";
};
32C88DFF0371C24200C91783 /* Other Sources */ = { 32C88DFF0371C24200C91783 /* Other Sources */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
Expand All @@ -127,6 +141,7 @@
24DD6CF91134B3F500162E58 /* TiViewshadowModule.h in Headers */, 24DD6CF91134B3F500162E58 /* TiViewshadowModule.h in Headers */,
24DE9E1111C5FE74003F90F6 /* TiViewshadowModuleAssets.h in Headers */, 24DE9E1111C5FE74003F90F6 /* TiViewshadowModuleAssets.h in Headers */,
0918FC6B14C5BDA200513CDC /* TiUIView+WithShadow.h in Headers */, 0918FC6B14C5BDA200513CDC /* TiUIView+WithShadow.h in Headers */,
0988CDAF14FA3FDD00DB731F /* JRSwizzle.h in Headers */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
Expand Down Expand Up @@ -200,6 +215,7 @@
24DD6CFA1134B3F500162E58 /* TiViewshadowModule.m in Sources */, 24DD6CFA1134B3F500162E58 /* TiViewshadowModule.m in Sources */,
24DE9E1211C5FE74003F90F6 /* TiViewshadowModuleAssets.m in Sources */, 24DE9E1211C5FE74003F90F6 /* TiViewshadowModuleAssets.m in Sources */,
0918FC6C14C5BDA200513CDC /* TiUIView+WithShadow.m in Sources */, 0918FC6C14C5BDA200513CDC /* TiUIView+WithShadow.m in Sources */,
0988CDB014FA3FDD00DB731F /* JRSwizzle.m in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
Expand Down

0 comments on commit 66ba62e

Please sign in to comment.