blob: a2a68fb1db25580122ef291b446bf5bcf929e252 [file] [log] [blame] [view]
### Importing
Before using Ripple, you'll need to import it:
<!--<div class="material-code-render" markdown="1">-->
#### Swift
```swift
import MaterialComponents.MaterialRipple
```
#### Objective-C
```objc
#import "MaterialRipple.h"
```
<!--</div>-->
The Ripple component exposes two interfaces that you can use to add material-like
feedback to the user:
1. `MDCRippleView` is a subclass of `UIView` that draws and animates ripples
and can be placed anywhere in your view hierarchy.
2. `MDCRippleTouchController` bundles an `MDCRippleView` instance with a
`UITapGestureRecognizer` instance to conveniently drive the ripples from the
user's touches.
3. `MDCStatefulRippleView` is a subclass of `MDCRippleView` that provides support for states. This allows to set the ripple in a state and have the ripple visually represent that state as part of the Material guidelines.
### MDCRippleTouchController
The simplest method of using ripple in your views is to use a
`MDCRippleTouchController`:
Initialize using the default initializer:
<!--<div class="material-code-render" markdown="1">-->
#### Swift
```swift
let myButton = UIButton(type: .system)
myButton.setTitle("Tap Me", for: .normal)
let rippleTouchController = MDCRippleTouchController()
rippleTouchController.addRipple(to: myButton)
```
#### Objective-C
```objc
UIButton *myButton = [UIButton buttonWithType:UIButtonTypeSystem];
[myButton setTitle:@"Tap me" forState:UIControlStateNormal];
MDCRippleTouchController *rippleTouchController = [[MDCRippleTouchController alloc] init];
[rippleTouchController addRippleToView:myButton];
```
<!--</div>-->
Initialize using the `initWithView:` convenience initializer:
<!--<div class="material-code-render" markdown="1">-->
#### Swift
```swift
let myButton = UIButton(type: .system)
myButton.setTitle("Tap Me", for: .normal)
let rippleTouchController = MDCRippleTouchController(view: myButton)
```
#### Objective-C
```objc
UIButton *myButton = [UIButton buttonWithType:UIButtonTypeSystem];
[myButton setTitle:@"Tap me" forState:UIControlStateNormal];
MDCRippleTouchController *rippleTouchController = [[MDCRippleTouchController alloc] initWithView:myButton];
```
<!--</div>-->
The `MDCRippleTouchControllerDelegate` gives you some control over aspects of the
ripple/touch relationship and its placement in the view hierarchy.
In the example below we are using the delegate to declare that we only want to process ripple
touches if the touch is in a certain location. We also insert the Ripple view at the bottom of
the parent view's view hierarchy. The reason we insert the ripple view at the bottom of the parent view's
hierarchy in this example, is so the ripple's overlay color would not affect the visibility and contrast
of the view's subviews, which may be images conveying a message or text.
<!--<div class="material-code-render" markdown="1">-->
#### Swift
```swift
class MyDelegate: NSObject, MDCRippleTouchControllerDelegate {
func rippleTouchController(_ rippleTouchController: MDCRippleTouchController, shouldProcessRippleTouchesAtTouchLocation location: CGPoint) -> Bool {
// Determine if we want to display the ripple
return exampleView.frame.contains(location)
}
func rippleTouchController(_ rippleTouchController: MDCRippleTouchController,
insert rippleView: MDCRippleView,
into view: UIView) {
view.insertSubview(rippleView, at: 0)
}
func rippleTouchController(_ rippleTouchController: MDCRippleTouchController,
didProcessRippleView rippleView: MDCRippleView,
atTouchLocation location: CGPoint) {
print("Did process ripple view!")
}
}
...
let myButton = UIButton(type: .system)
myButton.setTitle("Tap Me", for: .normal)
let myDelegate = MyDelegate()
let rippleTouchController = MDCRippleTouchController(view: myButton)
rippleTouchController.delegate = myDelegate
```
#### Objective-C
```objc
@interface MyDelegate: NSObject <MDCRippleTouchControllerDelegate>
@end
@implementation MyDelegate
- (BOOL)rippleTouchController:(MDCRippleTouchController *)rippleTouchController
shouldProcessRippleTouchesAtTouchLocation:(CGPoint)location {
return CGRectContainsPoint(exampleView.frame, location);
}
- (void)rippleTouchController:(MDCRippleTouchController *)rippleTouchController
didProcessRippleView:(MDCRippleView *)rippleView
atTouchLocation:(CGPoint)location {
NSLog(@"Did process ripple view!");
}
- (void)rippleTouchController:(MDCRippleTouchController *)rippleTouchController insertRippleView:(MDCRippleView *)rippleView intoView:(UIView *)view {
[view insertSubview:rippleView atIndex:0];
}
@end
...
UIButton *myButton = [UIButton buttonWithType:UIButtonTypeSystem];
[myButton setTitle:@"Tap me" forState:UIControlStateNormal];
MyDelegate *myDelegate = [[MyDelegate alloc] init];
MDCRippleTouchController *rippleTouchController = [[MDCRippleTouchController alloc] initWithView:myButton];
rippleTouchController.delegate = myDelegate;
```
<!--</div>-->
**NOTE:** The ripple touch controller does not keep a strong reference to the view to which it is attaching the ripple view.
An easy way to prevent the ripple touch controller from being deallocated prematurely is to make it a property of a view controller (like in these examples.)
### MDCRippleView
Alternatively, you can use MDCRippleView directly to display ripples using your
own touch processing:
<!--<div class="material-code-render" markdown="1">-->
#### Swift
```swift
let myCustomView = MyCustomView(frame: .zero)
let rippleView = MDCRippleView()
rippleView.rippleColor = .red
myCustomView.addSubview(rippleView)
...
// When the touches begin, there is one animation
rippleView.beginRippleTouchDownAtPoint(at: touchPoint, animated: true, completion: nil)
...
// When the touches end, there is another animation
rippleView.beginRippleTouchUpAnimated(animated: true, completion: nil)
```
#### Objective-C
```objc
MyCustomView *myCustomView = [[MyCustomView alloc] initWithFrame:CGRectZero];
MDCRippleView *rippleView = [[MDCRippleView alloc] init];
rippleView.rippleColor = UIColor.redColor;
[myCustomView addSubview:rippleView];
...
// When the touches begin, there is one animation
[rippleView beginRippleTouchDownAtPoint:touchPoint animated:YES completion:nil];
...
// When the touches end, there is another animation
[rippleView beginRippleTouchUpAnimated:YES completion:nil];
```
<!--</div>-->
### MDCStatefulRippleView
You can also use MDCStatefulRippleView to display stateful ripples using your
own touch processing.
To fully benefit from MDCStatefulRipple's ability to move between states visually,
the view that is adding the stateful ripple view must override
UIView's `touchesBegan`, `touchesMoved`, `touchesEnded` and `touchesCancelled`
and call the stateful ripple view's corresponding APIs before calling the `super` implementation.
Here is an example:
<!--<div class="material-code-render" markdown="1">-->
#### Swift
```swift
let myCustomView = MyCustomView(frame: .zero)
let statefulRippleView = MDCStatefulRippleView()
statefulRippleView.setRippleColor(.blue, for: .selected)
myCustomView.addSubview(statefulRippleView)
...
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
statefulRippleView.touchesBegan(touches, with: event)
super.touchesBegan(touches, with: event)
statefulRippleView.isRippleHighlighted = true
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
statefulRippleView.touchesMoved(touches, with: event)
super.touchesMoved(touches, with: event)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
statefulRippleView.touchesEnded(touches, with: event)
super.touchesEnded(touches, with: event)
statefulRippleView.isRippleHighlighted = false
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
statefulRippleView.touchesCancelled(touches, with: event)
super.touchesCancelled(touches, with: event)
statefulRippleView.isRippleHighlighted = false
}
```
#### Objective-C
```objc
MyCustomView *myCustomView = [[MyCustomView alloc] initWithFrame:CGRectZero];
MDCStatefulRippleView *statefulRippleView = [[MDCStatefulRippleView alloc] init];
[statefulRippleView setRippleColor:UIColor.blueColor forState:MDCRippleStateSelected];
[myCustomView addSubview:statefulRippleView];
...
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[statefulRippleView touchesBegan:touches withEvent:event];
[super touchesBegan:touches withEvent:event];
statefulRippleView.rippleHighlighted = YES;
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
[statefulRippleView touchesMoved:touches withEvent:event];
[super touchesMoved:touches withEvent:event];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[statefulRippleView touchesEnded:touches withEvent:event];
[super touchesEnded:touches withEvent:event];
statefulRippleView.rippleHighlighted = NO;
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
[statefulRippleView touchesCancelled:touches withEvent:event];
[super touchesCancelled:touches withEvent:event];
statefulRippleView.rippleHighlighted = NO;
}
```
<!--</div>-->