Bagaimana semuanya dimulai
Sangat sering, ketika bekerja dengan aplikasi Perusahaan, kami harus berurusan dengan layar seperti itu jika tidak ada data yang diterima dari backend, ketika kami hanya perlu menampilkan daftar di layar.

, , , , , .
, , . , .

-, . -, UI- - .
public class EmptyStateViewModel : ViewModel
{
public EmptyStateViewModel(string image, string title, string description)
{
Image = image;
Title = title;
Description = description;
}
public string Image { get; }
public string Title { get; }
public string Description { get; }
}
( xaml Xamarin Forms) Bindings , , mvvm- .
?
- , - . - EmptyStateView, Retry, . EmptyStateViewModel, , .

public class ErrorStateViewModel : EmptyStateViewModel
{
public ErrorStateViewModel(string image, string title, string description, string actionTitle, Command actionCommand)
: base(image, title, description)
{
ActionTitle = actionTitle;
ActionCommand = actionCommand;
}
public string ActionTitle { get; }
public Command ActionCommand { get; }
}
?
- . . , -. None, null.
public static class OverlayFactory
{
public static T None<T>()
where T : EmptyStateViewModel
{
return null;
}
public static EmptyStateViewModel CreateCustom(string image, string title, string description)
{
return new EmptyStateViewModel(image, title, description);
}
public static ErrorStateViewModel CreateCustom(string image, string title, string description, string actionTitle, Command actionCommand)
{
return new ErrorStateViewModel(image, title, description, actionTitle, actionCommand);
}
}
- -, , ,
public class SomeViewModel : BaseViewModel
{
private IItemsLoadingService _itemsLoadingService;
public SomeViewModel(IItemsLoadingService itemsLoadingService)
{
_itemsLoadingService = itemsLoadingService;
}
public ObservableCollection<ItemViewModel> Items { get; } = new ObservableCollection<ItemViewModel>();
public EmptyStateViewModel EmptyState { get; protected set; }
public ErrorStateViewModel ErrorState { get; protected set; }
public override async Task InitializeAsync()
{
await base.InitializeAsync();
await LoadItemsAsync();
}
private async Task LoadItemsAsync()
{
try
{
var result = await _itemsLoadingService.GetItemsAsync();
var items = result.ToList();
ErrorState = OverlayFactory.None<ErrorStateViewModel>();
if (items.Count == 0)
{
EmptyState = OverlayFactory.CreateCustom("img_empty_state", "Title", "Description");
}
else
{
EmptyState = OverlayFactory.None<ErrorStateViewModel>();
// Add items to list
}
}
catch
{
ErrorState = OverlayFactory.CreateCustom("img_error_state", "Title", "Description", "Retry", new Command(() => LoadItemsAsync));
}
}
}
Binding EmptyState/ErrorState , mvvm-, , EmptyStateViewModel/ErrorStateViewModel null, . SetViewModel.
, View ViewModel View ViewState . ViewModel null - ViewState Gone, - Visible:
public void SetViewModel(EmptyStateViewModel viewModel)
{
ViewModel = viewModel;
View.Visibility = viewModel != null ? ViewStates.Visible : ViewStates.Gone;
}
iOS - constraints , - . enum, Android.
public void SetViewModel(EmptyStateViewModel viewModel)
{
ViewModel = viewModel;
View.SetVisibility(viewModel != null ? ViewStates.Visible : ViewStates.Gone);
}
extension
public static void SetVisibility(this UIView view, ViewVisibility visibility)
{
var constraints = GetViewConstraints(view) ?? new NSLayoutConstraint[] {};
if (visibility == ViewVisibility.Gone)
{
SaveViewConstraints(view, constraints);
NSLayoutConstraint.DeactivateConstraints(constraints);
view.Hidden = true;
return;
}
if (visibility == ViewVisibility.Visible)
{
SaveViewConstraints(view, null);
NSLayoutConstraint.ActivateConstraints(constraints);
view.Hidden = false;
return;
}
}
Di sini, dalam kasus pengaturan ViewVisibility.Gone, kami menyimpan terlebih dahulu batasan tampilan kami dan menonaktifkannya, dan ketika visibilitas dihidupkan, sebaliknya, kami mendapatkan batasan yang disimpan sebelumnya, mengatur ulang pelestarian, dan kemudian mengaktifkannya.
private static NSLayoutConstraint[] GetViewConstraints(UIView view)
{
return view.GetAssociatedObject<NSMutableArray<NSLayoutConstraint>>(Key)?.ToArray() ??
view.Superview?.Constraints
.Where(constraint => (constraint.FirstItem?.Equals(view) == true) || constraint.SecondItem.Equals(view))
.ToArray();
}
private static void SaveViewConstraints(UIView view, NSLayoutConstraint[] constraints)
{
NSMutableArray<NSLayoutConstraint> viewConstraints = null;
if (constraints.Length > 0)
{
viewConstraints = new NSMutableArray<NSLayoutConstraint>();
viewConstraints.AddObjects(constraints);
}
view.SetAssociatedObject(Key, viewConstraints, AssociationPolicy.RetainNonAtomic);
}
Metode pertama memungkinkan Anda untuk mendapatkan batasan yang disimpan sebelumnya, jika ada, atau, jika tidak, dapatkan batasan saat ini. Jika tidak ada tampilan induk, maka null akan dikembalikan.
Metode kedua akan menyimpan kendala saat ini sehingga dapat dipulihkan nanti.
Jadi, ternyata membuat layar lebih menyenangkan dengan data yang hilang, atau layar status kesalahan.
PS - artikel pertama tentang Habré, oleh karena itu jangan menilai dengan ketat. Tapi Anda harus mulai dari suatu tempat.