2014년 4월 30일 수요일

[iOS7] UIViewController의 Adjust Scroll View Insets는?

 ScrollView를 전체 화면에 넣고, 이미지를 중앙에 보여주게 하였는데, 아래로 내리면, ScrollView의 위치가, Top Bar밑으로 자동으로 조정이 되어서, 이 기능을 빼는 것을 블로그로 정리합니다.

iOS7에서, NavigationController의 Top Bar가 있을 경우, TableView등의 ScrollView에서는 자동으로 내부 Content영역이 Top Bar밑으로 내려오게 됩니다.

TableView의 경우에는 아래로 내리면 첫번째 Row가 Top Bar 밑으로 표시 되고, 그 밑으로 들어가면 화면에서 가리게 됩니다.
그리고, 위로 올리면, Top Bar뒷면으로 Cell들이 올라가게 됩니다.

어떤경우에 문제가 될까요?
TableView의 경우에는 아주 좋은 기능으로, 첫번째 Row가 잘 표시가 됩니다만, ScrollView의 경우에는 조금 달라집니다.
Top bar밑으로 나왔다가 안 나왔다가 하면 문제가 됩니다.

이 기능을 컨트롤 할 수 있는 방법은
Storyboard의 ViewController에서 Adjust Scroll View Insets를 체크하거나,

automaticallyAdjustsScrollViewInsets를 NO 로 설정하여 기능을 뺄 수 있습니다.
이 기능을 빼면, 자동으로 contentInsets.top 이 설정되지 않으므로 가장 위까지 content영역으로 됩니다.

TableViewController에서 automaticallyAdjustsScrollViewInsets를 NO로 설정하게 되면, row 0, 1은 top bar의 뒷면에 위치하게 되어서, 아래로 내리지 않는 이상을 볼 수도 없고, 선택할 수도 없게 됩니다.

이 기능은 iOS7에서만 지원하는 것이므로, iOS6에서 기능 설정을 무시하게 하려면, 함수가 지원하는지 respondsToSelector를 통해서 확인하고 설정하면 되겠습니다.
if ([self respondsToSelector:@selector(setAutomaticallyAdjustsScrollViewInsets:)]) {
    self.automaticallyAdjustsScrollViewInsets = NO;
}



[참고]
- iOS 7 UI Transition Guide : AppearanceCustomization

[iOS] Unwind Segue 사용하기.

Segue를 이용해서, Unwind Segue를 사용하는 방법을 간단히 정리합니다.

iOS에서 Storyboard를 이용하여, ViewController 1(이하 VC1)에서 다른 ViewController 2 (이하 VC2)로 이동을 하게 되고, 뒤로는 Back 버튼을 이용해서, 이동하게 됩니다.
어떤 에러로 인해서, 반대로 즉 뒤로 이동해야 할 경우도 있습니다.

이런 경우, Delegate를 사용해서, VC2의 델리게이트로 설정한 VC1의 함수를 호출하여, dismissViewControllerAnimated:complation을 이용해서 화면에 표시된 VC2를 제거하여 Back을 하였습니다.

화면이동으로 Segue를 이용하듯이, Unwind Segue를 이용해서, 화면 Back으로 이동할 수 있습니다.
 그럼, segue와 unwind segue의 차이점은 뭘까요?
 차이점은 Segue는 VC1에서 VC2로 이동하면, 신규로 VC2를 생성하고 NavigationViewController의 presentaion chain에 쌓아 가면서 이동하지만, unwind segue는  기존에 화면에서 표시되었던 VC로만 이동할 수 있어서, 신규로 VC를 만들지 않습니다.
즉, A-B-C-D의 형태로 뷰컨트롤러를 화면에 표시했다면, D에서 A로 Unwind segue로 이동하면서, 중간 VC들을 삭제하게 되는 것입니다.

이용방법

1. back으로 이동할 ViewController1에 함수를 설정합니다.

source code
- (IBAction) exitFromSecondViewController:(UIStoryboardSegue *)segue
{
    //Back으로 올때 호출되는 함수
    // segue를 통해서, 어느 뷰컨트롤러에서 오는 것인지 구분할 수 있다.
    NSLog(@"back from : %@", [segue.sourceViewController class]);
}

이름은 무엇이든 상관없고, 변수로 UIStoryboardSegue를 받아야 하고, 전 프로젝트에 걸쳐, 구분이 되도록 해야 합니다.
 주의 할 것은 VC 1에 위 함수를 추가해야 됩니다. 즉, VC 1으로 이동하고 난 후에 위 함수가 호출이 되는 것입니다.

