Pytanie Szybkie obracanie urządzenia iOS o 180 stopni powoduje odwrócenie obrazu z kamery


Zaimplementowałem poniższy kod, aby zmienić orientację znaku AVCaptureVideoSession w oparciu o UIInterfaceOrientation:

- (AVCaptureVideoOrientation)interfaceOrientationToVideoOrientation:(UIInterfaceOrientation)orientation {
    switch (orientation) {
        case UIInterfaceOrientationPortrait:
            return AVCaptureVideoOrientationPortrait;
        case UIInterfaceOrientationPortraitUpsideDown:
            return AVCaptureVideoOrientationPortraitUpsideDown;
        case UIInterfaceOrientationLandscapeLeft:
            return AVCaptureVideoOrientationLandscapeLeft;
        case UIInterfaceOrientationLandscapeRight:
            return AVCaptureVideoOrientationLandscapeRight;
        default:
            break;
    }
    NSLog(@"Warning - Didn't recognise interface orientation (%ld)",(long)orientation);
    return AVCaptureVideoOrientationPortrait;
}

Ten kod działa prawie idealnie. Jednak jeden problem, który występuje, to że jeśli szybko obrócisz urządzenie iOS o 180 stopni, kamera będzie wyświetlana do góry nogami.

Oto wygląd widoku kamery przed obrotem:

enter image description here

A oto jak wygląda po rotacji:

enter image description here

Dodatkowo, oto moja implementacja viewDidLayoutSubviews:

- (void)viewDidLayoutSubviews {

    [super viewDidLayoutSubviews];

    previewLayer.frame = self.view.bounds;

    self.view.translatesAutoresizingMaskIntoConstraints = NO;
    previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
    [self.view.layer addSublayer:previewLayer];

    if (previewLayer.connection.supportsVideoOrientation) {
        previewLayer.connection.videoOrientation = [self interfaceOrientationToVideoOrientation:[UIApplication sharedApplication].statusBarOrientation];
    }
}

Czy ktoś ma pojęcie, dlaczego widok kamery byłby odwrócony do góry nogami, gdy nastąpiłby obrót o 180 stopni?


11
2018-06-17 20:45


pochodzenie


Widziałem to w narzędziach Apple, więc nie jestem pewien, czy to twój błąd. - Paul Cezanne
@PaulCezanne Ah, interesujące! Które? Próbowałem go odtworzyć w aplikacji aparatu i nie mogłem tego zrobić. - narner
Widziałem to w aparacie i Wiadomościach, ale może uruchomiłem Aparat z Wiadomości. Nie pamiętam. Wydaje mi się również, że stało się bardziej w iOS8.0, nie przypominam sobie, że ostatnio widziałem go z kamery, ale z pewnością zauważyłem dziwność obrotową w 8.3 po uruchomieniu kamery z Messages. - Paul Cezanne
@PaulCezanne Weird ... Próbowałem go przetestować, uruchamiając aparat z Messages i również go tam nie widziałem. - narner
Miałem już ten sam problem, rozwiązanie, które zrobiłem i dla mnie pracowałem, to dodanie NSNotificationCenter, zmiana orientacji previewLayer kiedy powiadomienie zostało wywołane bezpośrednio w metodzie wskazanej przez powiadomienie .. nie mogę podać kodu teraz jest za późno, a mój kod jest w biurze .. hmm .. - 0yeoj


Odpowiedzi:


Wcześniej miałem ten sam problem, co rozwiązało mój problem.

Deklarowałem zmienną globalną:

@interface YourViewController ()
{
    ...
    UIDevice *iOSDevice;
}

..

@end

Pod implementation (.m plik) i zadeklarowany obserwator NSNotification pod -viewWillAppear lubić:

@implementation YourViewController

-(void)viewWillAppear:(BOOL)animated
{
    [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];

    [[NSNotificationCenter defaultCenter] addObserver:self  selector:@selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification  object:[UIDevice currentDevice]];

}

-(void)viewWillDisappear:(BOOL)animated
{
    [[NSNotificationCenter defaultCenter]removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil];
}

- (void)orientationChanged:(NSNotification *)notification
{
    iOSDevice = notification.object;

    previewLayer.connection.videoOrientation = [self interfaceOrientationToVideoOrientation];

    //or

    [self setAutoVideoConnectionOrientation:YES];

}

Zrobiłem funkcję, która zwraca orientację kopiowanego urządzenia prądowego, jak:

Weź łup na siebie -interfaceOrientationToVideoOrientation metoda, to konwertuje iOSDevice.orientation w AVCaptureVideoOrientation to samo, co tam masz ...


(W moim przypadku potrzebowałem tych funkcji poniżej, ale spójrz na to może być przydatne z jakiegoś powodu)

- (AVCaptureVideoOrientation)interfaceOrientationToVideoOrientation
{
    switch (iOSDevice.orientation) {

        case UIInterfaceOrientationPortraitUpsideDown:

            return AVCaptureVideoOrientationPortraitUpsideDown;

        case UIInterfaceOrientationLandscapeLeft:

            return AVCaptureVideoOrientationLandscapeLeft;

        case UIInterfaceOrientationLandscapeRight:

            return AVCaptureVideoOrientationLandscapeRight;

        default:
        case UIDeviceOrientationPortrait:
            return AVCaptureVideoOrientationPortrait;
    }
}

