События движения

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

Акселерометр фактически составлен из трех акселерометров, один для каждой оси — x, y, и z. Каждый измеряет изменения в скорости в течение долгого времени вдоль линейного контура. Объединение всех трех акселерометров позволяет Вам обнаружить перемещение устройств в любом направлении и получить текущую ориентацию устройства. Несмотря на то, что существует три акселерометра, остаток от этого документа именует их как единственный объект. Гироскоп измеряет уровень вращения вокруг этих трех осей.

Все события движения происходят из тех же аппаратных средств. Существует несколько различных способов, которыми можно получить доступ к тем аппаратным данным, в зависимости от потребностей приложения:

Получение текущей ориентации устройства с UIDevice

Используйте методы UIDevice класс, когда необходимо знать только общую ориентацию устройства а не точного вектора ориентации. Используя UIDevice просто и не требует, чтобы Вы вычислили вектор ориентации сами.

Прежде чем можно будет получить текущую ориентацию, необходимо сказать UIDevice класс, чтобы начать генерировать уведомления ориентации устройства путем вызова beginGeneratingDeviceOrientationNotifications метод. Это включает аппаратные средства акселерометра, которые могут быть прочь для сохранения заряда батареи. Перечисление 4-1 демонстрирует это в viewDidLoad метод.

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

Когда Вы больше не должны будете знать ориентацию устройства, всегда отключайте уведомления ориентации путем вызова UIDevice метод, endGeneratingDeviceOrientationNotifications. Это дает системе возможность отключить аппаратные средства акселерометра, если это не используется в другом месте, который сохраняет заряд батареи.

Перечисление 4-1  , Реагирующее на изменения в ориентации устройства

-(void) viewDidLoad {
     // Request to turn on accelerometer and begin receiving accelerometer events
     [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object:nil];
}
 
- (void)orientationChanged:(NSNotification *)notification {
     // Respond to changes in device orientation
}
 
-(void) viewDidDisappear {
     // Request to stop receiving accelerometer events and turn off accelerometer
     [[NSNotificationCenter defaultCenter] removeObserver:self];
     [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
}
 

Для другого примера ответа к UIDevice изменения ориентации, см. Альтернативный проект примера кода Представлений.

Обнаружение событий движения встряски с UIEvent

Когда пользователи встряхивают устройство, iOS оценивает данные акселерометра. Если данные удовлетворяют определенные критерии, iOS интерпретирует дрожащий жест и создает a UIEvent объект представлять его. Затем это отправляет объект-событие в в настоящее время активное приложение для обработки. Обратите внимание на то, что Ваше приложение может реагировать на события движения встряски и изменения ориентации устройства одновременно.

События движения более просты, чем сенсорные события. Система говорит приложение, когда движение запускается и останавливается, но не, когда происходит каждое отдельное движение. И, события движения включают только тип события (UIEventTypeMotion), подтип события (UIEventSubtypeMotionShake), и метка времени.

Обозначение первого респондента для событий движения

Для получения событий движения определяйте объект респондента как первого респондента. Это - объект респондента, что Вы хотите обработать события движения. Перечисление 4-2 показывает, как респондент может сделать себя первым респондентом.

Перечисление 4-2  , Становящееся первым респондентом

- (BOOL)canBecomeFirstResponder {
    return YES;
}
 
- (void)viewDidAppear:(BOOL)animated {
    [self becomeFirstResponder];
}

События движения используют цепочку респондента для нахождения объекта, который может обработать событие. Когда пользователь начинает встряхивать устройство, iOS отправляет первое событие движения первому респонденту. Если первый респондент не обрабатывает событие, оно развивается цепочка респондента. Посмотрите, что Цепочка Респондента Следует за Определенным Путем Поставки для получения дополнительной информации. Если событие встряхивающего движения перемещается цепочка респондента в окно, не будучи обработанным и applicationSupportsShakeToEdit свойство UIApplication установлен в YES (значение по умолчанию), iOS выводит на экран лист с командами Undo и Redo.

Реализация методов обработки движения

Существует три метода обработки движения: motionBegan:withEvent:, motionEnded:withEvent:, и motionCancelled:withEvent:. Для обработки событий движения необходимо реализовать любого motionBegan:withEvent: метод или motionEnded:withEvent: метод, и иногда оба. Респондент должен также реализовать motionCancelled:withEvent: метод для ответа, когда iOS отменяет событие движения. Событие отменяется, если движение встряски прервано или если iOS решает, что движение не допустимо, в конце концов —, например, если сотрясение длится слишком долго.

Перечисление 4-3 извлечено из проекта примера кода, GLPaint. В этом приложении пользователь подрисовывает экран, и затем встряхивает устройство для стирания рисования. Этот код обнаруживает, произошла ли встряска в motionEnded:withEvent: метод, и если это имеет, отправляет уведомление для выполнения функциональности встряски к стиранию.

Перечисление 4-3  , Обрабатывающее событие движения

- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
    if (motion == UIEventSubtypeMotionShake)
    {
        // User was shaking the device. Post a notification named "shake."
        [[NSNotificationCenter defaultCenter] postNotificationName:@"shake" object:self];
     }
}

