FluentLayout / Cirrious.FluentLayout

FluentLayout for Xamarin.iOS - sample uses MvvmCross
Microsoft Public License
148 stars 54 forks source link

Better AddConstraints method #53

Closed softlion closed 5 years ago

softlion commented 5 years ago

Could you add this method ?

It extends the existing method to accept both FuentLayout and IEnumerable<FluentLayout>

Usage:

            View.AddConstraints(
                back.WithSameRectOf(View),

                topBack.WithSameTop(okButton).Minus(5),
                topBack.AtLeftRightOf(View),
                topBack.Bottom().EqualTo().TopOf(picker),

                actionButton.Height().EqualTo(70),
                actionButton.AtLeftRightOf(View),
                actionButton.AtBottomOf(View)
                );

Example:

        public static IEnumerable<FluentLayout> WidthEqualHeight(this UIView view, UIView previous=null)
        {
            yield return view.Width().EqualTo().HeightOf(previous ?? view);
        }

Source

        /// <summary>
        /// </summary>
        /// <param name="view"></param>
        /// <param name="fluentLayouts">Either FluentLayout or IEnumerable&lt;FluentLayout&gt;</param>
        public static void AddConstraints(this UIView view, params object[] fluentLayouts)
        {
            view.AddConstraints((fluentLayouts
                .Where(fluent => fluent != null)
                .OfType<FluentLayout>()
                .SelectMany(fluent => fluent.ToLayoutConstraints())
                ).Concat(fluentLayouts
                    .OfType<IEnumerable<FluentLayout>>()
                    .SelectMany(fluent => fluent.SelectMany(fluent2 => fluent2.ToLayoutConstraints()))
                ).ToArray());
        }

Other examples:

        public static IEnumerable<FluentLayout> WithSameRectOf(this UIView view, UIView parent, nfloat? margin = null)
        {
            return
                view.FullWidthOf(parent, margin)
                    .Concat(view.FullHeightOf(parent, margin))
                    .Concat(view.AtTopLeftOf(parent, margin));
        }

        public static IEnumerable<FluentLayout> AtTopLeftOf(this UIView view, UIView parentView, nfloat? margin = null)
        {
            var marg = margin.GetValueOrDefault(DefaultMargin);
            yield return view.Top().EqualTo().TopOf(parentView).Plus(marg);
            yield return view.Left().EqualTo().LeftOf(parentView).Plus(marg);
        }

        public static IEnumerable<FluentLayout> AtTopRightOf(this UIView view, UIView parentView, nfloat? margin = null)
        {
            var marg = margin.GetValueOrDefault(DefaultMargin);
            yield return view.Top().EqualTo().TopOf(parentView).Plus(marg);
            yield return view.Right().EqualTo().RightOf(parentView).Minus(marg);
        }

        public static IEnumerable<FluentLayout> AtLeftRightOf(this UIView view, UIView parentView, nfloat? margin = null)
        {
            var marg = margin.GetValueOrDefault(DefaultMargin);
            yield return view.Left().EqualTo().LeftOf(parentView).Plus(marg);
            yield return view.Right().EqualTo().RightOf(parentView).Minus(marg);
        }

        public static IEnumerable<FluentLayout> AtTopLeftRightOf(this UIView view, UIView parentView, nfloat? margin = null)
        {
            var marg = margin.GetValueOrDefault(DefaultMargin);
            yield return view.Top().EqualTo().TopOf(parentView).Plus(marg);
            yield return view.Left().EqualTo().LeftOf(parentView).Plus(marg);
            yield return view.Right().EqualTo().RightOf(parentView).Minus(marg);
        }

        public static IEnumerable<FluentLayout> BelowAtLeftRightOf(this UIView view, UIView belowView, UIView parentView, nfloat? belowMargin = null, nfloat? margin = null)
        {
            var marg = margin.GetValueOrDefault(DefaultMargin);
            yield return view.Top().EqualTo().BottomOf(belowView).Plus(belowMargin.GetValueOrDefault(DefaultMargin));
            yield return view.Left().EqualTo().LeftOf(parentView).Plus(marg);
            yield return view.Right().EqualTo().RightOf(parentView).Minus(marg);
        }

        public static IEnumerable<FluentLayout> EqualRectOf(this UIView view, UIView parentView, nfloat? margin = null)
        {
            var marg = margin.GetValueOrDefault(DefaultMargin);
            yield return view.Top().EqualTo().TopOf(parentView).Plus(marg);
            yield return view.Left().EqualTo().LeftOf(parentView).Plus(marg);
            yield return view.Right().EqualTo().RightOf(parentView).Minus(marg);
            yield return view.Bottom().EqualTo().BottomOf(parentView).Minus(marg);
        }

        public static IEnumerable<FluentLayout> WithSameCenter(this UIView view, UIView previous)
        {
            yield return view.WithSameCenterX(previous);
            yield return view.WithSameCenterY(previous);
        }
