क्या मैं NSLayoutConstraint के लिए गुणक संपत्ति को बदल सकता हूं?


143

मैंने एक पर्यवेक्षक में दो दृश्य बनाए, और फिर विचारों के बीच बाधाओं को जोड़ा:

_indicatorConstrainWidth = [NSLayoutConstraint constraintWithItem:self.view1 attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.view2 attribute:NSLayoutAttributeWidth multiplier:1.0f constant:0.0f];
[_indicatorConstrainWidth setPriority:UILayoutPriorityDefaultLow];
_indicatorConstrainHeight = [NSLayoutConstraint constraintWithItem:self.view1 attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.view2 attribute:NSLayoutAttributeHeight multiplier:1.0f constant:0.0f];
[_indicatorConstrainHeight setPriority:UILayoutPriorityDefaultLow];
[self addConstraint:_indicatorConstrainWidth];
[self addConstraint:_indicatorConstrainHeight];

अब मैं गुणक गुण को एनीमेशन से बदलना चाहता हूं, लेकिन मैं यह नहीं समझ सकता कि गुणक गुण को कैसे बदला जाए। (मैंने हेडर फ़ाइल NSLayoutConstraint.h में निजी संपत्ति में _coefficient पाया, लेकिन यह निजी है।)

मैं गुणक संपत्ति कैसे बदलूं?

मेरा काम पुरानी बाधा को दूर करना और नए को अलग मूल्य के साथ जोड़ना है multipler


1
पुरानी बाधा को दूर करने और नए को जोड़ने का आपका वर्तमान तरीका सही विकल्प है। मैं समझता हूं कि यह "सही" महसूस नहीं करता है, लेकिन यह ऐसा तरीका है जिसे आप इसे करने वाले हैं।
रोब

4
मुझे लगता है कि गुणक को स्थिर नहीं होना चाहिए। यह एक बुरा डिजाइन है।
बोरज़

1
@Borzh क्यों गुणक के माध्यम से मैं अनुकूली बाधाओं को बनाते हैं। Resizeble ios के लिए।
बिमावा

1
हां, मैं मल्टीप्लायर को भी बदलना चाहता हूं, ताकि दृश्य इसे माता-पिता के दृष्टिकोण (दोनों चौड़ाई / ऊँचाई लेकिन सिर्फ चौड़ाई या ऊंचाई) के अनुपात में रख सकें, लेकिन अजीब बात यह है कि सेब इसकी अनुमति नहीं देता है। मेरा मतलब है कि यह Apple का बुरा डिज़ाइन है, आपका नहीं।
बोरझ

यह एक महान बात है और इसे और अधिक कवर करता है youtube.com/watch?v=N5Ert6LTruY
DogCfish

जवाबों:


117

यदि आपके पास केवल मल्टीप्लायरों के दो सेट हैं जिन्हें लागू करने की आवश्यकता है, तो iOS8 से आप दोनों बाधाओं को जोड़ सकते हैं और तय कर सकते हैं कि किसी भी समय सक्रिय होना चाहिए:

NSLayoutConstraint *standardConstraint, *zoomedConstraint;

// ...
// switch between constraints
standardConstraint.active = NO; // this line should always be the first line. because you have to deactivate one before activating the other one. or they will conflict.
zoomedConstraint.active = YES;
[self.view layoutIfNeeded]; // or using [UIView animate ...]

स्विफ्ट 5.0 संस्करण

var standardConstraint: NSLayoutConstraint!
var zoomedConstraint: NSLayoutConstraint!

// ...

// switch between constraints
standardConstraint.isActive = false // this line should always be the first line. because you have to deactivate one before activating the other one. or they will conflict.
zoomedConstraint.isActive = true
self.view.layoutIfNeeded() // or using UIView.animate

2
@micnguyen हां, आप कर सकते हैं :)
Ja Junck

