Предыстория
Воодушевившись тем, что в недавном времени затарился i девайсами решил научится таки под них программировать. Конечно, в выбранном мной пути web разработчика это мало чем поможет, но мне всегда были интересны новые языки.
Первое на что упал мой взгляд- это виртуальная школа iTunesU. Здесь я нашел лекции курса CS 193P которые ведут разработчики эпл для студентов Стэнфордского университета. Лекции достаточно хорошо охватывают базовые особенности языка и нативных фрэймворков, однако есть одно НО– лекции на английском и никакого перевода к ним нету. Для меня лично проблем это не вызвало, потому что Aple по всей видимости выбирает на эти лекции только кандидатов с хорошо поставленной речью, и прожитые в США 8 месяцев дали мне возможность смотреть лекции в оригинале и учится у «носителей языка» (шучу-шуткую).
Так как каждый год выходит новая версия Xcode, а лекции стэнфорда в которых рассматривается именно текущая версия становятся доступны с опозданием в семестр, я решил написать серию статей, которые будут идти по практической части (!) данного курса. Так как я слушал лекции осени 2010 для меня будет практическт полезным написать программу в xcode 4.2 который был изначально разработан под 3ю версию. Здесь я не буду углублятся в особенности ObjC а буду давать голую практику.
Итак приступим
Для работы я буду использовать Xcode 4.2, iOS SDK 5 и все это будет работать под Mac OS X 10.7.2 Lion.
Как вы молгли заметить из заголовка я (читай– лектор) буду использовать модель данных MVC. Вообще, честно сказать я не понмаю как можено кодить под iOS по другому. Напишем мы программу калькулятор. Простой калькулятор который будет складывать, вычитать, умножать, делить и извлекать квадратный корень из чисел.
Структура программы будет такой:
1. у нас будут мозги калькулятора, которые будут выполнять все действия (класс brain) — модель;
2. у нас будет контроллер, который будет обрабатывать нажатия на кнопки;
3. у нас будет View, в котором всегда возникают проблемы у новоиспеченных iOS кодеров. Об этом подробнее дальше.
Новый проект
Итак начнем с создания прокта. Для этого идем Файл — Новый — Новый проект. Среди общего множества шаблонов выбираем «Single View Application». Я назову приложение Calc. Так же стоит отметить что при создании проекта я снял галочку «Use Storyboard». Для чего это надо: в SDK 5 вместо былого .xib появился новый формат .storyboard. Чем они отличаются я еще не разобрался, поэтому использую по старинке xib файлы для IB (далее Interface Builder).
Итак у нас создался проект. В нем уже есть контроллер и view. Чтобы проверить работоспособность всей системы можно сделать пробный билд. Если все ок, то на экране появится эмулятор айфона и запустится наше приложение с серым экраном.
Для начала нам нужны мозги калькулятора (процессор) которые будут выполнять все опрации. Нажимаем CMD+N и выбираем Objective-C class и назовем его CalcBrain подкласс NSObject класса чтобы наши мозги унаследовали базовые методы. Теперь в проекте на два файла больше: CalcBrain.h и CalcBrain.m
Перейдем к ViewController.h. Там вы не увидите ничего кроме импорта стандартной бибилиотеки UIKit и объявление самого контроллера. Тут нам надо будет объявить следующие вещи:
— outlets: переменные которые буду туказывать на определенные элементы Interface Builder;
— actions: собсственно методы которые будут вызываться при совершении элементами IB определенных действий.
— также нам потребуется объявить переменную нашего нового класса CalcBrain чтобы все это дело заработало.
Итак поехали.
#import <UIKit/UIKit.h>
#import "CalcBrain.h"
@interface ViewController : UIViewController {
IBOutlet UILabel *display;
CalcBrain *brain;
}
-(IBAction)digitPressed:(UIButton *)sender; //++
-(IBAction)operationPressed:(UIButton *)sender; //++
@end
Тут я ничего объяснять не буду, потому что код можно интуитивно понять (если нет — увы, советую сначала почитать про ООП и прочие вкусности мира программистского).
Построение интерфейса
Сейчас мы немного отвлечемся от кода и займемся построением интерфейса. Я считаю что сейча необходимо сосредоточить все свое внимание, потому что на моем опыте много начинающих кодеров без прямой демонстрации действий просто валились именно
на этом этапе и опускали руки. Открываем ViewController.xib и видим уже существующие объекты:
Files's Owner — объект указывающий на класс к которому принодлежит данный xib (в нашем случае это ViewController- это значит что мы сможем использовать только экшены и оутлеты только этого сонтроллера);
First Responder;
View — непостредственно само представление нашего контроллера.
Сначала создадим кнопки. Начнем с цифр.
![image](http://pandanet.ru/habr/1.png)
Создаем первую цифру которая в будующем будет семеркой.
![image](http://pandanet.ru/habr/2.png)
Чтобы не мучаться дальше, сразу же привяжем к нашей кнопке экшн digitPressed. Для этого надо выбрать нашу кнопку и в Connections Inspector взять кружечек который находится напротив ивента Touch Down и перетащить его на объект File's Owner.
![image](http://pandanet.ru/habr/4.png)
Появится окно в котором вы сможете выбрать экшн на который будет отвечать контроллер когда на кнопку будет произведено касание.
![image](http://pandanet.ru/habr/3.png)
Нам нужен будет digitPressed.
Все что нам осталось это расплодить наш «шаблон кнопки» c зажатым Alt (options). Теперь можно задать нашим цифрам лэйблы. Сделать это можно либо через Attribute inspector либо двойным нажатием на кнопку. Так же не стоит забывать что у нас есть одна кнопка отделения целой части от дробной (и делается это символом "." а не "," как это делаем мы в россии).
![image](http://pandanet.ru/habr/5.png)
По той же схеме мы сделаем кнопки операций. Нам понядобятся сложение, вычитание, деление, умножение и извлечение квадратного корня. И еще знак равно который будет выводить результат нашего действия. Все. Теперь у нас есть устройство ввода. Делем устройство вывода, тоесть дисплей. Необходимо создать объект Label, сделать ему выравнивание по левому краю, установить шрифт на 26 и default text «0». Ничего сложного.
![image](http://pandanet.ru/habr/6.png)
![image](http://pandanet.ru/habr/7.png)
Продолжаем кодить
Больше с интерфейсами мы дела иметь не будем. Остался чистый код.
Кстати если интересно, можно запустить приложение и посмотреть как оно будет выглядеть на эмуляторе.
Приступим к написанию наших мозгов. Они будут работать так:
1 Объект будет получать первый операнд;
2 Объект получает действие;
3 Если действие не извлечение квадратного корня готовимся получать следующий операнд;
4 Если это корень или равно- выводим результат.
Текст файла CalcBrain.h
#import <Foundation/Foundation.h>
@interface CalcBrain : NSObject {
double operand;
}
-(void)setOperand:(double)aDouble;
-(double)performOperation:(NSString *)operation;
@end
С переменной operand надеюсь нет необходимости объяснять. Методы класса- тут все просто setOperand- это обычный сеттер для переменной operand. Можно было бы использовать и @syntesuze но тогда будет сгенерирован геттер который нам никчему. Так что обойдемся и таким костылем. Теперь опишем все наши методы.
- (void)setOperand:(double)aDouble{
operand = aDouble;
}
- (double)performOperation:(NSString *)operation{
if ([operation isEqual:@"sqrt"]) {
operand = sqrt(operand);
}
return operand;
}
Немного про второй метод. Второй метод будет обрабатывать наше действие (запрос операции, собственно так и называется). Пока что мы просто обрабатываем извлечение квадратного корня. Входящими данными в этот метод будет текст с кнопки операции. Теперь мы будем бороться с тем, что мозгам надо запоминать первый операнд и нажатую операцию прежде чем приступать к запоминанию второго операнда.
Дополним класс:
@interface CalcBrain : NSObject {
double operand;
NSString *waitingOperation;
double waitingOperand;
}
И описание:
- (double)performOperation:(NSString *)operation{
if ([operation isEqual:@"sqrt"]) {
operand = sqrt(operand);
} else {
[self performWaitingOperation];
waitingOperation = operation;
waitingOperand = operand;
}
return operand;
}
Метод performWaitingOperation будет выполнять непосредственно само действие надо операндами в том случае елси у нас действие не на один операнд (sqrt).
-(void)performWaitingOperation {
if ([@"+" isEqual:waitingOperation]) {
operand = waitingOperand + operand;
} else if ([@"*" isEqual:waitingOperation]) {
operand = waitingOperand * operand;
} else if ([@"-" isEqual:waitingOperation]) {
operand = waitingOperand - operand;
} else if ([@"/" isEqual:waitingOperation]) {
if (operand) {
operand = waitingOperand / operand;
}
}
}
Незабываем объявить этот метод в CalcBrain.h
Программируем контроллер
Как это гордо звучит. Но пода перейти к нашему контроллеру а конкретно к заголовочному файлу. Начнем описывать наши уже объявленные методы. Кстати все что создал Xcode в файле ViewController.m можно стереть. Пока. Для данного приложения они нам не понадобятся. Все что нам нужно:
#import "ViewController.h"
@implementation ViewController
-(IBAction)digitPressed:(UIButton *)sender {
}
-(IBAction)operationPressed:(UIButton *)sender {
}
@end
Теперь пойдем по методам. При нажатии на операции мы должны получить текст с кнопки. Делается это просто: в метод падает кнопка как объект со всеми параметрами:
-(IBAction)operationPressed:(UIButton *)sender {
NSString *operation = [[sender titleLabel] text];
}
Теперь нам надо запросить наши мозги выполнить эту операцию. Но сначала, нам надо эти мозги создать. Переменную под них мы выделили но еще не объявили:
-(CalcBrain *)brain {
if (!brain) brain = [[CalcBrain alloc] init];
return brain;
}
И допишем метод нажатия на операцию, сделаем запрос опирации и вывод результата на дисплей:
-(IBAction)operationPressed:(UIButton *)sender {
NSString *operation = [[sender titleLabel] text];
double result = [[self brain] performOperation:operation];
[display setText: [NSString stringWithFormat:@"%g", result]];
}
Теперь встает логическая проблема- когда пользователь нажимает «5» «5» то он подразумевает «55» а не «5» как поймут наши мозги сейчас. Тоесть нам необходима булевская переменная которая будет идентифицировать находится ли пользователь в процессе ввода или нет.
@interface ViewController : UIViewController {
IBOutlet UILabel *display;
CalcBrain *brain;
BOOL userIsInTheMiddleOfTypingANumber;
}
Теперь можно описать объявление операнда при нажатии на клавишу операции:
-(IBAction)operationPressed:(UIButton *)sender {
if (userIsInTheMiddleOfTypingANumber) {
[[self brain] setOperand:[[display text] doubleValue]];
userIsInTheMiddleOfTypingANumber = NO;
}
NSString *operation = [[sender titleLabel] text];
double result = [[self brain] performOperation:operation];
[display setText: [NSString stringWithFormat:@"%g", result]];
}
Теперь опишим нажатие на цифру:
-(IBAction)digitPressed:(UIButton *)sender {
NSString *digit = [[sender titleLabel] text];
if (userIsInTheMiddleOfTypingANumber) {
[display setText:[[display text] stringByAppendingString:digit]];
} else {
[display setText:digit];
userIsInTheMiddleOfTypingANumber = YES;
}
}
Тут логика проста: получаем цифру с лэйбла сендера, если stringByAppendingString true значит нам надо присоединить введенную цифру на дисплей. Если нет- просто добавить цифру на дисплей и перещелкнуть параметр stringByAppendingString.
Теперь осталось только Build and Run.
![image](http://pandanet.ru/habr/9.png)
Домашнее задание
Вы можете добавить три функции:
Функция ± которая будет переворачивать цисло с отрицательного на положительное;
Функцию сброса результата (С);
И функция повтора операции (когда сделал операцию и повторно нажал на = то повторяется последняя операция с последним операндом);