Установка и проверка требуемых возможностей оборудования для событий движения

Прежде чем Ваше приложение может связанные с устройством доступа функции, такие как данные акселерометра, необходимо добавить список требуемых возможностей к приложению. В частности Вы добавляете ключи к своему приложению Info.plist файл. Во время выполнения iOS запускает Ваше приложение, только если устройство имеет требуемые возможности. Например, если Ваше приложение полагается на данные гироскопа, перечислите гироскоп как требуется так, чтобы Ваше приложение не запускалось на устройствах без гироскопа. App Store также использует этот список для информирования пользователей так, чтобы они могли избежать загружать приложения, которые они не могут выполнить.

Объявите требуемые возможности своего приложения путем добавления ключей к списку свойств приложения. Существуют два UIRequiredDeviceCapabilities ключи для событий движения, на основе аппаратного источника:

Можно использовать или массив или словарь для указания значений ключа. Если Вы используете массив, перечисляете каждую требуемую функцию как ключ в массиве. При использовании словаря укажите булево значение для каждого требуемого ключа в словаре. В обоих случаях перечисление ключа для функции указывает, что не требуется функция. Для получения дополнительной информации посмотрите UIRequiredDeviceCapabilities.

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

Получение перемещения устройств с базовым движением

Базовая платформа Движения прежде всего ответственна за доступ к необработанному акселерометру и данным гироскопа и передаче тех данных к приложению для обработки. Базовое Движение использует уникальные алгоритмы для обработки необработанных данных, которые оно собирает, так, чтобы оно могло представить более усовершенствованную информацию. Эта обработка происходит на собственном потоке платформы.

Базовое Движение отлично от UIKit. Это не соединяется с UIEvent модель и не использует цепочку респондента. Вместо этого Базовое Движение просто поставляет события движения непосредственно приложениям, запрашивающим их.

Базовые события Motion представлены тремя объектами данных, каждый инкапсулирующий одно или более измерений:

CMMotionManager класс является центральной точкой доступа для Базового Движения. Вы создаете экземпляр класса, указываете интервал обновления, запрашиваете, чтобы обновления запустились и обработали события движения, когда они поставлены. Приложение должно создать только единственный экземпляр CMMotionManager класс. Многократные экземпляры этого класса могут влиять на уровень, на котором приложение получает данные от акселерометра и гироскопа.

Все инкапсулирующие данные классы Базового Движения являются подклассами CMLogItem, который определяет метку времени так, чтобы данные движения могли тегироваться со временем и регистрироваться к файлу. Приложение может сравнить метку времени событий движения с более ранними событиями движения для определения истинного интервала обновления между событиями.

Для каждого из описанных типов движения данных, CMMotionManager класс предлагает два подхода для получения данных движения:

