Подтвердить что ты не робот

Ориентация устройства iOS без учета блокировки ориентации

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

[UIDevice currentDevice].orientation

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

Есть ли способ высокого уровня для определения фактической ориентации устройства?

4b9b3361

Ответ 1

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

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

Edit:

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

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

Ответ 2

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

Алгоритм обнаружения ориентации:

  • если abs (y) abs (x) ваш iPhone находится в ландшафтном положении, посмотрите знак x, чтобы определить право или слева

  • иначе ваш 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()
}

Ответ 3

Настройте свой контроллер просмотра или что-то еще для поддержки 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 как переменную-член и установить.

Ответ 4

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

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

Ответ 5

Обработка всех 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). Это делает изменения менее чувствительными, предотвращая гистерезис в точках перегиба.

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

if (UIDeviceOrientationIsPortrait(orientation) || UIDeviceOrientationIsLandscape(orientation))
{
     // Do something
}

Ответ 6

Использование 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;

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

Ответ 7

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

    let motionManager: CMMotionManager = CMMotionManager()

для метода did load

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")
    }

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

Ответ 8

Использование 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()
}