Erhan Ballıeker

Xamarin.Forms ConfinstaApp Sample Bölüm 4 – SlideOverKit, Swipable Menu

Selamlar,

Bu serimizin son yazısında yine çok faydalı bir plugin den bahsetmek istiyorum. Hem bu örnek boyunca ilerlemiş olduğumuz Confinsta app i sona erdirecek hem de uygulamalarımızda bizden ekranın farklı yerlerinde farklı şekillerde açılıp kapanan menüler istendiğinde bunu kısa ve hızlı bir şekilde nasıl hallederiz buna bakacağız.

Kullanacak olduğumuz pluginimiz SlideOverKit

Yine solutionımızdaki her projeye bunu ekledikten sonrai plaform spesifik taraflarda gerekli initialize işlemini yapıyoruz.

Detaylı kullanımı için SlideOverKit in kendi github sayfasını ve örneklerini incelemenizi öneririm. Buradan ulaşabilirsiniz.

Bu plugin confinsta app boyunca Customrenderer yazmış olmamı gerekitiren tek şey. Ama aslında yazacağınız custom renderer ın örneklerden alıp copy paste yapmaktan başka neredeyse değiştirmeniz gereken peki bir kısmı kalmıyor(class isimleri hariç) 🙂

Ama arka planda neler yaptığını bilmek isterseniz benim gibi github dan inceleyebilirsiniz.

Önce bir ekranı hatırlayalım, sonra da tek tek kod taraflarına bakalım.

Resimde gördüğünüz gibi, sağ üst köşede ki hamburger buton ikonuna basıldığında ekranın yarısı kadar ene sahip ve tam boyda bir menü sayfası açıyoruz. Tıpkı instagramda olduğu gibi.

Önce sayfanın kendisine bir bakalım.

Sayfada yukarıdaki gibi bir navigation bar kullanmak eskiden custom renderer yazmak gerektirirdi. Ama yeni gelen özellikler ile sayfanın NavigationView ını istediğimiz gibi design edebiliyoruz artık xaml tarafında. Ben sağ üst köşeye bir imaj koydum ve buna bir tapgesture ekledim. Basıldığında da menü yü toggle ediyorum. Bu kadar. Xaml tarafı şu şekilde.

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="ConfinstaApp.Views.ProfileView">

    <NavigationPage.TitleView>
        <StackLayout HorizontalOptions="FillAndExpand" Orientation="Horizontal" Padding="10,0,10,0" Spacing="10">
            <Image Source="17.png" HorizontalOptions="StartAndExpand" WidthRequest="25" HeightRequest="25"></Image>
            <Label Text="erhanballieker" FontSize="Medium" FontAttributes="Bold" HorizontalOptions="CenterAndExpand" VerticalOptions="Center" Margin="0,0,25,0"/>
            <Image Source="11.png" HorizontalOptions="End" WidthRequest="25" HeightRequest="25">
                <Image.GestureRecognizers>
                    <TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped"></TapGestureRecognizer>
                </Image.GestureRecognizers>
            </Image>
        </StackLayout>
    </NavigationPage.TitleView>
    
    <ContentPage.Content>
        <StackLayout HorizontalOptions="Center" VerticalOptions="Center">
            <Label FontSize="Medium" FontAttributes="Bold" Text="erhanballieker Profile Page" HorizontalOptions="Center"></Label>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

Yukarıda gördüğünüz gibi NavigationPage.TitleView içerisinde navigation design ım var. Sayfanın content ine hiçbir şey koymadım (Bir adet label ) dışında çünkü burada odaklandığımız yer sayfa içerisine eklediğimiz açılıp kapanan menü.

Sayfanın backend tarafı ise aşağıdaki gibi. Dikkat etmemiz gereken tek şey, Sayfanın ContentPage dışında bir de IMenuContainerPage interface ini implemente etmiş olması.

Bu interface den bize 3 adet property geliyor.

Bunlar;

  • SlideMenu Tipinde bir SlideMenu Prop u
  • Action tipinde bir ShowMenuAction prop u
  • Action tipinde bir HideMenuAction prop u