Получение по запросу является рекомендуемым подходом для большинства приложений, особенно игры. Это обычно более эффективно и требует меньшего количества кода. Нажатие является подходящим для приложений сбора данных и подобных приложений, которые не могут пропустить единственное демонстрационное измерение. Оба подхода имеют мягкие эффекты потокобезопасности; с нажатием Ваш блок выполняется на потоке очереди работы, тогда как с получением по запросу, Базовое Движение никогда не прерывает Ваши потоки.

Всегда останавливайте обновления движения, как только Ваше приложение заканчивает обрабатывать необходимые данные. В результате Базовое Движение может выключить датчики движения, который сохраняет заряд батареи.

Выбор интервала обновления события движения

Когда Вы запрашиваете данные движения с Базовым Движением, Вы указываете интервал обновления. Необходимо выбрать самый большой интервал, удовлетворяющий потребности приложения. Чем больше интервал, тем меньше событий поставлено Вашему приложению, улучшающему время работы от батареи. Таблица 4-1 перечисляет некоторые общие частоты обновления и объясняет, что можно сделать с данными, сгенерированными в той частоте. Для немногих приложений нужны ускоряющие события, поставленные 100 раз в секунду.

Таблица 4-1  Общие интервалы обновления для ускоряющих событий

Частота события (Гц)

Использование

10–20

Подходящий для определения текущего вектора ориентации устройства.

30–60

Подходящий для игр и других приложений, использующих акселерометр для ввода данных пользователем в реальном времени.

70–100

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

Можно установить интервал создания отчетов, чтобы быть всего 10 миллисекундами (мс), соответствующий частоте обновления на 100 Гц, но большая часть приложения работает достаточно с большим интервалом.

Обработка событий акселерометра Используя базовое движение

Акселерометр измеряет скорость в течение долгого времени вдоль трех осей, как показано на рисунке 4-1. С Базовым Движением каждое перемещение получено в a CMAccelerometerData объект, инкапсулирующий структуру типа CMAcceleration.

Рисунок 4-1  акселерометр измеряет скорость вдоль x, y, и оси z

Чтобы начать получать и обрабатывать данные акселерометра, создайте экземпляр CMMotionManager класс и вызов один из следующих методов:

  • startAccelerometerUpdates— подход получения по запросу

    После вызова этого метода Базовое Движение постоянно обновляет accelerometerData свойство CMMotionManager с последним измерением действия акселерометра. Затем Вы периодически выбираете это свойство, обычно в цикле рендеринга, который распространен в играх. Если Вы принимаете этот подход опроса, устанавливаете свойство интервала обновления (accelerometerUpdateInterval) к максимальному интервалу, в котором Базовое Движение выполняет обновления.

  • startAccelerometerUpdatesToQueue:withHandler:— подход нажатия

    Прежде чем Вы вызовете этот метод, присвоите интервал обновления accelerometerUpdateInterval свойство, создайте экземпляр NSOperationQueue и реализуйте блок типа CMAccelerometerHandler это обрабатывает обновления акселерометра. Затем вызовите startAccelerometerUpdatesToQueue:withHandler: метод на объекте менеджера движения, передающем в очереди работы и блоке. В указанном интервале обновления Базовое Движение передает последнюю выборку действия акселерометра к блоку, выполняющемуся как задача в очереди.

    Перечисление 4-4 иллюстрирует этот подход.

Перечисление 4-4 извлечено из проекта примера кода MotionGraphs, который можно исследовать на большее количество контекста. В этом приложении пользователь перемещает ползунок для указания интервала обновления. startUpdatesWithSliderValue: метод использует значение ползунка для вычислений нового интервала обновления. Затем это создает экземпляр CMMotionManager класс, проверки, чтобы удостовериться, что устройство имеет акселерометр и присваивает интервал обновления менеджеру по движению. Это приложение использует подход нажатия, чтобы получить данные акселерометра и графически изобразить его на графике. Обратите внимание на то, что это останавливает обновления акселерометра в stopUpdates метод.

