आईपैड पोर्ट्रेट और लैंडस्केप मोड्स के लिए साइजिंग क्लास


97

मैं मूल रूप से अपने साक्षात्कारों को iPad (पोर्ट्रेट या लैंडस्केप) के अभिविन्यास के आधार पर अलग-अलग तैनात करना चाहता हूं, जो कि एक्सकोड 6 में पेश किए गए साइजिंग क्लासेस का उपयोग कर रहा है। मुझे कई ट्यूटोरियल मिल गए हैं, जो बताते हैं कि आईबी और पोर्ट्रेट पर आईफोन के लिए अलग-अलग आकार के वर्ग कितने उपलब्ध हैं। लेकिन हालाँकि ऐसा कुछ भी नहीं है जो आईपैड पर आईपैड के लिए अलग-अलग लैंडस्केप या पोर्ट्रेट मोड्स को कवर करता हो। क्या कोई मदद कर सकता है?


एक गैर-प्रोग्रामेटिक समाधान नहीं प्रतीत होता है, जिसे डिफ़ॉल्ट स्क्रीन के लिए आवश्यक होगा
SwiftArchitect

जवाबों:


174

यह प्रतीत होता है कि दोनों iPad के झुकावों को एक समान मानने के लिए Apple के इरादे हैं - लेकिन जैसा कि हम में से कई लोग पा रहे हैं, iPad पोर्ट्रेट बनाम iPad लैंडस्केप के लिए UI लेआउट को अलग करने के लिए बहुत वैध डिजाइन कारण हैं।

दुर्भाग्य से, वर्तमान ओएस इस अंतर के लिए समर्थन प्रदान करने के लिए प्रतीत नहीं होता है ... इसका मतलब है कि हम कोड या इसी तरह के वर्कअराउंड में हेरफेर करने के लिए वापस आ रहे हैं जिसे प्राप्त करने के लिए हम आदर्श रूप से अनुकूली UI का उपयोग करके मुफ्त में प्राप्त कर सकते हैं। ।

सुरुचिपूर्ण समाधान नहीं।

क्या उस जादू का लाभ उठाने का कोई तरीका नहीं है जो Apple ने पहले से ही एक दिए गए अभिविन्यास के लिए हमारे चयन के आकार वर्ग का उपयोग करने के लिए IB और UIKit में बनाया है ?

~

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

वास्तव में, एक 'आकार वर्ग' वास्तव में केवल एक जोड़ी है, जो कि बहुत अधिक मूल्यवान है। UIInterface.h से:

typedef NS_ENUM(NSInteger, UIUserInterfaceSizeClass) {
    UIUserInterfaceSizeClassUnspecified = 0,
    UIUserInterfaceSizeClassCompact     = 1,
    UIUserInterfaceSizeClassRegular     = 2,
} NS_ENUM_AVAILABLE_IOS(8_0);

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

अब, यह मानकर कि हम IB में एक वैकल्पिक लेआउट (अप्रयुक्त आकार वर्ग का उपयोग करके) बनाते हैं - कहते हैं, iPad पोर्ट्रेट के लिए ... क्या डिवाइस का उपयोग हमारी पसंद के आकार वर्ग (UI लेआउट) का उपयोग करना है जैसा कि रनटाइम में किया जाता है। ?

समस्या के कई अलग-अलग (कम सुरुचिपूर्ण) तरीकों की कोशिश करने के बाद, मुझे संदेह था कि डिफ़ॉल्ट आकार वर्ग को प्रोग्रामेटिक रूप से ओवरराइड करने का एक तरीका हो सकता है। और वहाँ (UIViewController.h में):

// Call to modify the trait collection for child view controllers.
- (void)setOverrideTraitCollection:(UITraitCollection *)collection forChildViewController:(UIViewController *)childViewController NS_AVAILABLE_IOS(8_0);
- (UITraitCollection *)overrideTraitCollectionForChildViewController:(UIViewController *)childViewController NS_AVAILABLE_IOS(8_0);

