Pull to refresh

MVC в Objective-C или калькулятор для iOS 5

Development for iOS *
Sandbox

Предыстория


Воодушевившись тем, что в недавном времени затарился 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

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

image

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

image

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

image

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

image

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

image
image

Продолжаем кодить


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

Домашнее задание


Вы можете добавить три функции:
Функция ± которая будет переворачивать цисло с отрицательного на положительное;
Функцию сброса результата (С);
И функция повтора операции (когда сделал операцию и повторно нажал на = то повторяется последняя операция с последним операндом);
Tags:
Hubs:
Total votes 48: ↑36 and ↓12 +24
Views 35K
Comments Comments 43