bunlar dışında sayfada sadece hamburger butonun gesturetapped event i var.

Bir diğer dikkat edeceğimiz nokta ise sayfanın constructor nda bana interface den gelen SlideMenu propertysine atadığım RightSideMenuContentView. 

	[XamlCompilation(XamlCompilationOptions.Compile)]
	public partial class ProfileView : ContentPage, IMenuContainerPage
        {
           public ProfileView()
	   {
	       InitializeComponent ();

               this.SlideMenu = new RightSideMenuContentView();
            }

        public SlideMenuView SlideMenu { get; set; }
        public Action ShowMenuAction { get; set; }
        public Action HideMenuAction { get; set; }

        private async void TapGestureRecognizer_Tapped(object sender, EventArgs e)
        {
            if (this.SlideMenu.IsShown)
            {
                HideMenuAction?.Invoke();
            }
            else
            {
                ShowMenuAction?.Invoke();
            }
        }
    }

RightSideMenuContentView ın xaml tarafına bir bakalım. Projemize bir adet ContentView ekleyip xaml tarafını aşağıdaki gibi düzenliyoruz.Basit bir StackLayout içerisinde bir kaç label var. Ama dikkat etmemiz gereken şey tüm sayfanın namespacinin SlideOverKit den geliyor olması.

<?xml version="1.0" encoding="utf-8" ?>
<t:SlideMenuView xmlns="http://xamarin.com/schemas/2014/forms" 
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
                 xmlns:t="clr-namespace:SlideOverKit" 
                 x:Class="ConfinstaApp.Views.SlideViews.RightSideMenuContentView">

    <StackLayout Padding="15,50,15,30" Spacing="20" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">

        <Label HeightRequest="30" Margin="0,0,0,80" Text="Menu" FontSize="20" XAlign="Center" TextDecorations="Underline" />

        <Label HeightRequest="30" Text="Insights" FontSize="20" XAlign="Start" YAlign="Center" TextDecorations="Underline" />
        <Label HeightRequest="30" Text="Your Activity" FontSize="20" XAlign="Start" YAlign="Center" TextDecorations="Underline"/>
        <Label HeightRequest="30" Text="Nametag" FontSize="20" XAlign="Start" YAlign="Center" TextDecorations="Underline"/>
        <Label HeightRequest="30" Text="Saved" FontSize="20" XAlign="Start" YAlign="Center" TextDecorations="Underline"/>
        <Label HeightRequest="30" Text="Discover People" FontSize="20" XAlign="Start" YAlign="Center" TextDecorations="Underline"/>
        <Image HeightRequest="40" Source="nature.png"></Image>
        <Label HeightRequest="30" Text="Settings" FontSize="20" XAlign="Start" YAlign="Center" VerticalOptions="EndAndExpand" TextDecorations="Underline"/>

    </StackLayout>
</t:SlideMenuView>

Slide menünün backend tarafına bakalım. ContentView dan değil de SlideMenuView dan miras alıyor olmamız ilk dikkat çeken nokta. Bu miras ın bize sunduğu bir kaç property ile de nasıl görüneceğini ayarlıyoruz.

IsFullScreen true diyerek tam sayfa açılır olmasını söylüyoruz.

WidthRequest ile istediğimiz genişliği söylüyoruz.

MenuOrienations ı RightToLeft diyerek sağdan sola açılmasını belirtiyoruz.

[XamlCompilation(XamlCompilationOptions.Compile)]
	public partial class RightSideMenuContentView : SlideMenuView
    {
		public RightSideMenuContentView()
		{
			InitializeComponent ();
                        this.IsFullScreen = true;
                        this.WidthRequest = 250;
                        this.MenuOrientations = MenuOrientation.RightToLeft;

                        this.BackgroundColor = Color.White;
                        this.BackgroundViewColor = Color.Transparent;
                }
    }

Daha öncede de söylediğim gibi tüm projenin içerisinde sadece bir adet custom renderer var. o da bu plugin i kullanılabilir kulmak için. iOS Custom Renderer Kısmı aşağıdaki gibi.

