Dziś kolejna ucieczka do alternatywnej mocno rzeczywistości Objective-C, do czasów wskaźników czy tworów nie spotykanych czasami nigdzie indziej. Tym razem z elementami na żywo, bo tak zawsze lepiej. W aktualnym XCode mamy w projektach do wyboru także Swift, ale dziś gramy klasykę. Chociaż i Objective-C trochę zdaje się ucywilizowało, wraz z upływem czasu trzeba pisać w nim coraz mniej, chociaż nadal w wielu przypadkach trzeba się opisać bardzo dużo w stosunku np. do JS i Angulara. Ale mamy za to wszystko natywne, oryginalnie pochodzące od twórców Mac czy iOS i jest to na swój sposób piękne. Z drugiej strony wiele rozwiązań jest jednak ideowo znajomych z innych języków czy platform i mimo innej formy nie czuje się, że to coś naprawdę innego.
Zrobię podsumowanie przez kilka spostrzeżeń
- Łączenie kodu z deklaratywnym UI - oryginalne dość łączenie strzałkami elementów UI z kodem, jak się coś później namiesza, to całość może się skompilować, a dopiero po uruchomieniu może nam sypnąć wyjątkami o jakichś osieroconych odwołaniach. Nie wydaje się to najmocniejszą stroną XCode.
- Kontrolery - mimo nazwy wydają się tu być takim code-behind, do nich przecież trafiają referencje do wizualnych elementów.
- Kilka dodatkowych rzeczy w Objective-C - odpowiedniki metod anonimowych, rozszerzeń metod (własne rozszerzenia interfejsów w plikach z implementacją), interpretacja pustego obiektu w if podobnie jak w JS
- Bogactwo klas NS* i ich metod - wszystko z dobrych podstaw mamy, a więc NSString, NSData, NSFormatter, NSArray, NSDictionary, a nawet takie rzeczy jak NSNumber, NSNull czy NSSet. To tylko kropla w morzu klas NS*. Zapewniają one także m.in operacje na systemie plików, komunikację sieciową, parsowanie JSON. Jak czegoś nie wiemy, to pewnie będzie to obiekt NS*. Metody klas *NS są liczne, mają różne warianty. Przykładowo obsługa napisów jest tak bogata jak w najnowszych współczesnych platformach, miejscami zdaje się nawet je przewyższać.
- Asynchroniczna obsługa połączenia sieciowego nienajgorsza. Pewna toporność czy nadmiar formy wyraża się w podawaniu kolejki, tworzenie kilku obiektów do wywołania połączenia czy uzupełnianie czasem wymaganych parametrów nil-ami. Niestety, widziałem w Objective-C synchroniczne API do operacji mogących potencjalnie trwać dłużej tj. operacje na systemie plików czy właśnie komunikacja sieciowa.
- Obsługa SQLLite całkiem prosta, kopiowanie pliku bazy z zasobów do folderu aplikacji skojarzył mi się z podobnymi zabiegami w Windows Phone 7.5 z bazą SQL Server
- Parsowanie JSON do listy obiektów nienajgorsze, całkiem logiczne, choć szału nie ma, nie ma takiej wygodnej automatyzacji jak w .NET czy bezobsługowości jak w Java Script
- Podpięcie pobranych z sieci danych do TableView to jednak XIX wiek, przynajmniej to, z czym się zaznajomiłem. Ten kod zadziałał także w aplikacji targetowanej pod iOS 8.1, może jednak powstało coś nowocześniejszego? A może nie jest najgorzej, parę metod do oprogramowania by zasilić pola w komórkach, referencja na TableView, trochę zabawy z łączeniem TableView strzałkami ? Nieeeeee, w XAML mam kontrolkę, binduję do kolekcji, ewentualnie definiuję łatwo dowolny szablon elementu i tyle, w Angularze jest jeszcze prościej, nie ma nawet często kontrolki i nikt się bawi w rzutowanie i opisywanie obiektów o jakichś typach…
- Możliwość dość prostego definiowania animacji przybliża nas do świata współczesnego
Moje notatki poniżej.
Intro
Najnowszy XCode 6.1.1 (Mac OS X 10.10)
łączenie UI z kodem: zaznaczamy element + CTRL + przeciągamy z designera na interfejs w kodzie…
Use Automatic Reference Counting
“nib” - plik xib, “NextStep Interface Builder”, NS (od “NextStep”) w Objective-C
XCode:
- Można przeciągnąć połączenie od przycisku w designerze do kodu z interfejsem. W pojawiającym się oknie wybieramy Action i wpisujemy nazwę.
- Można przeciągnąć labelkę z designera do kodu. Zostawiamy outlet i podajemy nazwę.
#import <UIKit/UIKit.h>
@interface XViewController: UIViewController
@property (retain, nonatomic) IBOutlet UILabel *xLabel;
- (IBAction) didTapButton: (id) sender;
@end
@implementation XViewController
@synthesize xLabel;
- (void) viewDidLoad
{
[super viewDidLoad];
}
- (IBAction) didTapButton: (id) sender {
xLabel.text = @”xxx”;
[UIView animateWithDuration: 1.0 animations: ^{
self.view.backgroundColor = [UIColor redColor];
xLabel.frame = CGRectMake(0, 0, 200, 50);
}];
}
@end
Stringi
%@ - taka podmiana w tekście zadziała dla każdego typu wymuszając jego konwersję do string
NSString *str = @”Kazik”;
str = [str stringByReplacingOccurrencesOfString: @“K” withString: @”G” ];
[str stringByReplacingCharactersInRange: NSMakeRange(0,1) withString: @”G”];
[str uppercaseString];
[str capitalizedString];
NSString *str = @”Polski mam paszport na sercu”;
NSRange range = [str rangeOfString: @”paszport”];
if (range.length > 0) {
}
NSString *subStr = [str substringFromIndex:7];
[str substringWithRange: NSMakeRange(12, 20)];
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern: @”Polski mam ( .*)$”
options: NSRegularExpressionCaseInsensitive error: nil];
NSArray *matches = [regex matchesInString: str options:0 range: NSMakeRange(0, [str.length])]
for (NSTextCheckingResult *result in matches) {
NSLog(@”%@”, [str substringWithRange: [result rangeAtIndex:1]]);
}
Liczby
NSNumber *x = [NSNumber numberWithInt:5];
[x intValue]
NSInteger - makro, w zależności od platformy odpowiada int lub long
CGFloat - float lub double
Daty
NSDate *today = [NSDate date];
NSDate *tomorrow = [today dateByAddingTimeInterval: 60 * 60 * 24];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat: @”dd/mm/yyyy”];
NSDate *date1 = [formatter dateFromString: @”06/12/2014”];
[formatter setDateStyle: NSDateFormatterLongStyle];
[formatter setTimeZone: [NSTimeZone localTimeZone ]];
NSLog(@”%@”, [formatter stringFromDate: today];
Własne rozszerzenia do zdefiniowanych już interfejsów (coś jak method extensions):
@interface NSDate (InaDiffrentStylee)
+ (NSDate *) tomorrow;
@end
@implementation NSDate (InaDifferentStylee)
+ (NSDate *) tomorrow {
}
@end
Użycie:
[NSDate tomorrow]
Pętle i tablice
NSArray *songs = [NSArray arrayWithObjects: @”Młody Junak”, @”Miś”, @”Czerwony autobus”, nil];
for (int i = 0; i < [songs count]; i++) {
NSLog(@”%@”, [songs objectAtIndex: i]);
};
for (NSString *song in songs) {
int idx = [songs indexOfObject: song];
};
[songs enumerateObjectsUsingBlock: ^(id obj, NSUInteger idx, BOOL *stop) {
} ];
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:
@ “Zuch”, @“Doktor Yry”, nil ];
for (NSString *key in [dictionary allKeys]) {
NSLog(@”%@ : %@”, key, [dictionary objectForKey: key]);
}
[NSNull null]
NSSet *albums = [NSSet setWithObjects: @”Melassa”, @“Hurra”, @“Prosto”, nil];
NSString *album = [albums anyObject];
for (NSString *album in albums) {
}
Zapytania i dane
XCode: Build phases
libsqlite3.dylib - dodajemy bibliotekę do linkowania
#import “FMDatabase.h” //wrapper Objective-C na sqllite
@implementation XViewController
- (void) viewDidLoad
{
[super viewDidLoad];
NSString *path = [[NSBundle mainBundle] pathForResource: @”baza” ofType: @”db” ];
FMDatabase *db = [FMDatabase databaseWithPath: path];
@try {
if (![db open])
return;
[db executeUpdate: @”INSERT into albums(id,name) VALUES(?,?)” withArgumentsInArray:
[NSArray arrayWithObjects: [NSNumber numberWithInt:1], @”Hurra”, nil ] ];
FMResultSet *result = [db executeQuery: @”select * from albums”];
while ([result next]) {
NSLog(@”%@”, [result stringForColumn: @”name”]);
}
}@catch(NSException *ex) {
}@finally {
[db close];
}
}
Na fizycznym urządzeniu należy dodatkowo w didFinishLaunchingWithOptions (AppDelegate.m), przed innymi operacjami:
NSString *directory = nil;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
directory = [paths objectAtIndex:0];
NSString *path = [directory stringByAppendingPathComponent: @”baza.db”];
if (! [[NSFileManager defaultManager] fileExistsAtPath: path]) {
NSString *sourcePath = [[NSBundle mainBundle] pathForResource: @”baza” ofType: @”db”];
[[NSFileManager defaultManager] copyItemAtPath: sourcePath toPath: path error:nil];
}
+ w widoku zaczytywanie ze ścieżki z katalogu dokumentów
Zdalne dane w JSON
@implementation XViewController
- (void) showMessage: (NSString *)message {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle: @”” message: message
delegate:nil cancelButtonTitle: @”OK” otherButtonTitles:nil];
[alert show];
}
- (void) viewDidLoad
{
[super viewDidLoad];
NSURL *url = [NSURL URLWithString: @”http://localhost/XXX/api/albums”];
NSURLRequest *req = [NSURLRequest requestWithURL: url ];
NSError *err = nil;
NSURLResponse *response = nil;
//synchronicznie
// NSData *data = [NSRLConnection sendSynchronousRequest: req returningResponse: &response error: &err ];
// NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
// NSLog(@”%d”, [httpResponse statusCode]);
//asynchronicznie
[NSURLConnection sendAsynchronousRequest: req queue: [NSOperationQueue mainQueue]
completionHandler: ^(NSURLResponse *response, NSData *data, NSError *err) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
// NSString *str = [[NSString alloc] initWithData:data encoding: NSUTF8StringEncoding];
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil ];
// NSLog(@”%d”, [httpResponse statusCode]);
[self showMessage: [dict objectForKey: @”title”] ];
}];
}
@end
Parsowanie JSON do kolekcji obiektów do wyświetlenia w TableView:
@interface MKAlbum: NSObject
- (id) initWithDictionary: (NSDictionary *) dictionary;
@property (nonatomic, copy) NSString *title;
@end
@implementation MKAlbum
@synthesize title = _title;
- (id) initWithDictionary: (NSDictionary *)dictionary {
self = [super init];
if (self) {
_title = [dictionary objectForKey: @”title”];
}
return self;
}
@end
Widok TableView
@interface XViewController: UIViewController <UITableViewDelegate, UITableViewDataSource>
@property (nonatomic, strong) IBOutlet UITableView *tableView;
@end
@interface XViewController ()
@property (nonatomic, strong) NSArray *albums;
@end
@implementation XViewController
@synthesize albums = _albums;
@synthesize tableView = _tableView;
- (void) viewDidLoad
{
[super viewDidLoad];
[self loadData];
}
…
- (NSInteger)tableView: (UITableView *) tableView numberOfRowsInSection: (NSInteger) section {
return self.albums.count;
}
- (UITableViewCell *) tableView: (UITableView *) tableView cellForRowAtIndexPath: (NSIndexPath *) indexPath {
static NSString *cellIdentifier = @”cell”;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: cellIdentifier];
if (!cell) { //podobnie jak w Javascript null jest false
cell = [[UITableViewCell alloc] initWithStyle: UITableViewCellStyleSubtitle reuseIdentifier: cellIdentifier];
}
MKAlbum *album = [self.albums objectAtIndex: indexPath.row];
cell.textLabel.text = album.title;
cell.detailTextLabel.text = @”ddd”;
return cell;
}
- (void) loadData {
}
- (void) parseResponse: (NSData *) jsonData {
NSError *error = nil;
NSDictionary *dictionary = [NSJSONSerialization JSONObjectWihData: jsonData options:NSJSONReadingAllowFragments error:&error];
NSMutableArray *albums = [NSMutableArray array];
for (NSDictionary *albumDictionary in [dictionary objectForKey: @”albums”]) {
MKAlbum *album = [[MKAlbum alloc] initWithDictionary: albumDictionary];
[albums addObject: album];
}
self.albums = albums;
[self.tableView reloadData];
}
@end
Łączymy TableView połączeniami w designerze z View Controller Scene w XCode (datasource i delegate). Przez przeciąganie łączymy też referencję do TableView z interfejsem.
Brak komentarzy:
Prześlij komentarz