SlideShare una empresa de Scribd logo
1 de 74
Descargar para leer sin conexión
Televisió de Catalunya
Formación en movilidad
Conceptos de desarrollo en iOS
4ª sesión mayo 2013
1
Qué veremos hoy
Alert
Search Bar
Action Sheet
Activity
Customizing
Testing
2
Alert
UIAlertView
“Use the UIAlertView class to display an alert
message to the user”
3
Alert
UIAlertView
// MasterViewController.m
- (void)insertNewObject:(NSDictionary *)values
{
// ...
if (![context save:&error])
{
// ...
}
else
{
UIAlertView *notice = [[UIAlertView alloc]
initWithTitle:@"Nuevo vídeo"
message:@"Se a creado un nuevo vídeo correctamente"
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil, nil];
[notice show];
}
4
Alert
UIAlertView
// MasterViewController.m
- (void)insertNewObject:(NSDictionary *)values
{
// ...
if (![context save:&error])
{
// ...
}
else
{
UIAlertView *notice = [[UIAlertView alloc]
initWithTitle:@"Nuevo vídeo"
message:@"Se a creado un nuevo vídeo correctamente"
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil, nil];
[notice show];
}
5
Alert
UIAlertView
// MasterViewController.m
- (void)insertNewObject:(NSDictionary *)values
{
// ...
if (![context save:&error])
{
// ...
}
else
{
UIAlertView *notice = [[UIAlertView alloc]
initWithTitle:@"Nuevo vídeo"
message:@"Se a creado un nuevo vídeo correctamente"
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:@"Deshacer", nil];
[notice show];
}
6
Alert
UIAlertView
// MasterViewController.m
- (void)insertNewObject:(NSDictionary *)values
{
// ...
if (![context save:&error])
{
// ...
}
else
{
UIAlertView *notice = [[UIAlertView alloc]
initWithTitle:@"Nuevo vídeo"
message:@"Se a creado un nuevo vídeo correctamente"
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:@"Deshacer", nil];
[notice show];
}
7
“The UISearchBar object does not actually
perform any searches.You use the
UISearchBarDelegate protocol to implement
the actions when text is entered and buttons
are clicked”
Search Bar
8
Search Bar
UISearchBar
9
Search Bar
UISearchBar
Placeholder ‘Buscar por título o autor’
Marcar ‘Shows Cancel Button’
Seleccionar ‘Correction: No’
Conectar ‘Search Bar’ delegate con ‘MasterView Controller’
10
Search Bar
UISearchBarDelegate
// MasterViewController.m
@interface MasterViewController ()
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath;
- (void)performSearch:(NSString *)searchText;
@end
11
Search Bar
UISearchBarDelegate
// MasterViewController.m
@interface MasterViewController ()
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath;
- (void)performSearch:(NSString *)searchText;
@end
Declarar método privado performSearch:
12
Search Bar
UISearchBarDelegate
// MasterViewController.m
@interface MasterViewController ()
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath;
- (void)performSearch:(NSString *)searchText;
@end
- (NSFetchedResultsController *)fetchedResultsController {
// ...
NSFetchedResultsController *aFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
// ...
}
13
Search Bar
UISearchBarDelegate
// MasterViewController.m
@interface MasterViewController ()
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath;
- (void)performSearch:(NSString *)searchText;
@end
- (NSFetchedResultsController *)fetchedResultsController {
// ...
NSFetchedResultsController *aFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
// ...
}
Anular uso de caché en initWithFetchRequest:
14
Search Bar
UISearchBarDelegate
// MasterViewController.m
#pragma mark - UISearchBarDelegate
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
}
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
}
Acciones para
botones ‘Search’ y ‘Cancel’ e introducción de texto
15
Search Bar
UISearchBarDelegate
// MasterViewController.m
#pragma mark - UISearchBarDelegate
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
[self performSearch:searchBar.text];
[searchBar resignFirstResponder];
[self.tableView reloadData];
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
}
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
}
searchBarSearchButtonClicked:
16
Search Bar
UISearchBarDelegate
// MasterViewController.m
#pragma mark - UISearchBarDelegate
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
[self performSearch:searchBar.text];
[searchBar resignFirstResponder];
[self.tableView reloadData];
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
[searchBar resignFirstResponder];
}
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
}
searchBarCancelButtonClicked:
17
Search Bar
UISearchBarDelegate
// MasterViewController.m
#pragma mark - UISearchBarDelegate
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
[self performSearch:searchBar.text];
[searchBar resignFirstResponder];
[self.tableView reloadData];
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
[searchBar resignFirstResponder];
}
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
[self performSearch:searchText];
}
searchBar:textDidChange:
18
Search Bar
UISearchBarDelegate + Core Data
// MasterViewController.m
#pragma mark - Private
- (void)performSearch:(NSString *)searchText {
NSPredicate *predicate;
NSError *error = nil;
if(searchText && searchText.length > 0) {
predicate = [NSPredicate predicateWithFormat:
@"title contains[cd] %@ or author contains[cd] %@", searchText, searchText];
[self.fetchedResultsController.fetchRequest setPredicate:predicate];
} else {
[self.fetchedResultsController.fetchRequest setPredicate:nil];
}
if(![self.fetchedResultsController performFetch:&error]) {
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
[self.tableView reloadData];
}
Implementar método privado performSearch:
19
Search Bar
UISearchBarDelegate + Core Data
// MasterViewController.m
#pragma mark - Private
- (void)performSearch:(NSString *)searchText {
NSPredicate *predicate;
NSError *error = nil;
if(searchText && searchText.length > 0) {
predicate = [NSPredicate predicateWithFormat:
@"title contains[cd] %@ or author contains[cd] %@", searchText, searchText];
[self.fetchedResultsController.fetchRequest setPredicate:predicate];
} else {
[self.fetchedResultsController.fetchRequest setPredicate:nil];
}
if(![self.fetchedResultsController performFetch:&error]) {
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
[self.tableView reloadData];
}
contains[cd] is case and diacritic insensitive
20
Search Bar
UISearchBarDelegate + Core Data
// MasterViewController.m
#pragma mark - Private
- (void)performSearch:(NSString *)searchText {
NSPredicate *predicate;
NSError *error = nil;
if(searchText && searchText.length > 0) {
predicate = [NSPredicate predicateWithFormat:
@"title contains[cd] %@ or author contains[cd] %@", searchText, searchText];
[self.fetchedResultsController.fetchRequest setPredicate:predicate];
} else {
[self.fetchedResultsController.fetchRequest setPredicate:nil];
}
if(![self.fetchedResultsController performFetch:&error]) {
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
[self.tableView reloadData];
}
Anular criterios = Buscar todos
21
Search Bar
UISearchBarDelegate
22
“Use the UIActionSheet class to present the
user with a set of alternatives for how to
proceed with a given task”
Action Sheet
23
Action Sheet
24
Action Sheet
Seleccionar ‘Identifier:Action’
25
Action Sheet
Seleccionar ‘Identifier:Action’
Conectar ‘Bar Button Item - Action’ con DetailView Controller
Connection:Action
Name: shareByEmail
Type: id
26
Action Sheet
TARGETS : MyVideos : Build Phases : Link With Binary Libraries
MessageUI.framework
27
Action Sheet
UIActionSheet
// DetailViewController.h
#import <UIKit/UIKit.h>
#import <MessageUI/MessageUI.h>
#import <MessageUI/MFMailComposeViewController.h>
@interface DetailViewController : UIViewController <
UISplitViewControllerDelegate, UIWebViewDelegate,
UIActionSheetDelegate, MFMailComposeViewControllerDelegate>
// ...
@end
Importar MessageUI.h y MFMailComposeViewController.h
28
Action Sheet
UIActionSheet
// DetailViewController.h
#import <UIKit/UIKit.h>
#import <MessageUI/MessageUI.h>
#import <MessageUI/MFMailComposeViewController.h>
@interface DetailViewController : UIViewController <
UISplitViewControllerDelegate, UIWebViewDelegate,
UIActionSheetDelegate, MFMailComposeViewControllerDelegate>
// ...
@end
UIActionSheetDelegate
29
// DetailViewController.h
#import <UIKit/UIKit.h>
#import <MessageUI/MessageUI.h>
#import <MessageUI/MFMailComposeViewController.h>
@interface DetailViewController : UIViewController <
UISplitViewControllerDelegate, UIWebViewDelegate,
UIActionSheetDelegate, MFMailComposeViewControllerDelegate>
// ...
@end
Action Sheet
UIActionSheet
MFMailComposeViewControllerDelegate
30
Action Sheet
UIActionSheet
// DetailViewController.m
- (IBAction)shareByEmail:(id)sender {
UIActionSheet *actionSheet = [[UIActionSheet alloc]
initWithTitle:@"Compartir"
delegate:self
cancelButtonTitle:@"Cancelar"
destructiveButtonTitle:nil
otherButtonTitles:@"Email", nil];
[actionSheet showInView:self.view];
}
initWithTitle:
31
Action Sheet
UIActionSheet
showInView:
// DetailViewController.m
- (IBAction)shareByEmail:(id)sender {
UIActionSheet *actionSheet = [[UIActionSheet alloc]
initWithTitle:@"Compartir"
delegate:self
cancelButtonTitle:@"Cancelar"
destructiveButtonTitle:nil
otherButtonTitles:@"Email", nil];
[actionSheet showInView:self.view];
}
32
Action Sheet
UIActionSheetDelegate
// DetailViewController.m
#pragma mark - UIActionSheetDelegate
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
}
@end
Implementar actionSheet:clickedButtonAtIndex:
33
Action Sheet
UIActionSheetDelegate
// DetailViewController.m
#pragma mark - UIActionSheetDelegate
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
if(buttonIndex == 0 && [MFMailComposeViewController canSendMail]) {
NSString *subject = @"Mira este vídeo!";
NSArray *recipients = @[ @"sergi.hernando@mobivery.com" ];
NSString *url = self.webView.request.URL.absoluteString;
NSString *body = [NSString stringWithFormat:@"Échale un ojo: %@", url];
}
}
@end
Parámetros del email
34
Action Sheet
UIActionSheetDelegate
// DetailViewController.m
#pragma mark - UIActionSheetDelegate
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
if(buttonIndex == 0 && [MFMailComposeViewController canSendMail]) {
NSString *subject = @"Mira este vídeo!";
NSArray *recipients = @[ @"sergi.hernando@mobivery.com" ];
NSString *url = self.webView.request.URL.absoluteString;
NSString *body = [NSString stringWithFormat:@"Échale un ojo: %@", url];
MFMailComposeViewController *mailComposer = [[MFMailComposeViewController alloc] init];
}
}
@end
Instanciar mail compose view controller
35
Action Sheet
UIActionSheetDelegate
// DetailViewController.m
#pragma mark - UIActionSheetDelegate
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
if(buttonIndex == 0 && [MFMailComposeViewController canSendMail]) {
NSString *subject = @"Mira este vídeo!";
NSArray *recipients = @[ @"sergi.hernando@mobivery.com" ];
NSString *url = self.webView.request.URL.absoluteString;
NSString *body = [NSString stringWithFormat:@"Échale un ojo: %@", url];
MFMailComposeViewController *mailComposer = [[MFMailComposeViewController alloc] init];
[mailComposer setSubject:subject];
[mailComposer setToRecipients:recipients];
[mailComposer setMessageBody:body isHTML:YES];
[mailComposer setMailComposeDelegate:self];
}
}
@end
Asignar parámetros a mail compose view controller
36
Action Sheet
UIActionSheetDelegate
// DetailViewController.m
#pragma mark - UIActionSheetDelegate
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
if(buttonIndex == 0 && [MFMailComposeViewController canSendMail]) {
NSString *subject = @"Mira este vídeo!";
NSArray *recipients = @[ @"sergi.hernando@mobivery.com" ];
NSString *url = self.webView.request.URL.absoluteString;
NSString *body = [NSString stringWithFormat:@"Échale un ojo: %@", url];
MFMailComposeViewController *mailComposer = [[MFMailComposeViewController alloc] init];
[mailComposer setSubject:subject];
[mailComposer setToRecipients:recipients];
[mailComposer setMessageBody:body isHTML:YES];
[mailComposer setMailComposeDelegate:self];
[self presentViewController:mailComposer animated:YES completion:nil];
}
}
@end
Mostrar mail compose view controller
37
Action Sheet
MFMailComposeViewControllerDelegate
Implementar mailComposeController:didFinishWithResult:error:
#pragma mark - MFMailComposeViewControllerDelegate
- (void)mailComposeController:(MFMailComposeViewController *)controller
didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error
{
}
38
Action Sheet
MFMailComposeViewControllerDelegate
Cerrar mail compose view controller
* [mailComposer setMailComposeDelegate:self];
#pragma mark - MFMailComposeViewControllerDelegate
- (void)mailComposeController:(MFMailComposeViewController *)controller
didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error
{
! [self dismissViewControllerAnimated:YES completion:nil];
}
39
Activity
UIActivityViewController
// DetailViewController.m
- (IBAction)shareByEmail:(id)sender {
}
40
Activity
UIActivityViewController
// DetailViewController.m
- (IBAction)shareByEmail:(id)sender {
NSString *url = self.webView.request.URL.absoluteString;
NSString *body = [NSString stringWithFormat:@"Échale un ojo a este vídeo: %@", url];
}
41
Activity
UIActivityViewController
// DetailViewController.m
- (IBAction)shareByEmail:(id)sender {
NSString *url = self.webView.request.URL.absoluteString;
NSString *body = [NSString stringWithFormat:@"Échale un ojo a este vídeo: %@", url];
UIActivityViewController *activity;
activity = [[UIActivityViewController alloc]
initWithActivityItems:@[body] applicationActivities:nil];
}
42
Activity
UIActivityViewController
// DetailViewController.m
- (IBAction)shareByEmail:(id)sender {
NSString *url = self.webView.request.URL.absoluteString;
NSString *body = [NSString stringWithFormat:@"Échale un ojo a este vídeo: %@", url];
UIActivityViewController *activity;
activity = [[UIActivityViewController alloc]
initWithActivityItems:@[body] applicationActivities:nil];
activity.excludedActivityTypes =
@[UIActivityTypeMessage, UIActivityTypeMail, UIActivityTypePostToWeibo];
}
43
Activity
UIActivityViewController
// DetailViewController.m
- (IBAction)shareByEmail:(id)sender {
NSString *url = self.webView.request.URL.absoluteString;
NSString *body = [NSString stringWithFormat:@"Échale un ojo a este vídeo: %@", url];
UIActivityViewController *activity;
activity = [[UIActivityViewController alloc]
initWithActivityItems:@[body] applicationActivities:nil];
activity.excludedActivityTypes =
@[UIActivityTypeMessage, UIActivityTypeMail, UIActivityTypePostToWeibo];
[self presentViewController:activity animated:YES completion:nil];
}
44
Coffee Break!
45
Customizing
UIAppearance
// AppDelegate.m
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// ...
return YES;
}
application:didFinishLaunchingWithOptions:
46
Customizing
UIAppearance
// AppDelegate.m
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// ...
[[UINavigationBar appearance] setTintColor:[UIColor blackColor]];
return YES;
}
UINavigationBar
47
Customizing
UIAppearance
// AppDelegate.m
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// ...
[[UINavigationBar appearance] setTintColor:[UIColor blackColor]];
[[UIBarButtonItem appearance] setTintColor:[UIColor blackColor]];
return YES;
}
UIBarButtonItem
48
Customizing
UIAppearance
// AppDelegate.m
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// ...
[[UINavigationBar appearance] setTintColor:[UIColor blackColor]];
[[UIBarButtonItem appearance] setTintColor:[UIColor blackColor]];
[[UISearchBar appearance] setTintColor:[UIColor blackColor]];
return YES;
}
UISearchBar
49
Customizing
→
+
Bar Button Item
+
Round Rect Button
50
Customizing
+
Type: Custom
Title: vacío
Image: comun-back-button.png
51
Customizing
→
52
Customizing
53
Customizing
54
Customizing
// VideoCell.h
#import <UIKit/UIKit.h>
@interface VideoCell : UITableViewCell
@property (nonatomic, strong) IBOutlet UILabel *titleLabel;
@property (nonatomic, strong) IBOutlet UILabel *authorLabel;
@end
55
Customizing
TableView Cell - Style: Custom
Custom Class - Class:VideoCell
56
Customizing
57
Customizing
Conectar labels con titleLabel y authorLabel
58
Customizing
// MasterViewController.m
#import "VideoCell.h"
// ...
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
NSManagedObject *object = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = [object valueForKey:@"title"];
}
59
Customizing
// MasterViewController.m
#import "VideoCell.h"
// ...
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
NSManagedObject *object = [self.fetchedResultsController objectAtIndexPath:indexPath];
if([cell isKindOfClass:[VideoCell class]]) {
VideoCell *videoCell = (VideoCell *)cell;
videoCell.titleLabel.text = [object valueForKey:@"title"];
videoCell.authorLabel.text = [object valueForKey:@"author"];
} else {
cell.textLabel.text = [object valueForKey:@"title"];
}
}
60
Testing
OCUnit
61
Testing
OCUnit
validate:
“Devuelve ‘cierto’ si título, autor y URL están
informados
y URL tiene el formato correcto.
Devuelve ‘falso’ en caso contrario.”
62
Testing
OCUnit
// MyVideosTests.m
- (void)testValidateMandatoryFields {
}
63
Testing
OCUnit
// MyVideosTests.m
- (void)testValidateMandatoryFields {
MasterViewController *viewController = [MasterViewController new];
NSDictionary *values = @{
@"title": @"Murmuration",
@"author": @"Islands & Rivers",
@"url": @"http://vimeo.com/m/31158841"
};
}
64
Testing
OCUnit
// MyVideosTests.m
- (void)testValidateMandatoryFields {
MasterViewController *viewController = [MasterViewController new];
NSDictionary *values = @{
@"title": @"Murmuration",
@"author": @"Islands & Rivers",
@"url": @"http://vimeo.com/m/31158841"
};
BOOL result = [viewController validate:values];
}
65
Testing
OCUnit
// MyVideosTests.m
- (void)testValidateMandatoryFields {
MasterViewController *viewController = [MasterViewController new];
NSDictionary *values = @{
@"title": @"Murmuration",
@"author": @"Islands & Rivers",
@"url": @"http://vimeo.com/m/31158841"
};
BOOL result = [viewController validate:values];
!
STAssertTrue(result, @"validate: returned false");
}
66
Testing
OCUnit
// MyVideosTests.m
- (void)testValidateMandatoryFields {
MasterViewController *viewController = [MasterViewController new];
NSDictionary *values = @{
@"title": @"Murmuration",
@"author": @"Islands & Rivers",
@"url": @"http://vimeo.com/m/31158841"
};
BOOL result = [viewController validate:values];
!
STAssertTrue(result, @"validate: returned false");
}
“result” should be true. validate: returned false
67
Testing
OCUnit
// MyVideosTests.m
- (void)testValidateMandatoryFields {
MasterViewController *viewController = [MasterViewController new];
NSDictionary *values = @{
@"title": @"Murmuration",
@"author": @"Islands & Rivers",
@"url": @"http://vimeo.com/m/31158841"
};
BOOL result = [viewController validate:values];
!
STAssertTrue(result, @"validate: returned false");
}
// MasterViewController.m
- (BOOL)validate:(NSDictionary *)values {
! return NO;
}
“result” should be true. validate: returned false
68
Testing
OCUnit
// MyVideosTests.m
- (void)testValidateMandatoryFields {
MasterViewController *viewController = [MasterViewController new];
NSDictionary *values = @{
@"title": @"Murmuration",
@"author": @"Islands & Rivers",
@"url": @"http://vimeo.com/m/31158841"
};
BOOL result = [viewController validate:values];
!
STAssertTrue(result, @"validate: returned false");
}
// MasterViewController.m
- (BOOL)validate:(NSDictionary *)values {
return([values objectForKey:@"title"]
&& [values objectForKey:@"author"]
&& [values objectForKey:@"url"]);
}
69
Testing
OCUnit
// MyVideosTests.m
- (void)testValidateMandatoryFields {
MasterViewController *viewController = [MasterViewController new];
NSDictionary *values = @{
@"title": @"Murmuration",
@"author": @"Islands & Rivers",
@"url": @"http://vimeo.com/m/31158841"
};
BOOL result = [viewController validate:values];
!
STAssertTrue(result, @"validate: returned false");
}
// MasterViewController.m
- (BOOL)validate:(NSDictionary *)values {
return([values objectForKey:@"title"]
&& [values objectForKey:@"author"]
&& [values objectForKey:@"url"]);
}
Test Case ‘-[MyVideosTests testValidateMandatoryFields]’ passed
70
Testing
OCUnit
// MyVideosTests.m
- (void)testValidatetMalformedURL {
MasterViewController *viewController = [MasterViewController new];
NSDictionary *values = @{
@"title": @"Murmuration",
@"author": @"Islands & Rivers",
@"url": @"not an url"
};
BOOL result = [viewController validate:values];
!
STAssertFalse(result, @"validate: returned true");
}
71
Testing
OCUnit
// MyVideosTests.m
- (void)testValidatetMalformedURL {
MasterViewController *viewController = [MasterViewController new];
NSDictionary *values = @{
@"title": @"Murmuration",
@"author": @"Islands & Rivers",
@"url": @"not an url"
};
BOOL result = [viewController validate:values];
!
STAssertFalse(result, @"validate: returned true");
}
“result” should be false. validate: returned true
72
Testing
OCUnit
// MyVideosTests.m
- (void)testValidatetMalformedURL {
MasterViewController *viewController = [MasterViewController new];
NSDictionary *values = @{
@"title": @"Murmuration",
@"author": @"Islands & Rivers",
@"url": @"not an url"
};
BOOL result = [viewController validate:values];
!
STAssertFalse(result, @"validate: returned true");
}
// MasterViewController.m
- (BOOL)validate:(NSDictionary *)values {
return([values objectForKey:@"title"]
&& [values objectForKey:@"author"]
&& [values objectForKey:@"url"]
&& [NSURL URLWithString:[values objectForKey:@"url"]]);
}
73
Testing
OCUnit
// MyVideosTests.m
- (void)testValidatetMalformedURL {
MasterViewController *viewController = [MasterViewController new];
NSDictionary *values = @{
@"title": @"Murmuration",
@"author": @"Islands & Rivers",
@"url": @"not an url"
};
BOOL result = [viewController validate:values];
!
STAssertFalse(result, @"validate: returned true");
}
// MasterViewController.m
- (BOOL)validate:(NSDictionary *)values {
return([values objectForKey:@"title"]
&& [values objectForKey:@"author"]
&& [values objectForKey:@"url"]
&& [NSURL URLWithString:[values objectForKey:@"url"]]);
}
Test Case ‘-[MyVideosTests testValidateMalformedURL]’ passed
74