Перечисление 4-4  , Получающее доступ к данным акселерометра в MotionGraphs

static const NSTimeInterval accelerometerMin = 0.01;
 
- (void)startUpdatesWithSliderValue:(int)sliderValue {
 
     // Determine the update interval
     NSTimeInterval delta = 0.005;
     NSTimeInterval updateInterval = accelerometerMin + delta * sliderValue;
 
     // Create a CMMotionManager
     CMMotionManager *mManager = [(APLAppDelegate *)[[UIApplication sharedApplication] delegate] sharedManager];
     APLAccelerometerGraphViewController * __weak weakSelf = self;
 
     // Check whether the accelerometer is available
     if ([mManager isAccelerometerAvailable] == YES) {
          // Assign the update interval to the motion manager
          [mManager setAccelerometerUpdateInterval:updateInterval];
          [mManager startAccelerometerUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMAccelerometerData *accelerometerData, NSError *error) {
               [weakSelf.graphView addX:accelerometerData.acceleration.x y:accelerometerData.acceleration.y z:accelerometerData.acceleration.z];
               [weakSelf setLabelValueX:accelerometerData.acceleration.x y:accelerometerData.acceleration.y z:accelerometerData.acceleration.z];
          }];
     }
 
     self.updateIntervalLabel.text = [NSString stringWithFormat:@"%f", updateInterval];
}
 
 
- (void)stopUpdates {
     CMMotionManager *mManager = [(APLAppDelegate *)[[UIApplication sharedApplication] delegate] sharedManager];
     if ([mManager isAccelerometerActive] == YES) {
          [mManager stopAccelerometerUpdates];
     }
}

Обработка данных скорости вращения

Гироскоп измеряет уровень, на котором устройство вращается вокруг каждой из трех пространственных осей, как показано на рисунке 4-2.

Рисунок 4-2  гироскоп измеряет вращение вокруг x, y, и оси z

Каждый раз, когда Вы запрашиваете обновление гироскопа, Базовое Движение берет смещенную оценку уровня вращения и возвращает эту информацию в a CMGyroData объект. CMGyroData имеет a rotationRate свойство, хранящее a CMRotationRate структура, получающая скорость вращения для каждой из этих трех осей в радианах в секунду. Обратите внимание на то, что скорость вращения, измеренная a CMGyroData объект смещается. Можно получить намного более точное, несмещенное измерение при помощи CMDeviceMotion класс. Посмотрите Обработку Обработанные Данные Движения Устройства для получения дополнительной информации.

При анализе данных скорости вращения — в частности, при анализе полей CMRotationMatrix структура — следует за «правилом правой руки» для определения направления вращения, как показано на рисунке 4-2. Например, при обертывании правой руки вокруг оси X, таким образом, что подсказка точек ползунка к положительному x, положительное вращение один к подсказкам других четырех пальцев. Отрицательное вращение уходит от подсказок тех пальцев.

Чтобы начать получать и обрабатывать данные скорости вращения, создайте экземпляр CMMotionManager класс и вызов один из следующих методов:

  • startGyroUpdates— подход получения по запросу

    После вызова этого метода Базовое Движение постоянно обновляет gyroData свойство CMMotionManager с последним измерением действия гироскопа. Затем Вы периодически выбираете это свойство. Если Вы принимаете этот подход опроса, устанавливаете свойство интервала обновления (gyroUpdateInterval) к максимальному интервалу, в котором Базовое Движение выполняет обновления.

  • startGyroUpdatesToQueue:withHandler:— подход нажатия

    Прежде чем Вы вызовете этот метод, присвоите интервал обновления gyroUpdateInterval свойство, создайте экземпляр NSOperationQueue, и реализуйте блок типа CMGyroHandler это обрабатывает обновления гироскопа. Затем вызовите startGyroUpdatesToQueue:withHandler: метод на объекте менеджера движения, передающем в очереди работы и блоке. В указанном интервале обновления Базовое Движение передает последнюю выборку действия гироскопа к блоку, выполняющемуся как задача в очереди.

    Перечисление 4-5 демонстрирует этот подход.

