स्विफ्ट - अभिविन्यास परिवर्तनों का पता लगाने के लिए कैसे


96

मैं एकल छवि दृश्य में दो छवियां जोड़ना चाहता हूं (यानी परिदृश्य एक छवि के लिए और दूसरी छवि के लिए), लेकिन मुझे नहीं पता कि स्विफ्ट भाषाओं का उपयोग करके अभिविन्यास परिवर्तनों का पता कैसे लगाया जाए।

मैंने इस उत्तर की कोशिश की लेकिन यह केवल एक छवि लेता है

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

मैं iOS के विकास में नया हूँ, किसी भी सलाह की बहुत सराहना की जाएगी!


1
क्या आप कृपया अपना प्रश्न संपादित कर सकते हैं और अपना कोड प्रारूपित कर सकते हैं? आप अपना कोड चुने जाने पर cmd + k के साथ कर सकते हैं।
jbehrens94

2
यह बाहर की जाँच करें stackoverflow.com/questions/25666269/…
DSAjit

क्या यह लैंडस्केप / पोर्ट्रेट को सही तरीके से प्रिंट करता है?
डैनियल

हाँ यह सही ढंग से प्रिंट करता है @simplebus
रघुराम

आपको उपकरण अभिविन्यास के बजाय इंटरफ़ेस ओरिएंटेशन का उपयोग करना चाहिए => stackoverflow.com/a/60577486/8780127
Wilfried Josset

जवाबों:


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

धन्यवाद, यह imageView के लिए काम करता है, क्या होगा यदि मैं viewController के लिए पृष्ठभूमि छवि जोड़ना चाहता हूं ??
रघुराम

1
बैकग्राउंड पर एक इमेज व्यू लगाएं। ऊपर, नीचे, अग्रणी, सभी पृष्ठभूमि को कवर करने के लिए ViewController मुख्य दृश्य के लिए अनुगामी के लिए छवि दृश्य के लिए बाधा दें।
रुतविक कंबरगी

4
Super.viewWillTransitionToSize को अपने ओवरराइड विधि के भीतर कॉल करना न भूलें
ब्रायन सचेटा

क्या आप जानते हैं कि viewWillTransitionजब हम उपवर्ग करते हैं तो मैं ओवरराइड क्यों नहीं कर सकता UIView?
Xcoder

2
एक अन्य अभिविन्यास प्रकार है .isFlat:। यदि आप केवल पकड़ना चाहते portraitहैं, तो मेरा सुझाव है कि आप दूसरे खंड को बदल दें else if UIDevice.current.orientation.isPortrait। नोट: .isFlatपोर्ट्रेट और लैंडस्केप दोनों में हो सकता है, और बस {} यह हमेशा डिफ़ॉल्ट होगा (भले ही यह फ्लैट लैन्सस्केप हो)।
पीएमटी

78

का उपयोग करते हुए NotificationCenter और UIDevice कीbeginGeneratingDeviceOrientationNotifications

स्विफ्ट 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")
    }
}

1
निश्चित रूप से - मेरे दृष्टिकोण को कुछ अतिरिक्त कोड की आवश्यकता है। लेकिन यह वास्तव में "अभिविन्यास परिवर्तन का पता लगाता है"।
मसलवसा

3
@ मिचेल क्योंकि viewWillTransition:परिवर्तन पूरा होने के बाद परिवर्तन का उपयोग करते हुए होने का अनुमान UIDeviceOrientationDidChangeहै।
लिंडसे स्कॉट

1
@LyndseyScott मेरा अभी भी मानना ​​है कि संभावित खतरनाक NSNotificationCenter का उपयोग किए बिना वर्णित व्यवहार को प्राप्त किया जा सकता है। कृपया निम्नलिखित उत्तर की समीक्षा करें, और मुझे अपने विचार बताएं: stackoverflow.com/a/26944087/1306884
माइकल

4
इस उत्तर में दिलचस्प यह है कि यह दृश्य नियंत्रक के shouldAutorotateसेट होने पर भी वर्तमान अभिविन्यास देगा false। यह कैमरा ऐप की मुख्य स्क्रीन जैसी स्क्रीन के लिए आसान है - ओरिएंटेशन लॉक है लेकिन बटन घूम सकते हैं।
योनिन्जा

1
IOS 9 के रूप में आप भी स्पष्ट रूप से deinit कॉल करने की जरूरत नहीं है। आप इसके बारे में और अधिक यहाँ पढ़ सकते हैं: useyourloaf.com/blog/…
जेम्स जॉर्डन टेलर

63

स्विफ्ट 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")
    }
}

1
क्या आप जानते हैं कि जब मैं UIView को उप-वर्ग कर रहा हूं तो मैं व्यूट्रॉलिशन को ओवरराइड क्यों नहीं कर सकता?
Xcoder

यदि आप नियंत्रक में किसी भी विचार को संदर्भित करने का प्रयास करते हैं तो यह क्रैश हो जाएगा।
जेम्स जॉर्डन टेलर

