12/08/2018, 14:28

CoreMotion

Nếu ai đã từng làm game trên nền tảng iOS thì đều đã sử dụng qua CoreMotion. Vì đơn giản CoreMotion cho phép bạn lấy được dữ liệu vận động của thiết bị(tác động của người dùng lên device). Cụ thể là nó cho phép developer lấy được cả dữ liệu gia tốc kế nguyên thuỷ và đã được xử lý thông qua Block. ...

Nếu ai đã từng làm game trên nền tảng iOS thì đều đã sử dụng qua CoreMotion. Vì đơn giản CoreMotion cho phép bạn lấy được dữ liệu vận động của thiết bị(tác động của người dùng lên device). Cụ thể là nó cho phép developer lấy được cả dữ liệu gia tốc kế nguyên thuỷ và đã được xử lý thông qua Block. Với những thiết bị có gắn cảm biến con quay (gyroscope), chúng ta còn lấy được dữ liệu phản ánh trạng thái và tốc độ quay(tác động của người dùng lên device) của thiết bị. Như vậy developer có thể kết hợp dữ liệu của gia tốc kế và con quay để tạo ra những trải nghiệm thú vị cho người dùng.

Để rõ ràng hơn thì chúng ta sẽ sử dụng CoreMotion để giải quyết 1 vấn đề cụ thể hơn. Đó là xác định orientation. Đọc tới đây thì nhiều developer cho rằng tại sao lại phải sử dụng CoreMotion để giải quyết 1 vấn đề cỏn con như vậy. Vì đơn giản chúng ta có ít nhất 3 cách để thực hiện :

  • Lấy trạng thái của interface
UIInterfaceOrientation interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
  • Lấy trạng thái của device
UIDeviceOrientation deviceOrientation = [[UIDevice currentDevice] orientation];
  • Sử dụng notification
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
    [[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handlerOrientation:) name:UIDeviceOrientationDidChangeNotification object:nil];

Chúng ta có thể bắt buộc 1 màn hình nào đó sẽ phải quay theo giá trị mà ta lấy được từ 1 trong 3 cách trên, bằng cách sử dụng thêm function

- viewDidLoad() {
    UIDeviceOrientation deviceOrientation = [[UIDevice currentDevice] orientation];
    if (UIDeviceOrientationIsLandscape(lastOrientation)) {
        NSNumber *value = [NSNumber numberWithInteger:deviceOrientation];
        [[UIDevice currentDevice] setValue:value forKey:@"orientation"];
    }
    [UIViewController attemptRotationToDeviceOrientation];
}

Mọi chuyện dường như đơn giản. Bởi vì rõ ràng giá trị deviceOrientation mà ta nhận được chính là kết quả của việc tính toán dữ liệu nhận được từ CoreMotion, chỉ khác là chúng ta sử dụng sẵn của hệ thống. Và đó cũng chính là điểm hạn chế. Bạn có thể search từ khoá Motion Events và đọc nội dụng của "Getting the Current Device Orientation with UIDevice" thì sẽ nhận thấy nội dung sau: "Use the methods of the UIDevice class when you need to know only the general orientation of the device and not the exact vector of orientation. Using UIDevice is simple and doesn’t require you to calculate the orientation vector yourself." Nghĩa là kết quả- giá trị của orientation, mà bạn nhận được không thực sự chính xác.

Điều này có thể kiểm chứng bằng cách log ra giá trị orientation khi thực hiện xoay device. Ví dụ như bạn đang ở view A (chỉ support portrait), bạn push sang view B (support portrait và lansapce) (2 view này có controller khác nhau). Khi ở view A bạn để device ở trạng thái lanscape và thực hiện chuyển sang view B. Kết quả orientation nhận được sau khi chuyển view xong đôi khi orientation = 2(nghĩa là portrait) trong khi device đang ở trạng thái landscape. Thật là chớ trêu.

Với trường hợp này CoreMotion sẽ là giải pháp. Cách sử dụng rất đơn giản. Bạn chỉ cần tạo 1 sự kiện lắng nghe dữ liệu CoreMotion trả về và tiến hành tính toán 1 cách rất đơn giả như sau

CMMotionManager *_motionManager;
- (void)initializeMotionManager{
    _motionManager = [[CMMotionManager alloc] init];
    _motionManager.accelerometerUpdateInterval = 1.0;
    _motionManager.gyroUpdateInterval = 1.0;

    [_motionManager startAccelerometerUpdatesToQueue:[NSOperationQueue currentQueue]
                                        withHandler:^(CMAccelerometerData  *accelerometerData, NSError *error) {
                                            if (!error) {
                                                [self outputAccelertionData:accelerometerData.acceleration];
                                            }
                                            else{
                                                NSLog(@"%@", error);
                                            }
                                        }];
}

- (void)outputAccelertionData:(CMAcceleration)acceleration{
    UIDeviceOrientation orientationNew;
    if (acceleration.x >= 0.75) {
        orientationNew = UIDeviceOrientationLandscapeRight;
    }
    else if (acceleration.x <= -0.75) {
        orientationNew = UIDeviceOrientationLandscapeLeft;
    }
    else {
        return;
    }
    if (orientationNew == _orientationLast)
        return;
    _orientationLast = orientationNew;
}

Trong trường hợp này chúng ta chỉ đơn giản làm thế nào đó để xác định device đang là lanscape hay portrait mà thôi. Vì vậy chỉ cần dựa vào giá trị của trục x do gia tốc kế trả về. Với trường hợp này sau khi thực hiện đo đạc thực tế thì giá trị +-0.75 là giá trị hợp lý để quyết định device là lanscape hay portrait. Và từ đó ta có thể hoàn toàn force trạng thái của interface.

Hy vọng với tips nhỏ trên sẽ giup đỡ các bạn giảm thiểu thời gian thực hiện dự án.

Có lưu ý nhỏ của Apple khi sử dụng CoreMotion:

An iOS app linked on or after iOS 10.0 must include in its Info.plist file the usage description keys for the types of data it needs to access or it will crash. To access motion and fitness data specifically, it must include NSMotionUsageDescription.

0