A colossal Dreamer: GR鐵塔-天生我材

[Objective-C] 철탑만 몰랐던 iOS #5 - (InterfaceBuilder 만으로 화면결과 Delegating - UnwindingSegue Action ) 본문

Development/아이폰

[Objective-C] 철탑만 몰랐던 iOS #5 - (InterfaceBuilder 만으로 화면결과 Delegating - UnwindingSegue Action )

江多林 2014. 6. 3. 14:27

InterfaceBuilder 만으로 화면결과 Delegating  - UnwindingSegue Action 

개요 
- iOS6 이상에서 UIStoryBoard를 사용하는 경우에 
  상세에서 상위 화면으로 메시지를 전달하는 명쾌한 방법 

관련 예제 
UnwindSegue Example : https://developer.apple.com/library/ios/samplecode/UnwindSegue/Introduction/Intro.html 

VC 의 관련 메소드 

/* canPerformUnwind -  unwindSegueAction 의 제공여부 확인*/
– canPerformUnwindSegueAction:fromViewController:withSender:
/* viewControlerFor - unwindSegueAction 을 처리할 VC 를 얻는 메소드 */
– viewControllerForUnwindSegueAction:fromViewController:withSender:
/* segueFor - unwindSegueAction 를 perform 할 때 사용할 segue 를 생성하는 메소드 */
– segueForUnwindingToViewController:fromViewController:identifier:



동작 로그 

// - View Controller Hierachy -
Controller Hierarchy <UIApplication: 0xc903750> { ControlResponder, <UIWindow: 0x13f4a260> } | <KSBSplashViewController: 0x13f4bab0> { ControlResponder, <KSBLoginRootViewController: 0xc911d70> } | | <KSBLoginRootViewController: 0xc911d70> { ControlResponder, <UINavigationController: 0x13fa5400> } | | | <UINavigationController: 0x13fa5400> { ControlResponder, <UILayoutContainerView: 0xdb7bc00> } | | | | <KSBLoginWebViewController: 0x13f53950> { ControlResponder, <UIView: 0x13fe9110> }
 
// - ViewController Hierachy 에서 unwindSegue Handle 가능한 VC 찾기
-[KSBLoginWebViewController canPerformUnwindSegueAction:fromViewController:withSender:] has called with { action = "returnActionForSegue:sender:"; fromVC = "<KSBLoginWebViewController: 0x13f53950>"; sender = "<UIBarButtonItem: 0x13fb3f00>"; superReturnCanPerform = 0; } -[KSBLoginRootViewController canPerformUnwindSegueAction:fromViewController:withSender:] has called with { action = "returnActionForSegue:sender:"; fromVC = "<KSBLoginWebViewController: 0x13f53950>"; sender = "<UIBarButtonItem: 0x13fb3f00>"; superReturnCanPerform = 1; }
 
// Action 전송을 위한 Segue 생성
-[KSBLoginRootViewController segueForUnwindingToViewController:fromViewController:identifier:] has called with { fromVC = "<KSBLoginWebViewController: 0x13f53950>"; identifier = "<null>"; superReturnSegue = { destinationViewController = "<KSBLoginRootViewController: 0xc911d70>"; identifier = "<null>"; performHandler = "<__NSMallocBlock__: 0xdba99d0>"; sourceViewController = "<KSBLoginWebViewController: 0x13f53950>"; }; toVC = "<KSBLoginRootViewController: 0xc911d70>"; }

// prepareForSegue 실행 on Source with sender(Event Firing Control)
-[KSBLoginWebViewController prepareForSegue:sender:] has called with segue:<UIStoryboardSegue:0xdbd2b00>{ destinationViewController = "<KSBLoginRootViewController: 0xc911d70>"; identifier = "<null>"; performHandler = "<__NSMallocBlock__: 0xdba99d0>"; sourceViewController = "<KSBLoginWebViewController: 0x13f53950>"; } sender:<UIBarButtonItem:0x13fb3f00>{ target = "<UIStoryboardUnwindSegueTemplate: 0x13f5d5b0>"; }