@JamesJordanTaylor क्या आप बता सकते हैं कि यह दुर्घटनाग्रस्त क्यों होगा?
ओरियम

यह 6 महीने पहले था जब मैंने टिप्पणी की थी, लेकिन मुझे लगता है कि जब मैंने कोशिश की थी तो इसका कारण एक्सकोड था, यह एक शून्य सूचक अपवाद था क्योंकि दृश्य अभी तक त्वरित रूप से तैयार नहीं किया गया था, बस दृश्य नियंत्रक। मैं हालांकि गलत हो सकता है।
जेम्स जॉर्डन टेलर

@Xcoder यह UIViewController पर एक फ़ंक्शन है UIView - यही कारण है कि आप इसे ओवरराइड नहीं कर सकते।
लाइटनिंगस्ट्रीम

24

EntDevice ओरिएंटेशन! = इंटरफ़ेस ओरिएंटेशन Ori

स्विफ्ट 5. * iOS14 और नीचे

आपको वास्तव में बीच में फर्क करना चाहिए:

  • डिवाइस ओरिएंटेशन => भौतिक डिवाइस के उन्मुखीकरण को दर्शाता है
  • इंटरफ़ेस ओरिएंटेशन => स्क्रीन पर प्रदर्शित इंटरफ़ेस के उन्मुखीकरण को इंगित करता है

ऐसे कई परिदृश्य हैं जहां उन 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()

मैंने एक नमूना परियोजना बनाई है जो उपकरण अभिविन्यास और इंटरफ़ेस अभिविन्यास के बीच अंतर को रेखांकित करती है। इसके अतिरिक्त यह आपको विभिन्न व्यवहार को समझने में मदद करेगा कि आप अपने यूआई को अपडेट करने के लिए किस जीवन चक्र के आधार पर कदम उठाते हैं।

निम्नलिखित रिपॉजिटरी को क्लोन और चलाने के लिए स्वतंत्र महसूस करें: https://github.com/wjosset/ReactToOrientation


IOS 13 से पहले यह कैसे करें?
डैनियल स्प्रिंगर

1
@DanielSpringer आपकी टिप्पणी के लिए धन्यवाद, मैंने पोस्ट को iOS 12 और उससे नीचे का समर्थन करने के लिए संपादित किया है
Wilfried Josset

1
मुझे यह उत्तर सबसे अच्छा और सबसे जानकारीपूर्ण लगता है। लेकिन iPadOS13.5 पर परीक्षण के सुझाव का उपयोग func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator)काम नहीं किया। मैंने इसका उपयोग करके काम किया है func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator)
Andrej

12

स्विफ्ट 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
    }
}

ठीक है, मुझे अपने iMessage ऐप में इसी तरह की समस्या है। क्या यह वास्तव में अजीब नहीं है? और, यहां तक ​​कि आपके समाधान विपरीत काम करता है !! ¯_ (¯) _ / ¯
श्याम

UIScreen.main.bounds का उपयोग न करें, क्योंकि यह आपको संक्रमण से पहले स्क्रीन सीमा दे सकता है (विधि में 'विल' मन), विशेष रूप से कई तेज घुमावों पर। आपको 'आकार' पैरामीटर का उपयोग करना चाहिए ...
दान बोडर

@ dan-bodnar क्या आपका मतलब है कि मूल निवासी पैरामीटर?
asetniop

3
@ ससेटनिओप, नहीं। sizeपैरामीटर कि viewWillTransitionToSize में इंजेक्ट किया जाता है: withCoordinator: विधि आप ऊपर लिखा। यह उस सटीक आकार को प्रतिबिंबित करेगा जो संक्रमण के बाद आपके विचार में होगा।
दान बोडर

यह एक अच्छा समाधान नहीं है यदि आप चाइल्ड व्यू कंट्रोलर्स का उपयोग कर रहे हैं, तो कंटेनर का आकार अभिविन्यास की परवाह किए बिना हमेशा चौकोर हो सकता है।
बोजन

8

स्विफ्ट 4.2, आरएक्सविफ्ट

अगर हमें collectionView को पुनः लोड करने की आवश्यकता है।

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

स्विफ्ट 4, RxSwift

अगर हमें collectionView को पुनः लोड करने की आवश्यकता है।

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

5

मेरा मानना है कि सही जवाब वास्तव में है दोनों के संयोजन दृष्टिकोण: viewWIllTransition(toSize:)और NotificationCenterके UIDeviceOrientationDidChange

viewWillTransition(toSize:)संक्रमण से पहले आपको सूचित करता है ।

NotificationCenter UIDeviceOrientationDidChangeआपको सूचित करता है के बाद