Más contenido relacionado

La actualidad más candente

Обзор фреймворка Twisted
Обзор фреймворка TwistedОбзор фреймворка Twisted
Обзор фреймворка Twisted
Maxim Kulsha
 
iOS. EventKit Framework. Work with calendars and reminders
iOS. EventKit Framework. Work with calendars and remindersiOS. EventKit Framework. Work with calendars and reminders
iOS. EventKit Framework. Work with calendars and reminders
Voityuk Alexander
 

La actualidad más candente (20)

Node js mongodriver
Node js mongodriverNode js mongodriver
Node js mongodriver
 
Nodejs do teste de unidade ao de integração
Nodejs  do teste de unidade ao de integraçãoNodejs  do teste de unidade ao de integração
Nodejs do teste de unidade ao de integração
 
Component lifecycle hooks in Angular 2.0
Component lifecycle hooks in Angular 2.0Component lifecycle hooks in Angular 2.0
Component lifecycle hooks in Angular 2.0
 
The zen of async: Best practices for best performance
The zen of async: Best practices for best performanceThe zen of async: Best practices for best performance
The zen of async: Best practices for best performance
 
Angular 2.0 Views
Angular 2.0 ViewsAngular 2.0 Views
Angular 2.0 Views
 
“iOS 11 в App in the Air”, Пронин Сергей, App in the Air
“iOS 11 в App in the Air”, Пронин Сергей, App in the Air“iOS 11 в App in the Air”, Пронин Сергей, App in the Air
“iOS 11 в App in the Air”, Пронин Сергей, App in the Air
 
