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
:
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.
3.14.142.115