A cleaner code approach to NSLayout

On our previous screen, we built a very simple user interface using NSLayoutContraints.

Would you agree that the code looked quite clunky?

With our AudioPlayerPage, we are going to use a cleaner approach to coding the NSLayoutConstraints. Firstly, create a new folder called Extras, and add a new file called DictionaryViews.cs:

A cleaner code approach to NSLayout

This class is going to inherit the IEnumerable interface in order to create an NSDictionary; part of this interface is we must specify the GetEnumerator function. It will pull this from the NSDictionary; we also have our Add function, which simply adds a new UIView to the dictionary. Then we have the static implicit operator which will return the object as an NSDictionary (this is used so we can directly pass the object as an NSDictionary to the FromVisualLayout function):

public class DictionaryViews : IEnumerable 
    { 
        private readonly NSMutableDictionary _nsDictionary; 
 
        public DictionaryViews() 
        { 
            _nsDictionary = new NSMutableDictionary(); 
        } 
 
        public void Add(string name, UIView view) 
        { 
            _nsDictionary.Add(new NSString(name), view); 
        } 
 
        public static implicit operator NSDictionary(DictionaryViews us) 
        { 
            return us.ToNSDictionary(); 
        } 
 
        public NSDictionary ToNSDictionary() 
        { 
            return _nsDictionary; 
        } 
 
        public IEnumerator GetEnumerator() 
        { 
            return ((IEnumerable)_nsDictionary).GetEnumerator(); 
        } 
    } 

Now let's go ahead and create one of these inside our AudioPlayerPage; paste the following under the declaration of the fast forward button:

            var views = new DictionaryViews() 
            { 
                {"mainView", mainView}, 
                {"buttonView", buttonView}, 
                {"imageView", imageView}, 
                {"descriptionLabel", descriptionLabel}, 
                {"startLabel", startLabel}, 
                {"endLabel", endLabel}, 
                {"progressSlider", progressSlider}, 
                {"playButton", playButton}, 
                {"rewindButton", rewindButton}, 
                {"fastForwardButton", fastForwardButton} 
            }; 

Great! We now have a new IEnumerable/NSDictionary with all the required views to be used through the entire interface. We can directly pass this object into the NSLayoutConstraint function FromVisualFormat so we don't need to repeat the declaration of new dictionaries when we create each NSLayoutContraint. Now add all the UI elements to the correct parent views:

View.Add(mainView); 
 
            mainView.Add(imageView); 
            mainView.Add(descriptionLabel); 
            mainView.Add(buttonView); 
            mainView.Add(startLabel); 
            mainView.Add(endLabel); 
            mainView.Add(progressSlider); 
 
            buttonView.Add(playButton); 
            buttonView.Add(rewindButton); 
            buttonView.Add(fastForwardButton); 

Then let's build all the NSLayoutConstraints; our first is the UIViewController'sUIView:

View.AddConstraints( 
                NSLayoutConstraint.FromVisualFormat("V:|[mainView]|", NSLayoutFormatOptions.DirectionLeftToRight, null, views) 
                .Concat(NSLayoutConstraint.FromVisualFormat("H:|[mainView]|", NSLayoutFormatOptions.AlignAllTop, null, views)) 
                .ToArray()); 

We have our new approach, using the System.Linq function Concat to combine all the NSLayoutContraints required for the view. We only have to call the AddConstraints function once, and pass in one array of all the required constraints for that parent view.

Let's add our constraint for mainView and buttonView:

mainView.AddConstraints( 
                NSLayoutConstraint.FromVisualFormat("V:|-100-[imageView(200)]-[descriptionLabel(30)]-[buttonView(50)]-[startLabel(30)]-[progressSlider]", NSLayoutFormatOptions.DirectionLeftToRight, null, views) 
                .Concat(NSLayoutConstraint.FromVisualFormat("V:|-100-[imageView(200)]-[descriptionLabel(30)]-[buttonView(50)]-[endLabel(30)]-[progressSlider]", NSLayoutFormatOptions.DirectionLeftToRight, null, views)) 
                .Concat(NSLayoutConstraint.FromVisualFormat("H:|-20-[progressSlider]-20-|", NSLayoutFormatOptions.AlignAllTop, null, views)) 
                .Concat(NSLayoutConstraint.FromVisualFormat("H:|-25-[startLabel(70)]", NSLayoutFormatOptions.AlignAllTop, null, views)) 
                .Concat(NSLayoutConstraint.FromVisualFormat("H:[endLabel(70)]-25-|", NSLayoutFormatOptions.AlignAllTop, null, views)) 
                .Concat(NSLayoutConstraint.FromVisualFormat("H:|-5-[descriptionLabel]-5-|", NSLayoutFormatOptions.AlignAllTop, null, views)) 
                .Concat(NSLayoutConstraint.FromVisualFormat("H:|-5-[imageView]-5-|", NSLayoutFormatOptions.AlignAllTop, null, views)) 
                .Concat(new[] { NSLayoutConstraint.Create(buttonView, NSLayoutAttribute.CenterX, NSLayoutRelation.Equal, mainView, NSLayoutAttribute.CenterX, 1, 0) }) 
                .ToArray()); 
 
            buttonView.AddConstraints( 
                NSLayoutConstraint.FromVisualFormat("V:|-5-[rewindButton]-5-|", NSLayoutFormatOptions.AlignAllTop, null, views) 
                .Concat(NSLayoutConstraint.FromVisualFormat("V:|-5-[playButton]-5-|", NSLayoutFormatOptions.AlignAllTop, null, views)) 
                .Concat(NSLayoutConstraint.FromVisualFormat("V:|-5-[fastForwardButton]-5-|", NSLayoutFormatOptions.AlignAllTop, null, views)) 
                .Concat(NSLayoutConstraint.FromVisualFormat("H:|-20-[rewindButton]-[playButton(100)]-[fastForwardButton]-20-|", NSLayoutFormatOptions.AlignAllTop, null, views)) 
                .ToArray()); 

This is exactly the same approach, but it looks much nicer and it reduces the number of times we call AddConstraints. The view only needs to add all the constraints once, and lay out the elements once, so it is much more efficient.

Our final step in building the user interface is to set up the MVVMCross bindings; we use the same approach as the MainPage. Let's create a new binding set between the AudioPlayerPage and the AudioPlayerPageViewModel:

            var set = CreateBindingSet<AudioPlayerPage, AudioPlayerPageViewModel>(); 
            set.Apply(); 

Before we get into creating our bindings, let's first build our AudioPlayerPageViewModel for the AudioPlayer.Portable project.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.14.142.115