Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TIMOB-1555][TIMOB-13294] iOS: Shadows and clipping on Views #5367

Merged
merged 11 commits into from
Mar 3, 2014
Merged
58 changes: 58 additions & 0 deletions apidoc/Titanium/UI/View.yml
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,31 @@ description: |

For best results on ImageView set up the `backgroundLeftCap` and `backgroundTopCap` properties such that the stretchable portion is always a 1x1 box.

#### iOS Clipping Behavior

Four new view related properties became available in Titanium Mobile 3.3.0 for iOS.

* <Titanium.UI.View.viewShadowRadius>
* <Titanium.UI.View.viewShadowColor>
* <Titanium.UI.View.viewShadowOffset>
* <Titanium.UI.View.clipMode>

The first three, `viewShadowColor`, `viewShadowRadius` and `viewShadowOffset` control the shadow associated with the view.
The shadow of the view is drawn using a rounded rectangle with the arc radius set to the `borderRadius` property.

The `clipMode` property lets the user control the clipping behavior of the View.
Setting this to <Titanium.UI.iOS.CLIP_MODE_ENABLED> enforces all child views to be clipped to this views bounds.
Setting this to <Titanium.UI.iOS.CLIP_MODE_DISABLED> allows child views to be drawn outside the bounds of this view.
When set to <Titanium.UI.iOS.CLIP_MODE_DEFAULT> or when this property is not set, clipping behavior is defined by the following rules applied in order.

* If the `viewShadowColor` is defined to be a color with alpha > 0, clipping is disabled.
* If the `borderWidth` or `borderRadius` of the view is set to a value > 0, clipping is enabled.
* If the view has one or more `children` clipping is enabled.
* If none of the conditions are met, clipping is disabled.

In earlier versions of Titanium Mobile, views had clipping enabled by default.


extends: Titanium.Proxy
since: "0.9"

Expand Down Expand Up @@ -1016,6 +1041,18 @@ properties:
type: Array<Titanium.UI.View>
permission: read-only

- name: clipMode
summary: View's clipping behavior.
description: |
Setting this to <Titanium.UI.iOS.CLIP_MODE_ENABLED> enforces all child views to be clipped to this views bounds.
Setting this to <Titanium.UI.iOS.CLIP_MODE_DISABLED> allows child views to be drawn outside the bounds of this view.
When set to <Titanium.UI.iOS.CLIP_MODE_DEFAULT> or when this property is not set, clipping behavior is inferred.
See section on iOS Clipping Behavior in <Titanium.UI.View>.
type: Number
default: Undefined. Behaves as if set to <Titanium.UI.iOS.CLIP_MODE_DEFAULT>.
platforms: [iphone, ipad]
since: "3.3.0"

- name: enabled
summary: Determines if the view is enabled or disabled.
description: Set to `true` to enable or `false` to disable the view.
Expand Down Expand Up @@ -1283,6 +1320,27 @@ properties:
default: Identity matrix
exclude-platforms: [blackberry]

- name: viewShadowRadius
summary: Determines the blur radius used to create the shadow.
type: Number
default: Undefined. Behaves as if set to 3.
platforms: [iphone,ipad]
since: "3.3.0"

- name: viewShadowColor
summary: Determines the color of the shadow.
type: String
default: Undefined. Behaves as if transparent.
platforms: [iphone,ipad]
since: "3.3.0"

- name: viewShadowOffset
summary: Determines the offset for the shadow of the view.
type: Point
default: Undefined. Behaves as if set to (0,-3)
platforms: [iphone,ipad]
since: "3.3.0"

- name: visible
summary: Determines whether the view is visible.
type: Boolean
Expand Down
21 changes: 21 additions & 0 deletions apidoc/Titanium/UI/iOS/iOS.yml
Original file line number Diff line number Diff line change
Expand Up @@ -758,6 +758,27 @@ properties:
permission: read-only
since: "1.8.1"