Перечисление 4-5 также извлечено из проекта примера кода MotionGraphs и почти идентично Перечислению 4-4. Приложение использует подход нажатия для получения данных гироскопа так, чтобы это могло графически изобразить данных на экране.

Перечисление 4-5  , Получающее доступ к данным гироскопа в MotionGraphs

static const NSTimeInterval gyroMin = 0.01;
 
- (void)startUpdatesWithSliderValue:(int)sliderValue {
 
     // Determine the update interval
     NSTimeInterval delta = 0.005;
     NSTimeInterval updateInterval = gyroMin + delta * sliderValue;
 
     // Create a CMMotionManager
     CMMotionManager *mManager = [(APLAppDelegate *)[[UIApplication sharedApplication] delegate] sharedManager];
     APLGyroGraphViewController * __weak weakSelf = self;
 
     // Check whether the gyroscope is available
     if ([mManager isGyroAvailable] == YES) {
          // Assign the update interval to the motion manager
          [mManager setGyroUpdateInterval:updateInterval];
          [mManager startGyroUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMGyroData *gyroData, NSError *error) {
               [weakSelf.graphView addX:gyroData.rotationRate.x y:gyroData.rotationRate.y z:gyroData.rotationRate.z];
               [weakSelf setLabelValueX:gyroData.rotationRate.x y:gyroData.rotationRate.y z:gyroData.rotationRate.z];
          }];
     }
     self.updateIntervalLabel.text = [NSString stringWithFormat:@"%f", updateInterval];
}
 
- (void)stopUpdates{
     CMMotionManager *mManager = [(APLAppDelegate *)[[UIApplication sharedApplication] delegate] sharedManager];
     if ([mManager isGyroActive] == YES) {
          [mManager stopGyroUpdates];
     }
}

Обработка обработанных данных движения устройства

Если устройство имеет и акселерометр и гироскоп, Базовое Движение предлагает услугу движения устройства, обрабатывающую необработанные данные движения от обоих датчиков. Движение устройства использует алгоритмы слияния датчика, чтобы совершенствовать необработанные данные и генерировать информацию для отношения устройства, его несмещенной скорости вращения, направления силы тяжести на устройстве и сгенерированного пользователями ускорения. Экземпляр CMDeviceMotion класс инкапсулирует все эти данные. Кроме того, Вы не должны фильтровать ускоряющие данные, потому что движение устройства разделяет пользовательское ускорение и сила тяжести.

Можно получить доступ к данным отношения через a CMDeviceMotion объект attitude свойство, инкапсулирующее a CMAttitude объект. Каждый экземпляр CMAttitude класс инкапсулирует три математических представления отношения:

  • кватернион

  • матрица вращения

  • три Эйлеровых угла (рулон, подача и отклонение от курса)

Чтобы начать получать и обрабатывать обновления движения устройства, создайте экземпляр CMMotionManager класс и вызов один из следующих двух методов на нем:

  • startDeviceMotionUpdates— подход получения по запросу

    После вызова этого метода Базовое Движение постоянно обновляет deviceMotion свойство CMMotionManager с последними усовершенствованными измерениями акселерометра и действием гироскопа, как инкапсулируется в a CMDeviceMotion объект. Затем Вы периодически выбираете это свойство. Если Вы принимаете этот подход опроса, устанавливаете свойство интервала обновления (deviceMotionUpdateInterval) к максимальному интервалу, в котором Базовое Движение выполняет обновления.

    Перечисление 4-6 иллюстрирует этот подход.

  • startDeviceMotionUpdatesToQueue:withHandler:— подход нажатия

    Прежде чем Вы вызовете этот метод, присвоите интервал обновления deviceMotionUpdateInterval свойство, создайте экземпляр NSOperationQueue, и реализуйте блок CMDeviceMotionHandler введите, который обрабатывает обновления акселерометра. Затем вызовите startDeviceMotionUpdatesToQueue:withHandler: метод на объекте менеджера движения, передающем в очереди работы и блоке. В указанном интервале обновления Базовое Движение передает последнюю выборку объединенного акселерометра и действие гироскопа, как представлено a CMDeviceMotion объект, к блоку, выполняющемуся как задача в очереди.

