sobota, 28 lutego 2015

Kulisy Xamarina - odc. 8: Xamarin.Forms cz.4

Jeszcze jedna dawka możliwości Xamarin.Forms. Dysponujemy siłą frameworków XAML pozwalającą budować swoje elementy list. Pewnym uzupełnieniem w tematyce layoutów jest przybliżenie możliwości AbsoluteLayout i RelativeLayout. Uzupełnieniem odnośnie stron są MasterDetailPage, TabbedPage i CarouselPage.  Z pewnością warto wiedzieć jak zrobić fotkę w iOS i Android z Xamarin / Xamarin.Forms. Dostajemy API z async!  Filozofia Androida w tej materii przekłada się na zauważalnie dłuższy kod niż w iOS, ale nie jest to zaskakujące.  Pamiętajmy o MessageCenter jako o wygodnym sposobie na komunikację publish/subscribe.

 

var cell = new DataTemplate(typeof(TextCell));

// cell.SetBinding(TextCell.TextProperty, new Binding(“.”));

cell.SetBinding(TextCell.TextProperty, new Binding(“Title”));

cell.SetBinding(TextCell.DetailProperty, new Binding(“Author”));

cell.SetValue(TextCell.TextColorProperty, Color.Red);

cell.SetValue(TextCell.DetailColorProperty, Color.Yellow);

listView.ItemTemplate = cell;

 

class MyCell: ViewCell

{

          public MyCell()

          {

                   //label1

                   label1.SetBinding(Label.TextProperty, new Binding(“Title”));

                   …

                   View = new StackLayout

                   {

                            Children = { label1, … }

                   };                  

          }

}

listView.ItemTemplate = new DataTemplate(typeof(MyCell));

 

AbsoluteLayout

<AbsoluteLayout>

          <BoxView Color=”Blue”

                    AbsoluteLayout.LayoutBounds=”0, 0, 0.5, 0.5” //pół ekranu od lewego górnego rogu

                    AbsoluteLayout.LayoutFlags=”All”

</AbsoluteLayout>

 

RelativeLayout

var layout = new RelativeLayout();

layout.Children.Add(label,

       Constraint.Constant(0),

       Constraint.RelativeToParent(parent => {

                return parent.Height / 2;   //od lewej, w połowie wysokości strony

       }));

  …

  layout.Children.Add(label2,

        Constraint.RelativeToView(label1, (parent, otherView) => {

                 return otherView.X + otherView.Width;                   

        }),

        Constraint.RelativeToView(label1, (parent, otherView) => {

                 return otherView.Y + otherView.Height;                   

        }));

   …

   layout.Children.Add(label3,

         Constraint.RelativeToView(label2, (parent, otherView) => {

                 return (otherView.X + otherView.Width) - label3.Width; //nie zadziała bez wymuszenia

         }),

         Constraint.RelativeToView(label1, (parent, otherView) => {

                  return otherView.Y;

         }));

    label3.SizeChanged += (o, e) => {   layout.ForceLayout();  };  //wymuszenie, wymiar jest liczony po pozycjonowaniu

 

Strony

class AlbumMasterDetail: MasterDetailPage   //odsuwana zawartość przykrywa listę

{

          public AlbumMasterDetail()

          {

                    …

                    listView.ItemSelected += (sender, e) =>

                            {

                                     if (e.SelectedItem != null)

                                     {

                                              //Detail = new AlbumPage(e.SelectedItem as Album);

                                              Detail.BindingContext = e.SelectedItem;

                                              IsPresented = false;

                                     }

                            };

                    Master = new ContentPage

                    {

                             Title = “Albumy”,

                             Content = listView

                    };

                    //Detail = new AlbumPage(/*pierwsza pozycja*/);

                    Detail = new AlbumPage();  //wewnątrz strony zdefiniowane bindingi

                    Detail.BindingContext = /* pierwszy element */;

          }

}

strona z zakładkami:

var page = new TabbedPage();

page.Title = “Albumy”;

foreach (var album in albums)

{

         var albumPage = new AlbumPage();

         albumPage.BindingContext = album;

         page.Children.Add(albumPage);

}

karuzela:

var page = new CarouselPage();   //przewijanie z jednej strony do kolejnej

page.Title = “Albumy”;

foreach (var album in albums)

{

         var albumPage = new AlbumPage();

         albumPage.BindingContext = album;

         page.Children.Add(albumPage);

}

 

Device

  • BeginInvokeOnMainThread(Action)
  • StartTimer(TimeSpan, Func<bool>)
  • OpenUri(Uri)

Xamarin.Mobile

iOS:

var picker = new MediaPicker();

var mediaFile = await picker.PickPhotoAsync();

Android:

var activity = Forms.Context as Activity;   //pozyskiwanie kontekstu Android w Xamarin.Forms

var picker = new MediaPicker(activity /*Android Context, zwykle this w aktywności */);

var intent = picker.GetTakePhotoUI(new StoreCameraMediaOptions

{

        Name = “test.jpg”,

        Directory = “Xxx”

});

activity.StartActivityForResult(intent);

activity.StartActivityForResult(intent, 1);

protected override async void OnActivityResult(int requestCode, Result resultCode, Android.Content.Intent data)

{

        if (resultCode == Result.Canceled)

               return;

        var mediaFile = await data.GetMediaFileExtraAsync(Forms.Context);

}

 

MessageCenter

  • luźna komunikacja oparta na wymianie wiadomości
  • identyfikacja wiadomości
    • grupowanie po typie
    • string ID

MessagingCenter.Send<IPictureTaker, string>(this, “pictureTaken”, mediaFile.Path);

MessagingCenter.Subscribe<IPictureTaker, string>(this, “pictureTaken”,

       (sender, arg) =>

       {

       });

 

Propercje bindable tylko do odczytu

static readonly BindablePropertyKey WordCountKey =        

BindableProperty.CreateReadOnly("WordCount",   // propertyName

typeof(int),    // returnType

typeof(CountedLabel),   // declaringType

0);    // defaultValue

 

public static readonly BindableProperty WordCountProperty = WordCountKey.BindableProperty;

 

public int WordCount

{        

         private set { SetValue(WordCountKey, value); }

         get { return (int)GetValue(WordCountProperty); }

}

środa, 25 lutego 2015

Kulisy Xamarina - odc. 7: Xamarin.Forms cz.3

Jeszcze trochę o Xamarin.Forms, a nawet i samym Xamarin. Kolejną wspaniałą rzeczą w C#, z której możemy tutaj skorzystać jest async (póki co w innych językach nie występuje w tak wygodnej postaci albo dopiero jest w trakcie wymyślania jak w TypeScript/JavaScript). Data binding i Grid są bardzo wyraźnie wzorowane na frameworkach XAML Microsoft. Widzimy nowoczesne podejście do nawigacji i okien dialogowych, gdzie mamy metody async. Widać też trochę ideowych naleciałości z Android (np. AbsoluteLayout) czy iOS (np. nawigacja), jak również realizację własnych pomysłów na zwiększenie wygody pisania (np. możliwość stosowania lambda expressions w definicjach bindingów i bindable properties).

 

Wprowadzanie tekstu

InputView - klasa bazowa

  • Keyboard: Keyboard (Chat, Default, Email, Numeric, Telephone, Text, Url)

Entry: InputView - pojedyncza linia tekstu

  • TextColor
  • IsPassword
  • Placeholder

Keyboard

  • CapitalizeSentence
  • Spellcheck
  • Suggestions
  • All

editor.Keyboard = Keyboard.Create(KeyboardFlags.All);

