• collisionBehavior: beganContactForItem: withBoundaryIdentifier: atPoint: – вызывается в делегате, когда один из элементов, обладающих поведением столкновения, ударяется об одну из границ, добавленных к этому поведению;
   • collisionBehavior: endedContactForItem: withBoundaryIdentifier: atPoint: – вызывается, когда элемент, столкнувшийся с границей, отскочил от нее и, таким образом, контакт элемента с границей прекратился.
 
   Чтобы продемонстрировать вам делегат в действии и показать, как его можно использовать, расширим приведенный пример. Как только квадратики достигают нижней границы опорного вида, мы делаем их красными, увеличиваем на 200 %, а потом заставляем рассыпаться, как при взрыве, и исчезать из виду:
 
   NSString *const kBottomBoundary = @"bottomBoundary";
 
   @interface ViewController () <UICollisionBehaviorDelegate>
   @property (nonatomic, strong) NSMutableArray *squareViews;
   @property (nonatomic, strong) UIDynamicAnimator *animator;
   @end
 
   @implementation ViewController
 
   – (void)collisionBehavior:(UICollisionBehavior*)paramBehavior
   beganContactForItem:(id <UIDynamicItem>)paramItem
   withBoundaryIdentifier:(id <NSCopying>)paramIdentifier
   atPoint:(CGPoint)paramPoint{
 
   NSString *identifier = (NSString *)paramIdentifier;
 
   if ([identifier isEqualToString: kBottomBoundary]){
 
   [UIView animateWithDuration:1.0f animations: ^{
   UIView *view = (UIView *)paramItem;
 
   view.backgroundColor = [UIColor redColor];
   view.alpha = 0.0f;
   view.transform = CGAffineTransformMakeScale(2.0f, 2.0f);
   } completion: ^(BOOL finished) {
   UIView *view = (UIView *)paramItem;
   [paramBehavior removeItem: paramItem];
   [view removeFromSuperview];
   }];
 
   }
 
   }
 
   – (void)viewDidAppearBOOL)animated{
   [super viewDidAppear: animated];
 
   /* Создаем виды */
   NSUInteger const NumberOfViews = 2;
 
   self.squareViews = [[NSMutableArray alloc] initWithCapacity: NumberOfViews];
   NSArray *colors = @[[UIColor redColor], [UIColor greenColor]];
 
   CGPoint currentCenterPoint = CGPointMake(self.view.center.x, 0.0f);
   CGSize eachViewSize = CGSizeMake(50.0f, 50.0f);
   for (NSUInteger counter = 0; counter < NumberOfViews; counter++){
 
   UIView *newView =
   [[UIView alloc] initWithFrame:
   CGRectMake(0.0f, 0.0f, eachViewSize.width, eachViewSize.height)];
 
   newView.backgroundColor = colors[counter];
   newView.center = currentCenterPoint;
 
   currentCenterPoint.y += eachViewSize.height + 10.0f;
   [self.view addSubview: newView];
 
   [self.squareViews addObject: newView];
 
   }
 
   self.animator = [[UIDynamicAnimator alloc]
   initWithReferenceView: self.view];
 
   /* Создаем тяготение */
   UIGravityBehavior *gravity = [[UIGravityBehavior alloc]
   initWithItems: self.squareViews];
   [self.animator addBehavior: gravity];
 
   /* Создаем обнаружение столкновений */
   UICollisionBehavior *collision = [[UICollisionBehavior alloc]
   initWithItems: self.squareViews];
   [collision
   addBoundaryWithIdentifier: kBottomBoundary
   fromPoint: CGPointMake(0.0f, self.view.bounds.size.height – 100.0f)
   toPoint: CGPointMake(self.view.bounds.size.width,
   self.view.bounds.size.height – 100.0f)];
   collision.collisionDelegate = self;
 
   [self.animator addBehavior: collision];
 
   }
 
   Объясню, что происходит в коде. Во-первых, мы создаем два вида и кладем их один на другой. Эти виды представляют собой два обычных разноцветных квадрата: второй находится на первом. Оба они добавлены к контроллеру вида. Как и в предыдущих примерах, мы добавляем к аниматору поведение тяготения. Это означает, что, как только анимация начнет действовать, виды станут как будто сползать по опорному виду сверху вниз. Мы не задаем границы опорного вида в качестве границ столкновения, а самостоятельно проводим границу столкновения, располагая ее в 100 точках над нижней границей экрана. У нас получается невидимая линия, проходящая по экрану слева направо. Она не позволяет видам бесконечно падать вниз и выходить за пределы опорного вида.
   Кроме того, как видите, мы задаем вид с контроллером в качестве делегата поведения столкновения. Таким образом, он получает обновления от этого поведения, сообщающие, произошло ли столкновение и если произошло, то когда. Как только вы узнаете о факте столкновения, то, вероятно, захотите определить, было ли это столкновение с границей (например, созданной нами) или с одним из элементов сцены. Например, если вы создали в опорном виде множество виртуальных стен, а маленькие виды-квадраты могут сталкиваться с этими стенами, то можете реализовать иной эффект (скажем, взрыв), зависящий от того, с чем именно произошло столкновение. О том, с каким элементом произошло столкновение, вы сможете узнать из делегатного метода, вызываемого в контроллере вида и дающего идентификатор той границы, с которой столкнулся элемент. Зная, какой это был объект, мы можем решить, что делать дальше.
   В примере мы сравниваем идентификатор, получаемый от поведения столкновения, с константой kBottomBoundary, которую присвоили барьеру при создании этого барьера. Создаем для объекта такую анимацию: квадрат под действием тяготения движется по экрану вниз, вплоть до установленной нами границы. Граница гарантирует, что квадрат остановится на расстоянии 100 точек от нижнего края экрана, на проведенной линии.
   Одним из самых интересных свойств класса UIGravityBehavior является collisionMode. Это свойство описывает, как столкновение должно обрабатываться в аниматоре. Например, в предыдущем примере мы рассмотрели типичное столкновение, добавленное в аниматор без изменения значения collisionMode. В данном случае это поведение регистрирует столкновения между квадратиками, а также между квадратиками и теми границами, которые мы провели в опорном виде. Однако мы можем модифицировать это поведение, изменив значение упомянутого свойства. Вот значения, которые можно для него задать:
   • UICollisionBehaviorModeItems – при таком значении поведение будет регистрировать столкновения между динамическими элементами – в данном случае между движущимися квадратиками;
   • UICollisionBehaviorModeBoundaries – при этом значении регистрируются столкновения между динамическими элементами и установленными нами границами, расположенными в опорном виде;
   • UICollisionBehaviorModeEverything – при таком значении регистрируются любые столкновения, независимо от того, участвуют в них сами элементы, элементы и границы или что-либо еще. Это значение для данного свойства задается по умолчанию.
   Можно комбинировать рассмотренные ранее значения с помощью побитового оператора ИЛИ и создавать сочетания режимов столкновения, соответствующие поставленным перед вами бизнес-требованиям.
   Рекомендую поэкспериментировать со значениями свойства collisionMode и в предыдущем примере задать для этого свойства значение UICollisionBehaviorModeBoundaries, а потом запустить приложение. Вы увидите, как оба квадратика упадут в нижнюю часть экрана, окажутся на проведенной границе, но не столкнутся, а вдвинутся друг в друга. Дело в том, что код просто проигнорирует столкновение между ними.