[assembly: ExportRenderer(typeof(ProfileView), typeof(ProfileViewRenderer))]

namespace ConfinstaApp.iOS
{
    public class ProfileViewRenderer : PageRenderer, ISlideOverKitPageRendereriOS
    {
        public Action ViewDidAppearEvent { get; set; }

        public Action OnElementChangedEvent { get; set; }

        public Action ViewDidLayoutSubviewsEvent { get; set; }

        public Action ViewDidDisappearEvent { get; set; }

        public Action<CGSize, IUIViewControllerTransitionCoordinator> ViewWillTransitionToSizeEvent { get; set; }

        public ProfileViewRenderer()
        {
            new SlideOverKitiOSHandler().Init(this);
        }

        protected override void OnElementChanged(VisualElementChangedEventArgs e)
        {
            base.OnElementChanged(e);

            OnElementChangedEvent?.Invoke(e);
        }

        public override void ViewDidLayoutSubviews()
        {
            base.ViewDidLayoutSubviews();
            ViewDidLayoutSubviewsEvent?.Invoke();

        }

        public override void ViewDidAppear(bool animated)
        {
            base.ViewDidAppear(animated);
            ViewDidAppearEvent?.Invoke(animated);

        }

        public override void ViewDidDisappear(bool animated)
        {
            base.ViewDidDisappear(animated);
            ViewDidDisappearEvent?.Invoke(animated);
        }

        public override void ViewWillTransitionToSize(CGSize toSize, IUIViewControllerTransitionCoordinator coordinator)
        {
            base.ViewWillTransitionToSize(toSize, coordinator);
            ViewWillTransitionToSizeEvent?.Invoke(toSize, coordinator);
        }
    }
}

Custom renderer kullanmak zorunda olmanın yanında bir iyi haber ise şu. SlideOverKit sample larından aldığınız bir ios yada android rendererlarda sadece class isimlerini düzeltip başka bir customization yapmasanız bile proje çalışır hale gelecektir. Yani aslında tam da custom renderer yazıyor sayılmazsınız.

Android Custom Renderer kısmı

[assembly: ExportRenderer(typeof(ProfileView), typeof(ProfileViewRenderer))]
namespace ConfinstaApp.Droid
{

    public class ProfileViewRenderer : PageRenderer, ISlideOverKitPageRendererDroid
    {
        public Action OnElementChangedEvent { get; set; }

        public Action<bool, int, int, int, int> OnLayoutEvent { get; set; }

        public Action<int, int, int, int> OnSizeChangedEvent { get; set; }

        public ProfileViewRenderer (Context context) : base (context)
        {
            new SlideOverKitDroidHandler ().Init (this, context);
        }

        protected override void OnElementChanged (ElementChangedEventArgs e)
        {
            base.OnElementChanged (e);
            OnElementChangedEvent?.Invoke (e);
        }

        protected override void OnLayout (bool changed, int l, int t, int r, int b)
        {
            base.OnLayout (changed, l, t, r, b);
            OnLayoutEvent?.Invoke (changed, l, t, r, b);
        }

        protected override void OnSizeChanged (int w, int h, int oldw, int oldh)
        {
            base.OnSizeChanged (w, h, oldw, oldh);
            OnSizeChangedEvent?.Invoke (w, h, oldw, oldh);
        }
    }
}

Evet tüm hikaye bu kadar. .NetConf 2018 de sunumunu yapmış olduğum ve yaklaşık 1 iş günümü almış olan (sadece ui olduğu için :)) bir app in, xamarin forms ile ve xamarin forms a yeni gelen özellikler ile de nasıl bir şey ortaya çıkarabildiğini gördük.

Ekranları bir hatırlayalım.

Xamarin.Forms özelinde faydalı örneklerle dolu olduğunu düşündüğüm bu Confinsta app in proje haline buradan ulaşabilirsiniz. Aynı şekilde aynı gün yapmış olduğum diğer sunum dosyaları ve projeler burada mevcut.

Bir sonraki yazımda görüşmek üzere.