Статьи

Представляем Three20

Библиотека Objective-C для разработчиков iPhone.

Одна из вещей, которые мне нравятся в iPhone, это то, насколько большинство приложений совместимо с внешним видом ОС, сохраняя при этом свой уникальный характер. Тем не менее, я не понял , как воспроизвести некоторые из более интересных функций, пока не обнаружил библиотеку Three20 .

Three20 — это библиотека Objective-C с открытым исходным кодом, которая первоначально была разработана для приложения Facebook, но с тех пор используется многими другими известными брендами и проектами. Он предоставляет мощные контроллеры представления и является модульным, так что вы можете выбирать, какие элементы включать или создавать расширения.

В этой статье я расскажу о шагах, необходимых для создания простого средства чтения RSS-каналов, в котором реализована замечательная функция « pull-to-reload», присутствующая во многих приложениях iPhone. Мы установим библиотеку Three20, а затем рассмотрим примеры кода, необходимые для этого.

Установка

Установка библиотеки Three20 занимает довольно много времени, но следовать инструкциям просто. У вас должно быть хорошее место для хранения проектов и экспериментов, для этого я создал папку «Проекты» в своем домашнем каталоге и предположу, что вы приняли аналогичное решение.

Прежде всего, откройте окно терминала и перейдите в каталог ваших проектов ( cd чтобы изменить каталог, введите cd Projects или имя вашей папки). Введите следующую команду, чтобы загрузить и установить необходимые библиотеки Three20. Как видите, мы клонируем репозиторий git.

 git clone git://github.com/facebook/three20.git 

не так ли плохо это было? Загрузка всех файлов может занять несколько минут, поэтому пока это работает, мы настроим ваш проект Xcode. Запустите Xcode и нажмите «Начать новый проект», затем выберите «iOS Project» и, наконец, «View Based Project», прежде чем нажать «Next».

Вам нужно будет ввести имя. Я использовал «BuildMobileThree20», и вам будет легче следовать за ним, если вы сделаете то же самое, особенно если вы все еще изучаете свой путь в Xcode. После завершения проекта нам нужно закрыть Xcode и завершить установку библиотеки Three20.

Вернитесь в Терминал и проверьте, что библиотека Three20 завершила загрузку. Запустите следующие команды в окне терминала, которые установят базовые библиотеки Three20, а также очень полезное расширение XML, которое поможет нам прочитать RSS-канал BuildMobile.

 python three20/src/scripts/ttmodule.py -p BuildMobileThree20/BuildMobileThree20.Xcodeproj Three20 python src/scripts/ttmodule.py -p ../BuildMobileThree20/BuildMobileThree20.Xcodeproj/ extThree20XML 

Теперь вы можете снова открыть свой проект Xcode, и вы должны увидеть, что библиотеки Three20 включены в ваш путь сборки. После завершения установки мы все готовы и можем перейти к этапу сборки деталей.

Строим кусочки

Теперь самое интересное, собирая маленькие кусочки вместе, чтобы сделать наше умное маленькое приложение. Одна вещь, которая мне нравится в библиотеках, это то, как они мягко помогают вам следовать хорошим практикам программирования, и Three20 не является исключением. Части, которые мы создаем, красивые и аккуратные, регламентированные люди.

Первое, что нам нужно сделать, это Предмет Подачи. Элемент фида — это не что иное, как набор переменных, используемых для представления отдельной статьи или элемента в ленте RSS. Создайте новый файл, используя класс Objective C с подклассом ‘NSObject’

FeedItem.h

 @interface FeedItem : NSObject { NSString *title; NSString *description; NSDate *publishedDate; NSString *link; } @property (nonatomic, copy) NSString *title; @property (nonatomic, copy) NSString *description; @property (nonatomic, copy) NSString *link; @property (nonatomic, retain) NSDate *publishedDate; @end 

FeedItem.m

 @implementation FeedItem @synthesize title, description, link, publishedDate; - (void)dealloc { TT_RELEASE_SAFELY(title); TT_RELEASE_SAFELY(description); TT_RELEASE_SAFELY(link); TT_RELEASE_SAFELY(publishedDate); [super dealloc]; } @end 

Обратите внимание на использование TT_RELEASE_SAFELY вместо вызова release. TT_RELEASE_SAFELY — это удобный метод, который безопасно выпустит переменную, а затем установит для этой переменной значение nil чтобы вы случайно не вызвали ее снова. Это очень полезно, поскольку может предотвратить превращение небольшой ошибки в сеанс отладки на всю ночь.