- (AVCaptureConnection *)setAutoVideoConnectionOrientation:(BOOL)status
{
    AVCaptureConnection *videoConnection = nil;

    //This is for me
    //for (AVCaptureConnection *connection in stillImageOutput.connections) {

    //This might be for you
    for (AVCaptureConnection *connection in previewLayer.connection) {

        for (AVCaptureInputPort *port in [connection inputPorts])
        {
            if ([[port mediaType] isEqual:AVMediaTypeVideo])
            {
                videoConnection = connection;

                if (status == YES)
                {
                    [videoConnection setVideoOrientation:[self interfaceOrientationToVideoOrientation]];
                }
                else
                {
                    [videoConnection setVideoOrientation:AVCaptureVideoOrientationPortrait];
                }
            }
            if (videoConnection)
            {
                break;
            }
        }
    }
    return videoConnection;
}

Edytować

Domyślna orientacja: musisz sprawdzić orientację "bieżącą".

- (void)viewDidLoad
{
    [super viewDidLoad];

    // assuming you finish setting the `previewLayer`

    ..
    // after all of that code, when the view is ready for displaying
    // if you implemented this approach the best thing to do is:

    // set this 
    iOSDevice = [UIDevice currentDevice];

    // then call 
    previewLayer.connection.videoOrientation = [self interfaceOrientationToVideoOrientation];
    // to update the `previewLayer.connection.videoOrientation ` for default orientation        

}

Uwaga:

Za pomocą NSNotificationCenter pozwól, aby rotacja urządzenia została ustalona przed uruchomieniem.

Mam nadzieję, że to ci pomoże ..


4
2018-06-24 02:50



Działa to prawie idealnie! Jedyny problem, jaki mam teraz, to to, że jeśli uruchomię aplikację na iPadzie i zacznę w trybie poziomym, widok z kamery będzie wyświetlany w orientacji poziomej. Jeśli obrócę urządzenie do portretu, a następnie z powrotem do pejzażu, aparat wyświetli się poprawnie. - narner
@narner, O tym, musisz sprawdzić orientację w środku -viewDidLoad.. dla domyślnego ustawienia .. przepraszam, nie uwzględniłem tego tutaj .. Myślałem, że już masz to .. - 0yeoj
To ma sens. Tak, coś w stylu `previewLayer.connection.videoOrientation = [[UIDevice currentDevice] orientacja];`? - narner
Niezupełnie potrzebujesz wewnętrznej orientacji wewnątrz -interfaceOrientationToVideoOrientation metoda .. spróbuję edytować moją odpowiedź .. :) - 0yeoj
Piękny!!! Dziękuję Ci. Akceptuję :) - narner


Wierzę, że to się dzieje, ponieważ viewDidLayoutSubviews nie jest wywoływana, gdy zmienia się widok z orientacji pionowej na UpsideDown. Kiedy obraca się bardzo szybko, przechodzi od jednej do drugiej prostej. Jeśli obracasz się powoli, przechodzisz przez orientację poziomą i tak viewDidLayoutSubviews zostaje wywołana, a następnie twoja metoda.

Radzę użyć jednej z metod, które zostaną wywołane, gdy zmienia się orientacja. Wiem, że Apple radzi, aby z nich korzystać coraz rzadziej, ponieważ chcą, aby po zmianie rozmiaru zmieniały się widoki, ale są przypadki, takie jak ten, kiedy naprawdę trzeba wiedzieć, że orientacja uległa zmianie.

Możesz na przykład zarejestrować się, aby otrzymywać powiadomienia: UIApplicationDidChangeStatusBarOrientationNotification


5
2018-06-23 14:24



Rozumiem, to miałoby sens. Sugerujesz więc wprowadzenie kodu, w którym aktualnie się znajduję viewDidLayoutSubviews wewnątrz czegoś takiego jak - (void)orientationChanged:(NSNotification *)notification lub adjustViewsForOrientation, poprawne? - narner
Powiedziałbym, że wystarczy umieścić ostatni if w orientationChanged:. Ta reszta kodu, która obsługuje zmianę rozmiaru widoku, jest bardzo dobrze umieszczona w pliku viewDidLayoutSubviews. Nie sądzę jednak, że powinieneś dodać tę podwarstwę za każdym razem, gdy metoda jest wywoływana. - pteofil
To też ma sens. Jak myślisz, gdzie byłoby lepiej umieścić tę podwarstwę? (Przepraszam za pytania, to bolało mnie przez chwilę, haha). - narner
Prawdopodobnie możesz sprawdzić, czy ma superlayeri dodaj go tylko wtedy, gdy tak nie jest. - pteofil
Dla Swift 3.0 - NotificationCenter.default.addObserver (self, selector: #selector (deviceRotated (notification :)), name: NSNotification.Name.UIApplicationDidChangeStatusBarOrientation, object: .none) - Bret Smith