Zabbix LLD from a C Module by Jan-Piet Mens
Zabbix LLD from a C Module by Jan-Piet MensZabbix LLD from a C Module by Jan-Piet Mens
Zabbix LLD from a C Module by Jan-Piet Mens
 
AngularJS Services
AngularJS ServicesAngularJS Services
AngularJS Services
 
Django quickstart
Django quickstartDjango quickstart
Django quickstart
 
Practical JavaScript Programming - Session 1/8
Practical JavaScript Programming - Session 1/8Practical JavaScript Programming - Session 1/8
Practical JavaScript Programming - Session 1/8
 
Jest
JestJest
Jest
 
Silex meets SOAP & REST
Silex meets SOAP & RESTSilex meets SOAP & REST
Silex meets SOAP & REST
 
Database Design Patterns
Database Design PatternsDatabase Design Patterns
Database Design Patterns
 
The History of PHPersistence
The History of PHPersistenceThe History of PHPersistence
The History of PHPersistence
 
Обзор фреймворка Twisted
Обзор фреймворка TwistedОбзор фреймворка Twisted
Обзор фреймворка Twisted
 
Introduction to CQRS and Event Sourcing
Introduction to CQRS and Event SourcingIntroduction to CQRS and Event Sourcing
Introduction to CQRS and Event Sourcing
 
