iOS. Приемы программирования Нахавандипур Вандад
@end
@implementation ViewController
И поместим вид с изображением внутрь прокручиваемого вида:
— (void)viewDidLoad{
[super viewDidLoad];
UIImage *iToLoad = [UIImage iNamed:@"MacBookAir"];
self.myImageView = [[UIImageView alloc] initWithImage: iToLoad];
self.myScrollView = [[UIScrollView alloc] initWithFrame: self.view.bounds];
[self.myScrollView addSubview: self.myImageView];
self.myScrollView.contentSize = self.myImageView.bounds.size;
[self.view addSubview: self.myScrollView];
}
Если теперь загрузить эту программу в эмуляторе iOS, можно убедиться в том, что изображение прокручивается и по горизонтали, и по вертикали. Основная задача в данном случае — найти картинку, которая будет довольно велика и не поместится в пределах экрана. Так, если взять изображение размером 20 20 пикселов, то особой пользы от функции прокрутки не будет. Подобную, картинку и не следует помещать в прокручиваемый вид, поскольку в данной ситуации такой вид решительно бесполезен. Прокручивать будет нечего, так как размер экрана больше, чем размер изображения.
UIScrollView обладает такой удобной особенностью, как поддержка делегирования. Поэтому такой вид может сообщать приложению о действительно важных событиях с помощью делегата. Делегат для прокручиваемого вида должен отвечать требованиям протокола UIScrollViewDelegate. Вот некоторые методы, определяемые в этом протоколе:
• scrollViewDidScroll: — вызывается всякий раз, когда содержимое прокручиваемого вида прокручивается;
• scrollViewWillBeginDecelerating: — вызывается, когда пользователь прокручивает содержимое вида и отрывает палец от сенсорного экрана в то время, как вид продолжает прокручиваться;
• scrollViewDidEndDecelerating: — вызывается, когда прокручивание информации, содержащейся в виде, заканчивается;
• scrollViewDidEndDragging: willDecelerate: — вызывается, когда пользователь завершает перетаскивание содержимого в прокручиваемом виде. Этот метод очень напоминает scrollViewDidEndDecelerating:, но следует помнить, что пользователь может перетаскивать элементы содержимого такого вида и не прокручивая его. Можно просто прикоснуться пальцем к элементу содержимого, переместить палец в другую точку на экране, а потом оторвать палец от экрана, не сдвинув содержимое самого вида ни на миллиметр. Этим перетаскивание и отличается от прокрутки. Прокрутка напоминает перетаскивание, но пользователь «сообщает импульс», приводящий к перемещению содержимого, если снимает палец с экрана, пока информация еще прокручивается. То есть пользователь убирает палец, не дождавшись завершения прокрутки. Перетаскивание можно сравнить с тем, как вы удерживаете педаль газа в машине или педаль велосипеда. Продолжая эту аналогию, можно сравнить прокрутку с движением по инерции на машине или велосипеде.
Сделаем предыдущее приложение немного интереснее. Теперь нужно установить уровень яркости картинки в нашем виде с изображением (этот показатель также называется «альфа-уровень» или «альфа-значение») равным 0.50f (полупрозрачный) на момент, когда пользователь начинает прокрутку изображения, и вернуть этот уровень к значению 1.0f (непрозрачный) к моменту, когда прокрутка завершается. Сначала обеспечим соответствие протоколу UIScrollViewDelegate:
#import «ViewController.h»
@interface ViewController () <UIScrollViewDelegate>
@property (nonatomic, strong) UIScrollView *myScrollView;
@property (nonatomic, strong) UIImageView *myImageView;
@end
@implementation ViewController
Потом реализуем данную функциональность:
— (void)scrollViewDidScroll:(UIScrollView *)scrollView{
/* Вызывается, когда пользователь совершает прокрутку
или перетаскивание. */
self.myScrollView.alpha = 0.50f;
}
— (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
/* Вызывается только после прокрутки. */
self.myScrollView.alpha = 1.0f;
}
— (void)scrollViewDidEndDragging:(UIScrollView *)scrollView
willDecelerate:(BOOL)decelerate{
/* Гарантируем, что альфа-значение вернется к исходному,
даже если пользователь просто перетаскивает элементы. */
self.myScrollView.alpha = 1.0f;
}
— (void)viewDidLoad{
[super viewDidLoad];
UIImage *iToLoad = [UIImage iNamed:@"MacBookAir"];
self.myImageView = [[UIImageView alloc] initWithImage: iToLoad];
self.myScrollView = [[UIScrollView alloc] initWithFrame: self.view.bounds];
[self.myScrollView addSubview: self.myImageView];
self.myScrollView.contentSize = self.myImageView.bounds.size;
self.myScrollView.delegate = self;
[self.view addSubview: self.myScrollView];
}
Как можно заметить, в прокручиваемых видах имеются индикаторы Индикатор — это тонкая контрольная линия, которая отображается с краю прокручиваемого вида, когда его содержимое прокручивается или перемещается (рис. 1.63).
Индикаторы просто показывают пользователю, как вид расположен в настоящий момент относительно его содержимого (в верхней части, на полпути к низу и т. д.). Внешним видом индикаторов можно управлять, изменяя значение свойства indicatorStyle. Например, в следующем коде я делаю индикатор прокручиваемого вида белым:
self.myScrollView.indicatorStyle = UIScrollViewIndicatorStyleWhite;
Рис. 1.63. Черные индикаторы, появляющиеся справа и снизу прокручиваемого вида
Одна из наиболее замечательных особенностей прокручиваемых видов заключается в том, что в них возможна разбивка на страницы. Она функционально подобна прокрутке, но прокрутка прекращается, как только пользователь переходит на следующую страницу. Вероятно, вы уже знакомы с этой функцией, если вам доводилось пользоваться программой Photos (Фотографии) в iPhone или iPad. Просматривая фотографии, можно перемещаться между ними скольжением. Каждое скольжение открывает на экране предыдущую или последующую фотографию. При одном скольжении вы никогда не прокручиваете последовательность до самого начала или до самого конца. Когда начинается прокручивание и вид обнаруживает следующее изображение, прокрутка останавливается на этом изображении и оно начинает подрагивать на экране. Таким образом, анимация прокрутки прерывается. Это и есть разбивка на страницы. Если вы еще не пробовали ее на практике, настоятельно рекомендую попробовать. Весь дальнейший рассказ останется непонятен, если вы не будете представлять, как выглядит приложение, поддерживающее разбивку на страницы.
В следующем примере с кодом я использую три изображения: iPhone, iPad и MacBook Air. Каждое из них я поместил в отдельный вид типа i view, а потом добавил эти виды к прокручиваемому виду. Затем включаем разбивку на страницы, задавая для свойства pagingEnabled прокручиваемого вида значение YES:
— (UIImageView *) newImageViewWithImage:(UIImage *)paramImage
frame:(CGRect)paramFrame{
UIImageView *result = [[UIImageView alloc] initWithFrame: paramFrame];
result.contentMode = UIViewContentModeScaleAspectFit;
result.i = paramImage;
return result;
}
— (void)viewDidLoad{
[super viewDidLoad];
UIImage *iPhone = [UIImage iNamed:@"iPhone"];
UIImage *iPad = [UIImage iNamed:@"iPad"];
UIImage *macBookAir = [UIImage iNamed:@"MacBookAir"];
CGRect scrollViewRect = self.view.bounds;
self.myScrollView = [[UIScrollView alloc] initWithFrame: scrollViewRect];
self.myScrollView.pagingEnabled = YES;
self.myScrollView.contentSize = CGSizeMake(scrollViewRect.size.width *
3.0f, scrollViewRect.size.height);
[self.view addSubview: self.myScrollView];
CGRect iViewRect = self.view.bounds;
UIImageView *iPhoneImageView = [self newImageViewWithImage: iPhone
frame: iViewRect];
[self.myScrollView addSubview: iPhoneImageView];
/* Для перехода на следующую страницу изменяем положение
следующего вида с изображением по оси X. */
iViewRect.origin.x += iViewRect.size.width;
UIImageView *iPadImageView = [self newImageViewWithImage: iPad
frame: iViewRect];
[self.myScrollView addSubview: iPadImageView];
/* Для перехода на следующую страницу изменяем положение
следующего вида с изображением по оси X. */
iViewRect.origin.x += iViewRect.size.width;
UIImageView *macBookAirImageView =
[self newImageViewWithImage: macBookAir
frame: iViewRect];
[self.myScrollView addSubview: macBookAirImageView];
}
Итак, теперь у нас есть три страницы, содержимое которых можно прокручивать (рис. 1.64).
Рис. 1.64. Прокрутка содержимого в виде, в котором поддерживается разбивка на страницы
1.24. Загрузка веб-страниц с помощью UIWebView
Постановка задачи
Необходимо динамически загрузить веб-страницу прямо в ваше приложение для iOS.
Решение
Воспользуйтесь классом UIWebView.
Обсуждение
Веб-вид (Web View) — это окно, которое браузер Safari использует для загрузки в систему iOS информации из Сети. Класс UIWebView позволяет использовать в приложениях для iOS всю мощь Safari. Все, что вам нужно сделать, — поместить веб-вид в вашем пользовательском интерфейсе и применить один из методов загрузки:
• loadData: MIMEType: textEncodingName: baseURL: — загружает в веб-вид экземпляр класса NSData;
• loadHTMLString: baseURL: — загружает в веб-вид экземпляр класса NSString. Строка должна содержать валидный HTML-код так, чтобы ее мог обработать браузер;
• loadRequest: — загружает экземпляр класса NSURLRequest. Этот метод пригодится в тех случаях, когда вы хотите загрузить в веб-вид, расположенный в вашем приложении, удаленное содержимое, на которое указывает URL.
Рассмотрим пример. Начнем с файла реализации контроллера нашего вида:
#import «ViewController.h»
@interface ViewController ()
@property(nonatomic, strong) UIWebView *myWebView;
@end
@implementation ViewController
Теперь я хочу загрузить в веб-вид строку iOS 7 Programming Cookbook. Чтобы убедиться в том, что все работает как надо и что наш веб-вид способен отображать насыщенный (форматированный) текст, я на этом не остановлюсь и выделю слово Cookbook полужирным шрифтом, а остальной текст оставлю без изменений (рис. 1.65):
— (void)viewDidLoad{
[super viewDidLoad];
self.myWebView = [[UIWebView alloc] initWithFrame: self.view.bounds];
[self.view addSubview: self.myWebView];
NSString *htmlString = @"iOS 7 Programming <strong>Cookbook</strong>";
[self.myWebView loadHTMLString: htmlString
baseURL: nil];
}
Рис. 1.65. Загрузка форматированного текста в веб-вид
Еще один способ работы с веб-видом — загрузка в него удаленного контента, на который указывает URL. Для этого можно пользоваться методом loadRequest:. Перейдем к следующему примеру, в котором загрузим основную страницу сайта Apple в веб-вид, расположенный в нашей программе для iOS (рис. 1.66):
— (void)viewDidLoad{
[super viewDidLoad];
self.myWebView = [[UIWebView alloc] initWithFrame: self.view.bounds];
self.myWebView.scalesPageToFit = YES;
[self.view addSubview: self.myWebView];
NSURL *url = [NSURL URLWithString:@"http://www.apple.com"];
NSURLRequest *request = [NSURLRequest requestWithURL: url];
[self.myWebView loadRequest: request];
}
Рис. 1.66. Веб-вид, в который загружена домашняя страница Apple
Может понадобиться какое-то время, прежде чем в веб-вид загрузится содержимое, которое вы туда передали. Наверное, вы заметили, что при загрузке информации в браузере Safari в левом верхнем углу экрана появляется тонкий индикатор процесса, показывающий, что ваше устройство занято загрузкой контента (рис. 1.67).
Рис. 1.67. Индикатор процесса загрузки
В iOS эта задача решается с помощью делегирования. Мы сделаем подписку на делегат веб-вида, и веб-вид будет получать уведомление всякий раз, когда делегат станет загружать контент. Когда загрузка контента завершится, мы получим от веб-вида соответствующее сообщение. Все это мы сделаем, применив свойство delegate веб-вида. Делегат веб-вида должен соответствовать протоколу UIWebViewDelegate.
Идем дальше. Теперь реализуем в контроллере нашего вида небольшой индикатор процесса. Не забывайте, что индикатор протекающего процесса уже имеется в составе приложения и мы не должны создавать его сами. Управлять этим индикатором можем с помощью метода setNetworkActivityIndicatorVisible:, относящегося к UIApplication. Итак, начнем с файла реализации контроллера вида:
@interface ViewController () <UIWebViewDelegate>
@property(nonatomic, strong) UIWebView *myWebView;
@end
@implementation ViewController
Потом перейдем к реализации. Здесь мы будем использовать три метода из тех, которые объявляются в протоколе UIWebViewDelegate:
webViewDidStartLoad: — вызывается, как только вид начинает загрузку содержимого;
webViewDidFinishLoad: — вызывается, как только вид заканчивает загрузку содержимого;
webView: didFailLoadWithError: — вызывается, как только вид останавливает загрузку содержимого, например, из-за возникшей ошибки или разрыва сетевого соединения:
— (void)webViewDidStartLoad:(UIWebView *)webView{
[[UIApplication sharedApplication]
setNetworkActivityIndicatorVisible: YES];
}
— (void)webViewDidFinishLoad:(UIWebView *)webView{
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible: NO];
}
— (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error{
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible: NO];
}
— (void)viewDidLoad{
[super viewDidLoad];
self.myWebView = [[UIWebView alloc] initWithFrame: self.view.bounds];
self.myWebView.delegate = self;
self.myWebView.scalesPageToFit = YES;
[self.view addSubview: self.myWebView];
NSURL *url = [NSURL URLWithString:@"http://www.apple.com"];
NSURLRequest *request = [NSURLRequest requestWithURL: url];
[self.myWebView loadRequest: request];
}
1.25. Отображение протекания процессов с помощью UIProgressView
Постановка задачи
Необходимо отображать на экране индикатор протекания процесса (Progress Bar), отражающий ход выполнения той или иной задачи, например индикатор загрузки файла, скачиваемого c определенного URL.
Решение
Инстанцируйте вид типа UIProgressView и разместите его в другом виде.
Обсуждение
Вид протекания процесса программисты обычно называют прогресс-баром. Образец такого вида показан на рис. 1.68.
Рис. 1.68. Простой вид с индикатором протекания процесса
Виды, отображающие протекание процессов, обычно демонстрируются пользователю для показа выполнения задачи с четко определенными начальной и конечной точками. Примером такой задачи является, например, скачивание 30 файлов. Очевидно, что такая задача будет выполнена, когда все 30 файлов будут скопированы на устройство. Вид, отображающий протекание процесса, является экземпляром UIProgressView и инициализируется с помощью специального метода-инициализатора данного класса — initWithProgressViewStyle:. В качестве параметра данный метод принимает стиль (оформление) панели протекания, которую предполагается создать. Этот параметр относится к типу UIProgressViewStyle и, соответственно, может иметь одно из следующих значений:
• UIProgressViewStyleDefault — это стандартное оформление вида протекания процесса. Именно в этом стиле оформлен вид, показанный на рис. 1.68;
• UIProgressViewStyleBar — напоминает UIProgressViewStyleDefault, но предназначено для использования с видами отображения протекания процессов, добавляемыми на панель инструментов.
Экземпляр UIProgressView определяет свойство под названием progress (типа float). Это свойство сообщает системе iOS, как должна отображаться полоса в виде, отражающем протекание процесса. Значение этого свойства должно быть в диапазоне от 0 до 1.0. Если сообщается значение 0, то заполнение индикатора состояния еще не началось. Значение 1.0 соответствует 100 %-ной завершенности. Степень прогресса, показанная на рис. 1.68, составляет 0.5 (или 50 %).
Чтобы научиться создавать виды, отражающие протекание процессов, создадим вид, похожий на тот, что приведен на рис. 2.74. Начинаем с главного — определяем свойство для вида протекания процесса:
#import «ViewController.h»
@interface ViewController ()
@property (nonatomic, strong) UIProgressView *progressView;
@end
@implementation ViewController
Далее инстанцируем объект типа UIProgressView:
— (void)viewDidLoad{
[super viewDidLoad];
self.progressView = [[UIProgressView alloc]
initWithProgressViewStyle: UIProgressViewStyleBar];
self.progressView.center = self.view.center;
self.progressView.progress = 20.0f / 30.0f;
[self.view addSubview: self.progressView];
}
Итак, создать вид протекания процесса совсем не сложно. В сущности, нужно просто правильно отобразить ход процесса, так как свойство progress данного вида должно иметь значение в диапазоне от 0 до 1.0, то есть нормализованное значение. Итак, если вам предстоит решить 30 задач и вы уже выполнили 20 из них, то нужно присвоить свойству progress вида протекания процесса результат следующего равенства:
self.progressView.progress = 20.0f / 30.0f;
Значения 20 и 30 передаются данному равенству как значения с плавающей точкой, поскольку компилятору нужно сообщить, что операция деления будет производиться над числами с плавающей точкой и в результате деления получится десятичная дробь. Если приказать компилятору поместить в свойстве progress вида протекания процесса целочисленное деление 20/30, то вы получите целочисленный результат 0. Это происходит потому, что компилятор выполняет целочисленное деление, отсекая полученный результат до ближайшего предшествующего целого числа. Короче говоря, на индикаторе протекания действия прогресс все время будет оставаться нулевым, пока процесс не завершится и частное от деления 30/30 не станет равно 1. Пользователю такой индикатор загрузки будет ни к чему.
1.26. Создание и отображение текстов с оформлением
Постановка задачи
Требуется возможность отображать в элементах вашего пользовательского интерфейса насыщенный форматированный текст, избегая при этом необходимости создавать отдельный компонент пользовательского интерфейса для каждого атрибута. Например, может потребоваться отобразить в UILabel предложение, в котором всего одно слово записано полужирным шрифтом.
Решение
Создайте экземпляр класса NSAttributedString или его изменяемого варианта, NSMutableAttributedString, и либо задайте его как текст компонента пользовательского интерфейса (например, как текст подписи UILabel) с помощью специального строкового свойства, снабженного атрибутами, либо просто воспользуйтесь встроенными методами атрибутированной строки для отрисовки текста на холсте.
Обсуждение
О насыщенном тексте слагают легенды. Многим из наших коллег-программистов приходилось сталкиваться с необходимостью отображения в пользовательском интерфейсе такой текстовой строки, в которой применяется сразу несколько видов форматирования. Например, в одной строке может понадобиться одновременно вывести и обычный текст, и курсив, причем курсивом будет записано всего одно слово. Возможно, одно из слов в предложении потребуется подчеркнуть. Для этого некоторые пытаются использовать веб-виды (Web Views), но это решение не является оптимальным, поскольку веб-виды довольно медленно отображают свой контент и неизбежно негативно воздействуют на производительность приложения. В iOS 7 можно приступать к применению атрибутированных строк. Не знаю, почему Apple решила внедрить такую возможность в iOS только сейчас, ведь Mac-разработчики пользуются атрибутированными строками уже довольно давно.
Прежде чем приступить к основной части раздела, я хотел бы четко пояснить, что понимается под термином «атрибутированная строка». Взгляните на рис. 1.69. Мы собираемся написать программу, которая будет достигать именно такого эффекта.