source

Swift - 방향 변화를 감지하는 방법

itover 2023. 4. 17. 21:44
반응형

Swift - 방향 변화를 감지하는 방법

단일 이미지 뷰에 2개의 이미지를 추가하고 싶은데(한 이미지는 가로로, 다른 이미지는 세로로) 빠른 언어를 사용하여 방향 변화를 감지하는 방법을 알 수 없습니다.

나는 이 대답을 시도해 보았지만, 그것은 하나의 이미지만을 가지고 있다.

override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
    if UIDevice.currentDevice().orientation.isLandscape.boolValue {
        print("Landscape")
    } else {
        print("Portrait")
    }
}

iOS 개발은 처음이라 조언해주시면 감사하겠습니다!

let const = "Background" //image name
let const2 = "GreyBackground" // image name
    @IBOutlet weak var imageView: UIImageView!
    override func viewDidLoad() {
        super.viewDidLoad()

        imageView.image = UIImage(named: const)
        // Do any additional setup after loading the view.
    }

    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransition(to: size, with: coordinator)
        if UIDevice.current.orientation.isLandscape {
            print("Landscape")
            imageView.image = UIImage(named: const2)
        } else {
            print("Portrait")
            imageView.image = UIImage(named: const)
        }
    }

Notification Center 및 UIDevice 사용beginGeneratingDeviceOrientationNotifications

Swift 4.2 이상

override func viewDidLoad() {
    super.viewDidLoad()        

    NotificationCenter.default.addObserver(self, selector: #selector(ViewController.rotated), name: UIDevice.orientationDidChangeNotification, object: nil)
}

deinit {
   NotificationCenter.default.removeObserver(self, name: UIDevice.orientationDidChangeNotification, object: nil)         
}

func rotated() {
    if UIDevice.current.orientation.isLandscape {
        print("Landscape")
    } else {
        print("Portrait")
    }
}

스위프트 3

override func viewDidLoad() {
    super.viewDidLoad()        

    NotificationCenter.default.addObserver(self, selector: #selector(ViewController.rotated), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
}

deinit {
     NotificationCenter.default.removeObserver(self)
}

func rotated() {
    if UIDevice.current.orientation.isLandscape {
        print("Landscape")
    } else {
        print("Portrait")
    }
}

⚠(장치 방향!= 인터페이스 방향⚠)

Swift 5.* iOS16 이하

다음과 같은 차이를 두어야 합니다.

  • 디바이스 방향 => 물리 디바이스의 방향을 나타냅니다.
  • Interface Orientation(인터페이스 방향) => 화면에 표시되는 인터페이스의 방향을 나타냅니다.

이들 2개의 값이 일치하지 않는 시나리오는 다음과 같습니다.

  • 화면 방향을 잠글 때
  • 단말기가 플랫인 경우

대부분의 경우 인터페이스 방향을 사용하고 싶기 때문에 창을 통해 얻을 수 있습니다.

private var windowInterfaceOrientation: UIInterfaceOrientation? {
    return UIApplication.shared.windows.first?.windowScene?.interfaceOrientation
}

< iOS 13 (iOS 12 등)도 서포트하고 싶은 경우는, 다음의 조작을 실시합니다.

private var windowInterfaceOrientation: UIInterfaceOrientation? {
    if #available(iOS 13.0, *) {
        return UIApplication.shared.windows.first?.windowScene?.interfaceOrientation
    } else {
        return UIApplication.shared.statusBarOrientation
    }
}

이제 윈도우 인터페이스 방향 변경에 대한 대응 위치를 정의해야 합니다., 좋은 은 그 입니다. willTransition(to newCollection: UITraitCollection.

덮어쓸 수 있는 이 상속된 UIViewController 메서드는 인터페이스 방향이 변경될 때마다 트리거됩니다.따라서 모든 수정은 후자에서 수행할 수 있습니다.

다음은 해결 예를 제시하겠습니다.

class ViewController: UIViewController {
    override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) {
        super.willTransition(to: newCollection, with: coordinator)
        
        coordinator.animate(alongsideTransition: { (context) in
            guard let windowInterfaceOrientation = self.windowInterfaceOrientation else { return }
            
            if windowInterfaceOrientation.isLandscape {
                // activate landscape changes
            } else {
                // activate portrait changes
            }
        })
    }
    
    private var windowInterfaceOrientation: UIInterfaceOrientation? {
        return UIApplication.shared.windows.first?.windowScene?.interfaceOrientation
    }
}

이 방법을 구현하면 인터페이스에 대한 방향 변경에 대응할 수 있습니다.은 없기 에, .viewWillAppear().

디바이스 방향과 인터페이스 방향의 차이를 강조하는 샘플 프로젝트를 만들었습니다.또한 UI를 업데이트하기로 결정한 라이프사이클 단계에 따라 다른 동작을 이해하는 데 도움이 됩니다.

다음 저장소를 자유롭게 복제하여 실행하십시오.https://github.com/wjosset/ReactToOrientation

Swift 3 위 코드 업데이트됨:

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)

    if UIDevice.current.orientation.isLandscape {
        print("Landscape")
    } else {
        print("Portrait")
    }
}

