How to implement custom UIDynamicBehavior action

2019-05-05 02:17发布

问题:

I have been looking for an example showing how to implement a custom UIDynamicBehavior in UIKit dynamics. All the tutorials and examples show only how to assemble a UIDynamicBehavior using primitives (collision, gravity, attachment, push, snap etc.)

In my app, some views are floating around the screen (using dynamics) and I want to make them disappear when they overlap other stationary views. To do this, I wanted to test for overlap in the UIDynamicAnimator and UICollisionBehavior delegate methods, but unfortunately those methods do not provide enough granularity to get perform the tests I need.

EDIT: apparently I had to wait a day before answering my own question (new user), so my solution is posted below as an answer now.

回答1:

The approach I chose was to develop my own UIDynamicBehavior class and add that to the animator, and it now makes the floating views disappear when they overlap the stationary views.

Sample code below shows how to write your own UIDynamicBehavior class to plug your own behaviour into the UIDynamicAnimator. I called the class UISinkBehavior, because it "sinks" a view when the view moves over the "sinkhole".

// UISinkBehavior.h
#import <UIKit/UIKit.h>

@protocol UISinkBehaviorDelegate <NSObject>
- (void)sunk:(id)item;
@end

@interface UISinkBehavior : UIDynamicBehavior
@property (weak, nonatomic) id<UISinkBehaviorDelegate> delegate;
- (id)initWithItems:(NSMutableArray*)items withSinkhole:(UIView*)sinkhole;
@end

// UISinkBehavior.m
#import "UISinkBehavior.h"

@interface UISinkBehavior ()
@property (nonatomic) NSMutableArray *items;
@property (nonatomic) id<UIDynamicItem> sinkhole;
@end

@implementation UISinkBehavior

- (id)initWithItems:(NSMutableArray*)items withSinkhole:(UIView*)sinkhole
{
    if (self = [super init])
    {
        _items = items;
        _sinkhole = sinkhole;
        // weak self ref to avoids compiler warning about retain cycles
        __weak typeof(self) ref = self;
        // this is called by the UIDynamicAnimator on every tick
        self.action = ^{
            UIView *item;
            // check each item if it overlaps sinkhole
            for (item in ref.items)
                if (CGRectIntersectsRect(item.frame, sinkhole.frame))
                {
                    // sink it (handled by delegate
                    [ref.delegate sunk:item];
                    // remove item from animation
                    [ref.items removeObject:item];
                    // remove behaviour from animator when last item sunk
                    if (ref.items.count < 1)
                        [ref.dynamicAnimator removeBehavior:ref];
                }
        };
    }    
    return self;
}
@end