Предыстория
Воодушевившись тем, что в недавном времени затарился 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 — непостредственно само представление нашего контроллера.
Сначала создадим кнопки. Начнем с цифр.

Создаем первую цифру которая в будующем будет семеркой.

Чтобы не мучаться дальше, сразу же привяжем к нашей кнопке экшн digitPressed. Для этого надо выбрать нашу кнопку и в Connections Inspector взять кружечек который находится напротив ивента Touch Down и перетащить его на объект File's Owner.

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

Нам нужен будет digitPressed.
Все что нам осталось это расплодить наш «шаблон кнопки» c зажатым Alt (options). Теперь можно задать нашим цифрам лэйблы. Сделать это можно либо через Attribute inspector либо двойным нажатием на кнопку. Так же не стоит забывать что у нас есть одна кнопка отделения целой части от дробной (и делается это символом "." а не "," как это делаем мы в россии).

По той же схеме мы сделаем кнопки операций. Нам понядобятся сложение, вычитание, деление, умножение и извлечение квадратного корня. И еще знак равно который будет выводить результат нашего действия. Все. Теперь у нас есть устройство ввода. Делем устройство вывода, тоесть дисплей. Необходимо создать объект Label, сделать ему выравнивание по левому краю, установить шрифт на 26 и default text «0». Ничего сложного.


Продолжаем кодить
Больше с интерфейсами мы дела иметь не будем. Остался чистый код.
Кстати если интересно, можно запустить приложение и посмотреть как оно будет выглядеть на эмуляторе.
Приступим к написанию наших мозгов. Они будут работать так:
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.

Домашнее задание
Вы можете добавить три функции:
Функция ± которая будет переворачивать цисло с отрицательного на положительное;
Функцию сброса результата (С);
И функция повтора операции (когда сделал операцию и повторно нажал на = то повторяется последняя операция с последним операндом);