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
Brak komentarzy:
Prześlij komentarz