इस प्रकार, यदि आप अपने व्यू कंट्रोलर पदानुक्रम को 'चाइल्ड' व्यू कंट्रोलर के रूप में पैकेज कर सकते हैं, और इसे टॉप-लेवल पैरेंट व्यू कंट्रोलर में जोड़ सकते हैं ... तो आप बच्चे को सशर्त रूप से यह सोचकर ओवरराइड कर सकते हैं कि यह डिफॉल्ट से अलग साइज की क्लास है। ओएस से।

यहाँ एक नमूना कार्यान्वयन है जो ऐसा करता है, 'मूल' दृश्य नियंत्रक में:

@interface RDTraitCollectionOverrideViewController : UIViewController {
    BOOL _willTransitionToPortrait;
    UITraitCollection *_traitCollection_CompactRegular;
    UITraitCollection *_traitCollection_AnyAny;
}
@end

@implementation RDTraitCollectionOverrideViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self setUpReferenceSizeClasses];
}

- (void)setUpReferenceSizeClasses {
    UITraitCollection *traitCollection_hCompact = [UITraitCollection traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassCompact];
    UITraitCollection *traitCollection_vRegular = [UITraitCollection traitCollectionWithVerticalSizeClass:UIUserInterfaceSizeClassRegular];
    _traitCollection_CompactRegular = [UITraitCollection traitCollectionWithTraitsFromCollections:@[traitCollection_hCompact, traitCollection_vRegular]];

    UITraitCollection *traitCollection_hAny = [UITraitCollection traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassUnspecified];
    UITraitCollection *traitCollection_vAny = [UITraitCollection traitCollectionWithVerticalSizeClass:UIUserInterfaceSizeClassUnspecified];
    _traitCollection_AnyAny = [UITraitCollection traitCollectionWithTraitsFromCollections:@[traitCollection_hAny, traitCollection_vAny]];
}

-(void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    _willTransitionToPortrait = self.view.frame.size.height > self.view.frame.size.width;
}

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]
    _willTransitionToPortrait = size.height > size.width;
}

-(UITraitCollection *)overrideTraitCollectionForChildViewController:(UIViewController *)childViewController {
    UITraitCollection *traitCollectionForOverride = _willTransitionToPortrait ? _traitCollection_CompactRegular : _traitCollection_AnyAny;
    return traitCollectionForOverride;
}
@end

एक त्वरित डेमो के रूप में यह देखने के लिए कि क्या यह काम करता है, मैंने विशेष रूप से आईबी में चाइल्ड कंट्रोलर लेआउट के 'रेगुलर / रेगुलर' और 'कॉम्पेक्ट / रेगुलर' वर्जन में कस्टम लेबल जोड़े।

यहां छवि विवरण दर्ज करें यहां छवि विवरण दर्ज करें

और यहाँ क्या चल रहा है जैसा दिखता है, जब iPad दोनों झुकाव में है: यहां छवि विवरण दर्ज करें यहां छवि विवरण दर्ज करें

देखा! कस्टम आकार वर्ग विन्यास रनटाइम पर।

उम्मीद है कि Apple ओएस के अगले संस्करण में इसे अनावश्यक बना देगा। इस बीच, यह ऑटो-लेआउट बाधाओं के साथ प्रोग्रामिंग करने या कोड में अन्य जोड़तोड़ करने की तुलना में अधिक सुरुचिपूर्ण और स्केलेबल दृष्टिकोण हो सकता है।

~

EDIT (6/4/15): कृपया ध्यान रखें कि उपरोक्त नमूना कोड अनिवार्य रूप से तकनीक को प्रदर्शित करने के लिए अवधारणा का प्रमाण है। अपने स्वयं के विशिष्ट अनुप्रयोग के लिए आवश्यकतानुसार अनुकूलित करने के लिए स्वतंत्र महसूस करें।

~

