[OODesign Principles] ISP (Interface Segregation Principle)

5 Principles of Class Design

  1. SRP (Single Responsibility Principle)
  2. OCP (Open Closed Principle)
  3. LSP (Liskov Substitution Principle)
  4. ISP (Interface SegregationPrinciple)
  5. DIP (Dependency Inversion Principle)

클래스 디자인에 있어 다섯가지 원칙이 있다. ISP에 대한 내용이 기억이 나질 않아 검색 중에 나온 좋은 링크가 있어서 올려보고자 한다.

Link

결론: 뚱뚱한 interface를 구지 구현하지 말고 더 잘게 잘게 인터페이스를 나누어서 구현하도록 하자.

[iOS7 UI Programming - part 1] Animation Transitioning

Animation Transitioning in iOS7

20130313_osxdock_minimize_genie

많은 개발자들은 UI 개발에 있어서 애니메이션이란 자신이 만든 프로덕에 있어서 선물 보장지와 같은 생각을 가지고 있는 것 같다. 중요하지 않지만 있으면 좋을 것 같고, 유행에 예민할 것 같은 주제라고나 할까? 뭐 사실 소프트웨어 개발에 있어서 트랜드에 예민한 디자인 요소라는 것을 부정하지는 않는다. 하지만 Mac OS X에서는 위 이미지와 같은 요술 램프 지니 애니메이션은 10년이 넘은 효과이기에 위 효과만 보더라도 아직까지 쓰이고 있는 것으로 보아 어찌보면 유행이라는 단어와 어울리지 않을지도 모른다는 생각을 해본다.

이번 주제는 iOS7의 Animation Transitioning에 대한 샘플 코드를 올려보고자 한다. iPhone에 있어서 UINavigationController 같은 앱 전체의 굵은 뼈대가 되는 컴포넌트에서 Push라든지 MadalView를 보여주는 기본 애니메이션이 이제 사용자들에게 슬슬 지루감을 주고 있기 때문일까? iOS7에서 해당 기술을 지원하는 코드가 잘 녹아들어있다. UINavigationController나 UIViewController 클래스만 보더라도 Delegate Protocol들을 통해서 이를 완벽하게 ‘레고 블럭’처럼 잘 설계를 해놓았다. UIViewControllerTransitioningDelegate이나 UINavigationControllerDelegate에 정의된 method에서 따로 구현된 클래스 객체만 잘 리턴해도 멋진 자신만의 애니메이션을 구현할 수 있다.

 

Output

 

Source Code