2. VC2에서 unwind segue를 설정합니다.

Storyboard에서 VC2에서 아래와 같이 VC아이콘에서 exit 아이콘으로 Ctrl+드래그를 선택합니다.
Ctrl+드래그를 합니다.

VC1에 추가하였던 함수가 표시되고, 그것을 선택합니다.
위와 같이 unwind segue를 추가하고, identification을 "UnwindingSegueID"로 설정합니다.

3. Unwind Segue를 호출 합니다.

source code
- (IBAction)backButton:(id)sender //Back버튼은 VC2 화면에 추가된 버튼입니다.
{
    [self performSegueWithIdentifier:@"UnwindingSegueID" sender:self];
}

[추가] 만약, 여러 VC2에서 VC3를 호출하였고, VC3에서 VC1으로 이동하면 어떻게 될까요?

아래와 같이 설정을 하고 테스트를 하면,

VC1 - VC2 - VC3까지 Segue를 통해서 호출을 하면, presentation chain에 순서대로 쌓이게 됩니다.
VC3에서 VC1으로 중간에 VC2를 거치지 않고, unwind segue를 통해서 이동을 하면, VC1으로 이동하고, VC2와 VC3를 차례로 dealloc하게 됩니다.
log
 

[2339:60b] VC1 performSegueWithIdentifier called : GoSegueID
               : VC1에서 segue를 코드로 호출해서 VC2로 이동하도록 호출합니다.
[2339:60b] VC1 prepareForSegue id:GoSegueID to : DBUSecondViewController
               : VC1의 prepareForSegue함수가 호출이됩니다.

[2339:60b] VC2 prepareForSegue id:GoThirdSegueID to : DBUThirdViewController
               : VC2에서 버튼으로 바로 segue 를 호출합니다.

[2339:60b] VC3 performSegueWithIdentifier called : backToOneSegueID
               : VC3에서 unwind segue를 호출합니다.
[2339:60b] VC3 PrepareForSegue id: backToOneSegueID, to DBUViewController
               : prepareForSegue로 segue가 호출 되었음을 알려줍니다.
[2339:60b] VC1 exitFunction called in VC1 from : DBUThirdViewController
               : VC1으로 이동되고, 어디서 왔는지 sourceController를 통해서 알 수 있습니다.
[2339:60b] dealloc : <dbusecondviewcontroller: 0x8e0d670="">
               : VC2가 삭제되고..
[2339:60b] dealloc : <dbuthirdviewcontroller: 0x9947ee0="">
               : VC3가 삭제됩니다.

[참고]

Apple Tech Notes 2298
Using iOS Storyboard Segues

- Segue관련 함수는? (블로그 : [iOS] Segue 관련 함수]



2014년 4월 18일 금요일

[iOS] ZipArchive를 사용해서, Zip파일을 Unzip합니다.

iOS기기에서 zip으로 압축된 데이터를 다운 받고, 그것을 풀어야(Unzip)할 경우가 있습니다.
이 경우, ZipArchive를 이용해서 해결할 수 있습니다.

[ZipArchive는?]

오픈소스 코드인 MinZip을 기반으로 zip파일을 압축/해지를 iOS에서 사용할 수 있도록, Objective-C Class로 만들어 놓은 것입니다.

[어디서 구할 수 있나요?]