आपको बहुत सावधान रहना होगा। उदाहरण के लिए, में UISplitViewControllerजब कुछ झुकाव में डिवाइस घूमता है, DetailViewControllerबंद हो जाता है पॉप UISplitViewControllerकी viewcontrollersसरणी, और मास्टर की पर धक्का दे दिया UINavigationController। यदि आप रोटेशन समाप्त होने से पहले डिटेल व्यू कंट्रोलर की खोज करते हैं, तो यह मौजूद नहीं है और क्रैश हो सकता है।


5

यदि आपका स्विफ्ट संस्करण> = 3.0 का उपयोग कर रहा है तो कुछ कोड अपडेट हैं जिन्हें आपको लागू करना होगा जैसा कि अन्य पहले ही कह चुके हैं। बस सुपर कॉल करने के लिए मत भूलना:

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

}

यदि आप इंटरफ़ेस बिल्डर का उपयोग कर रहे हैं तो दाईं ओर स्थित पहचान निरीक्षक अनुभाग में इस UIStackView ऑब्जेक्ट के लिए कस्टम वर्ग का चयन करना न भूलें। फिर कस्टम UIStackView उदाहरण के लिए IBOutlet संदर्भ (इंटरफेस इंटरफ़ेस के माध्यम से भी) बस बनाएं:

@IBOutlet weak var stackView: MyStackView!

विचार लें और इसे अपनी आवश्यकताओं के अनुकूल बनाएं। आशा है इससे आपको सहायता मिलेगी!


सुपर को बुलाने के बारे में अच्छी बात। यह वास्तव में एक मैला काम में सुपर विधि को कॉल नहीं करने के लिए मैला कोडिंग है और यह क्षुधा में अप्रत्याशित व्यवहार पैदा कर सकता है। और संभावित कीड़े जो वास्तव में नीचे ट्रैक करने के लिए कठिन हैं!
एडम फ्रीमैन

क्या आप जानते हैं कि जब मैं UIView को उप-वर्ग कर रहा हूं तो मैं व्यूट्रॉलिशन को ओवरराइड क्यों नहीं कर सकता?
Xcoder

@Xcoder ऑब्जेक्ट के कारण (आमतौर पर) ओवरराइड को लागू नहीं करने के लिए रनटाइम उदाहरण में निहित है जो कस्टम वर्ग के साथ नहीं बल्कि डिफ़ॉल्ट के साथ बनाया गया था। यदि आप इंटरफ़ेस बिल्डर का उपयोग कर रहे हैं, तो दाईं ओर स्थित पहचान निरीक्षक अनुभाग में इस UIStackView ऑब्जेक्ट के लिए कस्टम वर्ग का चयन करना सुनिश्चित करें।
WeiseRatel

5

स्विफ्ट 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)

क्या मुझे परियोजना स्तर से अभिविन्यास समर्थन चालू करना चाहिए?
एरिकपून

3

पिछले सभी योगदान ठीक हैं, लेकिन थोड़ा ध्यान दें:

a) यदि अभिविन्यास को केवल चित्र या उदाहरण में सेट किया गया है, तो आपको viewWillTransition के माध्यम से सूचित नहीं किया जाएगा

ख) अगर हमें किसी भी तरह से यह जानने की जरूरत है कि क्या उपयोगकर्ता ने डिवाइस घुमाया है, (उदाहरण के लिए एक गेम या समान ..) हम केवल उपयोग कर सकते हैं:

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

Xcode8, iOS11 पर परीक्षण किया गया


2

आप संक्रमण पूर्ण होने के बाद इंटरफ़ेस ओरिएंटेशन प्राप्त करने के लिए उपयोग viewWillTransition(to:with:)और टैप कर सकते हैं animate(alongsideTransition:completion:)। आपको घटना में टैप करने के लिए इसके समान एक प्रोटोकॉल को परिभाषित और कार्यान्वित करना होगा। ध्यान दें कि यह कोड स्प्राइटिट गेम के लिए उपयोग किया गया था और आपका विशिष्ट कार्यान्वयन भिन्न हो सकता है।

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)अद्यतन अभिविन्यास के बाद कहा जाता है ।


1

ऐप स्टार्ट पर सही ओरिएंटेशन प्राप्त करने के लिए आपको इसे जांचना होगा 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()
}

यह हमेशा ऐप पर काम करेगा, पहली बार शुरू होने पर , और अगर ऐप चल रहा है तो घुमाएगा ।


0

डिवाइस ओरिएंटेशन का पता लगाने का एक और तरीका फ़ंक्शन traitCollectionDidChange (_ :) के साथ है। जब iOS इंटरफ़ेस वातावरण बदलता है, तो सिस्टम इस विधि को कॉल करता है।

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

इसके अलावा, आप फ़ंक्शन का उपयोग कर सकते हैं। अभिविन्यास लागू होने से ठीक पहले जानकारी प्राप्त करने के लिए (to: with :) (जिसे traitCollectionDidChange (_ :)) से पहले कहा जाता है।

 override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator)
{
    super.willTransition(to: newCollection, with: coordinator)
    //...
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.