- name: CLIP_MODE_DEFAULT
summary: |
Use with <Titanium.UI.View.clipMode> to specify clipping behavior.
type: Number
permission: read-only
since: "3.3.0"

- name: CLIP_MODE_DISABLED
summary: |
Use with <Titanium.UI.View.clipMode> to specify clipping behavior.
type: Number
permission: read-only
since: "3.3.0"

- name: CLIP_MODE_ENABLED
summary: |
Use with <Titanium.UI.View.clipMode> to specify clipping behavior.
type: Number
permission: read-only
since: "3.3.0"

- name: COLLISION_MODE_ALL
summary: |
Use with <Titanium.UI.iOS.CollisionBehavior.collisionMode> to specify collisions with both items and
Expand Down
42 changes: 37 additions & 5 deletions iphone/Classes/TiAnimation.m
Original file line number Diff line number Diff line change
Expand Up @@ -527,8 +527,17 @@ -(void)animate:(id)args
{
CABasicAnimation *boundsAnimation = nil;
CABasicAnimation *positionAnimation = nil;
bool hasGradient = ([uiview gradientLayer] != nil);
if (hasGradient) {
CALayer* gradientLayer = [uiview gradientLayer];
CALayer* bgdLayer = [uiview backgroundImageLayer];
BOOL hasGradient = (gradientLayer != nil);
BOOL hasBackgroundImage = (bgdLayer != nil);

if (hasGradient && hasBackgroundImage) {
//Avoid duplicte animations on the same layer
hasBackgroundImage = gradientLayer != bgdLayer;
}

if (hasGradient || hasBackgroundImage) {
boundsAnimation = [CABasicAnimation animationWithKeyPath:@"bounds"];
boundsAnimation.fromValue = [NSValue valueWithCGRect:[uiview bounds]];
boundsAnimation.duration = animationDuration;
Expand All @@ -540,9 +549,18 @@ -(void)animate:(id)args
positionAnimation.timingFunction = [self timingFunction];
}

BOOL hasShadow = ([uiview shadowLayer].shadowOpacity > 0);
CABasicAnimation *shadowAnimation = nil;
if (hasShadow) {
shadowAnimation = [CABasicAnimation animationWithKeyPath:@"shadowPath"];
shadowAnimation.fromValue = (id)[UIBezierPath bezierPathWithRoundedRect:[uiview bounds] cornerRadius:uiview.layer.cornerRadius].CGPath;
shadowAnimation.duration = animationDuration;
shadowAnimation.timingFunction = [self timingFunction];
}

[(TiViewProxy *)[uiview proxy] reposition];

if (hasGradient) {
if (hasGradient || hasBackgroundImage) {
boundsAnimation.toValue = [NSValue valueWithCGRect:[uiview bounds]];
positionAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake([uiview bounds].size.width / 2, [uiview bounds].size.height / 2)];
if (repeatCount > 0) {
Expand All @@ -552,9 +570,23 @@ -(void)animate:(id)args
positionAnimation.autoreverses = (reverseAnimation != nil);
positionAnimation.repeatCount = repeatCount;
}
if (hasGradient) {
[gradientLayer addAnimation:boundsAnimation forKey:@"animateBounds"];
[gradientLayer addAnimation:positionAnimation forKey:@"animatePosition"];
}
if (hasBackgroundImage) {
[bgdLayer addAnimation:boundsAnimation forKey:@"animateBounds"];
[bgdLayer addAnimation:positionAnimation forKey:@"animatePosition"];
}
}

[[uiview gradientLayer] addAnimation:boundsAnimation forKey:@"animateBounds"];
[[uiview gradientLayer] addAnimation:positionAnimation forKey:@"animatePosition"];
if (hasShadow) {
shadowAnimation.toValue = (id)[UIBezierPath bezierPathWithRoundedRect:[uiview bounds] cornerRadius:uiview.layer.cornerRadius].CGPath;
if (repeatCount > 0) {
shadowAnimation.autoreverses = (reverseAnimation != nil);
shadowAnimation.repeatCount = repeatCount;
}
[[uiview shadowLayer] addAnimation:shadowAnimation forKey:@"animateShadowPath"];
}
}
}
Expand Down
1 change: 0 additions & 1 deletion iphone/Classes/TiUILabel.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
@private
UILabel *label;
UIView* wrapperView;
CALayer* bgdLayer;
BOOL requiresLayout;
CGRect padding;
CGRect textPadding;
Expand Down
15 changes: 2 additions & 13 deletions iphone/Classes/TiUILabel.m
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ @implementation TiUILabel
-(id)init
{
if (self = [super init]) {
bgdLayer = nil;
padding = CGRectZero;
initialLabelFrame = CGRectZero;
verticalAlign = UIControlContentVerticalAlignmentFill;
Expand All @@ -33,7 +32,6 @@ -(id)init
-(void)dealloc
{
RELEASE_TO_NIL(label);
RELEASE_TO_NIL(bgdLayer);
RELEASE_TO_NIL(wrapperView);
[super dealloc];
}
Expand Down Expand Up @@ -140,7 +138,7 @@ -(void)padLabel
[label setFrame:initialLabelFrame];
}

if (bgdLayer != nil && !CGRectIsEmpty(initialLabelFrame))
if ([self backgroundImageLayer] != nil && !CGRectIsEmpty(initialLabelFrame))
{
[self updateBackgroundImageFrameWithPadding];
}
Expand Down Expand Up @@ -432,16 +430,7 @@ -(void)setMinimumFontSize_:(id)size

}

