Ориентация устройства iOS без учета ориентационной блокировки - PullRequest
30 голосов
/ 01 января 2011

Я хочу запросить ориентацию, в которой сейчас находится iPhone. Использование

[UIDevice currentDevice].orientation

работает до тех пор, пока устройство не заблокировано по ориентации.Однако, если он заблокирован, он всегда отвечает заблокированной ориентацией, а не фактической ориентацией устройства.

Существует ли высокоуровневый способ получения фактической ориентации устройства?

Ответы [ 8 ]

32 голосов
/ 23 января 2015

Также вы можете использовать CoreMotion

Алгоритм определения ориентации:

  • , если abs (y)

  • , в противном случае ваш iPhone находится в портретном положении, посмотрите на знак y, чтобы обнаружить его вверх или вниз.

  • Если вы заинтересованы в обращении вверх или вниз, посмотрите значение z.


import CoreMotion

var uMM: CMMotionManager!

override func
viewWillAppear( p: Bool ) {
    super.viewWillAppear( p )
    uMM = CMMotionManager()
    uMM.accelerometerUpdateInterval = 0.2

    //  Using main queue is not recommended. So create new operation queue and pass it to startAccelerometerUpdatesToQueue.
    //  Dispatch U/I code to main thread using dispach_async in the handler.
    uMM.startAccelerometerUpdatesToQueue( NSOperationQueue() ) { p, _ in
        if p != nil {
            println(
                abs( p.acceleration.y ) < abs( p.acceleration.x )
                ?   p.acceleration.x > 0 ? "Right"  :   "Left"
                :   p.acceleration.y > 0 ? "Down"   :   "Up"
            )
        }
    }
}

override func
viewDidDisappear( p: Bool ) {
    super.viewDidDisappear( p )
    uMM.stopAccelerometerUpdates()
}
9 голосов
/ 01 января 2011

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

Чтобы ответить на ваш вопрос, невозможно прочитать необработанные значения с акселерометра без использования частных API.

Edit:

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

Кроме того, посмотрите это руководство для обработки событий движения. Это может предоставить вам другой способ определения ориентации.

8 голосов
/ 03 сентября 2012

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