EDIT (7/24/15): यह समझ में आता है कि उपरोक्त स्पष्टीकरण समस्या को ध्वस्त करने में मदद करता है। जबकि मैंने इसका परीक्षण नहीं किया है, mohamede1945 द्वारा कोड [नीचे] व्यावहारिक उद्देश्यों के लिए एक सहायक अनुकूलन जैसा दिखता है। बेझिझक इसका परीक्षण करें और हमें बताएं कि आप क्या सोचते हैं। (पूर्णता के हित में, मैं नमूना कोड को इस प्रकार छोड़ दूँगा।)


1
शानदार पोस्ट @RonDiamond!
अमेरिकन

3
Apple सफारी में चौड़ाई के आकार को फिर से परिभाषित करने के लिए एक समान दृष्टिकोण लेता है, इसलिए आप यह आश्वासन दे सकते हैं कि यह अधिक या कम समर्थित दृष्टिकोण है। आपको वास्तव में अतिरिक्त ivars की आवश्यकता नहीं होनी चाहिए; UITraitCollectionपर्याप्त रूप से अनुकूलित है और overrideTraitCollectionForChildViewControllerइसे शायद ही कभी पर्याप्त कहा जाता है कि यह चौड़ाई की जांच करने और फिर इसे बनाने के लिए एक मुद्दा नहीं होना चाहिए।
ज्वालोद्स्की

1
@zwaldowski धन्यवाद। यहां नमूना कोड केवल तकनीक को प्रदर्शित करने के लिए है, और जाहिर है कि वांछित के रूप में विभिन्न तरीकों से अनुकूलित किया जा सकता है। (एक सामान्य नियम के रूप में, किसी वस्तु को बार-बार इस्तेमाल किए जाने के साथ [जैसे, जब भी उपकरण अभिविन्यास बदलता है], मुझे नहीं लगता कि किसी वस्तु पर पकड़ बनाना एक बुरा विचार है; लेकिन जैसा कि आप बताते हैं, यहां प्रदर्शन अंतर न्यूनतम हो सकता है।)
रॉनडैमंड

1
@ रश्मि: जैसा कि मैंने स्पष्टीकरण में बताया, यह अंततः कोई फर्क नहीं पड़ता कि आप किसे चुनते हैं - आप लेआउट (ओं) को केवल एनम मानों की एक जोड़ी के लिए मैप कर रहे हैं। तो आप वास्तव में "आकार वर्ग" का उपयोग कर सकते हैं जो आपके लिए समझ में आता है। मैं सिर्फ यह सुनिश्चित करूंगा कि यह कुछ अन्य (वैध) आकार वर्ग के साथ संघर्ष न करे जो कि डिफ़ॉल्ट के रूप में कहीं विरासत में मिला हो।
रॉनडायमंड

1
चाइल्ड व्यू कंट्रोलर के लिए, मुझे नहीं लगता कि यह मायने रखता है। जब तक यह ठीक से कंटेनर में एक बाल नियंत्रक के रूप में जोड़ा जाता है, तब तक आपको इसे प्रोग्राम से या नीब से तुरंत करने में सक्षम होना चाहिए।
रॉनडायमंड

41

RonDiamond द्वारा बहुत लंबे उत्तर के सारांश के रूप में। आपको बस अपने रूट व्यू कंट्रोलर की जरूरत है।

उद्देश्य सी

- (UITraitCollection *)overrideTraitCollectionForChildViewController:(UIViewController *)childViewController
{
    if (CGRectGetWidth(self.view.bounds) < CGRectGetHeight(self.view.bounds)) {
        return [UITraitCollection traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassCompact];
    } else {
        return [UITraitCollection traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassRegular];
    }
}

स्विफ्ट:

override func overrideTraitCollectionForChildViewController(childViewController: UIViewController) -> UITraitCollection! {
        if view.bounds.width < view.bounds.height {
            return UITraitCollection(horizontalSizeClass: .Compact)
        } else {
            return UITraitCollection(horizontalSizeClass: .Regular)
        }
    }