Перечисление 4-6 использует код из проекта примера кода парка продемонстрировать, как запустить и остановить обновления движения устройства. startDeviceMotion метод использует подход получения по запросу для запуска обновлений устройств со ссылочного кадра. Посмотрите Отношение Устройства и Ссылочный Кадр для больше о ссылочных кадрах движения устройства.

Перечисление 4-6  Стартовые и останавливающиеся обновления движения устройства

- (void)startDeviceMotion {
     // Create a CMMotionManager
     motionManager = [[CMMotionManager alloc] init];
 
     // Tell CoreMotion to show the compass calibration HUD when required
     // to provide true north-referenced attitude
     motionManager.showsDeviceMovementDisplay = YES;
     motionManager.deviceMotionUpdateInterval = 1.0 / 60.0;
 
     // Attitude that is referenced to true north
     [motionManager startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXTrueNorthZVertical];
}
 
 
- (void)stopDeviceMotion {
     [motionManager stopDeviceMotionUpdates];
}

Отношение устройства и ссылочный кадр

A CMDeviceMotion объект содержит информацию об отношении устройства или ориентацию в пространстве. Отношение устройства всегда измеряется в связи со ссылочным кадром. Когда Ваше приложение запускает обновления движения устройства, базовое Движение устанавливает ссылочный кадр. Затем CMAttitude дает вращение от того начального ссылочного кадра до текущего ссылочного кадра устройства.

В Базовом ссылочном кадре Движения ось z является всегда вертикальной, и x-и ось y являются всегда ортогональными к силе тяжести, делающей вектор силы тяжести [0, 0,-1]. Это также известно как ссылка силы тяжести. Если Вы умножаетесь, матрица вращения получила из a CMAttitude объект ссылкой силы тяжести, Вы получаете силу тяжести в кадре устройства. Или, математически:

../Art/rm_times_gravityret_2x.png

Можно изменить ссылочный кадр это CMAttitude использование. Чтобы сделать это, кэшируйте объект отношения, содержащий ссылочный кадр, и передайте его как параметр multiplyByInverseOfAttitude:. Параметр отношения, получающий сообщение, изменяется так, чтобы это представляло изменение в отношении от переданного - в ссылочном кадре.

Большинство приложений интересуется изменением в отношении устройства. Чтобы видеть, как это могло бы быть полезно, рассмотрите бейсбольный матч, где пользователь поворачивает устройство для покачивания. Обычно, в начале подачи, летучая мышь была бы при некоторой покоящейся ориентации. После этого летучая мышь представляется на основе того, как отношение устройства изменилось от запуска подачи. Перечисление 4-7 иллюстрирует, как Вы могли бы сделать это.

Перечисление 4-7  , Получающее изменение в отношении до рендеринга

-(void) startPitch {
    // referenceAttitude is a property
    self.referenceAttitude = self.motionManager.deviceMotion.attitude;
}
 
- (void)drawView {
    CMAttitude *currentAttitude = self.motionManager.deviceMotion.attitude;
    [currentAttitude multiplyByInverseOfAttitude: self.referenceAttitude];
    // Render bat using currentAttitude
    [self updateModelsWithAttitude:currentAttitude];
    [renderer render];
}

В этом примере, после multiplyByInverseOfAttitude: возвраты метода, currentAttitude представляет изменение в отношении от referenceAttitude к последний раз выбранному CMAttitude экземпляр.