Swift 4+: 소프트 키보드 디자인으로 사용하고 있었습니다만, 어떤 이유에서인지,UIDevice.current.orientation.isLandscape방법은 계속 나에게 말했다.Portrait 이렇게

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)

    if(size.width > self.view.frame.size.width){
        //Landscape
    }
    else{
        //Portrait
    }
}

Swift 버전 > = 3.0을 사용하는 경우 다른 사용자가 이미 말한 것처럼 몇 가지 코드 업데이트를 적용해야 합니다.Super에게 전화하는 것을 잊지 마세요.

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {

   super.viewWillTransition(to: size, with: coordinator)

   // YOUR CODE OR FUNCTIONS CALL HERE

}

이미지에 StackView를 사용하는 경우 다음과 같은 작업을 수행할 수 있습니다.

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {

   super.viewWillTransition(to: size, with: coordinator)

   if UIDevice.current.orientation.isLandscape {

      stackView.axis = .horizontal

   } else {

      stackView.axis = .vertical

   } // else

}

Interface Builder를 사용하는 경우 오른쪽 패널에 있는 Identity Inspector 섹션에서 이 UIStackView 객체의 커스텀클래스를 선택하는 것을 잊지 마십시오.그런 다음 Interface Builder를 통해 커스텀 UIStackView인스턴스에 대한 IBOutlet 참조를 만듭니다.

@IBOutlet weak var stackView: MyStackView!

아이디어를 받아들여 니즈에 맞게 조정하십시오.도움이 되길 바랍니다!

Swift 4.2, RxSwift

collectionView를 새로고침해야 하는 경우.

NotificationCenter.default.rx.notification(UIDevice.orientationDidChangeNotification)
    .observeOn(MainScheduler.instance)
    .map { _ in }            
    .bind(to: collectionView.rx.reloadData)
    .disposed(by: bag)

Swift 4, RxSwift

collectionView를 새로고침해야 하는 경우.

NotificationCenter.default.rx.notification(NSNotification.Name.UIDeviceOrientationDidChange)
    .observeOn(MainScheduler.instance)
    .map { _ in }            
    .bind(to: collectionView.rx.reloadData)
    .disposed(by: bag)

다음은 최신 Combine 솔루션입니다.

import UIKit
import Combine

class MyClass: UIViewController {

     private var subscriptions = Set<AnyCancellable>()

     override func viewDidLoad() {
         super.viewDidLoad()
    
         NotificationCenter
             .default
             .publisher(for: UIDevice.orientationDidChangeNotification)
             .sink { [weak self] _ in
            
                 let orientation = UIDevice.current.orientation
                 print("Landscape: \(orientation.isLandscape)")
         }
         .store(in: &subscriptions)
    }
}

두합니다.viewWIllTransition(toSize:) ★★★★★★★★★★★★★★★★★」NotificationCenter의 »UIDeviceOrientationDidChange.

viewWillTransition(toSize:)는 이행 전에 통지합니다.

NotificationCenter UIDeviceOrientationDidChange가 다음에 통지합니다.

아주 조심해야 합니다.를 들어, 「 」에서는,UISplitViewController가 특정때, 「동작」은, 「동작」으로 회전합니다.DetailViewController the the the the 에서 떨어지다UISplitViewController의 »viewcontrollers어레인지로 .UINavigationController로테이션이 종료되기 전에 상세 뷰 컨트롤러를 검색하면 컨트롤러가 존재하지 않고 크래쉬 할 수 있습니다.

스위프트 4

를 사용하여 할 때 약간의 UIDevice.current.orientation예를 들어, 회전 중 또는 서브뷰 애니메이션 중에 테이블뷰 셀의 구속조건을 갱신하는 등입니다.