Editor: InputView - wiele linii

Entry, Editor

  • Text
  • Completed
  • TextChanged

Chowanie klawiatury

  • dotknięcie czegoś innego na ekranie
  • dotknięcie przycisku
    • iPhone:  Return w Entry, Done w Editor
    • Android: Done w Entry, przycisk na dole ekranu do Editor
    • WP: Enter w Entry, sprzętowy przycisk Back do EditorView
  • Focus

 

Dostęp do plików

iOS i Android - klasy tak jak w “klasycznym” .NET

Standardowo w PCL klasy do obsługi plików nie są widoczne, z uwagi na WinRT API

 

Async

http://developer.xamarin.com/guides/cross-platform/advanced/async_support_overview/

Implementując funkcjonalność przy pomocy DependencyService na różne platformy, można używać metod z async (w Windows często mogą to być metody z WinRT API, na innych platformach używamy asynchronicznej wersji metod z .NET np. do operacji na plikach albo zwracamy taska na podstawie danych pozyskanych w sposób synchroniczny)

return Task<bool>.FromResult(exists)

Device.BeginInvokeOnMainThread(() => { … });   - wymuszenie wykonania w wątku UI

 

Data binding

BindableObject (implementuje INotifyPropertyChanged, definiuje SetValue i GetValue)

w klasach wizualnych kontrolek pola statyczne read-only typu BindableProperty  (coś ala DependencyProperty)

entry.Text = “Xxx”;

entry.SetValue(Entry.TextProperty, “Xxx”);

 

public static readonly BindableProperty TextProperty;

public string Text

{    

        set { SetValue(Entry.TextProperty, value); }

        get { return (string) GetValue(Entry.TextProperty); }

}

BindableProperty

  • definiowanie propercji
  • domyślne wartości
  • przechowywanie bieżących wartości
  • walidacja
  • utrzymywanie spójności między powiązanymi propercjami w klasie
  • zmiany propercji
  • notyfikacje o zamiarze zmiany i po zmianie
  • mechanizmy do animacji i data bindingu

BindingContext – propagacja w dół drzewa wizualnego

entry.BindingContext = data;

entry.SetBinding(Entry.TextProperty, “Title”);

Domyślny rodzaj data bindingu w BindableProperty

BindingMode

  • Default
  • OneWay
  • OneWayToSource
  • TwoWay

 

Nawigacja

NavigationPage

public interface INavigation

{    

        Task PushAsync(Page page);

        Task<Page> PopAsync(); 

        Task PopToRootAsync();

        Task PushModalAsync(Page page);

        Task<Page> PopModalAsync();

}

 

VisualElement

        Navigation:  INavigation

Aby włączyć nawigację metoda App.GetMainPage musi zwracać instancję NavigationPage (można do niej przekazać instancję ContentPage)

public class App

{        

          public static Page GetMainPage() 

          {           

                    return new NavigationPage(new HomePage());

           }

  }

//Home page

this.Navigation.PushAsync(new DetailsPage());

ContentPage

  • protected virtual void OnAppearing()
  • protected virtual void OnDisappearing()

ListView

  • ItemsSource
  • SelectedItem
  • event ItemSelected

INotifyCollectionChanged,  ObservableCollection

Okno dialogowe

Page

  • DisplayAlert(…)

bool confirm = await this.DisplayAlert("Xxx", "Yyy?”, "Yes", "No");   //true – przy naciśnięciu pierwszego przycisku

Element listy

ViewCell

  • TextCell

ListView listView = new ListView

{  

          ItemsSource = App.NoteFolder.Notes,

          ItemTemplate = new DataTemplate(typeof(TextCell)),

          VerticalOptions = LayoutOptions.FillAndExpand 

};  
listView.ItemTemplate.SetBinding(TextCell.TextProperty, "Xxx");

ToolbarItem

  • Name
  • Icon - nazwa pliku (najczęściej png, w iOS trzeba dostarczyć ikony samemu)
  • Order:  ToolbarItemOrder (Default, Primary - obrazek, Secondary - tekst)
  • Priority: int
  • event Activated
  • Command
  • CommandParameter

Page

  • ToolbarItems

Restrykcja w iOS i Android: NavigationPage lub strona nawigowana z NavigationPage

WP: może być ContentPage

Image

  • Source: ImageSource
  • Aspect: Aspect (AspectFit, Fill)

ImageSource

  • ImageSource.FromUri - z sieci
  • ImageSource.FromResource - bitmapa trzymana jako zasób w PCL  (EmbeddedResource)
  • ImageSource.FromFile - bitmapa trzymana jako content w projektach na platformy lub w SAP (iOS: BundleResource, Android: AndroidResource, WP: Content)
  • ImageSource.FromStream - ładowanie bitmapy przy użyciu strumienia .NET
  • UriImageSource
  • FileImageSource
  • StreamImageSource

new Image {  Source = “http://xxx.yyy.zzz/aaa/bbb”  };

 

AbsoluteLayout

Rectangle rect = new Rectangle(x, y, width, height);

AbsoluteLayout.SetLayoutBounds(child, rect);

child.SetValue (AbsoluteLayout.LayoutBoundsProperty, rect);  //attached bindable property

absoluteLayout.Children.Add(child, new Rectangle(x, y, width, height));

absoluteLayout.Children.Add(child, new Point(x, y));  // auto size

AbsoluteLayout.SetLayoutFlags(child, flags);  // automatyczne skalowanie i pozycjonowanie dzieci w zależności od swojego rozmiaru

absoluteLayout.Children.Add(child, rect, flags);

AbsoluteLayoutFlags

  • None
  • XProportional
  • YProportional
  • PositionProportional
  • WidthProportional
  • HeightProportional
  • SizeProportional
  • All

 

Kontrolki

  • Slider (nie wspiera wertykalnej orientacji)
  • Stepper
  • Entry
  • Editor
  • Switch
  • DatePicker
  • TimePicker
  • SearchBar
  • WebView
  • Picker  - wybór
  • ListView
  • TableView - lista elementów podzielonych na kategorie
  • ActivityIndicator
  • ProgressBar
  • OpenGLView

 

Data bindingi

view-to-view bindings = element binding w technologiach XAML Microsoft

targetView.BindingContext = sourceView;

konwerter danych:

stepperValueLabel.BindingContext = stepper; 

stepperValueLabel.SetBinding (Label.TextProperty,

    new Binding ("Value", BindingMode.Default, null, null,

    "The Stepper value is {0}"));

 

sliderValueLabel.SetBinding<Slider> (Label.OpacityProperty, src => src.Value);

sliderValueLabel.SetBinding (Label.TextProperty,

     Binding.Create<Slider> (src => src.Value, BindingMode.OneWay, null, null, 

    "The Slider value is {0:F2}"));

własna kontrolka dziedzicząca po ContentView:

  • propercje typu BindableProperty

public static readonly BindableProperty FontProperty =  BindableProperty.Create<RadioButton, Font>

       (radio => radio.Font, Font.SystemFontOfSize(NamedSize.Large));

kontrolka kontekstem bindingów kontrolek zawartych w jej contencie

 

public static readonly BindableProperty IsToggledProperty =  BindableProperty.Create("IsToggled",

typeof(bool),  //typ propercji

typeof(RadioButton),  //typ kontrolki

false,  //wartość początkowa

BindingMode.TwoWay, 

null,  //walidacja

OnIsToggledPropertyChanged);

 

static void OnIsToggledPropertyChanged(BindableObject sender,  object oldValue, object newValue)   {    …     }

 

Grid

grid.Children.Add (child, 2, 3);   //kolumny i wiersze od zera, automatyczne wnioskowanie o liczbie wierszy i kolumn na podstawie wywołań Add

odstępy: ColumnSpacing, RowSpacing

grid.Children.Add (child, left, top);

grid.Children.Add (child, left, right, top, bottom);

grid.Children.Add (child, 2, 5, 7, 8);   // kolumny 2,3,4; wiersz 7

domyślnie kolumny i wiersze przyjmują rozmiary elementów, ale poprzez ColumnDefinitions i RowDefinitions możemy narzucić im określone rozmiary albo zdefiniować podział proporcjonalny.

ColumnDefinition

  • Width: GridLength (Auto, Absolute, Star)

RowDefinition

  • Height: GridLength

grid.ColumnDefinitions.Add(new ColumnDefinition  {  

         Width = new GridLength(1, GridUnitType.Star) 

});

Grid.SetColumn (child, 2);

Grid.SetRow (child, 3);

//także SetColumnSpan, SetRowSpan

child.SetValue (Grid.ColumnProperty, 2);

 

Konwerter

IValueConverter - analogicznie jak w WPF

label.SetBinding(Label.TextProperty, new Binding("Value", BindingMode.OneWay, valueConverter, null, format));

poniedziałek, 23 lutego 2015

Newsy - nowa odsłona Visual Studio 2015, Microsoft Band dla firm trzecich, kolejne ciekawostki Windows 10

Pasuje zrobić małe podsumowanie informacji dnia.

Niewątpliwie największym wydarzeniem było wydanie Visual Studio 2015 CTP 6. Widzimy tu konsekwentną politykę wspierania różnych opcji zarówno tych ze świata Web, jak i ze świata XAML, .NET czy C++.  Ale po kolei. ASP.NET 5 zyskał nowe szablony i inne usprawnienia. Zwolennicy Xamarina otrzymali możliwość dołączania, debugowania, budowania bibliotek C++ z poziomu projektów aplikacji na Android. Visual Studio Tools for Apache Cordova zyskały debugowanie aplikacji na Windows Phone 8.1. Piszący desktopowe aplikacje WPF otrzymali narzędzia do debugowania XAML - Live Visual Tree i Live Property Explorer. Trwają prace nad udostępnieniem tych narzędzi w przyszłości także dla aplikacji Windows Store. Kibice Androida oprócz C++ w Xamarinie otrzymali także udoskonalony Visual Studio Emulator for Android, który wspiera nowego Androida Lollipop (API Level 21), OpenGL ES, symulację multi-touch i emuluje w sposób zaawansowany kamerę! Trzeba tutaj nadmienić, że standardowe narzędzia do Androida pozwalają emulować kamerę za pomocą kamerki internetowej, pisałem nawet o tym kiedyś jak porównywałem z narzędziami Windows / Windows Phone, które tego obecnie nie mają. Visual Studio dla Androida wprowadza dziś tę możliwość. Jak skończę instalację najnowszego Visual Studio z pewnością będę się dzielił sukcesywnie wrażeniami.

Aktualizacja opaski Band, a także udostępnienie dla niej SDK dla programistów firm trzecich to kolejna dobra nowina dnia tego.

Tradycyjnie już jak co kilka, kilkanaście dni mamy jakieś kolejne przecieki o Windows 10. Moją uwagę zwróciła tajna informacja, jakoby nowa wersja menu Start było zaimplementowane w XAML zamiast w DirectX. Szykuje się też może jakiś wyciek, mamy zdjęcia buildu 10022, które jednak nie zdradzają nic nowego.

sobota, 21 lutego 2015

Kulisy Xamarina - odc. 6: Xamarin.Forms cz.2

Książka, o której wspominałem ostatnio, na obecny moment mogę uznać za przyczytaną (co tydzień pojawiają się nowe rozdziały w wersji preview). Widać kunszt autora, dbałość o szczegółowe wyjaśnienie zagadnień, a jednoczesne stopniowe dawkowanie wiedzy, co czasami może niecierpliwić.  Wynotowałem kolejne rzeczy, które chciałem jakoś utrwalić. Pasuje też niektóre z nich okrasić jakimiś komentarzami. Oto i one:

  • brak koncepcji marginesu - pierwsza poznana przeze mnie technologia UI,  w której twórcy udostępniają wyłącznie padding
  • obsługa przechowywania stanu aplikacji - bardzo prosta obsługa  i na wszystkie platformy
  • nie wszystko działa na wszystkich platformach np. zaokrąglenia na przyciskach w WP są ignorowane  - no cóż, czasami zdarzają się pewne ograniczenia, ale zasadniczo i tak wiele otrzymujemy
  • XAML - naprawdę kawał dobrej roboty, cross-platformowy XAML ma się lepiej niż myślałem, wspiera nawet częściowo nowszą specyfikację tę z 2009 (pamiętam jak była ogłaszana przy pokazach związanych z WF 4), dużo rzeczy jak we frameworkach XAML Microsoftu np. bindingi, markup extensions (w tym własne), słowniki… DynamicResource mieliśmy do tej pory jedynie w WPF, a tu proszę.  Ostatnio twórcy chwalą się już triggerami i behaviorami…
  • gesty dotykowe  - gesture recognizer kojarzy mi się z jakimś starym WP Toolkitem, tutaj tylko mamy proste zdarzenia dotykowe
  • obsługa różnic między platformami - mamy wszystko co trzeba:  proste zróżnicowanie deklaratywnie w XAML, w kodzie możemy natomiast dostarczać platformowe implementacje do zaproponowanego wcześniej przez siebie interfejsu (korzystamy z DependencyService używającego refleksji lub robimy własny mechanizm w oparciu o dependecy injection)

 

Rozmiary

Specyficzne dla platformy niezależne od urządzenia unity

  • iOS, Android - 160 unit / cal
  • WP - 240 unit / cal

Rozmiary na WP ok. 150% większe od rozmiarów na iPhone i Android

VisualElement

  • event SizeChanged (jedyny sposób na wykrycie zmiany orientacji ekranu bez używania API specyficznego dla danej platformy)
  • protected virtual OnSizeAllocated

Blokada zmiany orientacji

  • iOS:  Info.plist (iPhone Deployment Info –> Supported Device Orientations)
  • Android:  ScreenOrientation w atrybucie  Activity nad aktywnością 

Skalowanie tekstu w ustawieniach Androida

Wysokość linii

  • iOS:  1.2 * label.FontSize
  • Android:  1.2  * label.FontSize
  • WP:   1.3  * label.FontSize

Szerokość znaku:   0.5 * label.FontSize

Brak koncepcji marginesu

Device.StartTimer(TimeSpan.FromSeconds(1),  () =>

{

        …

        return true;

};

SizeRequest sizeRequest = label.GetSizeRequest(width, Double.PositiveInfinity);

Przyciski

Button

  • FontFamily
  • FontSize
  • FontAttributes
  • TextColor
  • BorderColor
  • BorderWidth
  • BorderRadius
  • Image
  • IsEnabled
  • StyleId

Nie wszystko działa na wszystkich platformach:

  • iPhone: BorderWidth musi mieć dodatnią wartość
  • Android: BackgroundColor musi być ustawiony, BorderColor - ustawiony na wartość inną niż domyślna, BorderWidth - ustawiony na wartość dodatnią
  • WP:  BorderRadius nie działa

var button = new Button { Text = “XXX” };

button.Clicked += OnButtonClicked;

void OnButtonClicked(object sender, EventArgs args)

{

}

Rodzic w drzewie wizualnym:

button.ParentView

Dziecko w drzewie wizualnym:

outerLayout.Children[1]

Persystencja stanu aplikacji:

Application

  • Properties: słownik klucz-wartość  (kombinacja ustawień i danych tymczasowych typowych dla danej strony)
  • OnStart, OnSleep, OnResume: metody protected virtual

Application.Current.Properties[“xxx”] = “yyy”;

OnSleep

  • trzeba brać pod uwagę, że może to zakończyć się zamknięciem
  • wyzwolenie:  przycisk Start

OnResume

  • wyzwolenie:  wybór aplikacji z menu start (iOS i Android),  przycisk Back (Android i WP)
  • system automatycznie przywraca stan aplikacji
  • czasami czyszczenie słownika
  • przywracanie połączenia sieciowego lub pobranie najnowszych danych

Przełączanie między aplikacjami:

  • iOS:  podwójne naciśnięcie przycisku Home
  • Android:  naciśnięcie przycisku Multitask (lub przytrzymanie Home na starszych urządzeniach)
  • WP: przytrzymanie klawiszu Back

Przechowywanie danych do zapisania w obiekcie aplikacji (słownik Properties dostępny już w konstruktorze, w którym tworzona jest też główna strona, w jej konstruktorze możemy odczytać dane, a zapisywać jako reakcję na zdarzenia)

Instancja aplikacji musi być pobierana za każdym razem (w Androidzie może się zmienić podczas wykonywania aplikacji)

 

XAML

Możliwe używanie bindingów

Nie ma narzędzi designerskich

<Label Text="Hello from XAML!" 

       IsVisible="True"

       Opacity="0.75"

       XAlign="Center"

       VerticalOptions="CenterAndExpand"

       TextColor="Blue"

       BackgroundColor="#FF8080"

       FontSize="Large"

       FontAttributes="Bold,Italic" />

Przy solution z portable library w tym projekcie dodajemy strony:

Add New Item –> Visual C# –> Code –> Forms Xaml Page

<ContentPage xmlns=http://xamarin.com/schemas/2014/forms

                     xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 

                     x:Class="CodePlusXaml.CodePlusXamlPage">

</ContentPage>

public class App : Application

{

     public App()

    {

            MainPage = new CodePlusXamlPage();   //tak samo jak przy całej stronie w C#

     }

      …

}

Dzięki implementacji części specyfikacji XAML z 2009 możemy zdefiniować typ dla klasy generycznej i definiować wartości uzależnione od platform:

<OnPlatform x:TypeArguments=”Thickness”

              iOS = “0, 20, 0, 0”

              Android=”0”

              WinPhone = “0” />

Typy podstawowe ze specyfikacji XAML z 2009:

  • x:Object
  • x:Boolean
  • x:Byte
  • x:Int16
  • x:Int32
  • x:Int64
  • x:Single
  • x:Double
  • x:Decimal
  • x:Char
  • x:String
  • x:TimeSpan
  • x:Array

<Label VerticalOptions="CenterAndExpand">

       <Label.FormattedText>

              <FormattedString>

                     <Span Text="A single line with " />

                     <Span Text="bold" FontAttributes="Bold" />

               </FormattedString>

         </Label.FormattedText>

   </Label>

Konstruktory z parametrami (XAML 2009):

<Color>

        <x:Arguments>

                    <x:Double>1</x:Double>

                    <x:Double>0</x:Double>

                    <x:Double>0</x:Double>

         </x:Arguments>

</Color>  

Wywoływanie publicznych statycznych metod tworzących obiekt klasy, w której są zdefiniowane  [metody tworzące / fabryczne] (XAML 2009):

       <Color x:FactoryMethod="FromRgb">

                <x:Arguments>

                         <x:Double>0</x:Double>

                         <x:Double>1.0</x:Double>

                         <x:Double>0</x:Double>

                </x:Arguments>

        </Color>

OnPlatform:

<OnPlatform x:TypeArguments="View">

         <OnPlatform.iOS>

               <Label Text="Kocham iOS"

                   HorizontalOptions="Center"

                   VerticalOptions="Center" />

         </OnPlatform.iOS> 
       <OnPlatform.Android>

             <Label Text="Kocham Androida"

                    HorizontalOptions="Center"

                    VerticalOptions="Center" />

         </OnPlatform.Android> 
       <OnPlatform.WinPhone>

             <Label Text="Kocham Windows Phone"

                    HorizontalOptions="Center"

                    VerticalOptions="Center" />

         </OnPlatform.WinPhone>

   </OnPlatform>

<Label x:Name="deviceLabel"

            HorizontalOptions="Center"

           VerticalOptions="Center">

         <Label.Text>

             <OnPlatform x:TypeArguments="x:String"

                        iOS="Kocham iOS"

                         Android="Kocham Androida"

                         WinPhone="Kocham Windows Phone" />

         </Label.Text>

  </Label>

 

Własna kontrolka

Add –> New Item –> Visual C# > Code –> Forms Xaml Page i zmieniamy ręcznie ContentPage na ContentView

Własna przestrzeń nazw:

xmlns:local="clr-namespace:MyApp;assembly=MyApp"

 

Gesty dotykowe

  • View - nie ma zdarzeń, ale jest kolekcja GestureRecognizers

var tapGesture = new TapGestureRecognizer(); 

tapGesture.Tapped += OnViewTapped;

view.GestureRecognizers.Add(tapGesture);

void OnViewTapped(object sender, EventArgs args)

{

        …

}

 

Warunki View na generowanie zdarzeń:

  • IsEnabled = true
  • IsVisible = true
  • InputTransparent = false

TapGestureRecognizer:

  • NumberOfTapsRequired = 1 (domyślnie)
  • Command
  • CommandParameter

XAML:

<BoxView Color="Blue">

     <BoxView.GestureRecognizers>

         <TapGestureRecognizer Tapped="OnBoxViewTapped" />

     </BoxView.GestureRecognizers>

</BoxView>

 

API specyficzne dla platformy

Sharowanie plików

Portable Class Library odczytujące przez refleksję API typowe dla używanej w danym momencie platformy - gotowe rozwiązanie:  DependencyService

Interfejs w PCL

Klasy implementujące interfejs w projektach pod konkretne platformy.

[assembly:  Dependency(typeof(klasa_implementująca))]   /* w pliku klasy przed deklaracją namespace */

w PCL:

var platformInfo = DependencyService.Get<IPlatformInfo>();  //instancje obiektów pozyskiwanych przez Get są cachowane, stan w implementacjach na platformy jest przechowywany między wywołaniami Get

Kto chce może też za pomocą dependecy injection zrealizować podobną funkcjonalność.

Odtwarzanie audio (WAVE)

iOS:

       …

NSData data = NSData.FromStream(stream);

AVAudioPlayer audioPlayer = AVAudioPlayer.FromData(data);

audioPlayer.Play();  

Android:

if (previousAudioTrack != null)

{               

         previousAudioTrack.Stop();

         previousAudioTrack.Release(); 


 

AudioTrack audioTrack = new AudioTrack(Stream.Music,

                                                        samplingRate, 

                                                        ChannelOut.Mono,

                                                        Android.Media.Encoding.Pcm16bit,

                                                        pcmData.Length * sizeof(short),

                                                        AudioTrackMode.Static); 


audioTrack.Write(pcmData, 0, pcmData.Length);

audioTrack.Play();

 

previousAudioTrack = audioTrack;

 

XAML Markup Extensions

Ze specyfikacji XAML 2009:

  • x:Static  - każda statyczna propercja, pole lub wartość enum
  • x:Reference
  • x:Type
  • x:Null
  • x:Array

  <Label BackgroundColor=”{x: Static local:AppConstants.LightBackground}” />

<x:Array x:Key="array"  Type="{x:Type x:String}">

     <x:String>One</x:String>

     <x:String>Two</x:String>

</x:Array>

Markup extensions z frameworków XAML Microsoftu:

  • StaticResource
  • DynamicResource
  • Binding

Markup extension unikalny dla Xamarin.Forms

  • ConstraintExpression (powiązanie z RelativeLayout)

Własny markup extension:

public class HslColorExtension : IMarkupExtension

{        

        public HslColorExtension()

        {            

                A = 1; 

        }

 
        public double H { set; get; } 


        public double S { set; get; } 


        public double L { set; get; }

  
        public double A { set; get; }

 
        public object ProvideValue(IServiceProvider serviceProvider)

        {            

                  return Color.FromHsla(H, S, L, A);

        }

}

<BoxView Color="{local:HslColor H=0.67, S=1, L=0.5}" />

Biblioteka PCL z markup extension - aktualnie aplikacja oprócz referencji do biblioteki wymaga odwołania z kodu np. wywołania konstruktora markup extension w konstruktorze obiektu aplikacji

 

ResourceDictionary

VisualElement

  • Resources

Aplikacja także może mieć plik XAML, ale trzeba to przygotować ręcznie na podstawie strony XAML

piątek, 13 lutego 2015

Kulisy Xamarina - odc. 5: Xamarin.Forms cz.1

Kolejny raz z rzędu zdarzyło mi się,  że miałem bardziej niszowy, ale interesujący temat do poruszenia, a równocześnie pojawiła się kolejna odsłona Windows 10. W założeniu chciałem kreować to na bombę, ale po wczorajszej premierze uderzenie będzie z pewnością słabsze.  Ale do rzeczy. Pisanie aplikacji z najlepszymi IMHO natywnymi interfejsami użytkownika nawet w Xamarin wymaga z jednej strony pewnej wiedzy, z drugiej strony czasu, by wszystko na każdej platformie ułożyć. Ci, co kochają technologie webowe mogą zakrzyknąć do piszących w C#, ha przegraliście, my piszemy raz (IMHO gorsze UI, takie “na wszystko”), a wy trzy razy !  Hola, hola użytkujący Xamarin mają jeszcze w zanadrzu doskonałą broń w postaci Xamarin.Forms! To wspaniały kompromis by z jednej strony pisać interfejs raz, operując cross-platformową abstrakcją, a z drugiej strony mieć generowane piękne w pełni natywne interfejsy użytkownika!  Web może i daje współdzielenie, ale nie daje natywnego UI, bo zakłada stosowanie HTML, JS i CSS, które nadal nie mają takiego wsparcia jak Java w Android czy Objective-C/Swift w iOS. Microsoft wspiera swoją platformę WinRT w równym stopniu dla XAML/.NET lub C++  czy HTML/JS/CSS (WinJS). Jedynym “mankamentem” obecnej wersji Xamarin.Forms jest fakt, że wspiera na razie jedynie Windows Phone 8 Silverlight, co jest już mówiąc delikatnie pieśnią przeszłości, przejdzie jeszcze przy Windows Phone 8.1, ale dziś wyznacznikiem jest WinRT XAML dla aplikacji uniwersalnych. Zdają sobie z tego sprawę i twórcy, którzy na forum stosunkowo niedawno zapowiedzieli, że w przyszłych wersjach takie aplikacje będą wspierać. Natomiast na obecny moment chciałbym polecić książkę Charlsa Petzolda (znanego autora książek o technologiach związanych z XAML np. WPF czy Windows Phone) Creating Mobile Apps with Xamarin.Forms Book Preview 2 (drugie preview ukazało się 10 dni temu, opisuje świeżą wersję 1.3), którą jak do tej pory przeczytałem w połowie, wyławiając z niej pewne fakty w postaci poniższych notatek.

Pomyślany głównie o telefonach, ale można eksperymentować z tabletami.

Trzy projekty pod iPhone, Android i Windows Phone 8.0 oraz zasadniczy współdzielony projekt (SAP lub PCL).

API

  • Xamarin.Forms.Core (w zależności od platformy może używać odpowiednich bibliotek Xamarin.Forms.Platform.* - w większości klasy renderujące  -transformujące obiekty Xamarin.Forms na UI specyficzne dla danej platformy)
  • Xamarin.Forms.Xaml

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"  

   xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"  

   x:Class="PlatformVisuals.PlatformVisualsPage"   Title="XXX"> 
    <StackLayout Padding="10,0">        

        <Label Text="Hello"  VerticalOptions="CenterAndExpand"   HorizontalOptions="Center" />   
        <Button Text = "OK"  VerticalOptions="CenterAndExpand"    HorizontalOptions="Center" />  
        <Switch VerticalOptions="CenterAndExpand"   HorizontalOptions="Center" />
        <Slider VerticalOptions="CenterAndExpand" />    

    </StackLayout>
    <ContentPage.ToolbarItems>

        <ToolbarItem Name="edit" Order="Primary">            

             <ToolbarItem.Icon>

                    <OnPlatform x:TypeArguments="FileImageSource" 

                             iOS="edit.png"

                             Android="ic_action_edit.png"

                             WinPhone="Images/edit.png" />

             </ToolbarItem.Icon>

        </ToolbarItem>

        … 
         <ToolbarItem Name="explore" Order="Secondary" />

     </ContentPage.ToolbarItems>

</ContentPage>

Różnicowanie względem platformy:

  • <OnPlatform … />
  • klasa Device  (detekcja platformy w celu zróżnicowania wykonywania kodu)
  • klasa DependencyService  (serwis implementowany przez każdą platformę)
  • możliwość tworzenia własnych wizualnych obiektów (we wspólnym projekcie, renderowanie w projektach dla platform)

Nie jest to zastąpienie Xamarin.iOS i Xamarin.Android, a integracja.

Nie jest to zastąpienie natywnego programowania. Przy specyficznych kontrolkach, wektorowej grafice czy złożonych dotykowych interakcjach nadal potrzebujemy natywnego API.  Dobre do prototypowania i aplikacji biznesowych.

Wizualne elementy (VisualElement):

  • strona (Page)
  • layout (Layout)
  • widoki/kontrolki/widgety (View)

Projekty VS

  • Blank App (Xamarin.Forms Portable)
  • Blank App (Xamarin.Forms Shared)
  • Class Library (Xamarin.Forms Portable)

image

Cztery projekty w solution Blank App (Xamarin.Forms Portable)

  • portable
    • .NET Framework 4.5
    • Windows 8
    • Windows Phone Silverlight 8
    • Xamarin.Android
    • Xamarin.iOS
    • Xamarin.iOS (Classic)
  • iOS
  • Android
  • Windows Phone 8

App.cs w portable:

    public class App : Xamarin.Forms.Application
    {
        public App()
        {
             MainPage = new ContentPage
            {
                Content = new StackLayout
                {
                    VerticalOptions = LayoutOptions.Center,
                    Children = {
                        new Label {
                            XAlign = TextAlignment.Center,
                            Text = "XXX"
                        }
                    }
                }
            };
        }

        protected override void OnStart()
        {
        }

        protected override void OnSleep()
        {
        }

        protected override void OnResume()
        {
        }
    }

W jednostronicowych aplikacjach zwykle własna klasa rozszerzająca ContentPage.

Na każdej platformie aplikacja jest inicjalizowana przez metodę Forms.Init.

iOS

    [Register("AppDelegate")]
    public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
    {
        public override bool FinishedLaunching(UIApplication app, NSDictionary options)
        {
            global::Xamarin.Forms.Forms.Init();
            LoadApplication(new App());

            return base.FinishedLaunching(app, options);
        }
    }

Android

    //ConfigurationChanges - wył. ponownego tworzenia

    [Activity(Label = "App12", Icon = "@drawable/icon", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsApplicationActivity
    {
        protected override void OnCreate(Bundle bundle)
        {
            base.OnCreate(bundle);

            global::Xamarin.Forms.Forms.Init(this, bundle);
            LoadApplication(new App());
        }
    }

WP8

strona MainPage.xaml  (FormsApplicationPage)

    public partial class MainPage : global::Xamarin.Forms.Platform.WinPhone.FormsApplicationPage
    {
        public MainPage()
        {
            InitializeComponent();
            SupportedOrientations = SupportedPageOrientation.PortraitOrLandscape;

            global::Xamarin.Forms.Forms.Init();
            LoadApplication(new App12.App());
        }
    }

Projekt z sharowanymi plikami (SAP)

  • inny .NET dla iOS i Android, a inny dla WP
  • flagi preprocesora:  __IOS__, WINDOWS_PHONE  (Android nie ma)

Add New Item

  • Forms ContentPage
  • Forms ContentView
  • Forms Xaml Page

    public class XPage : ContentPage
    {
        public XPage()
        {
            Content = new StackLayout
            {
                Children = {
                    new Label { Text = "XXX" }
                }
            };
        }
    }

Na iOS7+ aplikacja z pojedynczym widokiem w górnej części jest przysłaniana przez pasek.

  • na tej platformie możemy dodać do strony Padding = new Thickness(0, 20, 0, 0);

Device

  • OS:  TargetPlatform (iOS, Android, WinPhone, Other)
  • Idiom: TargetIdiom (Phone, Tablet, Desktop, Unsupported)
  • OnPlatform<T> - argumenty, OnPlatform - akcje
  • timer i wykonywanie kodu w głównym wątku

Padding = Device.OnPlatform(new Thickness(0, 20, 0, 0), new Thickness(0), new Thickness(0));

Device.OnPlatform(/* opcjonalnie  iOS:*/ () =>

       {

                Padding = new Thickness(0, 20, 0, 0);

       });

Położenie względem rodzica

View

  • HorizontalOptions:  LayoutOptions 
  • VerticalOptions: LayoutOptions

LayoutOptions

  • Start
  • Center
  • End
  • Fill  (Label - domyślnie)
  • StartAndExpand
  • CenterAndExpand
  • EndAndExpand
  • FillAndExpand

Pozycjonowanie tekstu w Label

Label (domyślnie lewo u góry)

  • XAlign: TextAlignment
  • YAlign: TextAlignment

TextAlignment

  • Start
  • Center
  • End

Automatyczne przenoszenie tekstu w Label do następnych linii. LineBreakMode - inne możliwości

Kolory w Label

  • TextColor
  • BackgroundColor (VisualElement)

Color.Yellow

domyślnie Color.Default - wartości kolorów tła i tekstu dla danej platformy

Nie można bez używania specyficznych dla platform klas ustalić jakie kolory zostały wybrane.

Color

  • R, G, B
  • Hue, Saturation, Luminosity
  • A

Color.AccentColor – iPhone/Android: kolor przeciwny do domyślnego tła, WP - kolor tematu

Czcionki

  • Label - domyślnie domyślne ustawienia dla poszczególnych platform
  • Label i Button - jedyne klasy z propercjami odnoszącymi się do czcionek

Propercje

  • FontFamily
  • FontSize *
  • FontAttributes  (None, Bold, Italic)
  • Font (deprecated)

* możliwa pomoc przez Device.GetNamedSize(p, typ),

p: NamedSize (Default, Micro, Small, Medium, Large)

Formatowanie tekstu

Label

  • FormattedText:  FormattedString
    • Spans:  IList<Span>

Span

  • Text
  • FontFamily
  • FontSize
  • FontAttributes
  • ForegroundColor
  • BackgroundColor

Layouty

Layout<View>

  • AbsoluteLayout
  • Grid
  • RelativeLayout
  • StackLayout

StackLayout

  • Orientation:  StackOrientation (Vertical, Horizontal)
  • Spacing: double (domyślnie 6, może być ujemny)

Przewijanie

ScrollView

  • Orientation:  ScrollOrientation (Horizontal, Vertical)

StartAndExpand, CenterAndExpand, EndAndExpand,  FillAndExpand  - jeśli elementy mają mniejszą wysokość niż cały StackLayout, wtedy są umieszczane w równych częściach

BoxView - wypełniony prostokąt

  • Color

Frame - obramowanie wokół zawartości

  • Content
  • BackgroundColor
  • Padding
  • OutlineColor
  • HasShadow (pokazuje się tylko na iPhone)

VisualElement

  • Width, Height  - tylko do odczytu
  • WidthRequest, HeightRequest - inicjalizowane –1, czasami niektóre elementy sobie ustawiają, są ignorowane, gdy element ma pozwolenie na wypełnienie otaczającego go kontenera

ScrollView w StackLayout - aby wypełniał sobą wskazaną przestrzeń na wysokość musi mieć VerticalOptions = FillAndExpanded

Można używać zasobów .NET w projektach (EmbeddedResource).

czwartek, 12 lutego 2015

Windows 10 for phones - hurra!?

Tło obrazkowe dla Start. Więcej szybkich akcji z centrum notyfikacyjnego. Interaktywne notyfikacje (np. anulowanie alarmu, mapy, odpowiadanie na tekstowe wiadomości, także za pomocą głosu). Kojarzy mi się to trochę z Androidem. Nawet pisałem we wrześniu zeszłego roku, że tego jeszcze w Windows nie ma i mogłoby być przydatne. A tu proszę, zrobili.  Ale idźmy dalej. Poszerzony text-to-speech. Większe możliwości aplikacji Photos (synchronizacja wszystkich lokalnych zdjęć i tych z OneDrive). Aplikacja File Explorer!  Niestety… pierwsze preview nie zostało przewidziane na model mojej słuchawki  –:(  Wynika to z systemu partycji nad czym pracują. Tak więc póki co nie poczuję dreszczu ryzyka…  BTW są materiały w sieci jak sobie radzić nawet w najgorszych przypadkach (nie mam tu na myśli prostego skorzystania z narzędzia MS do przywracania WP 8.1).

Bardzo sprawnie przeprowadzona, klarowna demonstracja. Aczkolwiek jakoś mało mnie zaskakuje, w sumie wszystko albo prawie wszystko już gdzieś widziałem, zdaje się na ostatnim evencie związanym z Windows 10.

Linki

niedziela, 8 lutego 2015

Kulisy Xamarina - odc. 4: jeszcze trochę o iOS

Rozwinięcie niektórych zagadnień związanych z iOS i środowiskiem Xamarin. Do technicznie bardziej ciekawych z pewnością należy bindowanie bibliotek Objective-C z poziomu C# oraz framework  MonoTouch.Dialog do szybkiej budowy UI w oparciu o metadane lub opis strukturalny.  Poruszyłem też temat obsługi iBeaconów. Nie jest rzecz typowo związana z Xamarin, ale z pewnością godna uwagi. Przy okazji mamy przykładowe użycie centrum notyfikacyjnego i notyfikacji.

 

Ogólne uzupełnienia

.NET BCL + iOS SDK + Xamarin.Mobile

możliwa współpraca pomiędzy Objective-C i C#

kompilacja do natywnego maszynowego kodu - AOT (Ahead of Time Compilation) –> natywne assembly ARM

możliwość stosowania bibliotek .NET i Objective-C

http://developer.xamarin.com/guides/ios/advanced_topics/binding_objective-c/

 

Ustawienia

Settings.bundle/Root.plist

odczyt:

kontroler

public override void ViewWillAppear(bool animated)

{

          Label.Text = NSUserDefaults.StandardUserDefaults.StringForKey(“xxx”);

          base.ViewWillAppear(animated);

}

zapis:

NSUserDefaults.StandardUserDefaults.SetString(“wartosc”, “xxx”);

rejestracja domyślnych wartości:

AppDelegate FinishedLaunching

var settingsBundle = NSBundle.MainBundle.PathForResource(“Settings”, @”bundle”);

var keyString = new NSString(@”Key”);

var defaultString = new NSString(@”DefaultValue”);

var settings = NSDictionary.FromFile(Path.Combine(settingsBundle, @”Root.plist”);

var preferences = (NSArray) settings.ValueForKey(new NSString(@”PreferenceSpecifiers”);

var defaultsToRegister = new NSMutableDictionary();

for (uint i=0; i < preferences.Count; i++)

{

       var prefSpecification = new NSDictionary(preferences.ValueAt(i));

       var key = (NSString) prefSpecification.ValueForKey(keyString);

       if (key != null)

       {

               NSObject def = prefSpecification.ValueForKey(defaultString);

               if (def != null)

                     defaultsToRegister.SetValueForKey(def, key);

       }

}

NSUserDefaults.StandardUserDefaults.RegisterDefaults(defaultsToRegister);

 

Actions Sheets & Alerts

var alert = new UIAlertView(“Tytuł”, “Wiadomość”, null, “OK”);

alert.Show();

 

var alert = new UIAlertView(“Tytul”, “Pytanie”, null,  “Anuluj”, “OK”);

//alert.Canceled

alert.Clicked += HandleAlertClicked;

alert.Show();

 

void HandleAlertClicked (object sender, UIButtonEventArgs e)

{

         var alert = (UIAlertView) sender;

         if (e.ButtonIndex == alert.CancelButtonIndex)

                …

         else

                …

}

 

var actionSheet = new UIActionSheet(“Tytuł/pytanie”, null, “Anuluj”, “Usuń”, “Inna akcja”);

actionSheet.Clicked += HandleActionSheetClicked;

actionSheet.ShowInView(View);

 

void HandleActionSheetClicked(object sender, UIButtonEventArgs e)

{

         var actionSheet = (UIActionSheet)  sender;

         if (e.ButtonIndex == actionSheet.DestructiveButtonIndex)  //destructive - 0, cancel - ostatni

                …

         if (e.ButtonIndex == 1)  // actionSheet.FirstOtherButtonIndex

                …   

         if (e.ButtonIndex == actionSheet.CancelButtonIndex)  

                … 

}

 

Drobne uzupełnienia

Grupowanie w UITableView:

UITableViewDataSource / UITableViewSource

public override int NumberOfSections(UITableView tableView)

{

}

public override string TitleForHeader(UITableView tableView, int section)

{

}

public override string TitleForFooter(UITableView tableView, int section)

{

}

Obrazek w komórce UITableView:

cell.ImageView.Image = new UIImage(“xxx.jpg”);

Przycisk szczegółów w komórce UITableView:

cell.Accessory = UITableViewCellAccessory.DetailDisclosureButton;

Chowanie przyciskiem Return klawiatury w polu tekstowym:

textField.ShouldReturn = (s) =>

{

         textField.ResignFirstResponder();

         return true;

}

Ukrywanie paska nawigacyjnego na danym ekranie:

public override void ViewDidAppear(bool animated)

{

          …

          NavigationController.SetNavigationBarHidden(true, animated);

}

public override void ViewWillDisappear(bool animated)

{

          NavigationController.SetNavigationBarHidden(false, animated);

}

Zakładki:

public class TabBarController: UITabBarController

{

          UIViewController tab1;

          UIViewController tab2;

          public TabBarController()

          {

          }

          public override void ViewDidLoad()

          {

                    base.ViewDidLoad();

 

                    tab1 = new XViewController();

                    tab1.Title = “Xxx”;

                    tab1.TabBarItem = new UITabBarItem(UITabBarSystemItem.History, 0);  //obrazek

                    …

                    var tabs = new UIViewController [] { tab1, tab2 };

                    ViewControllers = tabs;

 

                    SelectedViewController = tab1;                   

          }

}

Wyświetlenie modalne:

var controller = new XViewController();

controller.ModalTransitionStyle = UIModalTransitionStyle.PartialCurl;

PresentViewController(controller, true, () => { //on completed });

 

MonoTouch.Dialog

Framework do łatwiejszego tworzenia UI, w tym dynamicznie definiowanego.

  • wsparcie dla wielu różnych kontrolek
  • pull to refresh (odświeżanie widoku po umieszczeniu danych w TableView)
  • search
  • JSON (definiowanie UI)
  • ładowanie obrazków w tle
  • duża rozszerzalność

http://developer.xamarin.com/guides/ios/user_interface/monotouch.dialog/

Jak to działa ?

  • Reflection API - jak ORM, mapowanie metadanych/atrybutów na obiekty
  • Elements API - odczytywanie drzewa obiektów i jego rysowanie

Główne elementy:

  • DialogViewController - dziedziczy po TableView
  • RootElement - reprezentuje stronę
  • Section - podobne do sekcji w TableView
  • Element – kontrolka

Dodajemy referencję do projektu.

public class Person

{

         public Person()

         {

         }

 

         [Entry(“Podaj imię i nazwisko”)]

         public string Name;

 

         [Entry(KeyboardType = UIKeyboardType.NumberPad)]

         public string Age;

 

         [Date]

         public DateTime Birthday = DateTime.Now;

 

         [OnTap(“ButtonClicked”)]

         public string Button; 

}

//budowa kontrollera

var person = new Person();

var binding = new BindingContext(this /* jeśli mamy callbacki, inaczej null*/, person, “Moje dane”);

var dialogViewController = new DialogViewController(binding.Root);

var navigationController = new UINavigationController(dialogViewController);

public void ButtonClicked()

{

}

Elements API:

var root = new RootElement(“Xxx”)  {   //skrót do Add(…)

        new Section {

                new RootElement(“Day 1”)  {   //podstrona

                        new Section  {   //skrót do Add(…)

                                 new StringElement(“12 grams”),

                                 new BooleanElement(“yyy”, true /*wartość*/)

                        }

                }

        }

}

var dialogViewController = new DialogViewController(root);

var navigationController = new UINavigationController(dialogViewController);

 

iOS 5 - notatka z historii

  • Cloud Storage
  • AirPlay Mirroring (Apple TV)
  • Centrum notyfikacyjne
  • Newsstand
  • Integracja z Twitterem
  • iMessage
  • Storyboards
  • Rozszerzenia Core Image
  • Emulacja lokalizacji

 

iCloud

Prawdziwe urządzenie

Opcja aplikacji na portalu

Ustawienia projektu VS

iOS Application –> Entitlements

  • iCloud Key-Value Store
  • iCloud Services
  • iCloud Containers

iOS Bundle Signing –> Custom entitlements (= Entitlements.plist)

odczyt:

var store = NSUbiquitousKeyValueStore.DefaultStore;

store.Synchronize();

Label.Text = store.GetString(“Xxx”);

zapis:

var store = NSUbiquitousKeyValueStore.DefaultStore;

store.SetString(“Xxx”, “wartosc”);

store.Synchronize();

 

iBeacons

Beacon

  • każde urządzenie BLE (Bluetooth Low Energy) (Bluetooth 4.0+)
  • urządzenie BLE, które transmituje sygnał pozwala odbierającemu urządzeniu wykryć swoją bliskość
  • nie wymagają parowania

iBeacon

  • termin i specyfikacja Apple
    • w jaki sposób są rozpoznawane przez urządzenia Apple
    • broadcast interval
    • advertisement/message format

iBeacon Advertisements

do 31 bajtów

  • Prefix: 9
  • Proximity UUID: 16
  • Major: 2
  • Minor: 2
  • Tx Power: 1

Wspierane platformy

  • iOS7+
  • Android 4.3 (Jelly Bean+)
  • Windows Phone 8 (Update 3+)

Warunki i ograniczenia

  • Bluetooth musi być włączony
  • Location Services dla aplikacji muszą być udostępnione
  • wymagany jest dostęp do Internetu (content & location services)
  • maksymalnie 20 regionów
  • do testowania potrzebne jest fizyczne urządzenie
  • iOS nie może skanować wszystkich używanych UUID-ów
  • aplikacje mają aktywny “zasięg” tylko w foreground (wyjątek: 10 s otrzymywania danych o zakresie przy zdarzeniach didEnter i didExitRegion)

Sposób działania

  • iOS Core Location Services, Bluetooth 4.0
  • definicja regionów przy użyciu UUID, [Major], [Minor]
  • aplikacja nasłuchująca sygnałów beacona
  • unikalny experience bazujący na zbliżeniu przy wchodzeniu/opuszczaniu regionów/zakresów beaconów

Core Location Manager  )))

  • StartMonitoring
  • StartRangingBeacons

Core Location Manager Delegate  (((

  • RegionEntered
  • RegionLeft
  • DidRangeBeacons 
  • DidDetermineState

Cztery stany

  • Unknown:  > 30 m
  • Far:  2m - 30m (mała dokładność lub słaby sygnał)
  • Near:  0.5 - 2m (całkiem duża dokładność)
  • Immediate:  0 - 0.5m (wysoka dokładność)

Ufanie bliskości

  • Transmit Power (TXPower)
  • Received Signal Strength (RSSI)
  • Accuracy
  • Broadcast Interval

Kod

//definicja regionu

CLBeaconRegion region;

region = new CLBeaconRegion(new NSUuid(“B9407F30-F5F8-466E-AFF9-25556B57FE6D”), “XXX  Region”);

region.NotifyEntryStateOnDisplay = true;

region.NotifyOnEntry = true;

region.NotifyOnExit = true;

 

//nasłuchiwanie

[Register (“AppDelegate”)]

public partial class AppDelegate: UIApplicationDelegate

{

         …

         private CLLocationManager _locationManager;

         public CLLocationManager ShareLocationManager

         {

                  get

                  {

                           if (_locationManager == null)

                           {

                                     _locationManager = new CLLocationManager();

                                     _locationManager.Delegate = new CoreLocation();

                            }

                           return _locationManager;

                  }

         }

         …

         public override void OnActivated(UIApplication application)

         {

                   ShareLocationManager.StartMonitoring(GetRegion());

         }

}

public class CoreLocation: CLLocationManagerDelegate

{

          public CoreLocation() {}

          public override void RegionEntered (CLLocationManager manager, CLRegion region)

          {

                   manager.StartRangingBeacons(region as CLBeaconRegion);    //region.Identifier

                   NSNotificationCenter.DefaultCenter.PostNotificationName(“RegionUpdate”, this,

                   new NSDictionary (“RegionData”,  content /* instancja obiektu z danymi, dziedziczącego po NSObject*/);

          }

          public override void RegionLeft (CLLocationManager manager, CLRegion region)

          {

                   manager.StopRangingBeacons(region as CLBeaconRegion);    

          }

          //foreground: co sekundę dla monitorowanego regionu, background/closed: 10 s po wejściu/opuszczeniu regionu

          //budzenie ekranu (NotifyEntryStateOnDisplay = true): 10 s, warunek: wewnątrz monitorowanego regionu

          public override void DidRangeBeacons (CLLocationManager manager, CLBeacon[] beacons, CLBeaconRegion region)

          {

                   if (beacons.Length > 0)

                   {

                            var beacon = (CLBeacon) beacons.GetValue(0);

                            //beacon.ProximityUuid,  .Major, .Minor, .Proximity,  .Rssi, .Accuracy

                   }

          }

          public override void DidDetermineState (CLLocationManager manager, CLRegionState state, CLRegion region)

          {

                    if (state = CLRegionState.Inside)

                    {

                            manager.StartRangingBeacons(region as CLBeaconRegion);

                    }

          }

}

public partial class XViewController: UIViewController

{

          NSObject _regionNotify;

          …

          public override void ViewWillAppear(bool animated)

          {

                   base.ViewWillAppear(animated);

                   _regionNotify = NSNotificationCenter.DefaultCenter.AddObserver((NSString)”RegionUpdate”, OnRegionUpdate);

                  …

          }       

          public void OnRegionUpdate(NSNotification notification)

          {

                   var content = notification.UserInfo.ValueForKey((NSString)”RegionData”) as XContent; /*nasza klasa z danymi */

                   …

          }

          …

          public override void ViewWillDisappear(bool animated)

         {

                  _regionNotify.Dispose();

                  base.ViewWillDisappear(animated);

         }

}

Zdarzenia beacon-ów w tle lub przy wyłączonej aplikacji

[Register (“AppDelegate”)]

public partial class AppDelegate: UIApplicationDelegate

{

         …

         public override bool FinishedLaunching (UIApplication application, NSDictionary launchOptions)

         {

                 ShareLocationManager.StartMonitoring(GetRegion());

                 return true;

         }

}

public class CoreLocation: CCLocationManagerDelegate

{

         //obiekt używany do wysyłania danych przy obsłudze zdarzeń

         private XContent _content = new XContent();

         public void SetContent(XContent content)

         {

                 if (content != null)

                 {

                         if (UIApplication.SharedApplication.ApplicationState == UIApplicationState.Active) //foreground

                               NSNotificationCenter.DefaultCenter.PostNotificationName(“ContentUpdate”, this,

                               new NSDictionary (“Content”, content));

                         else //background

                              NotificationHelper.SendLocalAlertNotification(content);                        

                 }

         }

        …

        //DidRangeBeacons: wysyłamy jeśli beacon.Proximity != CLProximity.Unknown i aplikacja jest w foreground

}

public class NotificationHelper

{

         …

         public static void SendLocalAlertNotification(XContent message, int delayInSeconds = 1)

         {

                 var notification = new UILocalNotification();                

 

                 notification.FireDate = DateTime.Now.AddSeconds(delayInSeconds);

                 notification.AlertAction = message.NotificationTitle;

                 notification.AlertBody = message.NotificationMessage.ToString();

 

                 notification.ApplicationIconBadgeNumber += 1;

 

                 notification.SoundName = UILocalNotification.DefaultSoundName;

 

                 UIApplication.SharedApplication.ScheduleLocalNotification(notification);                

         }

}

 

Visual Studio na Mac-u

Wirtualna maszyna

  • Parallels Desktop
  • VMware Fusion

Boot Camp - program Apple umożliwiający uruchamianie Windows