The Gourmet iOS Developer's Cookbook: Even More Recipes for Better iOS App Development by Erica Sadun

The Gourmet iOS Developer's Cookbook: Even More Recipes for Better iOS App Development by Erica Sadun

Author:Erica Sadun [Sadun, Erica]
Language: eng
Format: epub
Publisher: UNKNOWN
Published: 2017-04-06T07:00:00+00:00


Implementing Snap Zones

One of my favorite dynamic animator tricks involves creating snap zones—areas of your interface that pull in dragged items once they overlap a particular region. This approach allows you to collect items into well

managed zones and offer a pleasing “snap-into-place” animation. In the general form shown in Listing 6-3, there’s no further test beyond whether a dragged view has strayed into a zone. However, you might want to expand the approach to limit blue items to blue zones or red items to red zones, and so forth.

Listing 6-3 assumes that users will have access to multiple zones and even that a view might move from one zone directly to another. It uses a tagging scheme to keep track of this potential reparenting. A free view has no current parent and can move freely about. When a free view overlaps a snap zone,

however, it suspends

dragging by disabling the view’s gesture recognizer and adds a snap-to-parent

behavior. The view slides into place into its new parent. Once it arrives, as the

dynamic animator pauses, the recognizer is re-enabled.

Allowing a view to escape from its new parent’s bounds is the tricky bit—and the motivating reason for the view tagging. You do not want a view to recapture its child unless the dragging gesture has ended, which is why this method keeps track of the gesture state. With new parents, however, the snap behavior is added (and the gesture is suspended) as soon as a view strays over the line. Balancing the escapes and the captures ensures that the user experience is snappy and responsive and does not thwart the user’s desires to remove a view from a parent.

Listing 6-3 Handling Multiple Snap Zones

Click here to view code image - (void)

draggableViewDidMove: (NSNotification *) note

{

// Check for view

participation

UIView

*draggedView =

note.object;

UIView *nca =

[draggedView

nearestCommonAncestorWithView: _animator.referenceView]; if (!nca) return; UIGestureRecognizerState state = [recognizer state];

// Retrieve state UIGestureRecognizer *recognizer =

(UIGestureRecognizer *)

draggedView.gestureRecognizers.lastObject;

// View frame and current attachment CGRect

draggedFrame =

draggedView.frame; BOOL free =

draggedView.tag == 0; // Make sure all drop zones are views

if (!

[dropZone

isKindOfClass:[UIView class]])

continue; dropFrame);

for (UIView *dropZone in

_dropZones)

{

// Overlap? CGRect

dropFrame =

dropZone.frame;

BOOL overlap =

CGRectIntersectsRect(draggedFrame,

// Free

moving

if (!overlap && free)

{

continue; }

(suspendedRecognizer) {

NSLog(@"Error: attempting to suspend second recognizer"); break; }

suspendedRecognizer.enabled = NO; // stop!

draggedView.tag = CAPTURED

dropZone.tag; // mark as captured

UISnapBehavior *behavior =

[[UISnapBehavior

alloc]

initWithItem:draggedView snapToPoint:RectGetCenter(dropFrame)]; [_animator addBehavior:behavior]; break; }

// Is this the current parent drop zone?

BOOL isParent = (dropZone.tag CAPTURED ==

draggedView.tag); {

case UIGestureRecognizerStateEnded: {

// Recapture

UISnapBehavior *behavior =

[[UISnapBehavior

alloc]

initWithItem:draggedView snapToPoint:RectGetCenter(dropFrame)]; [_animator addBehavior:behavior]; break; }

default: {

// Still captured but no op

break; }

}

break; }

suspendedRecognizer.enabled = NO; // stop!

draggedView.tag = CAPTURED

dropZone.tag;

UISnapBehavior *behavior =

[[UISnapBehavior

alloc]

initWithItem:draggedView snapToPoint:RectGetCenter(dropFrame)]; [_animator addBehavior:behavior]; break; }

}

}

// Newly captured

if (overlap && free)

{

if

// New parent.

//

CAPTURED is an

integer offset for tagging

suspendedRecognizer = recognizer;

// Current parent

if (overlap && isParent)

{

switch (state)

// New parent if (overlap) {

suspendedRecognizer = recognizer;



Download



Copyright Disclaimer:
This site does not store any files on its server. We only index and link to content provided by other sites. Please contact the content providers to delete copyright contents if any and email us, we'll remove relevant links or contents immediately.