फिर स्टोरीब्रैड में पोर्ट्रेट के लिए कॉम्पैक्ट चौड़ाई और लैंडस्केप के लिए नियमित चौड़ाई का उपयोग करें।


मैं सोच रहा हूँ कि कैसे नए विभाजन स्क्रीन मोड के साथ काम करने जा रहा हूँ ... यह पता लगाने का एक तरीका है!
mm2001

UITraitCollection केवल iOS 8.0+ के लिए है
mohamede1945

मेरे लिए काम नहीं करता है मेरे पास दो कंजर्वेटी के साथ एक दृष्टिकोण है, इन्हें अभिविन्यास के आधार पर एक अलग स्थान पर दिखाया जाना चाहिए। मैंने सभी आवश्यक आकार वर्गों को बनाया जो यह सभी iPhone के लिए काम करता है। आईपैड ओरिएंटेशन को अलग करने के लिए मैंने आपके कोड का उपयोग करने की कोशिश की। हालांकि यह सिर्फ विचारों पर अजीब कार्य करेगा। यह इसे नहीं बदलता है जैसे कि यह माना जाता है। कोई सुराग क्या हो सकता है?
NoSixties

1
यह मेरे लिए तब काम आया जब मैंने - (UITraitCollection *)traitCollectionइसके बजाय ओवररोड किया overrideTraitCollectionForChildViewController। इसके अलावा, बाधाओं को traitcollections से मेल खाना चाहिए, इसलिए wC (hAny)।
एच। डी। जॉन्ज

1
@Apollo मैं होगा, लेकिन यह वास्तविक सवाल का जवाब नहीं होगा। Apple के नमूने का उपयोग करने के तरीके के लिए यहां एक नज़र डालें ।OverrideTraitCollection github.com/ios8/AdaptivePhotosAnAdaptiveApplication/blob/master/…
malhal

5

IPad में क्षैतिज और ऊर्ध्वाधर दोनों आयामों के लिए 'नियमित' आकार का गुण है, जो चित्र और परिदृश्य के बीच कोई अंतर नहीं करता है।

ये आकार लक्षण आपके कस्टम UIViewControllerउपवर्ग कोड में ओवरराइड किए जा सकते हैं traitCollection, उदाहरण के लिए, विधि के माध्यम से :

- (UITraitCollection *)traitCollection {
    // Distinguish portrait and landscape size traits for iPad, similar to iPhone 7 Plus.
    // Be aware that `traitCollection` documentation advises against overriding it.
    UITraitCollection *superTraits = [super traitCollection];
    if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
        UITraitCollection *horizontalRegular = [UITraitCollection traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassRegular];
        UITraitCollection *verticalRegular = [UITraitCollection traitCollectionWithVerticalSizeClass:UIUserInterfaceSizeClassRegular];
        UITraitCollection *regular = [UITraitCollection traitCollectionWithTraitsFromCollections:@[horizontalRegular, verticalRegular]];

        if ([superTraits containsTraitsInCollection:regular]) {
            if (UIInterfaceOrientationIsPortrait([[UIApplication sharedApplication] statusBarOrientation])) {
                // iPad in portrait orientation
                UITraitCollection *horizontalCompact = [UITraitCollection traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassCompact];
                return [UITraitCollection traitCollectionWithTraitsFromCollections:@[superTraits, horizontalCompact, verticalRegular]];
            } else {
                // iPad in landscape orientation
                UITraitCollection *verticalCompact = [UITraitCollection traitCollectionWithVerticalSizeClass:UIUserInterfaceSizeClassCompact];
                return [UITraitCollection traitCollectionWithTraitsFromCollections:@[superTraits, horizontalRegular, verticalCompact]];
            }
        }
    }
    return superTraits;
}

