Permalink
Browse files

shadowPath bounds are now in sync with those of the view

  • Loading branch information...
1 parent 9041a76 commit 66ba62ed179f362d2388bf54f21a0f183d5e840b @omorandi committed Feb 26, 2012
@@ -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
@@ -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
@@ -3,7 +3,6 @@
@interface TiUIView (WithShadow)
-
-(void)setShadow_:(id)args;
@end
@@ -1,18 +1,69 @@
#import "TiUIView+WithShadow.h"
+#import "TiProxy.h"
+#import "JRSwizzle.h"
+
+
+@interface TiUIView ()
+-(void)checkBounds;
+@end
@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:
//http://developer.appcelerator.com/question/130784/trick-drop-real-shadows-in-titanium-ios
+
-(void)setShadow_:(id)args
{
if(args != nil)
{
self.layer.masksToBounds = NO;
+ [self.layer setShouldRasterize:YES];
if ([args objectForKey:@"shadowOffset"] != nil) {
CGPoint p = [TiUtils pointValue: [args objectForKey:@"shadowOffset"]];
@@ -31,21 +82,6 @@ -(void)setShadow_:(id)args
UIColor * shadowColor = [[TiUtils colorValue:[args objectForKey:@"shadowColor"]] _color];
[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"] ]];
- }
-
}
}
View
@@ -1,5 +1,3 @@
-require('ti.viewshadow');
-
var win = Ti.UI.createWindow({backgroundColor:'#fff'});
var view = Ti.UI.createView({
@@ -10,16 +8,14 @@ var view = Ti.UI.createView({
borderRadius:20,
backgroundColor:"green",
shadow:{
- shadowRadius:10,
+ shadowRadius:2,
shadowOpacity:0.5,
- shadowOffset:{x:10, y:10},
- 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
+ shadowOffset:{x:4, y:4},
}
})
var btn = Ti.UI.createButton({
- title:'Shadow from module',
+ title:'Button with shadow',
height:60,
width:200,
left:60,
@@ -47,7 +43,7 @@ var lbl = Ti.UI.createLabel({
});
lbl.setShadow({
- shadowRadius:5,
+ shadowRadius:3,
shadowOpacity:1,
shadowOffset:{x:0, y:0},
shadowColor:'#00f'
@@ -24,6 +24,8 @@
/* Begin PBXBuildFile section */
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 */; };
+ 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 */; };
24DD6CFA1134B3F500162E58 /* TiViewshadowModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 24DD6CF81134B3F500162E58 /* TiViewshadowModule.m */; };
24DE9E1111C5FE74003F90F6 /* TiViewshadowModuleAssets.h in Headers */ = {isa = PBXBuildFile; fileRef = 24DE9E0F11C5FE74003F90F6 /* TiViewshadowModuleAssets.h */; };
@@ -45,6 +47,8 @@
/* 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>"; };
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>"; };
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>"; };
@@ -97,6 +101,7 @@
08FB77AEFE84172EC02AAC07 /* Classes */ = {
isa = PBXGroup;
children = (
+ 0988CDA714FA0B3F00DB731F /* JRSwizzle */,
24DE9E0F11C5FE74003F90F6 /* TiViewshadowModuleAssets.h */,
24DE9E1011C5FE74003F90F6 /* TiViewshadowModuleAssets.m */,
24DD6CF71134B3F500162E58 /* TiViewshadowModule.h */,
@@ -107,6 +112,15 @@
name = Classes;
sourceTree = "<group>";
};
+ 0988CDA714FA0B3F00DB731F /* JRSwizzle */ = {
+ isa = PBXGroup;
+ children = (
+ 0988CDAD14FA3FDD00DB731F /* JRSwizzle.h */,
+ 0988CDAE14FA3FDD00DB731F /* JRSwizzle.m */,
+ );
+ name = JRSwizzle;
+ sourceTree = "<group>";
+ };
32C88DFF0371C24200C91783 /* Other Sources */ = {
isa = PBXGroup;
children = (
@@ -127,6 +141,7 @@
24DD6CF91134B3F500162E58 /* TiViewshadowModule.h in Headers */,
24DE9E1111C5FE74003F90F6 /* TiViewshadowModuleAssets.h in Headers */,
0918FC6B14C5BDA200513CDC /* TiUIView+WithShadow.h in Headers */,
+ 0988CDAF14FA3FDD00DB731F /* JRSwizzle.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -200,6 +215,7 @@
24DD6CFA1134B3F500162E58 /* TiViewshadowModule.m in Sources */,
24DE9E1211C5FE74003F90F6 /* TiViewshadowModuleAssets.m in Sources */,
0918FC6C14C5BDA200513CDC /* TiUIView+WithShadow.m in Sources */,
+ 0988CDB014FA3FDD00DB731F /* JRSwizzle.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

0 comments on commit 66ba62e

Please sign in to comment.