- PVSM.RU - https://www.pvsm.ru -
В последнее время обратил внимание на ролики программ, в которых реализован так называемый псевдо 3D эффект: когда картинка приложения изменяется в зависимости от положения пользователя относительно телефона. Или телефона относительно пользователя: смотря с какой стороны вы находитесь :). Для достижения этого эффекта можно использовать либо сенсоры либо отслеживать положение глаз пользователя (т.н. head tracking). Второй способ несколько сложнее, хотя даёт более правдоподобный результат.
В качестве эксперимента мы решили попробовать сделать такой 3Д фон в программе Deluxe Moon Pro [1] (В версии на маркете пока этот эффект не реализован!).
Вот пример того, что у нас получилось:
Итак начнем.
Для того, чтобы достичь 3Д эффекта необходимо изображение разбить на «слои» и в зависимости от показаний акселерометра смещать каждый слой на определённую величину.
Казалось бы всё просто, но есть несколько проблем.
Таким образом, общий подход к решению данной задачи такой:
Разбить фон на неско
Итак, в нашем случае у нас есть изображение фона, + 2 слоя звезд, + слои различных элементов экрана.
Для нужд фильтрации устанавливаем некоторые константы:
#define kUpdateFrequency 240.0 //частота обновления фильтра
#define kCutoffFrequency 5.0 //частота фильтрации
#define kAccDataScale 2.2 //масштаб данных
При запуске программы мы инициализируем низкочастотный фильтр для того чтобы убрать дребезжание фона, вызванное дрожанием рук.
про него можно почитать вот здесь [2]
и скачать пример реализации у Apple здесь [3]
-(void)awakeFromNib {
filter = [[LowpassFilter alloc] initWithSampleRate:kUpdateFrequency/10 cutoffFrequency:kCutoffFrequency]; //собственно фильтр низких частот
if ([Options instance].useSensors) {
[self performSelector:@selector(startAcc) withObject:nil afterDelay:4]; // стартуем с запаздыванием чтобы программа успела запуститься и заполнить нормальные начальные значения акселерометра
}
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didGoToBG:) name:UIApplicationDidEnterBackgroundNotification object:nil]; //программа ушла в фон
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didResume:) name:UIApplicationDidBecomeActiveNotification object:nil]; //Программа стала активна
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(useSensorsChanged) name:@"useSensors" object:nil]; //подписываемся на изменения опции
}
Реакцию на изменение опции по вкл/выкл 3д эффекта фона и на сворачивание программы в фон описывать думаю не стоит, там все банально
Теперь рассмотрим функцию которая запускает всю эту красоту
P.S. Используется ARC потому, никаких retain или release
-(void)startAcc {
@try {
if (!acc) {
startY = 100; //это стартовое положение сенсоров по Y чтобы не приходилось постоянно держать устройство в горизонтальном положении
acc = [UIAccelerometer sharedAccelerometer];
acc.updateInterval = 1/kUpdateFrequency;
acc.delegate = self;
}
}
@catch (NSException *exception) {
NSLog(@"startAcc %@",exception);
}
}
Чтобы остановить все это дело
-(void)stopAcc {
@try {
if (acc) {
acc.delegate = nil;
acc = nil;
[self resetState];
}
}
@catch (NSException *exception) {
NSLog(@"stopAcc %@",exception);
}
}
Дальше нам остается только ловить событие обновления акселерометра
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
@try {
[filter addAcceleration:acceleration]; //фильтруем данные чтобы избежать «тряски» фона
double x = filter.x*kAccDataScale; //масштабируем данные чтобы увеличить частоту
double y = filter.y*kAccDataScale;
if (startY == 100) {
startY = acceleration.y*kAccDataScale; // запоминаем начальные данные по Y
}
// убираем лишнее по X
if (x > 1) {
x = 1;
}
// учитываем начальное значение
y = (y - startY);
// убираем лишнее по Y
if (y > 1) {
y = 1;
}
//Сдвигаем необходимые элементы
[self setOffsetElementForX:x Y:y];
}
@catch (NSException *exception) {
NSLog(@"accelerometer %@",exception);
}
}
@end
И, наконец, в функции сдвига мы передвигаем разные слои на разную фиксированную величину. Поэтому, верхние элементы двигаются с меньшей скоростью чем фон что и создает эффект 3Д. При этом, все элементы должны сдвигаться в сторону поворота устройства.
-(void)setOffsetElementForX:(double)x Y:(double)y {
@try {
allEll.transform = CGAffineTransformMakeTranslation(-8.5*x, 11.5*(y - startY));
movingButtonsView.transform = CGAffineTransformMakeTranslation(-8.5*x, 11.5*(y - startY));
arcView.transform = CGAffineTransformMakeTranslation(-8.5*x, 11.5*(y - startY));
bgView.transform = CGAffineTransformMakeTranslation(-17*x, 23*(y - startY));
bgStar1View.transform = CGAffineTransformMakeTranslation(-13*x, 18*(y - startY));
bgStar2View.transform = CGAffineTransformMakeTranslation(-10*x, 13*(y - startY));
allEll2.transform = CGAffineTransformMakeTranslation(-17*x, 21*(y - startY));
}
@catch (NSException *exception) {
NSLog(@"offsetElementForX %@",exception);
}
}
Эти 4 простых шага позволят сделать программы привлекательнее, интереснее и инновационее. 21-й век всё-таки. Помните, что эффектный и стильный дизайн может положительно повлиять на оценку вашего приложения ревьюверами, а также выделить их из сотен тысяч конкурентов. Мы в Lifeware Solutions (http://www.lifewaresolutions.com/) активно занимаемся исследованиями и будем рады, если наш опыт будет вам полезен. Буду очень благодарен за ваши отзывы.
Автор: VasKravchuk
Источник [4]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/objective-c/22276
Ссылки в тексте:
[1] Deluxe Moon Pro: https://itunes.apple.com/us/app/deluxe-moon-pro-moon-phases/id482361332?mt=8
[2] здесь: http://en.wikipedia.org/wiki/Low-pass_filter
[3] здесь: https://developer.apple.com/library/ios/#samplecode/AccelerometerGraph/Introduction/Intro.html
[4] Источник: http://habrahabr.ru/post/162081/
Нажмите здесь для печати.