Модель

Теперь, когда у нас есть базовый класс для хранения нашей информации «FeedItem», нам понадобится еще один объект, который будет отвечать за загрузку RSS-канала BuildMobile и преобразование его в коллекцию «FeedItems». Этот класс, конечно же, является нашей FeedModel.

К счастью, Three20 имеет TTURLRequestModel для этой цели. Чтобы реализовать модель, все, что вам нужно сделать, это написать метод, который запускает асинхронный URL-запрос, используя себя в качестве делегата для события. Перехватывая метод requestDidFinishLoad , мы можем быстро преобразовать наш канал RSS в элементы канала, прежде чем обновлять любое из представлений, основанных на модели.

FeedModel.h

 #import <Three20/Three20.h> @interface FeedModel : TTURLRequestModel { NSString *rssFeed; NSArray *items; } @property (nonatomic, retain) NSArray *items; @end 

FeedModel.m

 // Send off a request - (void)load:(TTURLRequestCachePolicy)cachePolicy more:(BOOL)more { if (!self.isLoading) { // We create a new Request for the Build Mobile RSS feed // requestDidFinishLoad will be called once the request has ended TTURLRequest *request = [TTURLRequest requestWithURL:@"http://www.sitepoint.com/feed/" delegate:self]; request.cachePolicy = cachePolicy; request.cacheExpirationAge = TT_DEFAULT_CACHE_EXPIRATION_AGE; // This tells the URLRequest to return an XML Document (RSS) TTURLXMLResponse* response = [[TTURLXMLResponse alloc] init]; response.isRssFeed = YES; // RSS needs to be handled differently request.response = response; TT_RELEASE_SAFELY(response); [request send]; } - (void)requestDidFinishLoad:(TTURLRequest *)request { NSMutableArray *feedItems = [[NSMutableArray alloc] init]; // Our response should have an XML Object TTURLXMLResponse *response = (TTURLXMLResponse*) request.response; NSDictionary *feed = response.rootObject; NSDictionary *channel = [feed objectForKey:@"channel"]; NSObject *channelItems = mobile; if ([channelItems isKindOfClass:[NSArray class]]) { for (NSDictionary *item in (NSArray*) channelItems) { // Get the Required Elements from the Dictionary NSDictionary *titleElement = [item objectForKey:@"title"]; NSDictionary *descriptionElement = [item objectForKey:@"description"]; NSDictionary *linkElement = [item objectForKey:@"link"]; NSDictionary *pubDate = [item objectForKey:@"pubDate"]; // Format the Recieved String into a Date NSDateFormatter* dateFormatter = [[NSDateFormatter alloc] init]; [dateFormatter setTimeStyle:NSDateFormatterFullStyle]; [dateFormatter setDateFormat:@"EEE, dd MMMM yyyy HH:mm:ss ZZ"]; NSDate* date = [dateFormatter dateFromString:[pubDate objectForKey:@"___Entity_Value___"]]; TT_RELEASE_SAFELY(dateFormatter); // Build the Feed Item FeedItem *feedItem = [[FeedItem alloc] init]; feedItem.title = [titleElement objectForKey:@"___Entity_Value___"]; feedItem.description = [descriptionElement objectForKey:@"___Entity_Value___"]; feedItem.link = [linkElement objectForKey:@"___Entity_Value___"]; feedItem.publishedDate = date; [feedItems addObject:feedItem]; TT_RELEASE_SAFELY(feedItem); } } self.items = feedItems; TT_RELEASE_SAFELY(feedItems); [super requestDidFinishLoad:request]; } 

Источник данных

Теперь у нас есть FeedModel, которая находится одна и не связана с чем-либо, нам нужно это исправить, создав FeedDataSource. Источник данных отвечает за сохранение модели и преобразование элементов фида, возвращаемых из модели, в ячейки таблицы, требуемые компонентами представления Three20.

Такое отделение контента от логики отображения позволяет нам легко тестировать модель на наличие проблем или быстро заменять один источник данных на другой без изменения какой-либо сложной логики отображения. Такое разделение контента является продуктом хороших практик программирования, к которым ведет нас Три20.

Нужно создать еще один новый объект с именем «FeedDataSource», поскольку раньше это был класс Objective-C с подклассом NSObject. Все, что нам нужно сделать, — это реализовать метод, возвращающий нашу модель, и еще один метод, который обновляет элементы источников данных после перезагрузки нашей таблицы.