7
यदि आपको बाधाओं के 2 सेट से अधिक की आवश्यकता है, तो आप जितने चाहें उतने जोड़ सकते हैं और उनकी प्राथमिकता गुण बदल सकते हैं। इस तरह, 2 बाधाओं के लिए आपको एक को सक्रिय करने के लिए और दूसरे को निष्क्रिय के रूप में सेट करने की आवश्यकता नहीं है, आप बस प्राथमिकता को उच्च या निम्न निर्धारित करते हैं। उपरोक्त उदाहरण पर, मानक मानक प्राथमिकता निर्धारित करें, मान लें कि 997 है, और जब आप इसे सक्रिय करना चाहते हैं, तो बस ज़ूम किया गया ज़ूमस्ट्रीमिंट की प्राथमिकता 995 पर सेट करें, अन्यथा इसे 999 पर सेट करें। यह भी सुनिश्चित करें कि XIB की प्राथमिकताएँ 1000 पर सेट नहीं हैं, अन्यथा आप उन्हें बदल नहीं पाएंगे और आपको एक भयानक दुर्घटना होगी।
डॉग

1
@OnderDog का कोई विशेष लाभ नहीं है, हालांकि? सक्षम करने और अक्षम करने का मुहावरा मुझे सापेक्ष प्राथमिकताओं को पूरा करने की तुलना में पचाने में आसान लगता है।
जेक

2
@WonderDog / जैक मैं आपके दृष्टिकोण को संयोजित कर रहा हूं क्योंकि स्टोरीबोर्ड में मेरी बाधा को परिभाषित किया गया था। और अगर मैंने दो प्राथमिकताएं बनाईं, दोनों समान प्राथमिकता के साथ, लेकिन अलग-अलग गुणकों में, उन्होंने संघर्ष किया और मुझे बहुत सारे लाल दिए। और जो मैं आपको बता सकता हूं, वह आईबी के माध्यम से एक को निष्क्रिय नहीं कर सकता है। इसलिए मैंने अपनी मुख्य बाधा 1000 और मेरे माध्यमिक बाधा के साथ बनाई है जिसे मैं प्राथमिकता 999 के साथ चेतन करना चाहता हूं (आईबी में अच्छा खेलता है)। फिर एक एनीमेशन ब्लॉक के अंदर, मैंने प्राथमिक किसी की सक्रिय संपत्ति को झूठा करने के लिए सेट किया है और यह माध्यमिक के लिए अच्छी तरह से एनिमेटेड है। दोनों की मदद के लिए धन्यवाद!
क्ले गैरेट

2
ध्यान रखें कि आप संभवतः अपने अवरोधों को मजबूत करना चाहते हैं यदि उन्हें सक्रिय और निष्क्रिय करना है, क्योंकि "बाधा कॉल को सक्रिय या निष्क्रिय करना है addConstraint(_:)और removeConstraint(_:)इस बाधा द्वारा प्रबंधित वस्तुओं का निकटतम सामान्य पूर्वज है"।
qix

166

यहां स्विफ्ट में एक NSLayoutConstraint एक्सटेंशन है जो एक नए गुणक को बहुत आसान बनाता है:

स्विफ्ट में 3.0+

import UIKit
extension NSLayoutConstraint {
    /**
     Change multiplier constraint

     - parameter multiplier: CGFloat
     - returns: NSLayoutConstraint
    */
    func setMultiplier(multiplier:CGFloat) -> NSLayoutConstraint {

        NSLayoutConstraint.deactivate([self])

        let newConstraint = NSLayoutConstraint(
            item: firstItem,
            attribute: firstAttribute,
            relatedBy: relation,
            toItem: secondItem,
            attribute: secondAttribute,
            multiplier: multiplier,
            constant: constant)

        newConstraint.priority = priority
        newConstraint.shouldBeArchived = self.shouldBeArchived
        newConstraint.identifier = self.identifier

        NSLayoutConstraint.activate([newConstraint])
        return newConstraint
    }
}

डेमो उपयोग:

@IBOutlet weak var myDemoConstraint:NSLayoutConstraint!

override func viewDidLoad() {
    let newMultiplier:CGFloat = 0.80
    myDemoConstraint = myDemoConstraint.setMultiplier(newMultiplier)

    //If later in view lifecycle, you may need to call view.layoutIfNeeded() 
}

11
NSLayoutConstraint.deactivateConstraints([self])बनाने से पहले किया जाना चाहिए newConstraint, अन्यथा यह चेतावनी में परिणत हो सकता है: एक साथ बाधाओं को संतुष्ट करने में असमर्थ । यह newConstraint.active = trueभी अनावश्यक है क्योंकि NSLayoutConstraint.activateConstraints([newConstraint])बिल्कुल वही काम करता है।
तज़लगा

1
सक्रिय और निष्क्रिय करना ios8 से पहले काम नहीं करता है, क्या इसके लिए कोई विकल्प है ??
नरहरि_र्जुन