См. также

   Раздел 2.1.

2.3. Анимирование компонентов пользовательского интерфейса с помощью толчков

Постановка задачи

   Требуется визуально перебрасывать виды с одного места на экране на другое.

Решение

   Инициализируйте объект поведения типа UIPushBehavior с помощью относящегося к нему метода initWithItems: mode: и передайте ему значение UIPushBehaviorModeContinuous. Как только будете готовы толкать элементы под углом, вызовите для толчка метод setAngle:. Этот метод задает угол (в радианах) для данного поведения. Затем потребуется установить магнитуду, то есть силу толчка. Эта величина задается с помощью относящегося к толчку поведения setMagnitude:. Магнитуда рассчитывается следующим образом: магнитуда величиной 1 точка соответствует ускорению 100 точек/с2, прилагаемому к целевым видам.

Обсуждение

   Толчки, прилагаемые к экранным элементам, очень полезны – особенно толчки, вызывающие непрерывное движение. Допустим, вы разрабатываете приложение-фотоальбом для iPad. В верхней части экрана создали три слайда, каждый из которых соответствует странице альбома, созданной пользователем. В нижней части экрана располагаются различные картинки, которые пользователь может перетаскивать и раскладывать на страницах. Один из способов, позволяющих реализовать для пользователя такую возможность, – добавление к опорному виду регистратора жестов касания (tap gesture recognizer), создание которого рассмотрено в разделе 10.5. Этот регистратор обеспечивает отслеживание пользовательских жестов касания и позволяет перемещать изображения на целевой слайд. Процесс выглядит как перетаскивание. Другой, пожалуй, более оптимальный способ решения этой задачи – использование толчкового поведения, которое разработчики Apple включили в UIKit.
   Толчковое поведение относится к типу UIPushBehavior и обладает магнитудой и углом. Угол измеряется в радианах, а магнитуда в 1 точку приводит к ускорению движения, равному 100 точек/с2. Толчковые поведения создаются точно так же, как и любые другие: сначала их необходимо инициализировать, а потом добавить к аниматору типа UIDynamicAnimator.
   В этом примере мы собираемся создать вид и поместить его в центре более крупного вида контроллера. Мы подключим к аниматору поведение столкновений, благодаря чему маленький вид не будет вылетать за пределы большого вида (с контроллером). О том, как работать со столкновениями, мы поговорили в разделе 2.2. Затем добавим регистратор жестов касания (см. раздел 10.5) к контроллеру вида. Этот регистратор будет уведомлять нас о каждом жесте касания, произошедшем на экране.
   Когда касание будет зарегистрировано, рассчитаем угол между точкой касания и центром маленького квадратного вида. Так мы получим угол, выраженный в радианах, под которым сможем толкнуть этот квадратный вид. Затем рассчитаем расстояние между точкой касания и центром маленького вида, полученное значение используем в качестве магнитуды толчка. Таким образом, магнитуда будет тем больше, чем дальше от центра квадратного вида находится точка касания.
   В данном разделе предполагается, что читатель понимает основы тригонометрии. Но даже если вы с ними не знакомы – ничего страшного, поскольку для работы потребуются лишь те формулы, которые я описываю в примерах кода к этому разделу. На рис. 2.2 показано, как вычисляется угол между двумя точками. Итак, я надеюсь, что объяснение получится достаточно подробным, чтобы вы могли написать собственное решение данной проблемы.
 
   Рис. 2.2. Расчет угла между двумя точками
 
   Итак, начнем с определения всех важных свойств нашего контроллера вида:
 
   #import "ViewController.h"
 
   @interface ViewController ()
   @property (nonatomic, strong) UIView *squareView;
   @property (nonatomic, strong) UIDynamicAnimator *animator;
   @property (nonatomic, strong) UIPushBehavior *pushBehavior;
   @end
   @implementation ViewController
 
   <# Остальной ваш код находится здесь #>
   В этом примере мы добавим к аниматору поведение столкновения и толчковое поведение. Толчковое поведение добавляется к классу в качестве свойства, а поведение столкновений – просто как локальная переменная. Дело в том, что, как только мы добавим к аниматору поведение столкновения, именно аниматор будет вычислять все столкновения с границами опорного вида и нам больше не придется ссылаться на это поведение столкновений. Однако если говорить о толчковом поведении, то при обработке касаний придется обновлять это толчковое поведение, чтобы графический элемент подталкивался к точке касания. Вот почему нам требуется связь касания с толчковым поведением, но не требуется такая связь со столкновениями.
   Далее напишем метод, создающий маленький квадратный вид и помещающий его в центре большого вида с контроллером:
 
   – (void) createSmallSquareView{
   self.squareView =
   [[UIView alloc] initWithFrame:
   CGRectMake(0.0f, 0.0f, 80.0f, 80.0f)];
   self.squareView.backgroundColor = [UIColor greenColor];
 
   self.squareView.center = self.view.center;
 
   [self.view addSubview: self.squareView];
   }
   Затем применим регистратор жестов касания, чтобы обнаруживать прикосновения к виду с контроллером:
   – (void) createGestureRecognizer{
   UITapGestureRecognizer *tapGestureRecognizer =
   [[UITapGestureRecognizer alloc] initWithTarget: self
   action:@selector(handleTap:)];
   [self.view addGestureRecognizer: tapGestureRecognizer];
   }
   Эти методы выполняют за нас всю необходимую работу. Позже, когда вид отобразится на экране, мы будем вызывать эти методы и они будут действовать.
   И не забудем написать метод, который будет задавать поведение столкновения и толчковое поведение:
 
   – (void) createAnimatorAndBehaviors{
   self.animator = [[UIDynamicAnimator alloc]
   initWithReferenceView: self.view];
 
   /* Создаем обнаружение столкновений */
   UICollisionBehavior *collision = [[UICollisionBehavior alloc]
   initWithItems:@[self.squareView]];
   collision.translatesReferenceBoundsIntoBoundary = YES;
 
   self.pushBehavior = [[UIPushBehavior alloc]
   initWithItems:@[self.squareView]
   mode: UIPushBehaviorModeContinuous];
 
   [self.animator addBehavior: collision];
   [self.animator addBehavior: self.pushBehavior];
   }
 
   Подробнее о поведении столкновений рассказано в разделе 2.2. Как только мы запрограммируем все эти методы, нам понадобится вызывать их, когда вид появится на экране:
 
   – (void)viewDidAppear:(BOOL)animated{
   [super viewDidAppear: animated];
 
   [self createGestureRecognizer];
   [self createSmallSquareView];
   [self createAnimatorAndBehaviors];
 
   }
 
   Отлично. Теперь, взглянув на файл реализации метода createGestureRecognizer, вы увидите, что мы устанавливаем регистратор жестов касаний в методе контроллера вида – этот метод называется handleTap:. В методе handleTap: вычисляем расстояние между центральной точкой маленького квадратного вида и той точкой опорного вида, до которой дотронулся пользователь. В результате имеем магнитуду силы толчка. Кроме того, рассчитаем угол между центром маленького квадратного вида и точкой касания, чтобы определить угол толчка:
 
   – (void) handleTap:(UITapGestureRecognizer *)paramTap{
 
   /* Получаем угол между центральной точкой квадратного вида и точкой касания */
 
   CGPoint tapPoint = [paramTap locationInView: self.view];
   CGPoint squareViewCenterPoint = self.squareView.center;
 
   /* Вычисляем угол между центральной точкой квадратного вида и точкой касания, чтобы определить угол толчка
   Формула для определения угла между двумя точками:
   arc tangent 2((p1.x – p2.x), (p1.y – p2.y)) */
 
   CGFloat deltaX = tapPoint.x – squareViewCenterPoint.x;
   CGFloat deltaY = tapPoint.y – squareViewCenterPoint.y;
   CGFloat angle = atan2(deltaY, deltaX);
   [self.pushBehavior setAngle: angle];
 
   /* Используем расстояние между точкой касания и центром квадратного вида для вычисления магнитуды толчка
   Формула определения расстояния:
   Квадратный корень из ((p1.x – p2.x)^2 + (p1.y – p2.y)^2) */
 
   CGFloat distanceBetweenPoints =
   sqrt(pow(tapPoint.x – squareViewCenterPoint.x, 2.0) +
   pow(tapPoint.y – squareViewCenterPoint.y, 2.0));
   [self.pushBehavior setMagnitude: distanceBetweenPoints / 200.0f];
   }
   Не буду чрезмерно углубляться в тригонометрию, но в этом коде используется простая формула, изучаемая в школьном курсе. По этой формуле рассчитывается угол в радианах между двумя точками. Также применяется теорема Пифагора, позволяющая узнать расстояние между двумя точками. Эти формулы вы найдете, взглянув на комментарии, которые я оставил в коде. Если же хотите подробнее разобраться с такими понятиями, как углы и радианы, рекомендую проштудировать учебник по тригонометрии.
   Теперь, запустив приложение, вы сначала увидите маленький зеленый квадрат в центре экрана. Дотроньтесь до экрана в любой точке поля, окружающего квадрат (белое пространство), чтобы зеленый квадрат (вид) стал двигаться. В данном примере я беру расстояние между точкой касания и центром квадрата и делю его на 200, чтобы получить реалистичную магнитуду толчка, но вы в данном примере можете увеличить ускорение, выбрав, скажем, значение 100, а не 200. Всегда лучше экспериментировать с разными числовыми значениями, чтобы подобрать оптимальный вариант для вашего приложения.