위 동영상의 커스텀 Animation Transitioning은 두가지이다. 첫 번째 ModalView가 나올 때 적용된 효과는 공이 튀기는 것과 같은 효과를 주는 바운드 효과이고 살아질 때 나오는 것은 줄어드는 효과인데, 임의대로 만든 효과이다. 먼저 살펴보고자 하는 클래스는 UITableViewController를 상속 받고있는 MasterViewController이다.

  1. #import "MasterViewController.h"
  2. #import "DetailViewController.h"
  3. #import "AppDelegate.h"
  4. #import "Cat.h"
  5. #import "BouncePresentAnimationController.h"
  6. #import "ShrinkDismissAnimationController.h"
  7.  
  8. #pragma mark – Interface of 'MasterViewController'
  9.  
  10. @interface MasterViewController ()<UIViewControllerTransitioningDelegate>
  11.  
  12. @end
  13.  
  14. #pragma mark – Implementation of 'MasterViewController'
  15.  
  16. @implementation MasterViewController
  17. {
  18.     BouncePresentAnimationController *_bounceAnimationController;
  19.     ShrinkDismissAnimationController *_shrinkDismissAnimationController;
  20. }
  21.  
  22. #pragma mark – Getters & Setters
  23.  
  24. - (NSArray *)cats
  25. {
  26.     return ((AppDelegate *)[[UIApplication sharedApplication] delegate]).cats;
  27. }
  28.  
  29. #pragma mark – Life cycle
  30.  
  31. - (instancetype)initWithCoder:(NSCoder *)aDecoder
  32. {
  33.     if (self = [super initWithCoder:aDecoder])
  34.     {
  35.         _bounceAnimationController = [BouncePresentAnimationController new];
  36.         _shrinkDismissAnimationController = [ShrinkDismissAnimationController new];
  37.     }
  38.  
  39.     return self;
  40. }
  41.  
  42. - (void)viewDidLoad
  43. {}
  44.  
  45. - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
  46. {}
  47.  
  48. #pragma mark – Table View
  49.  
  50. - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
  51. {}
  52.  
  53. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
  54. {}
  55.  
  56. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
  57. {}
  58.  
  59. #pragma mark – Protocol of 'UIViewControllerTransitioningDelegate'
  60.  
  61. //#1.
  62. - (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
  63. {
  64.     return _bounceAnimationController;
  65. }
  66.  
  67. //#2.
  68. - (id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
  69. {
  70.     return _shrinkDismissAnimationController;
  71. }

위 코드는 Class extension에 UIViewControllerTransitioningDelegate를 implement한다고 선언한 후, animationControllerForPresentedController:presentingController:sourceController: 메서드와 animationControllerForDismissedController:메서드를 구현하여, 애니메이션이 구현 되어 있는 클래스 객체를 반환만 해주면 된다.

다음은 _bounceAnimationController 객체의 클래스 소스코드이다.

  1. #pragma mark – Interface of 'BouncePresentAnimationController'
  2. @import Foundation;
  3. @interface BouncePresentAnimationController : NSObject <UIViewControllerAnimatedTransitioning>
  4. @end
  5.  
  6. #pragma mark – Implementation of 'BouncePresentAnimationController'
  7. @implementation BouncePresentAnimationController
  8.  
  9. //0.
  10. - (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext
  11. {
  12.     return 0.5;
  13. }
  14.  
  15. - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext
  16. {
  17.     //1. obtain state from the context
  18.     UIViewController *toViewController = nil;
  19.     toViewController = [transitionContext
  20.                         viewControllerForKey:UITransitionContextToViewControllerKey];
  21.     CGRect finalFrame = [transitionContext finalFrameForViewController:toViewController];
  22.     UIViewController *fromViewController = nil;
  23.     fromViewController = [transitionContext
  24.                           viewControllerForKey:UITransitionContextFromViewControllerKey];
  25.  
  26.     //2. obtain the container view
  27.     UIView *containerView = [transitionContext containerView];
  28.  
  29.     //3. set initial state
  30.     CGRect screenBounds = [[UIScreen mainScreen] bounds];
  31.     toViewController.view.frame = CGRectOffset(finalFrame, 0, screenBounds.size.height);
  32.  
  33.     //4. add the view
  34.     [containerView addSubview:toViewController.view];
  35.  
  36.     //5. animate
  37.     NSTimeInterval duration = [self transitionDuration:transitionContext];
  38.  
  39.     [UIView animateWithDuration:duration
  40.                           delay:0.0
  41.          usingSpringWithDamping:0.6
  42.           initialSpringVelocity:0.0
  43.                         options:UIViewAnimationOptionCurveLinear
  44.                      animations:^{
  45.                          fromViewController.view.alpha = 0.2;
  46.                          toViewController.view.frame = finalFrame;
  47.                      }
  48.                      completion:^(BOOL finished) {
  49.                          //6. Set completed! (Important!)
  50.                          [transitionContext completeTransition:YES];
  51.                          fromViewController.view.alpha = 1.0;
  52.                      }];
  53. }
  54.  
  55. @end

 

BouncePresentAnimationController 코드 분석

0. transitionDuration: 메서드는 이름을 보면 알 수 있듯이 transition 타임을 결정 해준다.
1. 새로들어 올 ViewController과 사라지게 될 ViewController를 assign 해준다.
2. transitionContext에는 containerView라는 view가 있다. Transitioning중에 보여질 View 객체들은 ContainerView 안에 있어야 한다.
3. toViewController의 Frame을 세팅한다.
4. ContainerView에 toViewController를 추가한다.
5. UIView의 animateWithDuration:delay:usingSpringWithDamping:initialSpringVelocity:options:animations:completion:를 통해 바운스 애니메이션을 구현한다.

다음은 _shrinkDismissAnimationController의 클래스 코드를 살펴보자.

  1. #pragma mark – Interface of 'ShrinkDismissAnimationController'
  2.  
  3. @import Foundation;
  4. @interface ShrinkDismissAnimationController : NSObject<UIViewControllerAnimatedTransitioning>
  5. @end
  6.  
  7. #pragma mark – Implementation of 'ShrinkDismissAnimationController'
  8.  
  9. @implementation ShrinkDismissAnimationController
  10.  
  11. - (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext
  12. {
  13.     return 0.5;
  14. }
  15.  
  16. - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext
  17. {
  18.     //1.
  19.     UIViewController *toViewController = nil;
  20.     toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
  21.     UIViewController *fromViewController = nil;
  22.     fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
  23.     CGRect finalFrame = [transitionContext finalFrameForViewController:toViewController];
  24.     UIView *containerView = [transitionContext containerView];
  25.  
  26.     toViewController.view.frame = finalFrame;
  27.     toViewController.view.alpha = 0.5;
  28.  
  29.     //2
  30.     [containerView addSubview:toViewController.view];
  31.     [containerView sendSubviewToBack:toViewController.view];
  32.  
  33.     //3. Actual animation
  34.     CGRect fromViewFrame = fromViewController.view.frame;
  35.     CGRect screenBounds = [[UIScreen mainScreen] bounds];
  36.     CGRect shrunkenFrame = CGRectInset(fromViewFrame, fromViewFrame.size.width/4, fromViewFrame.size.height/4);
  37.     CGRect fromFinalFrame = CGRectOffset(shrunkenFrame, 0, screenBounds.size.height);
  38.     NSTimeInterval duration = [self transitionDuration:transitionContext];
  39.  
  40.     //4. animate
  41.     [UIView
  42.      animateKeyframesWithDuration:duration
  43.      delay:0.0
  44.      options:UIViewKeyframeAnimationOptionCalculationModeCubic
  45.      animations:^
  46.     {
  47.  
  48.         [UIView addKeyframeWithRelativeStartTime:0.0
  49.                                 relativeDuration:0.5
  50.                                       animations:^{
  51.                                           fromViewController.view.transform = CGAffineTransformMakeScale(0.5, 0.5);
  52.                                           toViewController.view.alpha = 0.5;
  53.                                       }];
  54.  
  55.         [UIView addKeyframeWithRelativeStartTime:0.5
  56.                                 relativeDuration:0.5
  57.                                       animations:^{
  58.                                           fromViewController.view.frame = fromFinalFrame;
  59.                                           toViewController.view.alpha = 1.0;
  60.                                       }];
  61.     }
  62.      completion:^(BOOL finished)
  63.     {
  64.         [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
  65.     }];
  66. }
  67.  
  68. @end

 

ShrinkDismissAnimationController 코드 분석

1. 새로들어 올 ViewController과 사라지게 될 ViewController를 assign 해준다.
2. transitionContext에는 containerView라는 view가 있다. Transitioning중에 보여질 View 객체들은 ContainerView 안에 있어야 한다.
3. 애니메이션에 필요한 값
4. 실제 애니메이션 구현은 UIView의 animateKeyframesWithDuration:delay:options:options:animations:completion: 안에 또 다른 애니메이션 메서드를 구현함을 통해 원하는 애니메이션을 구현한다.

 

마치며..

UI 프로그래밍이 시대의 유행이든 아니든 간에 SDK의 설계를 보며 느끼는 점이 많았던 리서치 같다. 더 공부하고 더 열심히 해야겠다는 생각을 해본다.

[펌글] 3D 기본 수학

해당 포스트는 iOS의 Core Animation을 공부하다가 본 좋은 블로그라 생각하여서, 링크를 공유해본다.

Basic 3D Math: Matrices by Egon Rath’s Notes

Basic 3D Math: Matrices

Matrices are rectangular mathematical objects which have a number of rows and columns – think of it as a two-dimensional array. Take a look at the following:

A = delim{[}{matrix{3}{4}{1 2 3 4 5 6 7 8 9 10 11 12}}{]}

Above is a 3×4 Matrix (because it has 3 rows and 4 columns) – speak: 3 by 4 Matrix. Each element of the Matrix can be indexed by using the following notation:

A_ij where i is the row and j the column: A_32 = 10 because the element at the 3rd rows and 2nd column has the value 10.

Matrices are used in CG to represent a various number of different things like transformations and orientations.

Part 1: Multiplying Matrices (Matrix Product)

Matrix multiplication is not commutative, which means that the order of operation is relevant (as opposed to the multiplication of real numbers). In order to be able to multiply two matrices they must satisfy the rule that column of the first must match the rows of the second. Take a look at the following graphic which works as a simple reminder for the rules of matrix multiplication:

But what’s the purpose of multiplying matrices? In 3D Programming you do Transforming a vertex with such a operation. Stop – how can i multiply a vector (which stores a vertex position) with a matrix? Vectors can be thought as Vectors with 1 Row and 4 Columns – and because multiplying a 1×4 Matrix with a 4×4 Matrix is a valid operation this works.

In the first step, lets examine how to actually do the multiplication – formally this looks like:

(AB)_ij = sum{k=1}{p}{A_ik B_kj}

i = row index
j = column index
p = number of columns of the first matrix (or number of rows of the second one)

Because a example often says more than thousand words, take a look at the following:

We have two matrices: A = delim{[}{matrix{2}{3}{1 2 3 4 5 6}}{]} and B = delim{[}{matrix{3}{2}{10 11 12 13 14 15}}{]}. Multiplication of those two is possible because the number of columns of A matches the number of rows of B – the result is a 2×2 Matrix.

Let’s perform the Calculation step by step.

Step 1: Write down the calculation of every index for the resulting matrix

C_{i=1,j=1}=1*10 + 2*12 + 3*14 C_{i=1,j=2}=1*11 + 2*13 + 3*15
C_{i=2,j=1}=4*10 + 5*12 + 6*14 C_{i=2,j=2}=4*11 + 5*13 + 6*15

Step 2: Do the calculations

10 + 24 + 42 11 + 26 + 45
40 + 60 + 84 44 + 65 + 90

results in:

C = delim{[}{matrix{2}{2}{76 82 184 199}}{]}

Part 2: Creating Matrices for Transformations

As mentioned above at the beginning of this document, matrices can be used to transform vectors. Its obvious that you need corresponding matrices which represent the transformation, so they are presented below – please note that they are for OpenGL only (The “why” is discussed at the end)

Identity Matrix:

I = delim{[}{matrix{4}{4}{1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1}}{]}

The Identity matrix can be seen as the “reset” state matrix. If you multiply a vector with the Identity matrix you get the original vector. You initialise new matrices to the identity.

Translation Matrix:

T = delim{[}{matrix{4}{4}{1 0 0 X 0 1 0 Y 0 0 1 Z 0 0 0 1}}{]}

X, Y and Z represent the amount you want to translate the vector on the axis.

Scaling Matrix:

S = delim{[}{matrix{4}{4}{X 0 0 0 0 Y 0 0 0 0 Z 0 0 0 0 1}}{]}

X,Y and Z represent the amount of scaling on the corresponding axis.

Rotation Matrix:

There are four rotation matrices – one about each Axis (X,Y and Z) and one for rotating about an arbitrary axe. alpha is the roation in radians.

Rot_X = delim{[}{matrix{4}{4}{1 0 0 0 0 {cos(alpha)} {-sin(alpha)} 0 0 {sin(alpha)} {cos(alpha)} 0 0 0 0 1}}{]}  Rot_Y = delim{[}{matrix{4}{4}{{cos(alpha)} 0 {sin(alpha)} 0 0 1 0 0 {-sin(alpha)} 0 {cos(alpha)} 0 0 0 0 1}}{]}  Rot_Z = delim{[}{matrix{4}{4}{{cos(alpha)} {-sin(alpha)} 0 0 {sin(alpha)} {cos(alpha)} 0 0 0 0 1 0 0 0 0 1}}{]}

The matrix used to rotate about an arbitrary axis is a little bit more complicated.

Assumptations:

c = cos(alpha) s = sin(alpha) t = 1-cos(alpha)

Then the matrix looks like:

Rot = delim{[}{matrix{4}{4}{{tX^2+c} {tXY + sZ} {tXZ - sY} 0 {txY - sZ} {tY^2+c} {tYZ + sX} 0 {tXY + sY} {tYZ - sX} {tZ^2+c} 0 0 0 0 1}}{]}

The Vector (x,y,z) which represents the rotation axis must be normalized

Most of the time you don’t need to create those matrices by ourself because either the math library you are using provides them or you write your own.

Part 3: Order of operation and combining transformations

The order of multiplying multiple transformation matrices is cruical. In OpenGL you get the final transformation by reading your statement from left to right. Example:

vec{U} = R * T * vec{V}

R is a rotation, T a translation matrix. The Vector V is first Translated and then rotated. Take a look at the pictures below to see the difference visually:

In this Picture, the vertices of the Object are first multiplied by a rotation matrix (about Z) – which rotates the local object coordinates. After that a translation matrix (move along X)  is applied, so the object moves along the rotated X axis. The order of the multiplications are:

vec{U} = M_{trans} * M_{rotation} * vec{V}

As mentioned before, we read from right to left (Take Vector V, then Rotate, then Translate)

This time we first multiply with a translation matrix (translate along X) and then apply a rotation matrix:

vec{U} = M_{rotation} * M_{translation} * vec{V}

Which means: Take Vector V, then translate, then rotate.

Please note that the order only applies to OpenGL. In DirectX it’s from left to right – also the matrices are a little bit different in how they look like.

Part 4: Going ahead

Most OpenGL based 3D Libraries and Toolkits provide easy ways of creating the matrices mentioned above (i REALLY like Richard Wright’s Math3D Library which he uses in his Book OpenGL Superbible).

If you want to dig deeper into the mathematics behind 3D Graphics, i highly recommend you the following two books: