How to modify a constraint using mvvmcross fluentL

2019-05-31 09:10发布

问题:

I've figuratively pulled my hair out on this one. I read from this article "http://gregshackles.com/fluentlayout-2-5/" that FluentLayout now supports constraint editing/removing but it doesn't seem to work on my end. My scenario is to toggle the visibility of a textfield within a UIView when a button is clicked.

I have tried the following.

A. Modifying the height constraint

 var height = isVisible ? textfield.Height().EqualTo(0) : textfield.WithSameHeight(textfieldContainer).Multiplier(1 / 3);
            textfieldContainer.Add(textfield);
            textfieldContainer.SubviewsDoNotTranslateAutoresizingMaskIntoConstraints();
            textfieldContainer.AddConstraints(
                    textfield.WithSameLeft(textfieldContainer).Plus(12),
                    textfield.WithSameTop(textfieldContainer).Plus(24),
                    textfield.WithSameWidth(textfieldContainer),
                    height
                );

B. Using SetActive(false) - Tried this out of desperation

 textfieldContainer.Add(textfield);
            textfieldContainer.SubviewsDoNotTranslateAutoresizingMaskIntoConstraints();
            textfieldContainer.AddConstraints(
                    textfield.WithSameLeft(textfieldContainer).Plus(12).SetActive(!isVisible),
                    textfield.WithSameTop(textfieldContainer).Plus(24).SetActive(!isVisible),
                    textfield.WithSameWidth(textfieldContainer).SetActive(!isVisible),
                    textfield.WithSameHeight(textfieldContainer).WithMultiplier(1 / 4).SetActive(!isVisible)
                );

Expected outcome

The textfield should be visible depending on the visibility

Actual outcome

The textfield's height never changes, thus it is always visible

回答1:

My guess is that your height variable is set once during the page lifecycle, and does not get change after that point. A way of achieving what you need is the following:

Firstly, bind your button click to a command that changes the state of a boolean on your ViewModel, so that the boolean's value changes when the button is clicked:

bindingSet.Bind(yourButton).To(vm => vm.YourCommand);

MvxCommand _yourCommand;
public MvxCommand YourCommand
    => _yourCommand ?? _yourCommand = new MvxCommand(ReverseMyBool);

void ReverseMyBool()
{
    YourBoolean = !YourBoolean;
}

If necessary, set YourBoolean to true during the construction of your ViewModel, depending on whether your want the field to be hidden during page load. Now that the ViewModel property holds an accurate true/false state for whether your UITextField should be hidden, bind the UITextField itself for Hidden to the boolean (you may need to use a value converter to reverse the value - if Hidden is true, the view is invisible):

bindingSet.Bind(textfield).For(c => c.Hidden).To(vm => vm.YourBoolean);

Next, create FluentLayout variables that relate to both situations (your view being visible and being hidden), and apply both of them:

var textFieldWithOneThirdContainerHeight = textfield.WithSameHeight(textFieldContainer).WithMultiplier(1f /3f);
var textFieldWithZeroHeight = textField.Height().EqualTo(0f);

textfieldContainer.AddConstraints(textFieldWithOneThirdContainerHeight, textFieldWithZeroHeight, /*other constraints here*/);

And finally, bind the constraints for Active to the boolean on your ViewModel - note that one will need to be reversed with a converter:

bindingSet.Bind(textFieldWithOneThirdContainerHeight).For(c => c.Active).To(vm => vm.YourBoolean).WithConversion(new ReverseBooleanValueConverter());
bindingSet.Bind(textFieldWithZeroHeight).For(c => c.Active).To(vm => vm.YourBoolean);

ReverseBooleanValueConverter would look something like this:

public class ReverseBooleanValueConverter: MvxValueConverter<bool, bool>
{
    protected override bool Convert(bool value, Type targetType, object parameter, CultureInfo culture) 
        => !value;
}

When YourBoolean is true, your UITextField should be invisible, and should have a height of 0f. When YourBoolean is false, it should be visible, and should have one third the height of it's container.