См. также

   Раздел 2.2.

2.4. Прикрепление нескольких динамических элементов друг к другу

Постановка задачи

   Требуется прикреплять друг к другу динамические элементы, например виды, так, чтобы движения одного вида автоматически приводили в движение второй. В качестве альтернативы можно прикреплять динамический элемент к точке привязки, чтобы при движении этой точки (в результате действий приложения или пользователя) этот элемент автоматически перемещался вместе с ней.

Решение

   Инстанцируйте поведение прикрепления, относящееся к типу UIAttachmentBehavior, с помощью метода экземпляра initWithItem: point: attachedToAnchor: этого класса. Добавьте это поведение к аниматору (см. раздел 2.0), отвечающему за динамику и физику движения.

Обсуждение

   На первый взгляд поведение прикрепления может показаться непонятным. Оно сводится к следующему: вы можете задать на экране точку привязки, а затем заставить точку следовать за этой привязкой. Но я хотел бы обсудить эту возможность подробнее.
   Допустим, у вас на столе лежит большая фотография. Если вы поставите указательный палец в верхний правый угол фотографии и начнете совершать им вращательные движения, то фотография, возможно, также будет вертеться на столе вместе с вашим пальцем. Такое же реалистичное поведение вы можете создать и в iOS, воспользовавшись поведением прикрепления из UIKit.
   В этом примере мы собираемся создать такой эффект, который продемонстрирован на рис. 2.3.
 
   Рис. 2.3. Именно такого эффекта мы хотим добиться в данном разделе с помощью поведения прикрепления
 
   Как видите, на экране находятся три вида. Основной вид расположен в центре, в правом верхнем углу этого вида есть еще один вид, более мелкий. Маленький вид – это и есть тот элемент, который будет следовать за точкой привязки, по принципу, который я описал в примере с фотографией. Наконец, необходимо отметить, что точка привязки в данном примере будет перемещаться по экрану под действием жеста панорамирования и регистратора соответствующих жестов (см. раздел 10.3). Затем в результате таких движений станет двигаться большой вид, расположенный в центре экрана. Итак, начнем с определения необходимых свойств контроллера вида:
 
   #import "ViewController.h"
 
   @interface ViewController ()
   @property (nonatomic, strong) UIView *squareView;
   @property (nonatomic, strong) UIView *squareViewAnchorView;
   @property (nonatomic, strong) UIView *anchorView;
   @property (nonatomic, strong) UIDynamicAnimator *animator;
   @property (nonatomic, strong) UIAttachmentBehavior *attachmentBehavior;
   @end
 
   @implementation ViewController
 
   <# Оставшаяся часть кода контроллера вида находится здесь #>
 
   Далее нам потребуется создать маленький квадратный вид. Но на этот раз мы поместим внутрь него еще один вид. Маленький вид, который будет располагаться в правом верхнем углу родительского вида, мы фактически соединим с точкой привязки поведения прикрепления, как было показано в примере с фотографией:
 
   – (void) createSmallSquareView{
   self.squareView =
   [[UIView alloc] initWithFrame:
   CGRectMake(0.0f, 0.0f, 80.0f, 80.0f)];
 
   self.squareView.backgroundColor = [UIColor greenColor];
   self.squareView.center = self.view.center;
 
   self.squareViewAnchorView = [[UIView alloc] initWithFrame:
   CGRectMake(60.0f, 0.0f, 20.0f, 20.0f)];
   self.squareViewAnchorView.backgroundColor = [UIColor brownColor];
   [self.squareView addSubview: self.squareViewAnchorView];
 
   [self.view addSubview: self.squareView];
   }
   Далее создадим вид с точкой привязки:
   – (void) createAnchorView{
 
   self.anchorView = [[UIView alloc] initWithFrame:
   CGRectMake(120.0f, 120.0f, 20.0f, 20.0f)];
   self.anchorView.backgroundColor = [UIColor redColor];
   [self.view addSubview: self.anchorView];
 
   }
 
   После этого потребуется создать регистратор жестов панорамирования и аниматор, как мы уже делали в предыдущих разделах этой главы:
 
   – (void) createGestureRecognizer{
   UIPanGestureRecognizer *panGestureRecognizer =
   [[UIPanGestureRecognizer alloc] initWithTarget: self
   action:@selector(handlePan:)];
   [self.view addGestureRecognizer: panGestureRecognizer];
   }
 
   – (void) createAnimatorAndBehaviors{
 
   self.animator = [[UIDynamicAnimator alloc]
   initWithReferenceView: self.view];
 
   /* Создаем распознавание столкновений */
   UICollisionBehavior *collision = [[UICollisionBehavior alloc]
   initWithItems:@[self.squareView]];
   collision.translatesReferenceBoundsIntoBoundary = YES;
 
   self.attachmentBehavior = [[UIAttachmentBehavior alloc]
   initWithItem: self.squareView
   point: self.squareViewAnchorView.center
   attachedToAnchor: self.anchorView.center];
   [self.animator addBehavior: collision];
   [self.animator addBehavior: self.attachmentBehavior];
   }
 
   – (void)viewDidAppear:(BOOL)animated{
   [super viewDidAppear: animated];
 
   [self createGestureRecognizer];
   [self createSmallSquareView];
   [self createAnchorView];
   [self createAnimatorAndBehaviors];
   }
 
   Как видите, мы реализуем поведение привязки с помощью его метода экземпляра initWithItem: point: attachedToAnchor:. Этот метод принимает следующие параметры:
   • initWithItem – динамический элемент (в нашем примере – вид), который должен быть подключен к точке привязки;
   • point – точка внутри динамического элемента, которая должна быть соединена с точкой привязки. В данном поведении центральная точка элемента используется для установки соединения с точкой привязки. Но вы можете изменить этот параметр, присвоив ему другое значение;
   • attachedToAnchor – сама точка привязки, измеряемая как значение CGPoint.
   Теперь, когда мы соединили верхний правый угол квадратного вида с точкой привязки (представленной как вид точки привязки), необходимо продемонстрировать, что, двигая точку привязки, мы опосредованно будем двигать и квадратный вид. Вернемся к методу createGestureRecognizer, написанному ранее. Там мы задействовали регистратор жестов касания, который будет отслеживать движение пальца пользователя по экрану. Мы решили обрабатывать регистратор жестов в методе handlePan: вида и реализуем этот метод так:
 
   (void) handlePan:(UIPanGestureRecognizer *)paramPan{
 
   CGPoint tapPoint = [paramPan locationInView: self.view];
   [self.attachmentBehavior setAnchorPoint: tapPoint];
   self.anchorView.center = tapPoint;
 
   }
 
   Здесь мы обнаруживаем в нашем виде движущуюся точку, а потом перемещаем в нее точку привязки. После того как мы это сделаем, произойдет прикрепление и мы сможем двигать также маленький квадрат.

