iOS. Приемы программирования - Вандад Нахавандипур
Шрифт:
Интервал:
Закладка:
(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;
[self.view addSubview: self.squareView];
}
Переходим к созданию аниматора (см. раздел 2.0), после чего прикрепляем к нему поведение зацепления. Инициализируем поведение зацепления типа UISnapBehavior с помощью метода initWithItem: snapToPoint:. Этот метод принимает два параметра:
• initWithItem — динамический элемент (в данном случае наш вид), к которому должно применяться поведение зацепления. Как и другие динамические поведения пользовательского интерфейса, этот элемент должен соответствовать протоколу UIDynamicItem. Все экземпляры UIView по умолчанию соответствуют этому протоколу, поэтому все нормально;
• snapToPoint — точка опорного вида (см. раздел 2.0), за которую должен зацепляться динамический элемент.
Следует сделать одно важное замечание о таком зацеплении: чтобы оно работало с конкретным элементом, к аниматору уже должен быть добавлен как минимум один экземпляр зацепления для этого элемента — кроме того экземпляра, который удерживает элемент на текущей позиции. После этого все последующие зацепления будут работать правильно. Позвольте это продемонстрировать. Сейчас мы реализуем метод, который будет создавать поведение зацепления и аниматор, а потом добавлять это поведение к аниматору:
— (void) createAnimatorAndBehaviors{
self.animator = [[UIDynamicAnimator alloc]
initWithReferenceView: self.view];
/* Создаем обнаружение столкновений */
UICollisionBehavior *collision = [[UICollisionBehavior alloc]
initWithItems:@[self.squareView]];
collision.translatesReferenceBoundsIntoBoundary = YES;
[self.animator addBehavior: collision];
/* Пока зацепляем квадратный вид с его актуальным центром */
self.snapBehavior = [[UISnapBehavior alloc]
initWithItem: self.squareView
snapToPoint: self.squareView.center];
self.snapBehavior.damping = 0.5f; /* Medium oscillation */
[self.animator addBehavior: self.snapBehavior];
}
Как видите, здесь мы зацепляем небольшой квадратный вид, связывая его с текущим центром, — в сущности, просто оставляем его на месте. Позже, когда мы регистрируем на экране жесты касания, мы обновляем поведение зацепления. Кроме того, необходимо отметить, что мы задаем для этого поведения свойство damping. Это свойство будет управлять эластичностью, с которой элемент будет зацеплен за точку. Чем выше значение, тем меньше эластичность, соответственно, тем слабее «колышется» элемент. Здесь можно задать любое значение в диапазоне от 0 до 1. Теперь, когда вид появится на экране, вызовем эти методы, чтобы инстанцировать маленький квадратный вид, установить регистратор жестов касания, а также настроить аниматор и поведение зацепления:
— (void)viewDidAppear:(BOOL)animated{
[super viewDidAppear: animated];
[self createGestureRecognizer];
[self createSmallSquareView];
[self createAnimatorAndBehaviors];
}
После создания регистратора жестов касания в методе createGestureRecognizer вида с контроллером мы приказываем регистратору сообщать о таких касаниях методу handleTap: вида с контроллером. В этом методе мы получаем точку, в которой пользователь прикоснулся к экрану, после чего обновляем поведение зацепления.
Здесь необходимо отметить, что вы не сможете просто обновить существующее поведение — потребуется повторно его инстанцировать. Итак, прежде, чем мы инстанцируем новый экземпляр поведения зацепления, понадобится удалить старый экземпляр (при его наличии), а потом добавить к аниматору новый. У каждого аниматора может быть всего одно поведение зацепления, ассоциированное с конкретным динамическим элементом, в данном случае с маленьким квадратным видом. Если добавить к одному и тому же аниматору несколько поведений зацепления, относящихся к одному и тому же динамическому элементу, то аниматор проигнорирует все эти поведения, так как не будет знать, какое из них выполнять первым. Поэтому, чтобы поведения зацепления работали, сначала удалите все зацепления для этого элемента из вашего аниматора, воспользовавшись его методом removeBehavior:, а потом добавьте новое поведение зацепления следующим образом:
— (void) handleTap:(UITapGestureRecognizer *)paramTap{
/* Получаем угол между центром квадратного вида и точкой касания */
CGPoint tapPoint = [paramTap locationInView: self.view];
if (self.snapBehavior!= nil){
[self.animator removeBehavior: self.snapBehavior];
}
self.snapBehavior = [[UISnapBehavior alloc] initWithItem: self.squareView
snapToPoint: tapPoint];
self.snapBehavior.damping = 0.5f; /* Средняя осцилляция */
[self.animator addBehavior: self.snapBehavior];