#define kAccelerometerFrequency        10.0 //Hz
-(void)viewDidAppear:(BOOL)animated {
    DLog(@"viewDidAppear");
    [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
    UIAccelerometer* a = [UIAccelerometer sharedAccelerometer];
    a.updateInterval = 1 / kAccelerometerFrequency;
    a.delegate = self;
}

-(void)viewWillDisappear:(BOOL)animated {
    DLog(@"viewWillDisappear");
    UIAccelerometer* a = [UIAccelerometer sharedAccelerometer];
    a.delegate = nil;
    [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
}

#ifdef DEBUG
+(NSString*)orientationToText:(const UIInterfaceOrientation)ORIENTATION {
    switch (ORIENTATION) {
        case UIInterfaceOrientationPortrait:
            return @"UIInterfaceOrientationPortrait";
        case UIInterfaceOrientationPortraitUpsideDown:
            return @"UIInterfaceOrientationPortraitUpsideDown";
        case UIInterfaceOrientationLandscapeLeft:
            return @"UIInterfaceOrientationLandscapeLeft";
        case UIInterfaceOrientationLandscapeRight:
            return @"UIInterfaceOrientationLandscapeRight";
    }
    return @"Unknown orientation!";
}
#endif

#pragma mark UIAccelerometerDelegate
-(void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
    UIInterfaceOrientation orientationNew;
    if (acceleration.x >= 0.75) {
        orientationNew = UIInterfaceOrientationLandscapeLeft;
    }
    else if (acceleration.x <= -0.75) {
        orientationNew = UIInterfaceOrientationLandscapeRight;
    }
    else if (acceleration.y <= -0.75) {
        orientationNew = UIInterfaceOrientationPortrait;
    }
    else if (acceleration.y >= 0.75) {
        orientationNew = UIInterfaceOrientationPortraitUpsideDown;
    }
    else {
        // Consider same as last time
        return;
    }

    if (orientationNew == orientationLast)
        return;

    NSLog(@"Going from %@ to %@!", [[self class] orientationToText:orientationLast], [[self class] orientationToText:orientationNew]);
    orientationLast = orientationNew;
}
#pragma mark -

Вам необходимо определить UIInterfaceOrientation orientationLast как переменную-члени все готово.

5 голосов
/ 06 декабря 2016

Обработка всех 6 ориентаций

Хотя мы не часто заботимся об ориентации FaceUp / FaceDown, они все же важны.

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

Вот как я справился с этим -

- (void)startMonitoring
{
    [self.motionManager startAccelerometerUpdatesToQueue:self.opQueue withHandler:^(CMAccelerometerData * _Nullable accelerometerData, NSError * _Nullable error) {

        if (error != nil)
        {
            NSLog(@"Accelerometer error: %@", error);
        }
        else
        {
            float const threshold = 40.0;

            BOOL (^isNearValue) (float value1, float value2) = ^BOOL(float value1, float value2)
            {
                return fabsf(value1 - value2) < threshold;
            };

            BOOL (^isNearValueABS) (float value1, float value2) = ^BOOL(float value1, float value2)
            {
                return isNearValue(fabsf(value1), fabsf(value2));
            };

            float yxAtan = (atan2(accelerometerData.acceleration.y, accelerometerData.acceleration.x)) * 180 / M_PI;
            float zyAtan = (atan2(accelerometerData.acceleration.z, accelerometerData.acceleration.y)) * 180 / M_PI;
            float zxAtan = (atan2(accelerometerData.acceleration.z, accelerometerData.acceleration.x)) * 180 / M_PI;

            UIDeviceOrientation orientation = self.orientation;

            if (isNearValue(-90.0, yxAtan) && isNearValueABS(180.0, zyAtan))
            {
                orientation = UIDeviceOrientationPortrait;
            }
            else if (isNearValueABS(180.0, yxAtan) && isNearValueABS(180.0, zxAtan))
            {
                orientation = UIDeviceOrientationLandscapeLeft;
            }
            else if (isNearValueABS(0.0, yxAtan) && isNearValueABS(0.0, zxAtan))
            {
                orientation = UIDeviceOrientationLandscapeRight;
            }
            else if (isNearValue(90.0, yxAtan) && isNearValueABS(0.0, zyAtan))
            {
                orientation = UIDeviceOrientationPortraitUpsideDown;
            }
            else if (isNearValue(-90.0, zyAtan) && isNearValue(-90.0, zxAtan))
            {
                orientation = UIDeviceOrientationFaceUp;
            }
            else if (isNearValue(90.0, zyAtan) && isNearValue(90.0, zxAtan))
            {
                orientation = UIDeviceOrientationFaceDown;
            }

            if (self.orientation != orientation)
            {
                dispatch_async(dispatch_get_main_queue(), ^{

                    [self orientationDidChange:orientation];
                });
            }
        }
    }];
}

Кроме того, я добавил threshold значение 40,0 (вместо 45,0). Это делает изменения менее чувствительными, предотвращая гистерезис в точках перегиба.

Если вы хотите реагировать только на изменение основных четырех ориентаций, просто сделайте это

if (UIDeviceOrientationIsPortrait(orientation) || UIDeviceOrientationIsLandscape(orientation))
{
     // Do something
}
5 голосов
/ 01 января 2011

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

Поэкспериментируйте с образцом приложения Apple AcceleromoterGraph, чтобы узнать, какие значения выводит акселерометр в разных ориентациях.

2 голосов
/ 01 мая 2016

мое решение использует coremotion, оно работает, даже когда устройство заблокировало его ориентацию.

    let motionManager: CMMotionManager = CMMotionManager()

на методе загрузки с помощью

motionManager.deviceMotionUpdateInterval = 0.01
    if motionManager.accelerometerAvailable{
        let queue = NSOperationQueue()
        motionManager.startAccelerometerUpdatesToQueue(queue, withHandler:
            {data, error in

                guard let data = data else{
                    return
                }
                let angle = (atan2(data.acceleration.y,data.acceleration.x))*180/M_PI;

                print(angle)
                if(fabs(angle)<=45){
                    self.orientation = AVCaptureVideoOrientation.LandscapeLeft
                    print("landscape left")
                }else if((fabs(angle)>45)&&(fabs(angle)<135)){

                    if(angle>0){
                        self.orientation = AVCaptureVideoOrientation.PortraitUpsideDown
                        print("portrait upside Down")


                    }else{
                        self.orientation = AVCaptureVideoOrientation.Portrait
                        print("portrait")

                    }
                }else{
                    self.orientation = AVCaptureVideoOrientation.LandscapeRight
                    print("landscape right")

                }


            }
        )
    } else {
        print("Accelerometer is not available")
    }

надеюсь, это поможет.

1 голос
/ 21 ноября 2017

Используя отличный ответ Satachito, вы получите код, который также обнаружит устройство лицевой стороной вверх или вниз

import CoreMotion

var mm: CMMotionManager!

init() {
    self.mm = CMMotionManager()
    self.mm.accelerometerUpdateInterval = 0.2
}

public func startOrientationUpdates() {
    //  Using main queue is not recommended. So create new operation queue and pass it to startAccelerometerUpdatesToQueue.
    //  Dispatch U/I code to main thread using dispach_async in the handler.
    self.mm.startAccelerometerUpdates( to: OperationQueue() ) { p, _ in
        if let p = p {
            if(p.acceleration.x > -0.3 && p.acceleration.x < 0.3 && p.acceleration.z < -0.95) {
                print("face up")
            }
            else if(p.acceleration.x > -0.3 && p.acceleration.x < 0.3 && p.acceleration.z > 0.95) {
                print("face down")
            }
            else {
                print(
                    abs( p.acceleration.y ) < abs( p.acceleration.x )
                        ?   p.acceleration.x > 0 ? "Right"  :   "Left"
                        :   p.acceleration.y > 0 ? "Down"   :   "Up"
                )
            }

        }
    }
}

public func endOrientationUpdates() {
    self.mm.stopAccelerometerUpdates()
}
1 голос
/ 01 апреля 2016

Использование CMMotionManager может помочь, но не вышеуказанным способом.Вышеуказанная логика не является стабильной.Я тщательно проверил и обнаружил, что, увидев, что значения acceleration.x/y/z не помогают определить ориентацию.

Вместо этого я получил способ найти ориентацию WRT по углу, т.е. float angle = (atan2(accelerometerData.acceleration.y,accelerometerData.acceleration.x))*180/M_PI;

А для ориентации - if(fabs(angle<=45)currOrientation=UIDeviceOrientationLandscapeRight; else if((fabs(angle)>45)&&(fabs(angle)<135))currOrientation=((angle>0)?UIDeviceOrientationPortraitUpsideDown:UIDeviceOrientationPortrait); else currOrientation = UIDeviceOrientationLandscapeLeft;

Это может пригодиться кому-то, хотя это не помогает мне найти 2 другие ориентации, например UIDeviceOrientationFaceUp & UIDeviceOrientationFaceDown.

...