- (BOOL)prefersStatusBarHidden {
    // Override to negate this documented special case, and avoid erratic hiding of status bar in conjunction with `traitCollection` override:
    // For apps linked against iOS 8 or later, this method returns true if the view controller is in a vertically compact environment.
    return NO;
}

यह आईपैड को आईफोन 7 प्लस के समान आकार देता है। ध्यान दें कि अन्य iPhone मॉडल में आमतौर पर 'कॉम्पैक्ट चौड़ाई' विशेषता (नियमित चौड़ाई के बजाय) अभिविन्यास की परवाह किए बिना होती है।

इस तरह से iPhone 7 प्लस की नकल करना उस मॉडल को Xcode के इंटरफ़ेस बिल्डर में iPad के लिए स्टैंड-इन के रूप में उपयोग करने की अनुमति देता है, जो कोड में अनुकूलन से अनजान है।

ज्ञात हो कि iPad पर स्प्लिट व्यू सामान्य पूर्ण स्क्रीन ऑपरेशन से विभिन्न आकार के लक्षणों का उपयोग कर सकता है।

यह उत्तर कुछ सुधारों के साथ इस ब्लॉग पोस्ट में लिए गए दृष्टिकोण पर आधारित है ।

अद्यतन 2019-01-02: आईपैड परिदृश्य में आंतरायिक छिपी हुई स्थिति पट्टी को ठीक करने के लिए अद्यतन किया गया, और (नए) लक्षणों की संभावित रौंद में UITraitCollection। यह भी कहा कि Apple प्रलेखन वास्तव में ओवरराइडिंग के खिलाफ की सिफारिश करता है traitCollection, इसलिए भविष्य में इस तकनीक के साथ समस्याएँ हो सकती हैं।


Apple निर्दिष्ट करता है कि traitCollectionसंपत्ति को केवल पढ़ा जाना चाहिए: developer.apple.com/documentation/uikit/uitraitenvironment/…
होशियार

4

रॉनडायमंड द्वारा लंबा और सहायक उत्तर सिद्धांतों को समझने के लिए एक अच्छी शुरुआत है, हालांकि मेरे लिए काम करने वाला कोड (iOS 8+) ओवरराइडिंग पद्धति पर आधारित है (UITraitCollection *)traitCollection

इसलिए, चौड़ाई के लिए रूपांतरों के साथ InterfaceBuilder में बाधाओं को जोड़ें - कॉम्पैक्ट, उदाहरण के लिए बाधा की संपत्ति स्थापित के लिए। तो चौड़ाई - कोई भी परिदृश्य के लिए मान्य होगा, चौड़ाई - पोर्ट्रेट के लिए कॉम्पैक्ट।

वर्तमान दृश्य नियंत्रक आकार के आधार पर कोड में बाधाओं को स्विच करने के लिए, बस अपने UIViewController वर्ग में निम्नलिखित जोड़ें:

- (UITraitCollection *)traitCollection
{
    UITraitCollection *verticalRegular = [UITraitCollection traitCollectionWithVerticalSizeClass:UIUserInterfaceSizeClassRegular];

    if (self.view.bounds.size.width < self.view.bounds.size.height) {
        // wCompact, hRegular
        return [UITraitCollection traitCollectionWithTraitsFromCollections:
                @[[UITraitCollection traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassCompact],
                  verticalRegular]];
    } else {
        // wRegular, hRegular
        return [UITraitCollection traitCollectionWithTraitsFromCollections:
                @[[UITraitCollection traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassRegular],
                  verticalRegular]];
    }
}

0

आपके चित्र मोड की तुलना में आपका परिदृश्य मोड कितना अलग है? यदि इसकी बहुत भिन्नता है, तो यह एक अच्छा विचार हो सकता है कि एक अन्य दृश्य नियंत्रक बनाएं और डिवाइस में लैंड होने पर इसे लोड करें

उदाहरण के लिए

    if (UIDeviceOrientationIsLandscape([UIDevice currentDevice].orientation)) 
    //load landscape view controller here