FeedDataSource.h

 #import <Three20/Three20.h> #import "FeedModel.h" @interface FeedDataSource : TTListDataSource { FeedModel *feedModel; } @end 

FeedDataSource.m

 #import "FeedDataSource.h" #import "FeedItem.h" #import <Three20Core/NSDateAdditions.h> #import <Three20Core/NSStringAdditions.h> @implementation FeedDataSource - (id)init { if (self = [super init]) { feedModel = [[FeedModel alloc] init]; } return self; } // Give the model to our TableView to use - (id<TTModel>)model { return feedModel; } // Our Model has loaded items, Convert to Table Cells - (void)tableViewDidLoadModel:(UITableView*)tableView { NSMutableArray *items = [[NSMutableArray alloc] init]; // Convert Feed Items into TableCells for (FeedItem *feedItem in feedModel.items) { // Get the Heading, make it safe NSString *headingString = [feedItem.title stringByRemovingHTMLTags]; NSString *descriptionString = [feedItem.description stringByRemovingHTMLTags]; NSString *publishedString = [feedItem.publishedDate formatRelativeTime]; NSString *content = [NSString stringWithFormat:@"<b>%@</b>n%@n<i>%@</i>", headingString, descriptionString, publishedString]; TTStyledText* styledText = [TTStyledText textFromXHTML:content lineBreaks:YES URLs:YES]; [items addObject:[TTTableStyledTextItem itemWithText:styledText URL:feedItem.link]]; } self.items = items; TT_RELEASE_SAFELY(items); } - (void)dealloc { TT_RELEASE_SAFELY(feedModel); [super dealloc]; } @end 

Контроллер

Теперь все, что сделано, мы можем создать контроллер. По сути, мы создаем TableView, поэтому мы должны расширяться от TTTableViewController и использовать преимущества классных функций, предоставляемых Three20. Контроллеру нужен только один метод для создания источника данных, используемого представлениями. Поскольку это основа, все классы знают, как обрабатывать данные.

А теперь немного о магии Three20. Мы продолжаем и реализуем createDelegate , возвращая один TTTableViewDragRefreshDelegate . Эта единственная строка кода добавляет функцию перезагрузки в наше табличное представление, насколько это просто? И немного магии.

 @implementation FeedListController - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) { self.title = @"Build Mobile RSS"; self.variableHeightRows = YES; } return self; } // Returns a FeedDataSource which will create a Model and define our Table View - (void)createModel { self.dataSource = [[[FeedDataSource alloc] init] autorelease]; } // Adds Pull to Reload to our table, sweet! - (id<UITableViewDelegate>)createDelegate { return [[[TTTableViewDragRefreshDelegate alloc] initWithController:self] autorelease]; } @end 

Делегат приложения

До сих пор мы создали класс «FeedItem», «FeedModel», «FeedDataSource» и, наконец, «FeedListController». Теперь все, что сделано, нам просто нужно изменить наш делегат приложения, чтобы использовать наш новый контроллер. Это последний этап, когда мы собираем все кусочки вместе.

AppDelegate был создан, когда вы сделали первоначальный проект, и будет называться BuildMobileThree20AppDelegate если вы до сих пор следовали инструкциям. Мы собираемся использовать TTNavigator для отображения нашего контроллера и включить методы для отображения любых ссылок, по которым мы TTNavigator в веб-представлении.

BuildMobileThree20AppDelegate.m

 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { TTNavigator* navigator = [TTNavigator navigator]; navigator.persistenceMode = TTNavigatorPersistenceModeTop; navigator.window = window; TTURLMap *map = navigator.URLMap; [map from:@"*" toViewController:[TTWebController class]]; // Shows web addresses in a browser [map from:@"tt://buildmobile" toSharedViewController:[FeedListController class]]; if (![navigator restoreViewControllers]) { [navigator openURLAction:[TTURLAction actionWithURLPath:@"tt://buildmobile"]]; } [self.window makeKeyAndVisible]; return YES; } 

Нажмите эту большую кнопку воспроизведения или «Выполнить» в XCode, и вы должны будете читать статьи BuildMobile в своем собственном приложении в кратчайшие сроки. Хотя это, конечно, не исчерпывающее руководство по структуре Three20, я надеюсь, что этого достаточно для начала экспериментов.