2
गुणक को फिर से बदलने पर यह काम नहीं करता है, यह शून्य मान प्राप्त करता है
जे डो

4
हमें समय बचाने के लिए धन्यवाद! मैं फ़ंक्शन को कुछ और बदलना चाहता हूं, cloneWithMultiplierताकि यह स्पष्ट हो सके कि यह केवल साइड इफेक्ट्स लागू नहीं करता है, बल्कि एक नई बाधा लौटाता है
एलेक्जेंडर जी

5
ध्यान दें कि यदि आप गुणक को सेट करते हैं तो आप 0.0इसे फिर से अपडेट नहीं कर पाएंगे।
डैनियल स्टॉर्म

58

multiplierसंपत्ति केवल पढ़ा जाता है। आपको पुराने NSLayoutConstraint को निकालना होगा और इसे बदलने के लिए इसे नए से बदलना होगा।

हालाँकि, जब से आप जानते हैं कि आप गुणक को बदलना चाहते हैं, आप केवल परिवर्तन को तब ही बदल सकते हैं जब परिवर्तन की आवश्यकता होती है जो अक्सर कम कोड होता है।


64
यह अजीब लगता है कि गुणक केवल पढ़ा जाता है। मुझे उम्मीद नहीं थी कि!
जूली

135
@jowie यह विडंबना है कि "स्थिर" मान को बदला जा सकता है जबकि गुणक नहीं कर सकता।
टॉमस आंद्रेल

7
ये गलत है। एक NSLayoutConstraint एक समानता (में) समानता बाधा को निर्दिष्ट करता है। उदाहरण के लिए: "View1.bottomY = A * View2.bottomY + B" 'A' गुणक है, 'B' स्थिर है। कोई फर्क नहीं पड़ता कि आप बी कैसे ट्यून करते हैं, यह ए के मूल्य को बदलने के समान समीकरण का उत्पादन नहीं करेगा।
तमसा ज़होला

8
@FruityGeek ठीक है, तो आप उपयोग कर रहे हैं View2.bottomYकी वर्तमान मूल्य बाधा के गणना करने के लिए नई निरंतर। यह त्रुटिपूर्ण है, चूँकि View2.bottomYपुराने मूल्य को स्थिर रखा जाएगा, इसलिए आपको घोषणात्मक शैली छोड़नी होगी और मैन्युअल रूप से किसी भी View2.bottomYपरिवर्तन में बाधा को अद्यतन करना होगा । इस मामले में आप मैन्युअल लेआउट कर सकते हैं।
तामसी ज़होला

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

49

एक सहायक फ़ंक्शन जिसका उपयोग मैं एक मौजूदा लेआउट बाधा के गुणक को बदलने के लिए करता हूं । यह एक नई बाधा बनाता है और सक्रिय करता है और पुराने को निष्क्रिय कर देता है।

struct MyConstraint {
  static func changeMultiplier(_ constraint: NSLayoutConstraint, multiplier: CGFloat) -> NSLayoutConstraint {
    let newConstraint = NSLayoutConstraint(
      item: constraint.firstItem,
      attribute: constraint.firstAttribute,
      relatedBy: constraint.relation,
      toItem: constraint.secondItem,
      attribute: constraint.secondAttribute,
      multiplier: multiplier,
      constant: constraint.constant)

    newConstraint.priority = constraint.priority

    NSLayoutConstraint.deactivate([constraint])
    NSLayoutConstraint.activate([newConstraint])

    return newConstraint
  }
}

उपयोग, बदलते गुणक को 1.2:

constraint = MyConstraint.changeMultiplier(constraint, multiplier: 1.2)

1
क्या यह iOS 8 से लागू होता है? या इसे पहले के संस्करणों में इस्तेमाल किया जा सकता है
दुरई अमुथान

1
NSLayoutConstraint.deactivateफ़ंक्शन केवल iOS 8.0+ है।
इवगेनी

आप इसे क्यों लौटा रहे हैं?
mskw

@mskw, फ़ंक्शन नई बाधा ऑब्जेक्ट को बनाता है जो इसे बनाता है। पहले वाले को छोड़ा जा सकता है।
Evgenii

42

एंड्रयू स्क्रैबर जवाब के लिए उद्देश्य-सी संस्करण

NSLayoutConstraint Class के लिए श्रेणी बनाएँ और इस तरह से .h फ़ाइल में विधि जोड़ें

    #import <UIKit/UIKit.h>

@interface NSLayoutConstraint (Multiplier)
-(instancetype)updateMultiplier:(CGFloat)multiplier;
@end

.M फ़ाइल में

#import "NSLayoutConstraint+Multiplier.h"

@implementation NSLayoutConstraint (Multiplier)
-(instancetype)updateMultiplier:(CGFloat)multiplier {

    [NSLayoutConstraint deactivateConstraints:[NSArray arrayWithObjects:self, nil]];

   NSLayoutConstraint *newConstraint = [NSLayoutConstraint constraintWithItem:self.firstItem attribute:self.firstAttribute relatedBy:self.relation toItem:self.secondItem attribute:self.secondAttribute multiplier:multiplier constant:self.constant];
    [newConstraint setPriority:self.priority];
    newConstraint.shouldBeArchived = self.shouldBeArchived;
    newConstraint.identifier = self.identifier;
    newConstraint.active = true;

    [NSLayoutConstraint activateConstraints:[NSArray arrayWithObjects:newConstraint, nil]];
    //NSLayoutConstraint.activateConstraints([newConstraint])
    return newConstraint;
}
@end

बाद में ViewController में उस बाधा के लिए आउटलेट बनाएं जिसे आप अपडेट करना चाहते हैं।

@property (strong, nonatomic) IBOutlet NSLayoutConstraint *topConstraint;

और गुणक को अद्यतन करें जहाँ आप कभी भी नीचे चाहते हैं।

self.topConstraint = [self.topConstraint updateMultiplier:0.9099];

मैंने इस नमूना कोड को निष्क्रिय कर दिया active = trueहै जो सक्रिय होने के समान है और इसलिए निष्क्रिय करने से पहले एक डुप्लिकेट बाधा को जोड़ने का कारण बनता है, यह कुछ परिदृश्यों में एक बाधा त्रुटि का कारण बनता है।
जूल्स

13

आप थोड़ा गणित के साथ एक ही लक्ष्य को प्राप्त करने के बजाय "निरंतर" संपत्ति को बदल सकते हैं। मान लें कि आपका डिफ़ॉल्ट गुणक 1.0f है। यह Xamarin C # कोड है जिसे उद्देश्य-सी में आसानी से अनुवादित किया जा सकता है

private void SetMultiplier(nfloat multiplier)
{
    FirstItemWidthConstraint.Constant = -secondItem.Frame.Width * (1.0f - multiplier);
}

यह एक अच्छा समाधान है
J. Doe

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

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

1
मेरे मामले के लिए महान समाधान, जिसमें दूसरी वस्तु की चौड़ाई वास्तव में है [UIScreen mainScreen] ।bounds.size. उपलब्धता (जो कभी नहीं बदलती है)
Arik Segal

7

जैसा कि अन्य उत्तरों में बताया गया है: आपको बाधा को दूर करने और नया बनाने की आवश्यकता है।


आप पैरामीटर के NSLayoutConstraintसाथ स्थैतिक विधि बनाकर नए अवरोध को वापस inoutलाने से बच सकते हैं , जो आपको उत्तीर्ण बाधा को पुन: सौंपने की अनुमति देता है

import UIKit

extension NSLayoutConstraint {

    static func setMultiplier(_ multiplier: CGFloat, of constraint: inout NSLayoutConstraint) {
        NSLayoutConstraint.deactivate([constraint])

        let newConstraint = NSLayoutConstraint(item: constraint.firstItem, attribute: constraint.firstAttribute, relatedBy: constraint.relation, toItem: constraint.secondItem, attribute: constraint.secondAttribute, multiplier: multiplier, constant: constraint.constant)

        newConstraint.priority = constraint.priority
        newConstraint.shouldBeArchived = constraint.shouldBeArchived
        newConstraint.identifier = constraint.identifier

        NSLayoutConstraint.activate([newConstraint])
        constraint = newConstraint
    }

}

उदाहरण उपयोग:

@IBOutlet weak var constraint: NSLayoutConstraint!

override func viewDidLoad() {
    NSLayoutConstraint.setMultiplier(0.8, of: &constraint)
    // view.layoutIfNeeded() 
}

2

इस कोड को एक्सकोड 10 और स्विफ्ट 4.2 में काम करने के लिए मेरे खुद के कोड को जांचने की कोशिश में मेरे लिए काम किया गया है।