Design Patterns avec PHP 5.3, Symfony et Pimple
Design Patterns avec PHP 5.3, Symfony et PimpleDesign Patterns avec PHP 5.3, Symfony et Pimple
Design Patterns avec PHP 5.3, Symfony et Pimple
 
CQRS and Event Sourcing in a Symfony application
CQRS and Event Sourcing in a Symfony applicationCQRS and Event Sourcing in a Symfony application
CQRS and Event Sourcing in a Symfony application
 
Why Sifu
Why SifuWhy Sifu
Why Sifu
 
iOS. EventKit Framework. Work with calendars and reminders
iOS. EventKit Framework. Work with calendars and remindersiOS. EventKit Framework. Work with calendars and reminders
iOS. EventKit Framework. Work with calendars and reminders
 

Similar a Formacion en movilidad: Conceptos de desarrollo en iOS (IV)

Beginning icloud development - Cesare Rocchi - WhyMCA
Beginning icloud development - Cesare Rocchi - WhyMCABeginning icloud development - Cesare Rocchi - WhyMCA
Beginning icloud development - Cesare Rocchi - WhyMCA
Whymca
 
MobileCity:Core Data
MobileCity:Core DataMobileCity:Core Data
MobileCity:Core Data
Allan Davis
 
Apple Templates Considered Harmful
Apple Templates Considered HarmfulApple Templates Considered Harmful
Apple Templates Considered Harmful
Brian Gesiak
 
iOS for ERREST - alternative version
iOS for ERREST - alternative versioniOS for ERREST - alternative version
iOS for ERREST - alternative version
WO Community
 
Models, controllers and views
Models, controllers and viewsModels, controllers and views
Models, controllers and views
priestc
 

Similar a Formacion en movilidad: Conceptos de desarrollo en iOS (IV) (20)

Beginning icloud development - Cesare Rocchi - WhyMCA
Beginning icloud development - Cesare Rocchi - WhyMCABeginning icloud development - Cesare Rocchi - WhyMCA
Beginning icloud development - Cesare Rocchi - WhyMCA
 
Formacion en movilidad: Conceptos de desarrollo en iOS (III)
Formacion en movilidad: Conceptos de desarrollo en iOS (III) Formacion en movilidad: Conceptos de desarrollo en iOS (III)
Formacion en movilidad: Conceptos de desarrollo en iOS (III)
 
Core Data with multiple managed object contexts
Core Data with multiple managed object contextsCore Data with multiple managed object contexts
Core Data with multiple managed object contexts
 
MobileCity:Core Data
MobileCity:Core DataMobileCity:Core Data
MobileCity:Core Data
 
I os 11
I os 11I os 11
I os 11
 
iOS
iOSiOS
iOS
 
Core Data with Swift 3.0
Core Data with Swift 3.0Core Data with Swift 3.0
Core Data with Swift 3.0
 
I os 04
I os 04I os 04
I os 04
 
Google App Engine Developer - Day3
Google App Engine Developer - Day3Google App Engine Developer - Day3
Google App Engine Developer - Day3
 
Codeigniter : Two Step View - Concept Implementation
Codeigniter : Two Step View - Concept ImplementationCodeigniter : Two Step View - Concept Implementation
Codeigniter : Two Step View - Concept Implementation
 
Taking a Test Drive
Taking a Test DriveTaking a Test Drive
Taking a Test Drive
 
Get the Most Out of iOS 11 with Visual Studio Tools for Xamarin
Get the Most Out of iOS 11 with Visual Studio Tools for XamarinGet the Most Out of iOS 11 with Visual Studio Tools for Xamarin
Get the Most Out of iOS 11 with Visual Studio Tools for Xamarin
 
