iPhone programozás alapjai II. Gyakorlat
1
A mai gyakorlat témái • I. Modell szétválasztás • Modell logika osztályainak létrehozásának módjai
• Szakácsköny model kialakítása • II. Hálózat kezelés • Hálózat kezelés típusai • ASI HTTP
2
I. MVC Architektúra 3
A hierarchia alapjai • A nézetek egymásra rétegződnek • A nézetek szülő - gyermek kapcsolatban állnak egymással • Nézetet és controllereket létrehozhatunk IB-ben, vagy
programatikusan, a controllerek implementációját el kell készíteni
4
Az aktív controller • Mindig csak egy controller aktív • Többféle módon tudunk váltani az aktív kontrollerek, vagy nézetek között
• addSubview - hozzáadja és megjeleníti a nézetet az aktuális nézethez, de a szülő nézet controllere marad aktív.
• [UIView presentModalViewController...] - modálisan
megjeleníti a kiválasztott nézetet, aktivizálja a controllerét, és beállítja parentControllernek az előzőleg aktuális kontrollert.
• pushViewController - NavigationController esetén ezzel a
metódussal tudunk a stackhez hozzáadni egy új nézetet, ez lesz az aktív controller, és a szülője a navigation controller példánya lesz. Ilyenkor elérhető a navigationController, vagy tabBarController property az adott controllerben.
5
A projekt struktúrálása •
Nincsenek package-k
•
Ún. groupokat hozhatunk létre (forrás könyvtárak)
•
Érdemes elkülöníteni a model és controller osztályokat (esetleg view-t is - programozott nézetek)
• •
XIB-ek általában a Resources könyvtárban vannak
Húzzuk át a controller osztályokat a Controller könyvtár alá
6
A singleton minta •
Objective-C-ben is gyakran használt pattern
•
Igen jó minta a program funkcionális részeinek szeparálásához
•
Modell funkciókra hasznos, mert így a legtöbb controllerből könnyen lehet kezelni az adatokat
•
Példák:
•
Működés főbb moduljainak megvalósítása
•
Hálózati kapcsolatért felelős osztályok
•
Utility osztályok
7
Singleton példa @interface MySingleton : NSObject { } + (MySingleton*) getInstance; @end
static MySingleton *instance = nil; +(MySingleton*)getInstance{ ! if (instance==NULL){ ! ! @synchronized([UIApplication sharedApplication]){ ! ! ! if (instance==NULL) instance=[[MySingleton alloc] init]; ! ! } ! } ! return instance; }
8
Selector • A @selector nem más mint egy módszer egy metódus kiválasztására.
! SEL aSelector = @selector(run); ! [anObject performSelector:aSelector];
• A selectoroknak fontos szerepe van ha több szálon dolgozunk
• A GUI-t nem szabad külső szálról módosítani // GUI módosítás beütemezése a fő szálba [handler performSelectorOnMainThread:@selector(messageArrived:) withObject:msg waitUntilDone:YES]; // Új szál indítása ha a szál implementációja az aktuális osztály [NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];
9
Folytassuk a szakácskönyvet! • Hozzunk létre egy CookBookManager singleton osztályt • Készítsük el az adatokat reprezentáló modell osztályokat: • Recipe • Készítsünk metódusokat a CookBookManager osztályba a következő feladatokra:
• Ajánlatok • Kategóriák lekérdezése • Kategóriához tartozó receptek lekérdezése • Kedvencek lekérdezése • Hozzáadás a kedvencekhez
10
Recipe Osztály Készítsünk egy új osztályt (Recipe), amely egy recept tárolására szolgál (Recipe.h) @interface Recipe : NSObject { ! NSString* title; ! NSString* subtitle; ! NSString* description; ! NSString* complexity; } - (id) initWithTitle:(NSString*)_title andSubtitle:(NSString*) _subtitle andDescription:(NSString*)_description andComplexity: (NSString*)_complexity; @property @property @property @property
(nonatomic, (nonatomic, (nonatomic, (nonatomic,
retain) retain) retain) retain)
NSString* NSString* NSString* NSString*
title; subtitle; description; complexity;
@end
11
Recipe Osztály Recipe.m (végéről ne felejtsük el a dealloc-ot!) @implementation Recipe @synthesize title, subtitle, description, complexity; - (id) initWithTitle:(NSString*)_title andSubtitle:(NSString*) _subtitle andDescription:(NSString*)_description andComplexity: (NSString*)_complexity; { ! self = [super init]; ! if(self != nil) { ! ! self.title = _title; ! ! self.subtitle = _subtitle; ! ! self.description = _description; ! ! self.complexity = _complexity; ! } ! return self;! } @end
12
Adattárolás a memóriában • Adatok (objektumok) tárolására használjuk valamelyiket az alábbiak közül:
• NSArray - tömb, mérete és tartalma a létrehozáskor eldől
• NSMutableArray - változó tartalmú és méretű tömb • NSDictionary - kulcs - érték párokat tartalmazó tároló •
NSMutableDictionary - előzőhöz hasonló, változó méretű és tartalmú tároló
13
CookBookManager Osztály Készítsünk egy új osztályt (CookBookManager), amely kezeli a receptekkel kapcsolatos műveleteket, és tárolja az adatokat (CookBookManager.h) @interface CookBookManager : NSObject { ! ! NSMutableArray* hot; ! NSMutableArray* favorites; ! NSMutableArray* categories; ! NSMutableDictionary* recipesByCategories; } + (CookBookManager*) getInstance; -
(NSArray*) (NSArray*) (NSArray*) (NSArray*)
getHot; getCategories; getRecipesByCategory: (NSString*) category; getFavourites;
@end
14
CookBookManager Osztály CookBookManager.m Singleton minta kezelése static CookBookManager *instance = nil;
+ (CookBookManager*) getInstance { if (instance==NULL){ @synchronized([UIApplication sharedApplication]){ if (instance==NULL) instance=[[CookBookManager alloc] init]; } } return instance; }
15
CookBookManager CookBookManager.m Konstruktor ! ! ! ! ! ! ! ! ! ! !
(id) init { self = [super init]; if(self) { ! hot = [[NSMutableArray alloc] initWithCapacity:5]; ! favorites = [[NSMutableArray alloc] initWithCapacity:5]; ! categories = [[NSMutableArray alloc] initWithCapacity:5]; ! recipesByCategories = [[NSMutableDictionary alloc] init]; ! ! [categories addObject:@"Levesek"]; ! [categories addObject:@"Előtelek"]; ! [categories addObject:@"Húsételek"];
! ! Recipe* r = [[Recipe alloc] initWithTitle:@"Bableves" andSubtitle:@"Mari néni receptje alapján" andDescription:@"Leírás" andComplexity:@"30 perc"]; Recipe* r2 = [[Recipe alloc] initWithTitle:@"Pulyka" andSubtitle:@"Rózsi néni receptje alapján" andDescription:@"Leírás" andComplexity:@"45 perc"]; . . .
16
CookBookManager CookBookManager.m Konstruktor . . . ! ! NSMutableArray* soups = [[NSMutableArray alloc] initWithCapacity:5]; ! ! [soups addObject:r]; ! ! NSMutableArray* meats = [[NSMutableArray alloc] initWithCapacity:5]; ! ! [meats addObject:r2]; ! ! ! ! [recipesByCategories setValue:soups forKey:@"Levesek"]; ! ! [recipesByCategories setValue:meats forKey:@"Húsételek"]; ! ! [favorites addObject:r2]; ! ! [hot addObject:r]; ! } ! return self; }
17
CookBookManager CookBookManager.m Lekérdező metódusok
! } ! }
(NSArray*) getHot { return hot; (NSArray*) getCategories { return categories;
- (NSArray*) getRecipesByCategory: (NSString*) category { ! return [recipesByCategories objectForKey:category]; } - (NSArray*) getFavourites { ! return favorites; }
18
Hozzáférés az adatokhoz • Minden controllerben használjuk a singleton CookBookManagert • Táblázat feltöltéséhez a UITableView néhány metódusát kell implementálni:
• numberOfRowsInSection - megadja hány sort kell a táblázatnak megjeleníteni
• cellForRowAtIndexPath - az adott cella indexre visszaad egy
cella nézetet, ami lehet bármi, akár egy tetszőleges saját nézet implementáció
• Töltsük fel minden listában a cellák adatait a megfelelő tartalommal • Használjuk egyelőre a manager osztályban definiált mock tartalmat
19
CategoryViewController Kiegészítjük a múlt órán megírt kontrollert a modell rész használatával:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { ! return [[[CookBookManager getInstance] getCategories] count]; }
20
CategoryViewController Kiegészítjük a múlt órán megírt kontrollert a modell rész használatával: - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; ! ! !
cell.imageView.image=[UIImage imageNamed:@"category.gif"]; cell.accessoryType=UITableViewCellAccessoryDetailDisclosureButton; }
cell.textLabel.text = [[[CookBookManager getInstance] getCategories] objectAtIndex:indexPath.row]; return cell; }
21
Receptlista • A fentiek elkészülte után most már megjelennek a kategóriák, úgy ahogy a modell részben meghatározásra kerültek.
• Csináljuk meg ugyanazt a recept listával is! • Ez már egy kicsit összetettebb hiszen a receptlista van mind az ajánlatoknál, mind a kategóriákon belül.
• Ezért itt felveszünk egy @propertyt, ami azt tárolja van e
kiválasztott kategória. Ha nincs akkor az ajánlatokat jelenítjük meg.
• Azért nem a konstruktort írjuk át, mert a tabokat nem mi példányosítjuk kódból.
22
RecipeListViewController Felvesszük a .h fájlba a propertyt és a segéd változót:
@interface RecipeListViewController : UITableViewController { ! NSString* selectedCategory; NSArray* recipes; } @property (nonatomic, retain) NSString* selectedCategory; @property (nonatomic, retain) NSArray* recipes;; @end
23
RecipeListViewController Az .m fájlban megjelenéskor lekérjük az új listát, majd felhasználjuk: - (void)viewWillAppear:(BOOL)animated { if (self.selectedCategory!=nil){ self.recipes=[[CookBookManager getInstance] getRecipesByCategory:self.selectedCategory]; } else { self.recipes=[[CookBookManager getInstance] getHot]; } } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { ! if(self.recipes!=nil) { ! ! return [recipes count]; ! } ! else return 0; }
24
RecipeListViewController // Customize the appearance of table view cells. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease]; ! ! cell.imageView.image=[UIImage imageNamed:@"recipe.jpeg"]; ! ! cell.accessoryType=UITableViewCellAccessoryDetailDisclosureButton; } ! ! if(self.recipes != nil) { ! ! Recipe* r = [recipes objectAtIndex:indexPath.row]; ! ! cell.textLabel.text = r.title; ! ! cell.detailTextLabel.text= r.subtitle; ! }! return cell; }
25
RecipeViewController Felvesszük a .h fájlban a recept elemeit (ne felejstük el IB-ben bekötni): @interface RecipeViewController : UIViewController { ! ! ! ! ! }
UILabel* recipeTitle; UILabel* recipeSubtitle; UILabel* complexity; UITextView* description; Recipe* recipe;!
@property @property @property @property @property
(nonatomic,retain) (nonatomic,retain) (nonatomic,retain) (nonatomic,retain) (nonatomic,retain)
IBOutlet UILabel* recipeTitle; IBOutlet UILabel* recipeSubtitle; IBOutlet UILabel* complexity; IBOutlet UITextView* description; Recipe* recipe;
@end
26
RecipeViewController Az .m fájlban megjelenéskor betöltjük a mezőket - (void)viewWillAppear:(BOOL)animated { ! ! ! ! ! ! ! ! ! ! }
if(self.recipe != nil){ ! self.title = self.recipe.title; ! self.recipeTitle.text = self.recipe.title; ! self.recipeSubtitle.text = self.recipe.subtitle; ! self.complexity.text = self.recipe.complexity; ! self.description.text = self.recipe.description; } [super viewWillAppear:animated];
27
Kattintások kezelése
• Mind a kategóriák, mind a receptlista, mind a receptek már képesek megjelenni, de még nem készítettük el, hogy egy sorra való kattintáskor működjön is.
• Lényegében a didSelectRowAtIndexPath hívást kell bővítenünk. • Eddigi ismereteink alapján ezt próbáljuk meg önállóan megcsinálni!
28
CategoryViewController Megfelelően felparaméterezzük a receptlistát: - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath: (NSIndexPath *)indexPath { ! ! ! RecipeListViewController *recipeListViewController = [[RecipeListViewController alloc] initWithNibName:@"RecipeList" bundle:nil]; ! ! NSString* category = [[[CookBookManager getInstance] getCategories] objectAtIndex:indexPath.row]; ! ! recipeListViewController.title = category; ! recipeListViewController.selectedCategory = category; ! ! [self.navigationController pushViewController:recipeListViewController animated:YES]; ! [recipeListViewController release]; }
29
RecipeListViewController Megfelelően felparaméterezzük a recept megjelenítőt: - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath: (NSIndexPath *)indexPath { ! RecipeViewController *recipeViewController = [[RecipeViewController alloc] initWithNibName:@"Recipe" bundle:nil]; ! recipeViewController.recipe = [recipes objectAtIndex:indexPath.row]; ! [self.navigationController pushViewController:recipeViewController animated:YES]; ! [recipeViewController release]; }
30
Hol tartunk most?
31
II. Hálózatkezelés 32
Iphone a hálózaton • Az Iphone része az IP hálózatnak amennyiben • A felhasználónak van internethasználat engedélyezve a
mobilszolgáltatónál (ez csak mobilhálózatra vonatkozik)
• Van megfelelő mobilhálózat • Van elérhető Wifi a környezetben Mobile Network, 3G, Edge, etc... Wireless LAN
Ip address is granted by the current network provider
33
Áttekintés • • • • •
BSD Sockets, OpenSSL WebKit, CFNetwork Bonjour - iTunes, iChat, printers, music sharing Peer2Peer - GameKit, Bluetooth, Bonjour ASIHTTPRequest - Third party library, POST
34
UIWebView • Internetes oldalak egyszerű beépítése tetszőleges helyre • WebView elérhető az InterfaceBuilderben • Támogatott formátumok a HTML-en kívül: Excel, Keynote, Numbers, Pages, PDF, Powerpoint, Word, MHTML
• MS Office documentumok Word 97 formátumban működnek csak
• IPhone OS 3.0: RTF, Keynote, Number, Pages ’09 verziók
35
NsUrlConnection • Különböző protokollokat támogat • Szinkron és aszinkron módon is tud működni, alapértelmezett üzemmód aszinkron
• A kérés kiszolgálásának folyamatát lehet követni, file fel/ letöltés hol tart, becslés számítása, stb.
• Feladatai: autentikáció, protokoll implementáció, cacheelés, cookie-k
36
ASIHTTPRequest • NsUrlConnection körülményes • Open source könyvtár • Aszinkron és szinkron módon is tud működni • HTTP post és file feltöltést egyszerűvé teszi NSURL *url = [NSURL URLWithString:@"http://www.ponte.hu"]; ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url]; [request startSynchronous]; NSError *error = [request error]; if (error) { ! NSLog(@"Error connecting... %@", error); } else { ! NSLog(@"Response arrived: %@", [request responseString]); }
http://allseeing-i.com/ASIHTTPRequest/
37
JSON • Javascript Object Notation • Általános eszköz a webes technológiákban • Tömörebb, hatékonyabb, mint az XML • Jól használható Iphone alkalmazásokban, ha egy
webes alkalmazáshoz kell kapcsolódni, könnyű az integráció { "firstName": "John", "lastName": "Smith", "age": 25, "address":{ "streetAddress": "21 2nd Street", "city": "New York", "state": "NY", "postalCode": "10021" }, "phoneNumber":[{"type": "home","number": "212 555-1234"}, {"type": "fax", "number": "646 555-4567"}] }
38
JSON értelmezése • •
JSON Api kiegészíti az NSString interface-t
•
Visszaadhat NSArray, NSDictionary a választól függően
JSON feldolgozása innentől az NSString osztály JSONValue metódusa lesz
NSArray* recipesJson = [[request responseString] JSONValue]; NSMutableArray* recipesList = [NSMutableArray arrayWithCapacity: [recipesJson count]]; for (NSDictionary* dict in recipesJson) { ! Recipe* r = [Recipe recipeWithDictionary:dict]; ! [recipesList addObject:r]; }
39
Külső komponensek http://ponte.hu/oktatas/utils.zip
• •
Húzzuk be a Classes alá a utils könyvtárat
•
Adjuk hozzá a projekthez a következő keretrendszereket:
Mindenképpen másoljuk be az állományokat a projekt alá
• • • •
CFNetwork
•
libz.1.2.3.dylib
CoreGraphics MobileCoreFramework SystemConfiguration
40
Folytassuk a szakácskönyvet! • Egészítsük ki a CookBookManager osztályunkat, hogy az adatokat mostantól a webes szerverünkről töltse le
• Ehhez hozzunk létre modell osztályokat, illetve
egészítsük ki a már meglévőket, hogy a megfelelő adatokat tudjuk tárolni
• Használjuk az ASIHTTP API-t a szerverhívásokhoz • A beérkező JSON választ dolgozzuk fel a JSON API segítségével
• Hozzunk létre objektumokat a JSON adatok alapján 41
Az interface • Három nézetünk van jelenleg, amihez szerver kommunikáció szükséges, ezekhez pedig a következő adatokat kell lekérdeznünk:
• Aktuális ajánlatok • Kategóriák • Egy adot kategóriához tartozó receptek • Tetszőleges recept az azonosítója alapján
42
A szerver •
Az alkalmazás szerver a következő lekérdezéseket támogatja:
http://cookbookserver.appspot.com/gethots http://cookbookserver.appspot.com/getrecipe?recid=%@ http://cookbookserver.appspot.com/getcategories http://cookbookserver.appspot.com/getrecipesbycategory?catid=%@
•
Próbáljuk ki, ha beírjuk a böngészőbe mit kapunk, nézzük meg a forrást is.
43
Kibővítjük a Recipe osztályt • Megjelent az imageURL mező • Készítsünk egy factory metódust! • ami a JSON adatokból létrehoz egy objektum példányt . . NSString* imageURL; } + (id) recipeWithDictionary: (NSDictionary*) dict; - (id) initWithTitle:(NSString*)_title andSubtitle:(NSString*) _subtitle andDescription:(NSString*)_description andComplexity: (NSString*)_complexity andImageURL:(NSString*)_imageURL; @property (nonatomic, retain) NSString* imageURL; . .
44
Kibővítjük a Recipe osztályt • Vezessük át az új mezőt (property, konstruktor, synthesize, dealloc), valamint vegyük fel a factory metódust:
+ { ! ! ! ! ! ! !
(id) recipeWithDictionary: (NSDictionary*) dict Recipe* r = [[[Recipe alloc] init] autorelease]; r.title = [dict objectForKey:@"title"]; r.subtitle = [dict objectForKey:@"subtitle"]; r.description = [dict objectForKey:@"description"]; r.complexity = [dict objectForKey:@"complexity"]; r.imageURL = [dict objectForKey:@"imageurl"];!
! return r; }
45
Category • Létrehozunk egy Category osztályt is: @interface Category : NSObject { ! NSString* catId; ! NSString* name; ! NSString* imageURL; ! } @property (nonatomic, retain) NSString* catId; @property (nonatomic, retain) NSString* name; @property (nonatomic, retain) NSString* imageURL; + (id) categoryWithDictionary: (NSDictionary*) dict; @end
46
Category @implementation Category @synthesize name, catId, imageURL; + { ! ! ! ! !
(id) categoryWithDictionary: (NSDictionary*) dict Category* cat = [[[Category alloc] init] autorelease]; cat.name = [dict objectForKey:@"name"]; cat.catId = [dict objectForKey:@"catid"]; cat.imageURL = [dict objectForKey:@"imageurl"];
! return cat; } -(void)dealloc{ [name release]; [catId release]; [imageURL release]; [super dealloc]; } @end
47
CookBookManager • Kibővítjük a metódusokat hálózati kommunikcióval - (NSArray*) getHotRecipes { ! NSURL *url = [NSURL URLWithString:@"http:// cookbookserver.appspot.com/gethots"]; ! ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url]; ! [request setDefaultResponseEncoding:NSUTF8StringEncoding]; ! [request startSynchronous]; ! NSError *error = [request error]; ! if (error) { ! ! NSLog(@"Error getting hot recipes from server: %@", error); ! ! return nil; ! } else { ! ! NSArray* recipesJson = [[request responseString] JSONValue]; ! ! NSMutableArray* recipesList = [NSMutableArray arrayWithCapacity:[recipesJson count]]; ! ! for (NSDictionary* dict in recipesJson) { ! ! ! Recipe* r = [Recipe recipeWithDictionary:dict]; ! ! ! [recipesList addObject:r]; ! ! } ! ! return recipesList; ! } }
48
CookBookManager - (NSArray*) getCategories { ! NSURL *url = [NSURL URLWithString:@"http:// cookbookserver.appspot.com/getcategories"]; ! ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url]; ! [request setDefaultResponseEncoding:NSUTF8StringEncoding]; ! [request startSynchronous]; ! NSError *error = [request error]; ! if (error) { ! ! NSLog(@"Error getting categories from server: %@", error); ! ! return nil; ! } else { ! ! NSArray* categoriesJSON = [[request responseString] JSONValue]; ! ! NSMutableArray* categoryList = [NSMutableArray arrayWithCapacity:[categoriesJSON count]]; ! ! for (NSDictionary* dict in categoriesJSON) { ! ! ! Category* c = [Category categoryWithDictionary:dict]; ! ! ! [categoryList addObject:c]; ! ! } ! ! return categoryList; ! } }
49
CookBookManager - (NSArray*) getRecipesByCategory: (NSString*) catId { ! NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://cookbookserver.appspot.com/ getrecipesbycategory?catid=%@", catId]]; ! ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url]; ! [request setDefaultResponseEncoding:NSUTF8StringEncoding]; ! [request startSynchronous]; ! NSError *error = [request error]; ! if (error) { ! ! NSLog(@"Error getting recipes for category from server: %@", error); ! ! return nil; ! } else { ! ! NSArray* recipesJson = [[request responseString] JSONValue]; ! ! NSMutableArray* recipesList = [NSMutableArray arrayWithCapacity:[recipesJson count]]; ! ! for (NSDictionary* dict in recipesJson) { ! ! ! Recipe* r = [Recipe recipeWithDictionary:dict]; ! ! ! [recipesList addObject:r]; ! ! } ! ! return recipesList; ! } }
50
CookBookManager - (Recipe*) getRecipeById: (NSString*) recId { ! NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://cookbookserver.appspot.com/getrecipe? recid=%@", recId]]; ! ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url]; ! [request setDefaultResponseEncoding:NSUTF8StringEncoding]; ! [request startSynchronous]; ! NSError *error = [request error]; ! if (error) { ! ! NSLog(@"Error getting recipe from server: %@", error); ! ! return nil; ! } else { ! ! NSDictionary* dict = [[request responseString] JSONValue]; ! ! return [Recipe recipeWithDictionary:dict]; ! } }
51
Controllerek Módosítsuk a kontrollereinket, hogy a plussz adatokat is megjelenítsék (CategoryViewController)! - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { . . . Category* category=[[[CookBookManager getInstance] getCategories] objectAtIndex:indexPath.row]; ! ! ! !
NSURL *url = [NSURL URLWithString:cat.imageURL]; NSData *data = [NSData dataWithContentsOfURL:url]; UIImage *img = [UIImage imageWithData:data]; cell.imageView.image = img; cell.textLabel.text = cat.name; return cell;
}
52
Controllerek Módosítsuk a kontrollereinket, hogy a plussz adatokat is megjelenítsék (RecipelistViewController)! - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { . . . ! ! Recipe* r = [recipes objectAtIndex:indexPath.row]; ! ! cell.textLabel.text = r.title; ! ! cell.detailTextLabel.text= r.subtitle; ! ! NSURL *url = [NSURL URLWithString:r.imageURL]; ! ! NSData *data = [NSData dataWithContentsOfURL:url]; ! ! UIImage *img = [UIImage imageWithData:data]; ! ! cell.imageView.image = img; ! }! return cell; }
53
A recept • Hozzunk létre egy UIImageView típusú adattagot a recept nézet controllerébe
• Kössük be az Interface Builderben a képhez tartozó elemet is
! ! ! ! ! ! ! ! ! ! ! }
(void)viewWillAppear:(BOOL)animated { if(self.recipe != nil){ ! self.recipeTitle.text = self.recipe.title; ! self.recipeSubtitle.text = self.recipe.subtitle; ! self.timeToMake.text = @"30 perc"; ! self.description.text = self.recipe.description; ! NSURL *url = [NSURL URLWithString:recipe.imageURL]; ! NSData *data = [NSData dataWithContentsOfURL:url]; ! UIImage *img = [UIImage imageWithData:data]; ! self.imageView.image = img; }! [super viewWillAppear:animated];
54
A memória felszabadítása • •
Ne feledjük el felszabadítani a lefoglalt memóriát A nézetekben tárolt listákat végül magunknak kell felszabadítani
• •
Receptlista nézetben a recepteket Recept nézetben a receptet
- (void)dealloc { ! [recipes release]; [super dealloc]; }
55
Próbáljuk ki!
56
Köszönöm a figyelmet!
Sallai Péter
[email protected] 57