구글 코드사이트에서 나와 있습니다. (https://code.google.com/p/ziparchive/)
설명도 위 사이트에 자세히 나와 있습니다.


[사용하기 위해서]

1. 라이브러리를 등록합니다.
 프로젝트 설정에서 Framwork 등록하는 부분에서 아래와 같이 libz.dylib를 추가합니다.


2. ZipArchive 파일을 프로젝트에 추가합니다.
 위 사이트에서 download 받아서, 프로젝트에 추가합니다.


[ 압축파일을 풀려면 어떻게 해야 되나요?]

ZipArchive의 인스턴스를 사용해서, 파일을 열고, Unzip 하고, 파일을 닫으면 됩니다.
압축을 푸는 것이 리소스를 많이 잡아먹으니, 파일을 이용하는 것 같습니다.
source code
- (void)UnzipFile:(NSString*)zipFile to:(NSString *)destFile
{
    ZipArchive *zip = [[ZipArchive alloc]init];
    if ([zip unzipOpenFile:zipFile]) {
       BOOL ret = [zip unzipFileTo:destFile overwrite:YES];
       NSAssert(ret, @"unzipfile Error");
       if (![zip unzipCloseFile]) {
           NSLog(@"unzipCloseFile Failed");
       }
    }
}

감사합니다.







2014년 4월 14일 월요일

[iOS] Segue 관련 함수

Storyboard의 Segue관련해서 몇가지 함수를 정리해 둡니다.

[Segue 선언]

- Xcode의 Storyboard에서 Ctrl+Drag를 통해서, 어디에서 어디로 이동할 것인지 Segue를 정의하고, 그 Identifier를 설정해 둡니다.

[Segue 실행]

- Storyboard에서 버튼이나 테이블뷰셀에서 호출되도록 할 수도 있고, ViewController에서 identifier로 호출할 수 있도록 설정할 수 있습니다.
- 버튼에서 Ctrl+Drag로 ViewController로 이동하면, 버튼의 Triggere Segues를 설정할 수 있습니다. 이렇게 되면, 그 버튼이 터치되면, 설정한 Segue를 실행하게 됩니다.
- ViewController에서 Ctrl+Drag를 해서 다른 ViewController로 이동해서 설정하고 id를 설정해 두면, id를 가지고 segue를 호출 할 수 있게 됩니다.
  실행함수는 performSegueWithIdentifier:sender: 함수로 id를 가지고 호출 할 수 있게 됩ㄴ디ㅏ.

[Segue 호출]

Segue가 호출되어 시작될 때, 시작됨을 알리는  -prepareForSegue:sender: 함수가 호출이되고, 이 함수에서 이동할 destinationViewController와 sourceViewController를 알 수 있고, 호출 ID를 통해서, 어떤 Segue인지를 알아서 구분할 수 있게 됩니다.
여기에서 ID에 따라서, destinationViewController에 설정을 추가해서 데이터를 전달 할 수 있게 됩니다.
- destinationViewController가 예상하는 특정 ViewController가 맞는지 확인을 위해서는 NSObject에 정의되어 있는 isKindOfClass: 함수를 이용하여 확인하는 것이 필요합니다.


[참고]
- Unwind Segue는? (블로그 : [iOS] Unwind Segue 사용하기)

2014년 4월 8일 화요일

[iOS7] Dynamic : Attachment, Push, DynamicItem Behavior

Dynamics에 대한 기본적인 구성과 사용에 대해서는 이전 블로그에서 정리를 하였고, 이번은 Dynamics를 가지고, UI를 좀더 사용자 친화적으로 꾸밀 수 있을 지 알아보겠습니다.

모든 UIKit에 물리적 성질을 추가 할 수 있는 것이 iOS7의 Dynamics 입니다.
그것을 이용해서, 초기 화면의 UILabel 들에 Dynamics를 적용해 보도록 하겠습니다.
이전에는 UIView Animation을 이용해서 하던 것을 Dynamics를 활용해서 만드는 것입니다.

버튼 밑에 설명 Label을 붙이고, 이 Label들이 모션에 의해서 흔들리도록 해보겠습니다.
아래에 사용된 이미지는 구글 이미지에서 찾은 것들로, 저작권은 전혀 저에게 있지 않습니다.( 원본은 아래에 링크를 넣었습니다.)

위 이미지에서 Travel 라벨이 그 위의 아이콘의 center와 UIAttachmentBehavior로 연결을 하고, CoreMotion으로 X,Y 값의 변화에 따라서 좌/우로 흔들리도록 UIPushBehavior를 이용했습니다.

각 컨트롤에 대해서, Property 연결 합니다.
source code
@property (strong, nonatomic) IBOutlet UIImageView *travelImageView;
@property (strong, nonatomic) IBOutlet UIImageView *bookImageView;
@property (strong, nonatomic) IBOutlet UIImageView *workoutImageView;
@property (strong, nonatomic) IBOutlet UIImageView *foodImageView;

@property (strong, nonatomic) IBOutlet UILabel *travelLogLabel;
@property (strong, nonatomic) IBOutlet UILabel *bookLogLabel;
@property (strong, nonatomic) IBOutlet UILabel *foodLogLabel;
@property (strong, nonatomic) IBOutlet UILabel *workoutLogLabel;


@property (strong, nonatomic) UIDynamicAnimator *animator;
@property (strong, nonatomic) UIGravityBehavior *gravityBehavior;
@property (strong, nonatomic) UICollisionBehavior *collisionBehavior;
@property (strong, nonatomic) UIPushBehavior *pushBehavior;

그리고, UIDynamicsAnimator, UIGravityBehavior 로 설정합니다.
- (void) setupDynamics
{
    self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
    self.gravityBehavior = [[UIGravityBehavior alloc] initWithItems:@[self.travelLogLabel, self.bookLogLabel, self.foodLogLabel, self.workoutLogLabel]];    
    [self.animator addBehavior:self.gravityBehavior];
    ...
}

각 UILabel에 대해서 UIAttachmentBehavior를 연결하고, Damping과 Frequency를 설정합니다.

#pragma mark - Dynamics Setting
- (void) setupDynamics
{
    ...
    
    //Label들에 대해서, 마찰저항을 설정하고, 회전하지 않도록 설정
    UIDynamicItemBehavior *labelItemBehavior = [[UIDynamicItemBehavior alloc] initWithItems:@[self.travelLogLabel, self.bookLogLabel, self.foodLogLabel, self.workoutLogLabel]];
    labelItemBehavior.resistance = 1.0f;
    labelItemBehavior.allowsRotation = NO;
    [self.animator addBehavior:labelItemBehavior];

    // Travel Label에 대해서 AttchmentBehavior를 연결함.
    [self addAttachmentBehavior:self.travelLogLabel
                        atPoint:CGPointMake(self.travelImageView.center.x, self.travelImageView.center.y)
                     toAnimator:self.animator];
    
    // Book Label에 대해서 AttachmentBehavior를 연결
    [self addAttachmentBehavior:self.bookLogLabel
                        atPoint:CGPointMake(self.bookImageView.center.x, self.bookImageView.center.y )
                     toAnimator:self.animator];
    
    // Food Label에 대해서 AttachmentBehavior 연결
    [self addAttachmentBehavior:self.foodLogLabel
                        atPoint:CGPointMake(self.foodImageView.center.x, self.foodImageView.center.y )
                     toAnimator:self.animator];
    
    // Workout Label에 대해서 AttachmentBehavior 연결
    [self addAttachmentBehavior:self.workoutLogLabel
                        atPoint:CGPointMake(self.workoutImageView.center.x, self.workoutImageView.center.y )
                     toAnimator:self.animator];
    
    ....
}
- (void) addAttachmentBehavior:(UILabel *)label atPoint:(CGPoint)point toAnimator:(UIDynamicAnimator *)animator
{
    UIAttachmentBehavior *attachment = [[UIAttachmentBehavior alloc] initWithItem:label attachedToAnchor:point];
    attachment.damping = 1.0f;
    attachment.frequency = 100.0f;
    [animator addBehavior:attachment];
}

이 두 값을 설정하더라도 적절하게 멈추는 것이 아니라, 자주 흔들리게 됩니다.
아래의 왼쪽이 Attachment만 적용한 것이고, 오른쪽이 UIDynamicItemBehavior로 마찰저항을 추가한 것입니다.

 


이제, CoreMotion을 추가해서, 기기가 좌우로 흔들릴 때 마다, PushBehavior를 추가하여, Label이 흔들리도록 하겠습니다.

먼저 CoreMotion을 추가합니다.

@import CoreMotion;
...
- (void)viewDidLoad
{
    [super viewDidLoad];
	// Do any additional setup after loading the view, typically from a nib.
    
    _motionManager = [[CMMotionManager alloc] init];
    [self startMonitoringAcceleration];
}
...

#pragma mark - Dynamics Setting
- (void) setupDynamics
{
    ...
    
    self.pushBehavior = [[UIPushBehavior alloc] initWithItems:@[self.travelLogLabel, self.bookLogLabel, self.foodLogLabel, self.workoutLogLabel] mode:UIPushBehaviorModeInstantaneous]; //Instantaneous로 설정
    self.pushBehavior.active = NO;
    float angle = arc4random() % 360;
    self.pushBehavior.angle = (angle*M_PI/180.0);
    self.pushBehavior.magnitude = 0.4f;
    [self.animator addBehavior:self.pushBehavior];
}
#pragma mark - CoreMotion
- (void)startMonitoringAcceleration
{
    if (_motionManager.accelerometerAvailable) {
        //[_motionManager startAccelerometerUpdates];
        NSLog(@"accelerometer updates on...");
        [_motionManager setAccelerometerUpdateInterval:0.1f];
        [_motionManager startAccelerometerUpdatesToQueue:[NSOperationQueue mainQueue]
                                             withHandler:^(CMAccelerometerData *accelerometerData, NSError *error) {
                                                 float angle = atan2f(-accelerometerData.acceleration.y, accelerometerData.acceleration.x);
                                                 //NSLog(@"accelerometer:angle:%2.02f, x:%.02f, y:%.02f, z:%.02f", 180.0+angle*180.0/M_PI, accelerometerData.acceleration.x, accelerometerData.acceleration.y, accelerometerData.acceleration.z);
                                                 
                                                 NSLog(@"angle: %0.2f, %02.2f", angle, angle / M_PI * 180.0);
                                                 if (angle > 100*M_PI/180.0) {
                                                     angle = 100*M_PI/180.0;
                                                 }
                                                 if(angle < 80*M_PI/180.0){
                                                     angle = 80*M_PI/180.0;
                                                 }
                                                 self.gravityBehavior.angle = angle;// - 180.0 * M_PI / 180.0;
                                             }
         ];
    }else{
        NSLog(@"accelerometer Unavailable");
    }
}


- (void)stopMonitoringAcceleration
{
    if (_motionManager.accelerometerAvailable && _motionManager.accelerometerActive) {
        [_motionManager stopAccelerometerUpdates];
        NSLog(@"accelerometer updates off...");
    }
}
@end

위에서, accelerometerData의 acceleration의 x,y 좌표 값을 가지고, Angle을 구하고, 그것을 바탕으로 PushBehavior의 Active를 YES로 입력합니다.

실제 Device에서 실행을 하면, 회전을 살짝하면, Label 들이 움직이게 됩니다.


[이미지 출처]
배경화면, 비행기, , 음식, 운동

참고 :
 - WWDC 2013 : Getting started with UIKit Dynamics
      : #206 세션으로, 다이나믹스에 대한 설명이 있음.
 - UIKit Dynamics and iOS 7: Building UIKit Pong
      : 다이나믹스를 이용해서, 바운싱을 예제로 설명하고 있는 곳.
 - RayWenderlich의 UIKit Dynamics 강좌

















2014년 4월 5일 토요일

[iOS7] UIKit Dynamics 사용법

UIKit Dynamics는 iOS7에서 추가된 것입니다.

iOS의 메시지에서 위로 올리다 보면, 마지막 부분에 튕기듯이 멈추는 부분과, Lock Screen에서 카메라 아이콘을 터치하거나, 위로 들었다가, 아래로 내리면 튕기 듯이 반응 하는 것이 UIKit의 Dynamics에서 지원하는 기능입니다.

 - UIKit Dynamics는 UIKit에 통합된 완전한 물리엔진 입니다. 즉, UIKit의 객체에 gravity, attachments, forces와 같은 동작(behavior)를 추가하여 현실처럼 느낄 수 있는 인터페이스를 만들 수 있게 지원하고 있습니다.
 - 물리특성을 정의하면, dynamics엔진이 알아서 처리를 해주고 있습니다.
 - iOS7 전에는 물리적인 시뮬레이션을 위해서, 다른 라이브러리를 사용해야 했었는데, iOS7에서는 간단한 물리적 시뮬레이션을 UIKit에서 해주는 것입니다.
 - 또한, CollectionView에서 아이콘들의 행동을 규정할 수 있습니다. 뭔가 추가되면, 다른 것들이 옆으로 흔들리게 만들 수 있습니다.

버튼들이 화면에 있다가 특정 순간이 되면, 아래로 떨어지도록 할 수도 있습니다.
이것을 가능하게 하는 것이 iOS7의 UIKit Dynamics입니다.


Dynamics는 UIKit내부에 내장되어 있으므로, 별도의 Framework를 import 할 필요가 없습니다.
위에 그림에서 처럼 Button 두개와, TextField를 움직이도록 해 보겠습니다.
먼저 Single View Application을 만들고, storyboad에서 위와 같이 객체를 등록하고, 각각 Property를 설정합니다.

UIDyanmicsAnimator를 현재 Main으로 등록된 View를 레퍼런스로 만듭니다.
Source Code

@interface DBUViewController ()

//아래로 떨어질 TextField
@property (strong, nonatomic) IBOutlet UITextField *textField; 
//아래로 떨어질 Button
@property (strong, nonatomic) IBOutlet UIButton *fallingButton; 
//스프링처럼 매달려 있을 Button
@property (strong, nonatomic) IBOutlet UIButton *danglingButton; 
//매달려 있을 버튼이 고정되어 있는 곳
@property (strong, nonatomic) IBOutlet UIView *redSquare; 
//버튼이 터치되면 결과를 알려줄 곳
@property (strong, nonatomic) IBOutlet UITextView *resultTextView; 

// 여기에 행동(Behavior)를 등록합니다.
@property (strong, nonatomic) UIDynamicAnimator *animator; 
//아래로 중력 행동을 만들것.
@property (strong, nonatomic) UIGravityBehavior* gravityBehavior; 

//원래 자리로 돌아가는 스냅 행동을 정의 돌아갈 위치가 달라지므로, 두개가 필요합니다.
@property (strong, nonatomic) UISnapBehavior * textFieldSnapBehavior; 
// 버튼 자리로 돌아갈 스냅 행동
@property (strong, nonatomic) UISnapBehavior *fallingButtonSnapBehavior;
@end

@implementation DBUViewController
- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    //
    self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
    
    UIAttachmentBehavior *springBehavior = [[UIAttachmentBehavior alloc] initWithItem:self.danglingButton offsetFromCenter:UIOffsetMake(/*20.0f*/0, /*self.danglingButton.frame.size.height/2*/0) attachedToAnchor:CGPointMake(self.redSquare.center.x, self.redSquare.center.y)];
    [springBehavior setFrequency:1.0f];
    [springBehavior setDamping:0.1f];
    
    [self.animator addBehavior:springBehavior];
}

시작버튼을 누르면, Gravity Behavior를 만들고, 등록을 시킨다.
source code
- (IBAction)startButton:(UIButton *)sender
{
    //gravity behavior가 없는 경우 새로 만들고 등록한다.
    if (!self.gravityBeahvior) {
        [sender setTitle:@"Stop" forState:UIControlStateNormal];
        [self.animator removeBehavior:self.textFieldSnapBehavior];
        [self.animator removeBehavior:self.fallingButtonSnapBehavior];
        
        //중력 행동을 만들때, 어느 Item을 행동에 넣을지 넣습니다.
        self.gravityBeahvior = [[UIGravityBehavior alloc] initWithItems:@[self.fallingButton, self.textField]];
        //만들고 난 다음에 추가할 수도 있습니다.
        [self.gravityBeahvior addItem:self.danglingButton];
        //Dynamic Animator에 행동을 추가하면, 실제 동작하게 됩니다.
        [self.animator addBehavior:self.gravityBeahvior];
    }else{
        //원래 위치로 돌아가기 위해서, 중력 행동을 빼고, 스냅 행동을 넣습니다.
        [sender setTitle:@"Start" forState:UIControlStateNormal];
        [self.animator removeBehavior:self.gravityBeahvior];
        self.gravityBeahvior = nil;
        [self.animator addBehavior:self.textFieldSnapBehavior];
        [self.animator addBehavior:self.fallingButtonSnapBehavior];
    }
}

스냅은 특정 Item이 어디로 이동할 지 넣는 것입니다.
이 스냅이 호출 될 때는 이미 아이템 2개가 아래로 떨어지고 난 다음입니다. 그래도, 원래의 위치는 그대로 저장되어 있는 상태로, Dynamics 엔진 내부에 위치만 달라진 것이므로, 자시느이 위치로 돌아오도록 center 값을 읽을 수 있습니다.
source code
- (UISnapBehavior *)textFieldSnapBehavior
{
    if (_textFieldSnapBehavior == nil) {
        _textFieldSnapBehavior = [[UISnapBehavior alloc] initWithItem:self.textField 
                                                          snapToPoint:self.textField.center];
    }
    return _textFieldSnapBehavior;
}
- (UISnapBehavior *)fallingButtonSnapBehavior
{
    if (_fallingButtonSnapBehavior == nil) {
        _fallingButtonSnapBehavior = [[UISnapBehavior alloc] initWithItem:self.fallingButton 
                                                   snapToPoint:self.fallingButton.center];
    }
    return _fallingButtonSnapBehavior;
}

감사합니다.

[소스코드: https://github.com/davidbae/iOS7-Dynamics-Test.git ]


참고 :
 - WWDC 2013 : Getting started with UIKit Dynamics
      : #206 세션으로, 다이나믹스에 대한 설명이 있음.
 - UIKit Dynamics and iOS 7: Building UIKit Pong
      : 다이나믹스를 이용해서, 바운싱을 예제로 설명하고 있는 곳.
 - Apple의 Dynamics 예제
 - RayWenderlich의 UIKit Dynamics 강좌
 -