-(CALayer *)backgroundImageLayer
{
if (bgdLayer == nil)
{
bgdLayer = [[CALayer alloc]init];
bgdLayer.frame = self.layer.bounds;
[self.layer insertSublayer:bgdLayer atIndex:0];
}
return bgdLayer;
}

-(void) updateBackgroundImageFrameWithPadding
{
CGRect backgroundFrame = CGRectMake(self.bounds.origin.x - padding.origin.x,
Expand Down
13 changes: 3 additions & 10 deletions iphone/Classes/TiUIListItemProxy.m
Original file line number Diff line number Diff line change
Expand Up @@ -82,19 +82,12 @@ -(void)dealloc

- (TiUIView *)view
{
return view = (TiUIView *)_listItem.contentView;
return nil;
}

- (void)detachView
-(UIView *)parentViewForChild:(TiViewProxy *)child
{
view = nil;
[super detachView];
}

-(void)_destroy
{
view = nil;
[super _destroy];
return _listItem.contentView;
}

-(void)propertyChanged:(NSString*)key oldValue:(id)oldValue newValue:(id)newValue proxy:(TiProxy*)proxy_
Expand Down
8 changes: 7 additions & 1 deletion iphone/Classes/TiUIView.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ void ModifyScrollViewForKeyboardHeightAndContentHeightWithResponderRect(UIScroll
TiAnimation *animation;

CALayer *gradientLayer;
CALayer *bgdImageLayer;
int clipMode;

CGAffineTransform virtualParentTransform;
id transformMatrix;
Expand Down Expand Up @@ -137,6 +139,7 @@ void ModifyScrollViewForKeyboardHeightAndContentHeightWithResponderRect(UIScroll
-(void)handleListenerAddedWithEvent:(NSString *)event;
-(BOOL)proxyHasGestureListeners;
-(void)ensureGestureListeners;
-(void)updateClipping;
/**
Returns CA layer for the background image of the view.
*/
Expand All @@ -145,7 +148,10 @@ void ModifyScrollViewForKeyboardHeightAndContentHeightWithResponderRect(UIScroll
Returns CA layer for the background gradient of the view.
*/
-(CALayer *)gradientLayer;

/**
Returns CA layer for shadow component of the view.
*/
-(CALayer *)shadowLayer;
/**
Tells the view to start specified animation.
@param newAnimation The animation to start.
Expand Down