हाँ यह एक विकल्प है। लेकिन मुझे नहीं लगता कि यह सबसे इष्टतम है। मेरा कहना यह है कि अगर आईओएस 8 में आईफोन के लिए पोर्ट्रेट और लैंडस्केप मोड के लिए अलग-अलग आकार के वर्ग लक्षणों का उपयोग करने का विकल्प है, तो आईपैड के लिए समान क्यों नहीं है?
neelIVP

Xcode 6 से पहले, हम अलग-अलग अभिविन्यास के लिए अलग-अलग स्टोरीबोर्ड का उपयोग कर सकते हैं। यदि अधिकांश दृश्य नियंत्रक समान हैं तो यह बहुत कुशल नहीं है। लेकिन यह विभिन्न लेआउट के लिए बहुत सुविधाजनक है। Xcode 6 में, ऐसा करने का कोई तरीका नहीं है। शायद अलग-अलग अभिविन्यास के लिए अलग-अलग व्यू कंट्रोलर बनाना ही एकमात्र उपाय है।
Bagusflyer

2
हर बार अलग-अलग दृश्य लोड करना नियंत्रित करना बहुत ही अक्षम लगता है, खासकर अगर स्क्रीन पर कुछ हो रहा हो। एक ही दृश्य का उपयोग करना बेहतर है और या तो कोड में ऑटोलॉययट बाधा या तत्वों की स्थिति में हेरफेर करना है। या ऊपर बताए गए हैक का उपयोग करने के लिए, जब तक कि सेब इस मुद्दे को संबोधित नहीं करता।
19

0

स्विफ्ट 5 संस्करण। यह बढ़िया काम करता है।

override func overrideTraitCollection(forChild childViewController: UIViewController) -> UITraitCollection? {
    if UIScreen.main.bounds.width > UIScreen.main.bounds.height {
        let collections = [UITraitCollection(horizontalSizeClass: .regular),
                           UITraitCollection(verticalSizeClass: .compact)]
        return UITraitCollection(traitsFrom: collections)
    }
    return super.overrideTraitCollection(forChild: childViewController)
}

-3

@RonDiamond समाधान के लिए स्विफ्ट 3.0 कोड

class Test : UIViewController {


var _willTransitionToPortrait: Bool?
var _traitCollection_CompactRegular: UITraitCollection?
var _traitCollection_AnyAny: UITraitCollection?

func viewDidLoad() {
    super.viewDidLoad()
    self.upReferenceSizeClasses = null
}

func setUpReferenceSizeClasses() {
    var traitCollection_hCompact: UITraitCollection = UITraitCollection(horizontalSizeClass: UIUserInterfaceSizeClassCompact)
    var traitCollection_vRegular: UITraitCollection = UITraitCollection(verticalSizeClass: UIUserInterfaceSizeClassRegular)
    _traitCollection_CompactRegular = UITraitCollection(traitsFromCollections: [traitCollection_hCompact,traitCollection_vRegular])
    var traitCollection_hAny: UITraitCollection = UITraitCollection(horizontalSizeClass: UIUserInterfaceSizeClassUnspecified)
    var traitCollection_vAny: UITraitCollection = UITraitCollection(verticalSizeClass: UIUserInterfaceSizeClassUnspecified)
    _traitCollection_AnyAny = UITraitCollection(traitsFromCollections: [traitCollection_hAny,traitCollection_vAny])
}

func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)
    _willTransitionToPortrait = self.view.frame.size.height > self.view.frame.size.width
}

func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
    _willTransitionToPortrait = size.height > size.width
}

func overrideTraitCollectionForChildViewController(childViewController: UIViewController) -> UITraitCollection {
    var traitCollectionForOverride: UITraitCollection = _willTransitionToPortrait ? _traitCollection_CompactRegular : _traitCollection_AnyAny
    return traitCollectionForOverride
}}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.