ios - दृश्य छुपाए जाने पर अन्य विचारों को स्थानांतरित करने के लिए ऑटो-लेआउट का उपयोग कैसे करें?




iphone cocoa-touch (13)

मैंने आईबी में अपना कस्टम सेल डिज़ाइन किया है, इसे सबक्लास किया है और मेरे आउटलेट को मेरी कस्टम क्लास में जोड़ा है। मेरे पास सेल सामग्री में तीन सबव्यूव हैं जो हैं: UIView (cdView) और दो लेबल (शीर्षक लेबल और ईमेल लेबल)। प्रत्येक पंक्ति के लिए उपलब्ध डेटा के आधार पर, कभी-कभी मैं अपने सेल में प्रदर्शित होने वाले UIView और दो लेबल और कभी-कभी केवल दो लेबल रखना चाहता हूं। जो मैं करने की कोशिश कर रहा हूं वह बाधाओं को सेट करना है अगर मैं UIView प्रॉपर्टी को छिपाने के लिए सेट करता हूं या मैं इसे पर्यवेक्षण से हटा दूंगा तो दो लेबल बाईं ओर चले जाएंगे। मैंने 10px के लिए Uerview अग्रणी बाधा को सुपरव्यू (सेल सामग्री) में सेट करने की कोशिश की और UILabels अगली दृश्य (UIView) में 10 पीएक्स के लिए अग्रणी बाधाओं को स्थापित करने की कोशिश की। बाद में मेरे कोड में

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(IndexPath *)indexPath {
...
Record *record = [self.records objectAtIndex:indexPath.row];

if ([record.imageURL is equalToString:@""]) {
     cell.cdView.hidden = YES;
}

मैं अपना सेल.cdView छुपा रहा हूं और मैं लेबल को बाईं ओर ले जाना चाहता हूं हालांकि वे सेल में एक ही स्थिति में रह रहे हैं। मैंने पर्यवेक्षण से cell.cdView को हटाने का प्रयास किया लेकिन यह या तो काम नहीं किया। मैंने यह स्पष्ट करने के लिए छवि संलग्न की है कि मैं क्या कर रहा हूं।

मुझे पता है कि यह प्रोग्रामेटिक तरीके से कैसे करें और मैं उस समाधान की तलाश नहीं कर रहा हूं। मैं जो चाहता हूं वह आईबी में बाधाओं को स्थापित करना है और मुझे उम्मीद है कि मेरे विचारों को गतिशील रूप से स्थानांतरित किया जाएगा यदि अन्य विचार हटा दिए जाते हैं या छुपाए जाते हैं। ऑटो-लेआउट के साथ आईबी में ऐसा करना संभव है?

.....

uiview और लेबल के बीच बाधा को आईबीओटलेट के रूप में कनेक्ट करें और छुपाएं सेट करें जब कम मूल्य पर प्राथमिकता सदस्य सेट करें = हाँ


अगर यह किसी की मदद करता है, तो मैंने दृश्य प्रारूप बाधाओं का उपयोग करने के लिए एक सहायक वर्ग बनाया। मैं इसे अपने वर्तमान ऐप में उपयोग कर रहा हूं।

AutolayoutHelper

यह मेरी आवश्यकताओं के अनुरूप थोड़ा सा हो सकता है, लेकिन आपको यह उपयोगी लगेगा या आप इसे संशोधित करना और अपना स्वयं का सहायक बनाना चाहते हैं।

मुझे ऊपर दिए गए उत्तर के लिए टिम धन्यवाद देना है , UIScrollView और यह tutorial बारे में यह उत्तर भी है।


इसे आज़माएं, मैंने कोड के नीचे लागू किया है,

मेरे पास व्यू कंट्रोलर पर एक दृश्य है जिसमें अन्य तीन विचार जोड़े गए हैं, जब कोई दृश्य छिपा हुआ है तो दो दृश्य देखेंगे, नीचे दिए गए चरणों का पालन करें। ,

1.ViewController.h फ़ाइल

#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@property (strong, nonatomic) IBOutlet UIView *viewOne;
@property (strong, nonatomic) IBOutlet UIView *viewTwo;
@property (strong, nonatomic) IBOutlet UIView *viewThree;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *viewOneWidth;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *viewTwoWidth;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *viewThreeWidth;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *viewBottomWidth;
@end

2.ViewController.m

 #import "ViewController.h"
 @interface ViewController ()
{
  CGFloat viewOneWidthConstant;
  CGFloat viewTwoWidthConstant;
  CGFloat viewThreeWidthConstant;
  CGFloat viewBottomWidthConstant;
}
@end

@implementation ViewController
@synthesize viewOne, viewTwo, viewThree;

- (void)viewDidLoad {
  [super viewDidLoad];
 // Do any additional setup after loading the view, typically from a 
  nib.

  /*
   0  0   0
   0  0   1
   0  1   0
   0  1   1
   1  0   0
   1  0   1
   1  1   0
   1  1   1
   */

  //    [viewOne setHidden:NO];
  //    [viewTwo setHidden:NO];
  //    [viewThree setHidden:NO];

  //    [viewOne setHidden:NO];
  //    [viewTwo setHidden:NO];
  //    [viewThree setHidden:YES];

  //    [viewOne setHidden:NO];
  //    [viewTwo setHidden:YES];
  //    [viewThree setHidden:NO];

  //    [viewOne setHidden:NO];
  //    [viewTwo setHidden:YES];
  //    [viewThree setHidden:YES];


  //    [viewOne setHidden:YES];
  //    [viewTwo setHidden:NO];
  //    [viewThree setHidden:NO];

  //    [viewOne setHidden:YES];
  //    [viewTwo setHidden:NO];
  //    [viewThree setHidden:YES];

 //    [viewOne setHidden:YES];
 //    [viewTwo setHidden:YES];
 //    [viewThree setHidden:NO];

//    [viewOne setHidden:YES];
//    [viewTwo setHidden:YES];
//    [viewThree setHidden:YES];

 [self hideShowBottomBar];
  }

- (void)hideShowBottomBar
{
  BOOL isOne = !viewOne.isHidden;
  BOOL isTwo = !viewTwo.isHidden;
  BOOL isThree = !viewThree.isHidden;

  viewOneWidthConstant = _viewOneWidth.constant;
  viewTwoWidthConstant = _viewTwoWidth.constant;
  viewThreeWidthConstant = _viewThreeWidth.constant;
  viewBottomWidthConstant = _viewBottomWidth.constant;

   if (isOne && isTwo && isThree) {
    // 0    0   0
    _viewOneWidth.constant = viewBottomWidthConstant / 3;
    _viewTwoWidth.constant = viewBottomWidthConstant / 3;
    _viewThreeWidth.constant = viewBottomWidthConstant / 3;
    }
    else if (isOne && isTwo && !isThree) {
     // 0    0   1
    _viewOneWidth.constant = viewBottomWidthConstant / 2;
    _viewTwoWidth.constant = viewBottomWidthConstant / 2;
    _viewThreeWidth.constant = 0;
    }
   else if (isOne && !isTwo && isThree) {
    // 0    1   0
    _viewOneWidth.constant = viewBottomWidthConstant / 2;
    _viewTwoWidth.constant = 0;
    _viewThreeWidth.constant = viewBottomWidthConstant / 2;
    }
    else if (isOne && !isTwo && !isThree) {
    // 0    1   1
    _viewOneWidth.constant = viewBottomWidthConstant;
    _viewTwoWidth.constant = 0;
    _viewThreeWidth.constant = 0;
   }
   else if (!isOne && isTwo && isThree) {
    // 1    0   0
    _viewOneWidth.constant = 0;
    _viewTwoWidth.constant = viewBottomWidthConstant / 2;
    _viewThreeWidth.constant = viewBottomWidthConstant / 2;
   }
   else if (!isOne && isTwo && !isThree) {
    // 1    0   1
    _viewOneWidth.constant = 0;
    _viewTwoWidth.constant = viewBottomWidthConstant;
    _viewThreeWidth.constant = 0;
   }
   else if (!isOne && !isTwo && isThree) {
    // 1    1   0
    _viewOneWidth.constant = 0;
    _viewTwoWidth.constant = 0;
    _viewThreeWidth.constant = viewBottomWidthConstant;
   }
   else if (isOne && isTwo && isThree) {
    // 1    1   1
    _viewOneWidth.constant = 0;
    _viewTwoWidth.constant = 0;
    _viewThreeWidth.constant = 0;
   }
  }

 - (void)didReceiveMemoryWarning {
  [super didReceiveMemoryWarning];
 // Dispose of any resources that can be recreated.
 }
 @end

उम्मीद है तो यह तर्क किसी की मदद करेगा।


उन लोगों के लिए जो केवल आईओएस 8+ का समर्थन करते हैं, वहां एक नई बूलियन संपत्ति active । यह केवल गतिशील रूप से आवश्यक बाधाओं को सक्षम करने में मदद करेगा

पीएस प्रतिबंध आउटलेट मजबूत होना चाहिए, कमजोर नहीं

उदाहरण:

@IBOutlet weak var optionalView: UIView!
@IBOutlet var viewIsVisibleConstraint: NSLayoutConstraint!
@IBOutlet var viewIsHiddenConstraint: NSLayoutConstraint!

func showView() {
    optionalView.isHidden = false
    viewIsVisibleConstraint.isActive = true
    viewIsHiddenConstraint.isActive = false
}

func hideView() {
    optionalView.isHidden = true
    viewIsVisibleConstraint.isActive = false
    viewIsHiddenConstraint.isActive = true
}

स्टोरीबोर्ड में एक त्रुटि को ठीक करने के लिए आपको इन बाधाओं में से किसी एक के लिए Installed चेकबॉक्स को अनचेक करना होगा।

UIStackView (आईओएस 9 +)
UIStackView में अपने विचार लपेटने का एक और विकल्प है। दृश्य देखने के बाद UIStackView स्वचालित रूप से लेआउट अपडेट करेगा


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

ReactiveCocoa का उपयोग करके स्निपेट यहां दिया गया है:

RACSignal* isViewOneHiddenSignal = RACObserve(self.viewModel, isViewOneHidden);
RACSignal* isViewTwoHiddenSignal = RACObserve(self.viewModel, isViewTwoHidden);
RACSignal* isViewThreeHiddenSignal = RACObserve(self.viewModel, isViewThreeHidden);
RAC(self.viewOne, hidden) = isViewOneHiddenSignal;
RAC(self.viewTwo, hidden) = isViewTwoHiddenSignal;
RAC(self.viewThree, hidden) = isViewThreeHiddenSignal;

RAC(self.viewFourBottomConstraint, priority) = [[[[RACSignal
    combineLatest:@[isViewOneHiddenSignal,
                    isViewTwoHiddenSignal,
                    isViewThreeHiddenSignal]]
    and]
    distinctUntilChanged]
    map:^id(NSNumber* allAreHidden) {
        return [allAreHidden boolValue] ? @(780) : @(UILayoutPriorityDefaultHigh);
    }];

RACSignal* updateFramesSignal = [RACObserve(self.viewFourBottomConstraint, priority) distinctUntilChanged];
[updateFramesSignal
    subscribeNext:^(id x) {
        @strongify(self);
        [self.view setNeedsUpdateConstraints];
        [UIView animateWithDuration:0.3 animations:^{
            [self.view layoutIfNeeded];
        }];
    }];

दो UIStackView क्षैतिज और लंबवत का उपयोग करें, जब स्टैक में कुछ सबव्यू व्यू छुपाया जाता है तो अन्य स्टैक सबव्यूव्स स्थानांतरित हो जाएंगे, वितरण का उपयोग करें -> दो UILabels के साथ वर्टिकल स्टैक के लिए उचित रूप से भरें और पहले UIView के लिए चौड़ाई और ऊंचाई स्थिरता सेट करें


मेरी परियोजना UILabel कस्टम @IBDesignable उप-वर्ग का उपयोग करती है (रंग, फ़ॉन्ट, इन्सेट इत्यादि में स्थिरता सुनिश्चित करने के लिए) और मैंने निम्न की तरह कुछ लागू किया है:

override func intrinsicContentSize() -> CGSize {
    if hidden {
        return CGSizeZero
    } else {
        return super.intrinsicContentSize()
    }
}

यह लेबल उप-वर्ग को ऑटो लेआउट में भाग लेने की अनुमति देता है, लेकिन छुपाए जाने पर कोई स्थान नहीं लेता है।


मेरे मामले में मैंने 0.0f की ऊंचाई बाधा को स्थिर रखा और hidden संपत्ति को YES भी सेट किया।

दृश्य (सबव्यू के साथ) को फिर से दिखाने के लिए मैंने विपरीत किया: मैंने ऊंचाई को निरंतर गैर-शून्य मान पर सेट किया और hidden संपत्ति को NO सेट किया।


मैं जो कर रहा था वह 2 xibs बना रहा था। एक बाएं दृश्य के साथ और इसके बिना एक। मैंने नियंत्रक में दोनों पंजीकृत किए और फिर फैसला किया कि सेलफॉररोएट इंडेक्सपैथ के दौरान किस का उपयोग करना है।

वे एक ही UITableViewCell कक्षा का उपयोग करते हैं। नकारात्मकता यह है कि xibs के बीच सामग्री का कुछ डुप्लिकेशंस है, लेकिन ये कोशिकाएं काफी बुनियादी हैं। उलझन यह है कि मेरे पास मैन्युअल रूप से दृश्य को हटाने, बाधाओं को अपडेट करने, प्रबंधित करने के लिए कोड का एक गुच्छा नहीं है।

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

[self.table registerNib:[UINib nibWithNibName:@"TrackCell" bundle:nil] forCellReuseIdentifier:@"TrackCell"];
[self.table registerNib:[UINib nibWithNibName:@"TrackCellNoImage" bundle:nil] forCellReuseIdentifier:@"TrackCellNoImage"];

TrackCell *cell = [tableView dequeueReusableCellWithIdentifier:(appDelegate.showImages ? @"TrackCell" : @"TrackCellNoImage") forIndexPath:indexPath];

यह एक पुराना सवाल है लेकिन फिर भी मुझे आशा है कि इससे मदद मिलेगी। एंड्रॉइड से आ रहा है, इस मंच में आपके पास एक आसान तरीका है, इसे दृश्य से छिपाने के लिए isVisible है, लेकिन isVisible दृश्य को आकर्षित करते समय फ्रेम भी नहीं माना जाता है।

एक्सटेंशन का उपयोग करके और "विस्तार" uiview आप आईओएस में एक समान कार्य कर सकते हैं (सुनिश्चित नहीं है कि यह पहले से ही UIKit में क्यों नहीं है) यहां स्विफ्ट 3 में एक कार्यान्वयन है:

    func isVisible(_ isVisible: Bool) {
        self.isHidden = !isVisible
        self.translatesAutoresizingMaskIntoConstraints = isVisible
        if isVisible { //if visible we remove the hight constraint 
            if let constraint = (self.constraints.filter{$0.firstAttribute == .height}.first){
                self.removeConstraint(constraint)
            }
        } else { //if not visible we add a constraint to force the view to have a hight set to 0
            let height = NSLayoutConstraint(item: self, attribute: .height, relatedBy: .equal , toItem: nil, attribute: .notAnAttribute, multiplier: 0, constant: 0)
            self.addConstraint(height)
        }
        self.layoutIfNeeded()
    }

यहां बताया गया है कि मैं अपना समाधान प्राप्त करने के लिए अपने uiviews को फिर से संरेखित करूंगा:

  1. एक UIImageView ड्रॉप करें और इसे बाईं ओर रखें।
  2. एक UIView ड्रॉप खींचें और इसे UIImageView के दाईं ओर रखें।
  3. UIView के अंदर दो UILabels ड्रॉप खींचें जिनकी अग्रणी और पिछली बाधाएं शून्य हैं।
  4. UIIiewView की बजाय पर्यवेक्षण में 2 लेबल युक्त UIView की अग्रणी बाधा सेट करें।
  5. अगर UIImageView छुपा हुआ है, तो पर्यवेक्षण के लिए अग्रणी बाधा स्थिर 10 पीएक्स सेट करें। ईएलएसई, अग्रणी बाधा को लगातार 10 पीएक्स + UIImageView.width + 10 पीएक्स पर सेट करें।

मैंने अपने अंगूठे का नियम बनाया। जब भी आपको किसी भी यूवीव को छिपाना / दिखाया जाना चाहिए, जिसकी बाधाओं को प्रभावित किया जा सकता है, तो यूवीव के अंदर सभी प्रभावित / आश्रित सबव्यू जोड़ें और प्रोग्रामिंग के बाद अपनी अग्रणी / पीछे / शीर्ष / नीचे बाधा अद्यतन करें।


रनटाइम के दौरान बाधाओं को जोड़ना या निकालना एक हेवीवेट ऑपरेशन है जो प्रदर्शन को प्रभावित कर सकता है। हालांकि, एक आसान विकल्प है।

दृश्य के लिए आप छिपाना चाहते हैं, एक चौड़ाई बाधा स्थापित करें। उस दृश्य के लिए एक प्रमुख क्षैतिज अंतर के साथ अन्य विचारों को रोकें।

छिपाने के लिए, चौड़ाई बाधा के .constant को 0.f पर अपडेट करें। अन्य विचार स्थिति को मानने के लिए स्वचालित रूप से बाएं स्थानांतरित हो जाएंगे।

अधिक जानकारी के लिए यहां मेरा दूसरा उत्तर देखें:

रनटाइम के दौरान लेबल बाधाओं को कैसे बदलें?


Googlers के लिए: मैक्स के जवाब पर निर्माण, पैडिंग मुद्दे को हल करने के लिए कि कई ने देखा है कि मैंने बस लेबल की ऊंचाई में वृद्धि की है और उस ऊंचाई को वास्तविक पैडिंग के बजाय विभाजक के रूप में इस्तेमाल किया है। विचारों के साथ किसी भी परिदृश्य के लिए इस विचार को विस्तारित किया जा सकता है।

यहां एक साधारण उदाहरण दिया गया है:

इस मामले में, मैं लेखक लेबल की ऊंचाई को उपयुक्त IBOutlet मैप करता हूं:

@property (retain, nonatomic) IBOutlet NSLayoutConstraint* authorLabelHeight;

और जब मैं बाधा की ऊंचाई 0.0f सेट करता हूं, तो हम "पैडिंग" को संरक्षित करते हैं, क्योंकि प्ले बटन की ऊंचाई इसके लिए अनुमति देती है।





autolayout