Apple Templates Considered Harmful
Apple Templates Considered HarmfulApple Templates Considered Harmful
Apple Templates Considered Harmful
 
Writing Maintainable JavaScript
Writing Maintainable JavaScriptWriting Maintainable JavaScript
Writing Maintainable JavaScript
 
iOS for ERREST - alternative version
iOS for ERREST - alternative versioniOS for ERREST - alternative version
iOS for ERREST - alternative version
 
Tricks to Making a Realtime SurfaceView Actually Perform in Realtime - Maarte...
Tricks to Making a Realtime SurfaceView Actually Perform in Realtime - Maarte...Tricks to Making a Realtime SurfaceView Actually Perform in Realtime - Maarte...
Tricks to Making a Realtime SurfaceView Actually Perform in Realtime - Maarte...
 
Protractor framework – how to make stable e2e tests for Angular applications
Protractor framework – how to make stable e2e tests for Angular applicationsProtractor framework – how to make stable e2e tests for Angular applications
Protractor framework – how to make stable e2e tests for Angular applications
 
10 tips for a reusable architecture
10 tips for a reusable architecture10 tips for a reusable architecture
10 tips for a reusable architecture
 
Models, controllers and views
Models, controllers and viewsModels, controllers and views
Models, controllers and views
 
Optimize CollectionView Scrolling
Optimize CollectionView ScrollingOptimize CollectionView Scrolling
Optimize CollectionView Scrolling
 

Más de Mobivery

Hoy es Marketing 2013: El móvil en la cadena de distribución
Hoy es Marketing 2013: El móvil en la cadena de distribuciónHoy es Marketing 2013: El móvil en la cadena de distribución
Hoy es Marketing 2013: El móvil en la cadena de distribución
Mobivery
 
Mobile Health - Nuevos retos y oportunidades. El caso de éxito de iPediatric
Mobile Health - Nuevos retos y oportunidades. El caso de éxito de iPediatricMobile Health - Nuevos retos y oportunidades. El caso de éxito de iPediatric
Mobile Health - Nuevos retos y oportunidades. El caso de éxito de iPediatric
Mobivery
 

Más de Mobivery (20)

Jornada Empresa UOC - Desarrollo de Aplicaciones para Formación en RRHH
Jornada Empresa UOC - Desarrollo de Aplicaciones para Formación en RRHHJornada Empresa UOC - Desarrollo de Aplicaciones para Formación en RRHH
Jornada Empresa UOC - Desarrollo de Aplicaciones para Formación en RRHH
 
Modelo start up: Una forma de encontrar trabajo
Modelo start up: Una forma de encontrar trabajo Modelo start up: Una forma de encontrar trabajo
Modelo start up: Una forma de encontrar trabajo
 
¿Persona o empleado? Algo está cambiando en nuestras empresas
¿Persona o empleado? Algo está cambiando en nuestras empresas¿Persona o empleado? Algo está cambiando en nuestras empresas
¿Persona o empleado? Algo está cambiando en nuestras empresas
 
Móvil y Retail: Cambiando las reglas del juego
Móvil y Retail: Cambiando las reglas del juegoMóvil y Retail: Cambiando las reglas del juego
Móvil y Retail: Cambiando las reglas del juego
 
Presentación de Mobivery en el FET2013
Presentación de Mobivery en el FET2013Presentación de Mobivery en el FET2013
Presentación de Mobivery en el FET2013
 
Curso de formación en Movilidad (Parte III) - Tecnología de Servidor
Curso de formación en Movilidad (Parte III) - Tecnología de ServidorCurso de formación en Movilidad (Parte III) - Tecnología de Servidor
Curso de formación en Movilidad (Parte III) - Tecnología de Servidor
 
Curso de formación en Movilidad (Parte II) - Personalización
Curso de formación en Movilidad (Parte II) - Personalización Curso de formación en Movilidad (Parte II) - Personalización
Curso de formación en Movilidad (Parte II) - Personalización
 
Curso de formación en Movilidad (Parte I) - Mobile Device Management
Curso de formación en Movilidad (Parte I) - Mobile Device ManagementCurso de formación en Movilidad (Parte I) - Mobile Device Management
Curso de formación en Movilidad (Parte I) - Mobile Device Management
 
Formacion en movilidad: Conceptos de desarrollo en iOS (II)
Formacion en movilidad: Conceptos de desarrollo en iOS (II) Formacion en movilidad: Conceptos de desarrollo en iOS (II)
Formacion en movilidad: Conceptos de desarrollo en iOS (II)
 
Formacion en movilidad: Conceptos de desarrollo en iOS (I)
Formacion en movilidad: Conceptos de desarrollo en iOS (I) Formacion en movilidad: Conceptos de desarrollo en iOS (I)
Formacion en movilidad: Conceptos de desarrollo en iOS (I)
 
Formación en movilidad: Conceptos de desarrollo en iOS (V)
Formación en movilidad: Conceptos de desarrollo en iOS (V) Formación en movilidad: Conceptos de desarrollo en iOS (V)
Formación en movilidad: Conceptos de desarrollo en iOS (V)
 
Hoy es Marketing 2013: El móvil en la cadena de distribución
Hoy es Marketing 2013: El móvil en la cadena de distribuciónHoy es Marketing 2013: El móvil en la cadena de distribución
Hoy es Marketing 2013: El móvil en la cadena de distribución
 
Introducción Mobile Apps
Introducción Mobile AppsIntroducción Mobile Apps
Introducción Mobile Apps
 
UrbanSensing - Escuchando la ciudad digital
UrbanSensing  - Escuchando la ciudad digitalUrbanSensing  - Escuchando la ciudad digital
UrbanSensing - Escuchando la ciudad digital
 
Mobile Health - Nuevos retos y oportunidades. El caso de éxito de iPediatric
Mobile Health - Nuevos retos y oportunidades. El caso de éxito de iPediatricMobile Health - Nuevos retos y oportunidades. El caso de éxito de iPediatric
Mobile Health - Nuevos retos y oportunidades. El caso de éxito de iPediatric
 
Marketing de Apps
Marketing de Apps Marketing de Apps
Marketing de Apps
 
Aplicaciones móviles: Usabilidad y Experiencia de Usuario
Aplicaciones móviles: Usabilidad y Experiencia de UsuarioAplicaciones móviles: Usabilidad y Experiencia de Usuario
Aplicaciones móviles: Usabilidad y Experiencia de Usuario
 
Workshop I: Diseño de aplicaciones para iPhone
Workshop I: Diseño de aplicaciones para iPhoneWorkshop I: Diseño de aplicaciones para iPhone
Workshop I: Diseño de aplicaciones para iPhone
 
Cómo monetizar tus apps: Cantidad y Calidad
Cómo monetizar tus apps: Cantidad y CalidadCómo monetizar tus apps: Cantidad y Calidad
Cómo monetizar tus apps: Cantidad y Calidad
 
Formación Aecomo Academy - Monetización de aplicaciones
Formación Aecomo Academy - Monetización de aplicacionesFormación Aecomo Academy - Monetización de aplicaciones
Formación Aecomo Academy - Monetización de aplicaciones
 

Último

CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Service
giselly40
 
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
Earley Information Science
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
vu2urc
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI Solutions
Enterprise Knowledge
 

Último (20)

The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Service
 
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024
 
GenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdfGenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdf
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI Solutions
 