현재 위의 방법 대신 전환 크기를 뷰 컨트롤러 뷰 크기와 비교하고 있습니다.코드의 이 시점에서 양쪽 모두에 액세스 할 수 있기 때문에, 이것은 적절한 방법이라고 생각됩니다.

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)
    print("Will Transition to size \(size) from super view size \(self.view.frame.size)")

    if (size.width > self.view.frame.size.width) {
        print("Landscape")
    } else {
        print("Portrait")
    }

    if (size.width != self.view.frame.size.width) {
        // Reload TableView to update cell's constraints.
    // Ensuring no dequeued cells have old constraints.
        DispatchQueue.main.async {
            self.tableView.reloadData()
        }
    }


}

iPhone 6에서의 출력:

Will Transition to size (667.0, 375.0) from super view size (375.0, 667.0) 
Will Transition to size (375.0, 667.0) from super view size (667.0, 375.0)

하시면 됩니다.viewWillTransition(to:with:)에 접속합니다.animate(alongsideTransition:completion:)전환 완료 후 인터페이스 방향을 얻습니다.이벤트를 이용하려면 이와 유사한 프로토콜을 정의하고 구현해야 합니다.이 코드는 SpriteKit 게임에 사용되었으며 구현 방식이 다를 수 있습니다.

protocol CanReceiveTransitionEvents {
    func viewWillTransition(to size: CGSize)
    func interfaceOrientationChanged(to orientation: UIInterfaceOrientation)
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransition(to: size, with: coordinator)

        guard
            let skView = self.view as? SKView,
            let canReceiveRotationEvents = skView.scene as? CanReceiveTransitionEvents else { return }

        coordinator.animate(alongsideTransition: nil) { _ in
            if let interfaceOrientation = UIApplication.shared.windows.first?.windowScene?.interfaceOrientation {
                canReceiveRotationEvents.interfaceOrientationChanged(to: interfaceOrientation)
            }
        }

        canReceiveRotationEvents.viewWillTransition(to: size)
    }

이러한 함수에 중단점을 설정하고 다음을 관찰할 수 있습니다.interfaceOrientationChanged(to orientation: UIInterfaceOrientation) ', 하다'의 이름을 부르죠.viewWillTransition(to size: CGSize)신신오최

이전 기고문은 모두 문제 없습니다만, 조금 주의해 주세요.

a) plist, 세로 또는 예시로 방향이 설정되어 있는 경우 view Transition을 통해 알리지 않습니다.

b) 사용자가 기기를 회전했는지 여부를 알 필요가 있는지 여부(예를 들어 게임 등)사용할 수 있는 것은 다음뿐입니다.

NotificationCenter.default.addObserver(self, selector: #selector(ViewController.rotated), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)

Xcode8, iOS11에서 테스트 완료

시작에서 올바른 방향을 얻으려면 체크인을 해야 합니다.viewDidLayoutSubviews(). 여기에 설명된 다른 방법은 작동하지 않습니다.

다음은 그 방법의 예입니다.

var mFirstStart = true

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    if (mFirstStart) {
        mFirstStart = false
        detectOrientation()
    }
}

func detectOrientation() {
    if UIDevice.current.orientation.isLandscape {
        print("Landscape")
        // do your stuff here for landscape
    } else {
        print("Portrait")
        // do your stuff here for portrait
    }
}

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    detectOrientation()
}

이것은 앱이 처음 시작할 때, 그리고 앱 실행 중에 회전할 때 항상 작동합니다.

디바이스의 방향을 검출하는 또 다른 방법은 함수 type Collection Did Change(_:)를 사용하는 것입니다.iOS 인터페이스 환경이 변경되면 시스템이 이 메서드를 호출합니다.

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?)
{
    super.traitCollectionDidChange(previousTraitCollection)
    //...
}

또한 willTransition(to:with:) 함수(attributeCollectionDidChange(_:) 이전 호출)를 사용하여 오리엔테이션 적용 직전에 정보를 얻을 수 있습니다.

 override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator)
{
    super.willTransition(to: newCollection, with: coordinator)
    //...
}

앱은 iOS 15에서 실행 중이며 iPhone/iPad에서만 확인했기 때문에 모든 사용 사례에 대해서는 말할 수 없지만 다음 환경 변수를 사용하고 있습니다.

@Environment(\.verticalSizeClass) private var verticalSizeClass

그런 다음 다음 값을 확인합니다.verticalSizeClass == .compact가로 방향verticalSizeClass == .regular세로 방향

https://developer.apple.com/documentation/swiftui/environmentvalues/verticalsizeclass

언급URL : https://stackoverflow.com/questions/38894031/swift-how-to-detect-orientation-changes

반응형