Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

shadowPath bounds are now in sync with those of the view

  • Loading branch information...
commit 66ba62ed179f362d2388bf54f21a0f183d5e840b 1 parent 9041a76
@omorandi authored
View
11 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
View
132 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
View
1  Classes/TiUIView+WithShadow.h
@@ -3,7 +3,6 @@
@interface TiUIView (WithShadow)
-
-(void)setShadow_:(id)args;
@end
View
66 Classes/TiUIView+WithShadow.m
@@ -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
12 example/app.js
@@ -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'
View
16 tiviewshadow.xcodeproj/project.pbxproj
@@ -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;
};
Please sign in to comment.
Something went wrong with that request. Please try again.