import UIKit
extension NSLayoutConstraint {
/**
 Change multiplier constraint

 - parameter multiplier: CGFloat
 - returns: NSLayoutConstraintfor 
*/i
func setMultiplier(multiplier:CGFloat) -> NSLayoutConstraint {

    NSLayoutConstraint.deactivate([self])

    let newConstraint = NSLayoutConstraint(
        item: firstItem,
        attribute: firstAttribute,
        relatedBy: relation,
        toItem: secondItem,
        attribute: secondAttribute,
        multiplier: multiplier,
        constant: constant)

    newConstraint.priority = priority
    newConstraint.shouldBeArchived = self.shouldBeArchived
    newConstraint.identifier = self.identifier

    NSLayoutConstraint.activate([newConstraint])
    return newConstraint
}
}



 @IBOutlet weak var myDemoConstraint:NSLayoutConstraint!

override func viewDidLoad() {
let newMultiplier:CGFloat = 0.80
myDemoConstraint = myDemoConstraint.setMultiplier(newMultiplier)

//If later in view lifecycle, you may need to call view.layoutIfNeeded() 
 }

धन्यवाद, ठीक काम करने के लिए लगता है
राडू उर्सशे

आपका स्वागत है
रेडू

2

हाँ w बहुस्तरीय मूल्यों को बदल सकता है बस NSLayoutConstraint का विस्तार करें और इसका उपयोग करें जैसे ->

   func setMultiplier(_ multiplier:CGFloat) -> NSLayoutConstraint {

        NSLayoutConstraint.deactivate([self])

        let newConstraint = NSLayoutConstraint(
            item: firstItem!,
            attribute: firstAttribute,
            relatedBy: relation,
            toItem: secondItem,
            attribute: secondAttribute,
            multiplier: multiplier,
            constant: constant)

        newConstraint.priority = priority
        newConstraint.shouldBeArchived = shouldBeArchived
        newConstraint.identifier = identifier

        NSLayoutConstraint.activate([newConstraint])
        return newConstraint
    }

            self.mainImageViewHeightMultiplier = self.mainImageViewHeightMultiplier.setMultiplier(375.0/812.0)

1

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

पूर्णता की खातिर, विवशता को बांधने के लिए, माउस दाएं बटन का उपयोग करके कोड को कसना खींचें, किसी भी अन्य ग्राफिक ग्राफिक्स की तरह:

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

मैंने एक का नाम प्रोसीपेड और दूसरे का प्रॉपर फोन रखा।

फिर, viewDidLoad पर निम्न कोड जोड़ें

override open func viewDidLoad() {
   super.viewDidLoad()
   if ... {

       view.removeConstraint(proportionIphone)
       view.addConstraint(proportionIpad) 
   }     
}

मैं xCode 10 और स्विफ्ट 5.0 का उपयोग कर रहा हूं


0

यहाँ C # में @ तियानफू के उत्तर के आधार पर एक उत्तर दिया गया है। अन्य उत्तर जिनके लिए सक्रियता और बाधाओं को निष्क्रिय करने की आवश्यकता है, मेरे लिए कारगर नहीं थे।

    var isMapZoomed = false
@IBAction func didTapMapZoom(_ sender: UIButton) {
    let offset = -1.0*graphHeightConstraint.secondItem!.frame.height*(1.0 - graphHeightConstraint.multiplier)
    graphHeightConstraint.constant = (isMapZoomed) ? offset : 0.0

    isMapZoomed = !isMapZoomed
    self.view.layoutIfNeeded()
}

0

गुणक के मान को बदलने के लिए, नीचे दिए गए चरणों का पालन करें: 1. आवश्यक बाधा के लिए आउटलेट बनाएं जैसे someConstraint। 2. गुणक मान को परिवर्तित करें जैसे someConstraint.constant * = 0.8 या कोई गुणक मान।

यह, खुश कोडिंग है।


-1

एक पढ़ सकते हैं:

var multiplier: CGFloat
The multiplier applied to the second attribute participating in the constraint.

इस प्रलेखन पृष्ठ पर । क्या इसका मतलब यह नहीं है कि एक गुणक को संशोधित करने में सक्षम होना चाहिए (क्योंकि यह एक var है)?


var multiplier: CGFloat { get }परिभाषा है जिसका अर्थ है कि इसमें केवल एक गटर है।
जॉनी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.