См. также

   Разделы 2.0 и 10.3.

2.5. Добавление эффекта динамического зацепления к компонентам пользовательского интерфейса

Постановка задачи

   С помощью анимации вы хотите прикрепить определенный вид, находящийся в вашем пользовательском интерфейсе, к конкретному месту на экране. При этом должна проявляться эластичность, напоминающая реальный эффект защелкивания. Таким образом, когда элемент пользовательского интерфейса прикрепляется к определенной точке экрана, пользователь ощущает, что этот элемент обладает встроенной эластичностью.

Решение

   Инстанцируйте объект типа UISnapBehavior и добавьте его к аниматору типа UIDynamicAnimator.

Обсуждение

   Чтобы по-настоящему понять, как работает динамика зацепления, представим себе небольшое количество желе, смазанное маслом и лежащее на очень гладком столе. К желе прикреплена струна. Представляю, насколько странным вам это кажется. Но следите за мыслью. Допустим, я стою возле стола и тяну за струну, чтобы желе переместилось из исходной точки на столе в другую, выбранную вами. Поскольку желе со всех сторон покрыто маслом, оно будет плавно двигаться в этом направлении. Но раз это желе, оно, оказавшись в выбранной вами точке, еще некоторое время будет колыхаться. Именно такое поведение реализуется с помощью класса UISnapBehavior.
   Один из способов практического применения такого эффекта заключается в следующем: если у вас есть приложение, на экране с которым расположено несколько видов, то, возможно, вы захотите предоставить пользователю возможность передвигать эти виды по экрану по своему желанию и самостоятельно настраивать компоновку интерфейса. Эту задачу вполне можно решить с помощью приемов, описанных в разделе 2.3, но такой вариант получится слишком негибким. Вообще техники из раздела 2.3 предназначены для решения иных задач. В этом разделе у нас есть экран, и мы добиваемся того, чтобы пользователь мог прикоснуться к любому виду на экране и переместить его. Но потом мы зацепим этот вид, ассоциировав его с точкой, в которой произошло касание.
   В данном рецепте мы собираемся создать маленький вид в центре основного вида контроллера, а потом прикрепить регистратор жестов касания (см. раздел 10.5) к виду с контроллером. Всякий раз, когда пользователь прикасается к экрану в какой-то точке, мы будем зацеплять за эту точку маленький квадратный вид. Итак, приступим к определению необходимых свойств вида с контроллером:
 
   #import "ViewController.h"
 
   @interface ViewController ()
   @property (nonatomic, strong) UIView *squareView;
   @property (nonatomic, strong) UIDynamicAnimator *animator;
   @property (nonatomic, strong) UISnapBehavior *snapBehavior;
   @end
   @implementation ViewController
 
   <# Остальной ваш код находится здесь #>
   Далее напишем метод, который будет создавать регистратор жестов касания:
   – (void) createGestureRecognizer{
 
   UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]
   initWithTarget: self
   action:@selector(handleTap:)];
   [self.view addGestureRecognizer: tap];
 
   }
 
   Как и в предыдущих разделах, нам также понадобится создать маленький вид в центре экрана. Я выбрал для этой цели именно центр, но вы можете использовать в таком качестве другую точку. Этот вид мы будем сцеплять с теми точками экрана, к которым прикоснется пользователь. Итак, вот метод для создания этого вида:
 
   – (void) createSmallSquareView{
   self.squareView =
   [[UIView alloc] initWithFrame:
   CGRectMake(0.0f, 0.0f, 80.0f, 80.0f)];
 
   self.squareView.backgroundColor = [UIColor greenColor];
   self.squareView.center = self.view.center;