// actionForSegue 실행 on Target with sender:(Target ViewController)
-[KSBLoginRootViewController returnActionForSegue:sender:] has called. segue:<UIStoryboardSegue:0xdbd2b00>{ destinationViewController = "<KSBLoginRootViewController: 0xc911d70>"; identifier = "<null>"; performHandler = "<__NSMallocBlock__: 0xdba99d0>"; sourceViewController = "<KSBLoginWebViewController: 0x13f53950>"; } sender:<KSBLoginRootViewController:0xc911d70>{ "*class* UIViewController" = { "*class* UIResponder" = { "__content" = "<KSBLoginRootViewController: 0xc911d70>"; "_responderForEditing" = "<KSBLoginRootViewController: 0xc911d70>"; "_textSelectingContainer" = "<KSBLoginRootViewController: 0xc911d70>"; }; "_modalSourceViewController" = "<KSBSplashViewController: 0x13f4bab0>"; "_parentModalViewController" = "<KSBSplashViewController: 0x13f4bab0>"; "_presentedStatusBarViewController" = "<UINavigationController: 0x13fa5400>"; childModalViewController = "<UINavigationController: 0x13fa5400>"; "ka_presentedViewController" = "<UINavigationController: 0x13fa5400>"; modalViewController = "<UINavigationController: 0x13fa5400>"; nibName = "BBF-ZV-3Lw-view-N2m-qU-VNs"; parentModalViewController = "<KSBSplashViewController: 0x13f4bab0>"; presentedViewController = "<UINavigationController: 0x13fa5400>"; presentingViewController = "<KSBSplashViewController: 0x13f4bab0>"; storyboard = "<UIStoryboard: 0x13f45040>"; storyboardSegueTemplates = ( "<UIStoryboardModalSegueTemplate: 0xc914f70>", "<UIStoryboardModalSegueTemplate: 0xc906cc0>" ); }; }

// perform Segue (View Life Cycle Delegate)

-[KSBLoginWebViewController viewWillDisappear:] has called -[KSBLoginRootViewController viewWillAppear:] has called -[KSBLoginRootViewController viewDidAppear:] has called -[KSBLoginWebViewController viewDidDisappear:] has called



* 추가사항 
- ContainerVC 에 속한 VC 의 경우에 ContainerVC 에서 구현되지 않은 경우에 
  ContainerVC 의 Parent 에게 전달된다. (responder Chain 과 같이 canPerformUnwind 메소드로 지원여부 확인)

- Unwinding Segue 는 -[targetVC segueForUnwindingToViewController:fromViewController:identifier:]에서 생성되며,

- 생성된 Unwinding Segue는 SourceVC 에서 실행되게 된다.

- 이 과정은 ViewLifeCycle 과 무관하게 그 이전에 실행되는 것으로 보인다.


- ViewController 의 동작의 결과를 다른 ViewController 로 전달하는 것은 매우 귀찮은 일이었다. 

- 일반적으로 delegate 를 만들어서 결과를 알려주고, 결과를 받은 쪽에서 ViewController 를 내려주는 방식으로 코딩해왔다. 
- iOS6 에서 등장한 UnwindingSegue 를 활용하면, delegate 사용보다 훨씬 직관적이고 단순하게 결과를 받을 수 있다. 
  * responder chain 을 이해하고 있다면, 개념파악에 도움이 된다. 
- ViewController Hierachy 의 상위 VC 에서 -(IBAction)xxxActionForSegue:sender: 메소드를 구현하고, 
  InterfaceBuilder 에서 'Exit' 링크로 연결하면 해당 Segue 기동시에 ActionForSegue: 가 호출된다.


복잡해보이지만, 실제로는 코딩은 한 줄과, IB 에서 action을 하나 연결한 것이 전부. 

- (IBAction)returnActionForSegue:(UIStoryboardSegue *)segue
{
    NSLog(@"%s has called. segue:%@", __PRETTY_FUNCTION__, [segue KCCPD_propertiesDescription]);
}