Formacion en movilidad: Conceptos de desarrollo en iOS (IV)

  • 1. Televisió de Catalunya Formación en movilidad Conceptos de desarrollo en iOS 4ª sesión mayo 2013 1
  • 2. Qué veremos hoy Alert Search Bar Action Sheet Activity Customizing Testing 2
  • 3. Alert UIAlertView “Use the UIAlertView class to display an alert message to the user” 3
  • 4. Alert UIAlertView // MasterViewController.m - (void)insertNewObject:(NSDictionary *)values { // ... if (![context save:&error]) { // ... } else { UIAlertView *notice = [[UIAlertView alloc] initWithTitle:@"Nuevo vídeo" message:@"Se a creado un nuevo vídeo correctamente" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil]; [notice show]; } 4
  • 5. Alert UIAlertView // MasterViewController.m - (void)insertNewObject:(NSDictionary *)values { // ... if (![context save:&error]) { // ... } else { UIAlertView *notice = [[UIAlertView alloc] initWithTitle:@"Nuevo vídeo" message:@"Se a creado un nuevo vídeo correctamente" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil]; [notice show]; } 5
  • 6. Alert UIAlertView // MasterViewController.m - (void)insertNewObject:(NSDictionary *)values { // ... if (![context save:&error]) { // ... } else { UIAlertView *notice = [[UIAlertView alloc] initWithTitle:@"Nuevo vídeo" message:@"Se a creado un nuevo vídeo correctamente" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:@"Deshacer", nil]; [notice show]; } 6
  • 7. Alert UIAlertView // MasterViewController.m - (void)insertNewObject:(NSDictionary *)values { // ... if (![context save:&error]) { // ... } else { UIAlertView *notice = [[UIAlertView alloc] initWithTitle:@"Nuevo vídeo" message:@"Se a creado un nuevo vídeo correctamente" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:@"Deshacer", nil]; [notice show]; } 7
  • 8. “The UISearchBar object does not actually perform any searches.You use the UISearchBarDelegate protocol to implement the actions when text is entered and buttons are clicked” Search Bar 8
  • 10. Search Bar UISearchBar Placeholder ‘Buscar por título o autor’ Marcar ‘Shows Cancel Button’ Seleccionar ‘Correction: No’ Conectar ‘Search Bar’ delegate con ‘MasterView Controller’ 10
  • 11. Search Bar UISearchBarDelegate // MasterViewController.m @interface MasterViewController () - (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath; - (void)performSearch:(NSString *)searchText; @end 11
  • 12. Search Bar UISearchBarDelegate // MasterViewController.m @interface MasterViewController () - (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath; - (void)performSearch:(NSString *)searchText; @end Declarar método privado performSearch: 12
  • 13. Search Bar UISearchBarDelegate // MasterViewController.m @interface MasterViewController () - (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath; - (void)performSearch:(NSString *)searchText; @end - (NSFetchedResultsController *)fetchedResultsController { // ... NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil]; // ... } 13
  • 14. Search Bar UISearchBarDelegate // MasterViewController.m @interface MasterViewController () - (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath; - (void)performSearch:(NSString *)searchText; @end - (NSFetchedResultsController *)fetchedResultsController { // ... NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil]; // ... } Anular uso de caché en initWithFetchRequest: 14
  • 15. Search Bar UISearchBarDelegate // MasterViewController.m #pragma mark - UISearchBarDelegate - (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar { } - (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar { } - (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText { } Acciones para botones ‘Search’ y ‘Cancel’ e introducción de texto 15
  • 16. Search Bar UISearchBarDelegate // MasterViewController.m #pragma mark - UISearchBarDelegate - (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar { [self performSearch:searchBar.text]; [searchBar resignFirstResponder]; [self.tableView reloadData]; } - (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar { } - (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText { } searchBarSearchButtonClicked: 16
  • 17. Search Bar UISearchBarDelegate // MasterViewController.m #pragma mark - UISearchBarDelegate - (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar { [self performSearch:searchBar.text]; [searchBar resignFirstResponder]; [self.tableView reloadData]; } - (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar { [searchBar resignFirstResponder]; } - (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText { } searchBarCancelButtonClicked: 17
  • 18. Search Bar UISearchBarDelegate // MasterViewController.m #pragma mark - UISearchBarDelegate - (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar { [self performSearch:searchBar.text]; [searchBar resignFirstResponder]; [self.tableView reloadData]; } - (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar { [searchBar resignFirstResponder]; } - (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText { [self performSearch:searchText]; } searchBar:textDidChange: 18
  • 19. Search Bar UISearchBarDelegate + Core Data // MasterViewController.m #pragma mark - Private - (void)performSearch:(NSString *)searchText { NSPredicate *predicate; NSError *error = nil; if(searchText && searchText.length > 0) { predicate = [NSPredicate predicateWithFormat: @"title contains[cd] %@ or author contains[cd] %@", searchText, searchText]; [self.fetchedResultsController.fetchRequest setPredicate:predicate]; } else { [self.fetchedResultsController.fetchRequest setPredicate:nil]; } if(![self.fetchedResultsController performFetch:&error]) { NSLog(@"Unresolved error %@, %@", error, [error userInfo]); abort(); } [self.tableView reloadData]; } Implementar método privado performSearch: 19
  • 20. Search Bar UISearchBarDelegate + Core Data // MasterViewController.m #pragma mark - Private - (void)performSearch:(NSString *)searchText { NSPredicate *predicate; NSError *error = nil; if(searchText && searchText.length > 0) { predicate = [NSPredicate predicateWithFormat: @"title contains[cd] %@ or author contains[cd] %@", searchText, searchText]; [self.fetchedResultsController.fetchRequest setPredicate:predicate]; } else { [self.fetchedResultsController.fetchRequest setPredicate:nil]; } if(![self.fetchedResultsController performFetch:&error]) { NSLog(@"Unresolved error %@, %@", error, [error userInfo]); abort(); } [self.tableView reloadData]; } contains[cd] is case and diacritic insensitive 20
  • 21. Search Bar UISearchBarDelegate + Core Data // MasterViewController.m #pragma mark - Private - (void)performSearch:(NSString *)searchText { NSPredicate *predicate; NSError *error = nil; if(searchText && searchText.length > 0) { predicate = [NSPredicate predicateWithFormat: @"title contains[cd] %@ or author contains[cd] %@", searchText, searchText]; [self.fetchedResultsController.fetchRequest setPredicate:predicate]; } else { [self.fetchedResultsController.fetchRequest setPredicate:nil]; } if(![self.fetchedResultsController performFetch:&error]) { NSLog(@"Unresolved error %@, %@", error, [error userInfo]); abort(); } [self.tableView reloadData]; } Anular criterios = Buscar todos 21
  • 23. “Use the UIActionSheet class to present the user with a set of alternatives for how to proceed with a given task” Action Sheet 23
  • 26. Action Sheet Seleccionar ‘Identifier:Action’ Conectar ‘Bar Button Item - Action’ con DetailView Controller Connection:Action Name: shareByEmail Type: id 26
  • 27. Action Sheet TARGETS : MyVideos : Build Phases : Link With Binary Libraries MessageUI.framework 27
  • 28. Action Sheet UIActionSheet // DetailViewController.h #import <UIKit/UIKit.h> #import <MessageUI/MessageUI.h> #import <MessageUI/MFMailComposeViewController.h> @interface DetailViewController : UIViewController < UISplitViewControllerDelegate, UIWebViewDelegate, UIActionSheetDelegate, MFMailComposeViewControllerDelegate> // ... @end Importar MessageUI.h y MFMailComposeViewController.h 28
  • 29. Action Sheet UIActionSheet // DetailViewController.h #import <UIKit/UIKit.h> #import <MessageUI/MessageUI.h> #import <MessageUI/MFMailComposeViewController.h> @interface DetailViewController : UIViewController < UISplitViewControllerDelegate, UIWebViewDelegate, UIActionSheetDelegate, MFMailComposeViewControllerDelegate> // ... @end UIActionSheetDelegate 29
  • 30. // DetailViewController.h #import <UIKit/UIKit.h> #import <MessageUI/MessageUI.h> #import <MessageUI/MFMailComposeViewController.h> @interface DetailViewController : UIViewController < UISplitViewControllerDelegate, UIWebViewDelegate, UIActionSheetDelegate, MFMailComposeViewControllerDelegate> // ... @end Action Sheet UIActionSheet MFMailComposeViewControllerDelegate 30
  • 31. Action Sheet UIActionSheet // DetailViewController.m - (IBAction)shareByEmail:(id)sender { UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"Compartir" delegate:self cancelButtonTitle:@"Cancelar" destructiveButtonTitle:nil otherButtonTitles:@"Email", nil]; [actionSheet showInView:self.view]; } initWithTitle: 31
  • 32. Action Sheet UIActionSheet showInView: // DetailViewController.m - (IBAction)shareByEmail:(id)sender { UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"Compartir" delegate:self cancelButtonTitle:@"Cancelar" destructiveButtonTitle:nil otherButtonTitles:@"Email", nil]; [actionSheet showInView:self.view]; } 32
  • 33. Action Sheet UIActionSheetDelegate // DetailViewController.m #pragma mark - UIActionSheetDelegate - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { } @end Implementar actionSheet:clickedButtonAtIndex: 33
  • 34. Action Sheet UIActionSheetDelegate // DetailViewController.m #pragma mark - UIActionSheetDelegate - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { if(buttonIndex == 0 && [MFMailComposeViewController canSendMail]) { NSString *subject = @"Mira este vídeo!"; NSArray *recipients = @[ @"sergi.hernando@mobivery.com" ]; NSString *url = self.webView.request.URL.absoluteString; NSString *body = [NSString stringWithFormat:@"Échale un ojo: %@", url]; } } @end Parámetros del email 34
  • 35. Action Sheet UIActionSheetDelegate // DetailViewController.m #pragma mark - UIActionSheetDelegate - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { if(buttonIndex == 0 && [MFMailComposeViewController canSendMail]) { NSString *subject = @"Mira este vídeo!"; NSArray *recipients = @[ @"sergi.hernando@mobivery.com" ]; NSString *url = self.webView.request.URL.absoluteString; NSString *body = [NSString stringWithFormat:@"Échale un ojo: %@", url]; MFMailComposeViewController *mailComposer = [[MFMailComposeViewController alloc] init]; } } @end Instanciar mail compose view controller 35
  • 36. Action Sheet UIActionSheetDelegate // DetailViewController.m #pragma mark - UIActionSheetDelegate - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { if(buttonIndex == 0 && [MFMailComposeViewController canSendMail]) { NSString *subject = @"Mira este vídeo!"; NSArray *recipients = @[ @"sergi.hernando@mobivery.com" ]; NSString *url = self.webView.request.URL.absoluteString; NSString *body = [NSString stringWithFormat:@"Échale un ojo: %@", url]; MFMailComposeViewController *mailComposer = [[MFMailComposeViewController alloc] init]; [mailComposer setSubject:subject]; [mailComposer setToRecipients:recipients]; [mailComposer setMessageBody:body isHTML:YES]; [mailComposer setMailComposeDelegate:self]; } } @end Asignar parámetros a mail compose view controller 36
  • 37. Action Sheet UIActionSheetDelegate // DetailViewController.m #pragma mark - UIActionSheetDelegate - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { if(buttonIndex == 0 && [MFMailComposeViewController canSendMail]) { NSString *subject = @"Mira este vídeo!"; NSArray *recipients = @[ @"sergi.hernando@mobivery.com" ]; NSString *url = self.webView.request.URL.absoluteString; NSString *body = [NSString stringWithFormat:@"Échale un ojo: %@", url]; MFMailComposeViewController *mailComposer = [[MFMailComposeViewController alloc] init]; [mailComposer setSubject:subject]; [mailComposer setToRecipients:recipients]; [mailComposer setMessageBody:body isHTML:YES]; [mailComposer setMailComposeDelegate:self]; [self presentViewController:mailComposer animated:YES completion:nil]; } } @end Mostrar mail compose view controller 37
  • 38. Action Sheet MFMailComposeViewControllerDelegate Implementar mailComposeController:didFinishWithResult:error: #pragma mark - MFMailComposeViewControllerDelegate - (void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error { } 38
  • 39. Action Sheet MFMailComposeViewControllerDelegate Cerrar mail compose view controller * [mailComposer setMailComposeDelegate:self]; #pragma mark - MFMailComposeViewControllerDelegate - (void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error { ! [self dismissViewControllerAnimated:YES completion:nil]; } 39
  • 41. Activity UIActivityViewController // DetailViewController.m - (IBAction)shareByEmail:(id)sender { NSString *url = self.webView.request.URL.absoluteString; NSString *body = [NSString stringWithFormat:@"Échale un ojo a este vídeo: %@", url]; } 41
  • 42. Activity UIActivityViewController // DetailViewController.m - (IBAction)shareByEmail:(id)sender { NSString *url = self.webView.request.URL.absoluteString; NSString *body = [NSString stringWithFormat:@"Échale un ojo a este vídeo: %@", url]; UIActivityViewController *activity; activity = [[UIActivityViewController alloc] initWithActivityItems:@[body] applicationActivities:nil]; } 42
  • 43. Activity UIActivityViewController // DetailViewController.m - (IBAction)shareByEmail:(id)sender { NSString *url = self.webView.request.URL.absoluteString; NSString *body = [NSString stringWithFormat:@"Échale un ojo a este vídeo: %@", url]; UIActivityViewController *activity; activity = [[UIActivityViewController alloc] initWithActivityItems:@[body] applicationActivities:nil]; activity.excludedActivityTypes = @[UIActivityTypeMessage, UIActivityTypeMail, UIActivityTypePostToWeibo]; } 43
  • 44. Activity UIActivityViewController // DetailViewController.m - (IBAction)shareByEmail:(id)sender { NSString *url = self.webView.request.URL.absoluteString; NSString *body = [NSString stringWithFormat:@"Échale un ojo a este vídeo: %@", url]; UIActivityViewController *activity; activity = [[UIActivityViewController alloc] initWithActivityItems:@[body] applicationActivities:nil]; activity.excludedActivityTypes = @[UIActivityTypeMessage, UIActivityTypeMail, UIActivityTypePostToWeibo]; [self presentViewController:activity animated:YES completion:nil]; } 44
  • 46. Customizing UIAppearance // AppDelegate.m - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // ... return YES; } application:didFinishLaunchingWithOptions: 46
  • 47. Customizing UIAppearance // AppDelegate.m - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // ... [[UINavigationBar appearance] setTintColor:[UIColor blackColor]]; return YES; } UINavigationBar 47
  • 48. Customizing UIAppearance // AppDelegate.m - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // ... [[UINavigationBar appearance] setTintColor:[UIColor blackColor]]; [[UIBarButtonItem appearance] setTintColor:[UIColor blackColor]]; return YES; } UIBarButtonItem 48
  • 49. Customizing UIAppearance // AppDelegate.m - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // ... [[UINavigationBar appearance] setTintColor:[UIColor blackColor]]; [[UIBarButtonItem appearance] setTintColor:[UIColor blackColor]]; [[UISearchBar appearance] setTintColor:[UIColor blackColor]]; return YES; } UISearchBar 49
  • 55. Customizing // VideoCell.h #import <UIKit/UIKit.h> @interface VideoCell : UITableViewCell @property (nonatomic, strong) IBOutlet UILabel *titleLabel; @property (nonatomic, strong) IBOutlet UILabel *authorLabel; @end 55
  • 56. Customizing TableView Cell - Style: Custom Custom Class - Class:VideoCell 56
  • 58. Customizing Conectar labels con titleLabel y authorLabel 58
  • 59. Customizing // MasterViewController.m #import "VideoCell.h" // ... - (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath { NSManagedObject *object = [self.fetchedResultsController objectAtIndexPath:indexPath]; cell.textLabel.text = [object valueForKey:@"title"]; } 59
  • 60. Customizing // MasterViewController.m #import "VideoCell.h" // ... - (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath { NSManagedObject *object = [self.fetchedResultsController objectAtIndexPath:indexPath]; if([cell isKindOfClass:[VideoCell class]]) { VideoCell *videoCell = (VideoCell *)cell; videoCell.titleLabel.text = [object valueForKey:@"title"]; videoCell.authorLabel.text = [object valueForKey:@"author"]; } else { cell.textLabel.text = [object valueForKey:@"title"]; } } 60
  • 62. Testing OCUnit validate: “Devuelve ‘cierto’ si título, autor y URL están informados y URL tiene el formato correcto. Devuelve ‘falso’ en caso contrario.” 62
  • 64. Testing OCUnit // MyVideosTests.m - (void)testValidateMandatoryFields { MasterViewController *viewController = [MasterViewController new]; NSDictionary *values = @{ @"title": @"Murmuration", @"author": @"Islands & Rivers", @"url": @"http://vimeo.com/m/31158841" }; } 64
  • 65. Testing OCUnit // MyVideosTests.m - (void)testValidateMandatoryFields { MasterViewController *viewController = [MasterViewController new]; NSDictionary *values = @{ @"title": @"Murmuration", @"author": @"Islands & Rivers", @"url": @"http://vimeo.com/m/31158841" }; BOOL result = [viewController validate:values]; } 65
  • 66. Testing OCUnit // MyVideosTests.m - (void)testValidateMandatoryFields { MasterViewController *viewController = [MasterViewController new]; NSDictionary *values = @{ @"title": @"Murmuration", @"author": @"Islands & Rivers", @"url": @"http://vimeo.com/m/31158841" }; BOOL result = [viewController validate:values]; ! STAssertTrue(result, @"validate: returned false"); } 66
  • 67. Testing OCUnit // MyVideosTests.m - (void)testValidateMandatoryFields { MasterViewController *viewController = [MasterViewController new]; NSDictionary *values = @{ @"title": @"Murmuration", @"author": @"Islands & Rivers", @"url": @"http://vimeo.com/m/31158841" }; BOOL result = [viewController validate:values]; ! STAssertTrue(result, @"validate: returned false"); } “result” should be true. validate: returned false 67
  • 68. Testing OCUnit // MyVideosTests.m - (void)testValidateMandatoryFields { MasterViewController *viewController = [MasterViewController new]; NSDictionary *values = @{ @"title": @"Murmuration", @"author": @"Islands & Rivers", @"url": @"http://vimeo.com/m/31158841" }; BOOL result = [viewController validate:values]; ! STAssertTrue(result, @"validate: returned false"); } // MasterViewController.m - (BOOL)validate:(NSDictionary *)values { ! return NO; } “result” should be true. validate: returned false 68
  • 69. Testing OCUnit // MyVideosTests.m - (void)testValidateMandatoryFields { MasterViewController *viewController = [MasterViewController new]; NSDictionary *values = @{ @"title": @"Murmuration", @"author": @"Islands & Rivers", @"url": @"http://vimeo.com/m/31158841" }; BOOL result = [viewController validate:values]; ! STAssertTrue(result, @"validate: returned false"); } // MasterViewController.m - (BOOL)validate:(NSDictionary *)values { return([values objectForKey:@"title"] && [values objectForKey:@"author"] && [values objectForKey:@"url"]); } 69
  • 70. Testing OCUnit // MyVideosTests.m - (void)testValidateMandatoryFields { MasterViewController *viewController = [MasterViewController new]; NSDictionary *values = @{ @"title": @"Murmuration", @"author": @"Islands & Rivers", @"url": @"http://vimeo.com/m/31158841" }; BOOL result = [viewController validate:values]; ! STAssertTrue(result, @"validate: returned false"); } // MasterViewController.m - (BOOL)validate:(NSDictionary *)values { return([values objectForKey:@"title"] && [values objectForKey:@"author"] && [values objectForKey:@"url"]); } Test Case ‘-[MyVideosTests testValidateMandatoryFields]’ passed 70
  • 71. Testing OCUnit // MyVideosTests.m - (void)testValidatetMalformedURL { MasterViewController *viewController = [MasterViewController new]; NSDictionary *values = @{ @"title": @"Murmuration", @"author": @"Islands & Rivers", @"url": @"not an url" }; BOOL result = [viewController validate:values]; ! STAssertFalse(result, @"validate: returned true"); } 71
  • 72. Testing OCUnit // MyVideosTests.m - (void)testValidatetMalformedURL { MasterViewController *viewController = [MasterViewController new]; NSDictionary *values = @{ @"title": @"Murmuration", @"author": @"Islands & Rivers", @"url": @"not an url" }; BOOL result = [viewController validate:values]; ! STAssertFalse(result, @"validate: returned true"); } “result” should be false. validate: returned true 72
  • 73. Testing OCUnit // MyVideosTests.m - (void)testValidatetMalformedURL { MasterViewController *viewController = [MasterViewController new]; NSDictionary *values = @{ @"title": @"Murmuration", @"author": @"Islands & Rivers", @"url": @"not an url" }; BOOL result = [viewController validate:values]; ! STAssertFalse(result, @"validate: returned true"); } // MasterViewController.m - (BOOL)validate:(NSDictionary *)values { return([values objectForKey:@"title"] && [values objectForKey:@"author"] && [values objectForKey:@"url"] && [NSURL URLWithString:[values objectForKey:@"url"]]); } 73
  • 74. Testing OCUnit // MyVideosTests.m - (void)testValidatetMalformedURL { MasterViewController *viewController = [MasterViewController new]; NSDictionary *values = @{ @"title": @"Murmuration", @"author": @"Islands & Rivers", @"url": @"not an url" }; BOOL result = [viewController validate:values]; ! STAssertFalse(result, @"validate: returned true"); } // MasterViewController.m - (BOOL)validate:(NSDictionary *)values { return([values objectForKey:@"title"] && [values objectForKey:@"author"] && [values objectForKey:@"url"] && [NSURL URLWithString:[values objectForKey:@"url"]]); } Test Case ‘-[MyVideosTests testValidateMalformedURL]’ passed 74