gshackles commented 5 years ago

Interesting suggestion! I don't love the need for object in the API versus finding a way for it to be a bit more well defined (though I understand the signature collision between IEnumerable<FluentLayout> and FluentLayout[] there, certainly). I'm wondering if it's worth just using a different set of method names to support this if we want to roll forward with it. Thoughts?

Also .ToLayoutConstraints() is obsolete - is it actually needed here?

softlion commented 5 years ago

It is because you want to obsolete ToLayoutConstraints for obscure reasons that I have to open source my private code.

gshackles commented 5 years ago

Not sure I'd call it obscure, but either way I'm asking/curious what it's actually needed for here? 😄

softlion commented 5 years ago

ToLayoutConstraints is to extract the built constraints for any fluentLayout object of type FluentLayout, as view.AddConstraints only supports iOS contraints objects.

gshackles commented 5 years ago

Closing this out for now since we wouldn't be able to move ahead with this as-is and it sounds like you've got a custom fork anyway, but more than happy to continue to explore this if it still seems like you'd want to use/contribute - thanks! 😄

softlion commented 5 years ago

Sample usage of "object" parameter (can't use any other one):

        public static UIView AddConstraints(this UIView view, params object[] fluentLayouts)
        {
            view.AddConstraints((fluentLayouts
                .Where(fluent => fluent != null)
                .OfType<FluentLayout>()
#pragma warning disable 618
                .SelectMany(fluent => fluent.ToLayoutConstraints())
#pragma warning restore 618
                ).Concat(fluentLayouts
                    .OfType<IEnumerable<FluentLayout>>()
#pragma warning disable 618
                    .SelectMany(fluent => fluent.SelectMany(fluent2 => fluent2.ToLayoutConstraints()))
#pragma warning restore 618
                ).ToArray());

            return view;
        }

        public static UIView AddConstraints(this UIView view, params Func<UIView, object>[] fluentLayoutActions)
        {
            var fluentLayouts = fluentLayoutActions.Select(fl => fl(view)).ToList();

            view.AddConstraints((fluentLayouts
                        .Where(fluent => fluent != null)
                        .OfType<FluentLayout>()
#pragma warning disable 618
                        .SelectMany(fluent => fluent.ToLayoutConstraints())
#pragma warning restore 618
                ).Concat(fluentLayouts
                        .OfType<IEnumerable<FluentLayout>>()
#pragma warning disable 618
                        .SelectMany(fluent => fluent.SelectMany(fluent2 => fluent2.ToLayoutConstraints()))
#pragma warning restore 618
                ).ToArray());

            return view;
        }

        public static IEnumerable<FluentLayout> WithSameCenter(this UIView view, UIView previous)
        {
            yield return view.WithSameCenterX(previous);
            yield return view.WithSameCenterY(previous);
        }
softlion commented 5 years ago

I don't have a custom fork