Разработка под Apple. Objective-C

25

Objective-C — это простой язык программирования, разработанный как язык объектно-ориентированного программирования. Objective-C, также как и C++ является расширением ANSI-C, и имеет поддержку таких возможностей, как описание классов, методов и свойств. В отличие от C++ Objective-C полностью совместим с языком С, то есть любой код, написанный на языке Си (это в первую очередь будет касаться сторонних библиотек), будет работать c Objective-C.

Настоятельно рекомендуется ознакомиться с основами языка Си (ссылка на предыдущую статью).

В самой первой статье на примере простого приложения для iOS мы уже сталкивались с классами, методами и объектами. Сегодня мы более подробно изучим данные конструкции и напишем более сложное приложение.

Начнем с методов, синтакси которых в общем виде выглядит так:

- (ТипВозвращаемыхДанных)названиеМетода:(ТипДанных)агрумент1продолжениеНазванияМетода:(ТипДанных)аргумент2;

И реальный пример:

- (NSArray *)shipsAtPoint:(CGPoint)bombLocation withDamage:(BOOL) damaged;

В данном случае представлен метод с двумя аргументами. Метод начинается с знака «-» для методов экземпляра класса или с «+» для методов класса. Подробнее об отличиях мы поговорим позже.

Далее идет тип возвращаемых значений, он может быть (void), если ничего не возвращается, или (id), если возвращается объект, при этом тип его может быть любым (NSString, NSArray, NSNumber, NSDictionary…). Однако рекомендуется по возможности явно указывать тип данных, для того, чтобы компилятор мог предупредить нас о возможной ошибке.

*Разрешается не писать тип возвращаемого значения и не ставить скобки в начале метода вообще, тогда это расценивается как (id). Также, когда мы указываем (IBAction), это аналогично (void), но при этом мы сообщаем Interface Builder, что этот метод будет вызван из интерфейса.

После идет название метода с маленькой буквы. Objective-C имеет не совсем обычные названия методов, которые разделены аргументами (в большинстве языков аргументы методов перечисляются после названия метода). В данном случае имя метода shipsAtPoint:withDamage:. Такой синтаксис во многом удобен, т.к. делает метод лучше читаемым.

К именам аргументов применяются те же правила, что и в С (не начинается с % и тп), при этом аргументы используются в реализации метода как локальные переменные.

Если имя метода слишком длинное, можно разделить его на строки, при этом Xcode умеет выравнивать их по знаку «:».

Как было сказано выше, есть instance methods (методы экземпляра класса) и class methods (методы класса).

Инстанс методы:

• Начинаются с «-»
• Оперируют с переменными экземпляра (инстанс-переменными) как с локальными переменными.
• Могут отправлять сообщения self и super. Разница в реализации: self использует вашу реализацию метода, super — реализацию метода суперкласса (не используется наследование).

В Objective-C объекты обмениваются между собой сообщениями, синтаксис сообщений: [получатель сообщение];

В качестве получателя выступает объект, а сообщением является метод. Сообщения могут содержать аргументы:

[объект метод:аргумент1 продолжениеНазванияМетода:аргумент2];

Например:

BOOL destroyed = [ship dropBomb:bombType at:dropPoint from:height];

Т.е. переменная destroyed получает возвращаемое методом dropBomb:at:from логическое значение типа BOOL. При этом реализуется метод объекта ship.

Методы класса адресуются классу и используются в трех случаях:

• для выделения памяти под объект, метод + (id)alloc;
• для создания объекта с помощью фабрик, например, + (Ship *)motherShip;
• методы для получения информации о структуре класса, + (int)

turrentsOnShipOfSize:(int)shipSize; (возвращает значение количества орудий на корабле определенного размера).

Методы класса не имеют доступа к инстанс-переменным.

Примеры вызова методов:

NSArray *myArray = [[NSArray alloc] init]; — создаем массив. При этом метод alloc является методом класса, а метод init — методом экземпляра класса (инстанса).

NSString *myString = [NSString stringWithString:@“Моя строка”]; — используем фабрику, при этом автоматически выделяется память и инициализируется инстанс. О преимуществах первого и второго подхода мы поговорим в статье управление памятью.

Мы немного отложили разговор о классах и объектах, чтобы наглядно представить, что это такое, и какое отношение они имеют друг к другу рассмотрим схему:

Таким образом, у класса может быть суперкласс, от которого он наследует методы, и инстанс (экземпляр класса) — конкретный объект, который использует переменные, методы и свойства определенные в классе. Существую абстрактные классы — это классы, предназначенные только для наследования, они не могут использоваться для непосредственного создания объекта.

Если взять в пример физические объекты, то «Транспортные средства» будут абстрактным суперклассом, «Легковые автомобили» — классом, а «BMW 318i» — экземпляром класса.

Класс мы описываем в .h (заголовки) и .m (реализация) файлах, синтаксис класса в файле заголовка (MyClass.h) выглядит так:

@interface MyClass : NSObject
{
NSString *name;
}
- (IBAction)valueChanged;

Здесь название класса MyClass, который наследует у суперкласса NSObject. Между фигурными скобками объявляются инстанс-переменные; а после скобок — методы и свойства.

В файле реализации (MyClass.m)

@implementation CalculatorBrain
- (IBAction)valueChanged {
//Какие-то действия
}

Cоздать экземпляр класса MyClass мы можем в другом классе, импортировав в его .h файл ссылку на #import "MyClass.h" наш класс и создав, его в файле реализации .m

@implementation MyApplicationViewController //какой-то класс
-(void)нашМетод {
MyClass *myClassObject = [[MyClass alloc] init]; //создали экземпляр класса
[myClassObject methodDefinedInMyClass]; //используем метод нашего класса
}

Свойства (property)

В редакции Objective-C 2.0 появились свойства (properties) и новый dot-синтаксис для доступа к ним. По сути свойства позволяют получать доступ к инстанс-переменным объекта используя dot-синтаксис: myObject.property

Свойства мы определяем в файле заголовка (.h) после фигурных скобок:

@interface MyClass : NSObject
{
double memoryStorage;
}
@property (double) memoryStorage; //свойство
- (IBAction)valueChanged; //методы

В файле реализации (.m) мы можем либо вручную прописать getter и setter для свойства:

– (double) memoryStorage {
return memoryStorage; //getter, просто возвращает значение memoryStorage
}
– (double) setMemoryStorage:(double)newValue {
memoryStorage = newValue; //setter
}

Либо использовать @synthesize memoryStorage, который синтезирует за нас стандартные сеттер и геттер. Если далее мы напишем свой геттер, например, то он будет использоваться, а сгенерированный геттер @synthesize будет проигнорирован. Также можно использовать

@synthesize (readonly) memoryStorage;

В этом случае генерируется только геттер (полезно для случаев, когда мы не хотим, чтобы кто-то имел доступ к изменению значения данного свойства).

Если мы хотим создать private property, свойство к которому может получить доступ только наш класс, используется следующий синтаксис. В файле реализации добавляется перед @implementation:

#import "ClassName.h"
@interface ClassName()
@property NSString *myString;
@end
@implementation ClassName
B //реализация класса
@end

Важно не забыть поставить скобки «()» после имени класса.

Как вы могли заметить, dot-синтаксис в свойствах значительно напоминает работу со структурами в Си. И это неспроста, объекты в Objective-C это по сути те же struct (структуры) в Cи, только в отличие от структур, имеющие методы.

На этом мы заканчиваем с теорией и переходим к практике!

Калькулятор

Сегодня мы создадим гораздо более сложное приложение — Калькулятор, где мы создадим собственный класс, познакомимся с новыми контроллами и
элементами UI.

Для начала создадим новый проект в Xcode и назовем его Calculator.

Создадим View-based Application — Xcode сгенерирует для нас View и Controller, а модель (наш собственный класс) мы создадим сами:

Рис. 1. View-based Application

Назовем приложение Calculator и сохраним проект в папке Developer, как мы делали в прошлом примере.

Итак, перед нами наш проект:

Рис. 2. Проект Calculator

Слева в списке файлов CalculatorViewController.h и CalculatorViewController.m — файлы заголовков и реализации нашего контроллера. CalculatorViewController.xib — наш графический интерфейс (View).

Создадим Модель. Нажмем Cmd + N или в меню File ➤ New ➤ New File

Рис. 3. Новый файл

Выберем Objective-C class, так как мы создаем наш собственный класс, нажмем далее и выберем суперкласс: NSObject и назовем наш класс CalculatorBrain. В итоге мы получим файлы CalculatorBrain.h и CalculatorBrain.m.

Начнем с нашего контроллера. Отроем файл CalculatorViewController.h.

Рекомендую также использовать Ассистент, который откроет CalculatorViewController.m параллельно с нашим файлом. Для этого нажмите Cmd + Alt + Return, а чтобы скрыть левую панель навигатора файлов, нажмите Cmd + 0:

Рис. 4. Использование Ассистента

В контроллере нам нужно создать:

1) outlets (инстанс-переменные, которые указывают на объекты в нашем графическом интерфейсе);
2) actions (методы, которые посылаются из графического интерфейса);
3) инстанс-переменную, которая указывает на нашу модель CalculatorBrain.

Добавим outlet, который будет отображать результат или текущее число, которое мы ввели:

@interface CalculatorViewController : UIViewController {
IBOutlet UILabel *display;
}
@end

Далее добавим инстанс-переменную, ссылающуюся на нашу модель, но перед этим также добавим ссылку на файл-заголовок нашей модели:

#import
#import "CalculatorBrain.h"
@interface CalculatorViewController : UIViewController {
IBOutlet UILabel *display;
CalculatorBrain *brain;
}
@end

Нам понадобится еще одна инстанс-переменная логического типа, для того, чтобы определить, что пользователь в процессе ввода числа, т.е. например, ввел 5 и далее нажал 3.

B CalculatorBrain *brain;
B BOOL userIsInTheMiddleOfTypingANumber;

Далее добавим методы. Всего в данный момент у нас предусмотрено два действия: нажатие кнопки с цифрами и нажатие кнопки с операцией:

}
- (IBAction)digitPressed:(UIButton *)sender;
- (IBAction)operationPressed:(UIButton *)sender;
@end

(UIButton *)sender в данном случае позволит нам узнать, какая кнопка нажата, так как передаст в сообщении объект UIButton в качестве аргумента.

Вот так это будет выглядеть:

Рис. 5. Header-файл контроллера

Как вы помните, контроллер отвечает за управление элементами графического интерфейса. Мы закончили с файлом-заголовком нашего контроллера и можем перейти к созданию самого интерфейса, чтобы вы наглядно себе представляли, как будет выглядеть наш калькулятор.

Нажмите Cmd+T, чтобы открыть новую вкладку (контроллер нам еще понадобится, когда мы перейдем к его реализации). Выберите CalculatorViewController.xib в выпадающем списке:

Рис. 6. CalculatorViewController.xib

Ассистент можно закрыть (Cmd + Return), а правую панель открыть (Cmd + Alt + 4). Если запомнить комбинации пока не удается, можно пользоваться кнопками в правом верхнем углу окна Xcode.

Наш контроллер расположен слева в виде светло-оранжевого полупрозрачного куба и называется File’s Owner, с ним мы и будем связывать элементы нашего UI, аналогично тому, как мы это делали в первой лекции.

Добавим UILabel и две кнопки, назовем первую «1», а вторую «+»:

Рис. 7. Создаем графический интерфейс.

Теперь свяжем данные элементы с контроллером, перетягиванием мыши с нажатым Ctrl от контроллера к UILabel и от UIButton к контроллеру. Для «1» выберем метод digitPressed, для «+» — operationPressed. Скопируем кнопки, отвечающие за цифры и переименуем их, чтобы получить клавиатуру калькулятора от 0 до 9. Аналогично поступим с операцией.

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

*Однако будьте внимательнее и не перепутайте кнопки с цифрами с кнопками с операциями.

Выделив элемент вы можете поэкспериментировать с его внешним видом, а также познакомиться с другими вкладками, присутствующими в инспекторе (панель справа).

Вот, что у вас должно получиться в итоге:

Рис. 8. UI калькулятора

Итак, перед тем как закончить работу с нашим контроллером, который обеспечит работу с интерфейсом и моделью, нам нужно написать реализацию модели (создать API, которое мы предоставим контроллеру).

Откроем новую вкладку и файл CalculatorBrain.h, используем инспектор, чтобы параллельно просматривать CalculatorBrain.m

В файле CalculatorBrain.h напишем:

@interface CalculatorBrain : NSObject {
B double operand;
}
@end

Чтобы устанавливать значение «операнда» мы можем реализовать метод — (void)setOperand:(double)aDouble; но мы используем свойства, которые рассматривали ранее:

}
@property double operand; //свойство для operand
- (double)performOperation:(NSString *)operation; //метод выполнить
операцию
@end

Контроллер может передавать нашей модели два типа значений — операцию и числовое значение. Для первого мы будем использовать метод performOperation, для второго свойство operand.

Перейдем к файлу реализации CalculatorBrain.m Синтезируем свойство operand и добавим метод:

@implementation CalculatorBrain
@synthesize operand;
- (double)performOperation:(NSString *)operation {
}
@end

Создадим обработку события «нажатие кнопки извлечения квадратного корня»:

- (double)performOperation:(NSString *)operation
{
if ([operation isEqual:@"√"])
{
operand = sqrt(operand);
}
return operand;
}

Мы только что отправили сообщение объекту operation, спросив у него соответствует ли он строке @"√". isEqual — метод класса NSObject, от которого наследуют все сабклассы, в том числе и NSString, экземпляром которого является operation.

Для операций вроде извлечения квадратного корня или 1/x получение результата возможно мгновенно, но для операций с двумя операндами 10 + 5 =
нам нужно хранить в памяти первый операнд.

Поэтому вернемся к файлу CalculatorBrain.h и добавим две инстанс-переменные:

@interface CalculatorBrain : NSObject {
double operand;
NSString *waitingOperation;
double waitingOperand;
}

Теперь в файле CalculatorBrain.m добавим поддержку операций с двумя операндами:

operand = sqrt(operand);
}
else
{
[self performWaitingOperation];
waitingOperation = operation;
waitingOperand = operand;
}
return operand;
}

Мы отправляем сообщение performWaitingOperation получателю self, т.е. объекту, который сейчас отправляет сообщение (данному экземпляру класса CalculatorBrain). performWaitingOperation будет частным (private) методом, к которому мы не будем иметь доступа из контроллера, поэтому не нужно объявлять этот метод в файле-заголовке CalculatorBrain.h.

Добавим метод performWaitingOperation, но необходимо поместить его перед performOperation, чтобы компилятор знал, что такой метод существует, поскольку он не объявлен в .h-файле:

@implementation CalculatorBrain
@synthesize operand;

- (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;
}
}
}

Мы просто в зависимости от waitingOperation выполняем соответствующее действие, а при делении мы дополнительно проверяем, что делитель не равен «0».

С моделью мы закончили, перейдем к реализации контроллера. У нас уже открыт в одной из вкладок CalculatorViewController.m. В файле вы видите методы, которые сгенерировал Xcode для нам. Они нам сейчас не нужны, выделите всё между @implementation и @end и удалите.

Нам нужно реализовать методы нажатия на цифровую кнопку и кнопку операции, также нам нужно создать экземпляр класса CalculatorBrain:

- (CalculatorBrain *)brain {
if (!brain) brain = [[CalculatorBrain alloc] init];
return brain;
}

Нам не нужно несколько объектов класса CalculatorBrain, поэтому мы перед тем как создать новый проверяем не существует ли уже такой объект.

Реализуем метод operationPressed:

- (IBAction)operationPressed:(UIButton *)sender {
if (userIsInTheMiddleOfTypingANumber) {
self.brain.operand = [display.text doubleValue];
userIsInTheMiddleOfTypingANumber = NO;
}
NSString *operation = sender.titleLabel.text;
double result = [self.brain performOperation:operation];
display.text = [NSString stringWithFormat:@"%g", result];
}

Вначале мы проверяем не в середине ли ввода чисел мы находились, если да, то мы должны передать модели (self.brain) полный операнд (число, которое мы видим на дисплее), и так как мы нажали на кнопку операции userIsInTheMiddleOfTypingANumber получит значение NO.

Далее мы выполняем получаем значение titleLabel.text (надпись на нажатой кнопке) и передаем сообщение модели выполнить вычисление. После чего мы просто выводим результат на экран.

Теперь реализуем метод digitPressed:

- (IBAction)digitPressed:(UIButton *)sender {
NSString *digit = sender.titleLabel.text;
if (userIsInTheMiddleOfTypingANumber) {
display.text = [display.text stringByAppendingString:digit];
}
else
{
display.text = digit;
userIsInTheMiddleOfTypingANumber = YES;
}
}

В начале мы получили значение названия кнопки (в данном случае какую-то цифру), далее, если мы в середине ввода числа, то мы добавляем цифру в конец строки, используя стандартный метод NSString — stringByAppendingString, подробнее об этом методе можно посмотреть в документации. И выводим новое число на экран.

Если мы только начали ввод, то мы просто выводим число на экран и меняем значение userIsInTheMiddleOfTypingANumber на YES, т.к. мы начали ввод числа.

*По умолчанию значение userIsInTheMiddleOfTypingANumber при старте программы равно NO.

Мы закончили с калькулятором и можем запускать его в симуляторе (Cmd + R) или кнопка Build and Run.

Скачать готовый калькулятор.

Задание

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

1) Добавить возможность совершать операции с плавающей точкой. Для этого вам может потребоваться добавить еще один метод floatPressed, а для отлавливания плавающей точки (нам ведь не нужно числа вроде 12.34.1244.012) посмотрите в документации класс NSRange и его метод rangeOfString.
2) У нас отсутствуют такие операции как 1/x, +/–, C (сброс). Добавление этих операций достаточно тривиально, однако нужно учесть то, что делить на ноль, например, нельзя.
3) Реализуйте систему сообщений об ошибках (при делении на ноль, попытке извлечения квадратного корня из отрицательного числа и тп). Посмотрите класс UIAlertView, представляющий стандартные информационные сообщения в iOS.

Задания повышенной сложности:

1) Добавьте возможность работы с памятью, используя кнопки M+, MC (стереть число в памяти), MR (извлечь число из памяти).
2) Добавьте возможность работы с Sin/Cos, а также переключатель режимов Deg/Rad. Для переключателя можно использовать UISegmentedControl, а для выполнения операций — стандартные мат. операции sin(operand);

25 комментариев

  1. 0
    bgeyts667

    Спасибо за полезную статью!

  2. 0

    А что то за баго-код с прибавлением NSString к double? Тучнеем бы на наковальне поровнять за такой стиль написания кода.

    • 0
      Виталий Ишкулов

      Parfeon, Где мы «прибавляем NSString к double»?

      • 0

        Виталий, Смотрим на типы данных в хеадере:

        double operand;
        NSString *waitingOperation;

        В имплементации смотрим на метод
        — (void)performWaitingOperation

        К if у меня вопросов нет
        if ([@»+» isEqual:waitingOperation])

        А вот строка, которая не есть хорошо:
        operand = waitingOperand + operand;

        Если просто написать типами перременных, то выйдет вот что:
        double = NSString + double;
        Вы что с компилятором сделали, что он вам руки сам то не правил?

        • 0
          Виталий Ишкулов

          Parfeon, NSString *waitingOperation и double waitingOperand — разные вещи, не находите?

          • 0

            Виталий, Господи, не узрел :/ Извиняюсь!
            А разве readonly не должно быть прописаным в файле хеадере а не в имплементации у synthesize? В synthesize лучше максимум что делать, это объявить кастомное имя для геттера и сеттера, тем самым скрыв имя переменной вовсе.

            readonly, read write, nonatomic, copy, retain ложны указываться у @property, но никак не у @synthesize

            • 0
              Виталий Ишкулов

              Parfeon, В коде мы этого и не использовали, только для теории. Такой синтаксис возможен. Например, когда в файле-заголовке мы не используем конструкцию @property, а прописываем метод-геттер, тогда в @synthesize мы будем генерировать реализацию только геттера с помощью (readonly).
              Не знаю, зачем такое может понадобиться на практике, однако также не вижу никаких проблем в использовании @synthesize (readonly) 🙂

          • 0

            Виталий, Кстати, как вариант, я предложил бы убрать такое понятие как waitingOperand и заменить его на result и над этим значением проделывать операции.
            Т.е. изначально оно равно 0. Когда юзер нажмет на действие, то оно уйдет в waitingOperation и после нажатия на цифру, операция будет проведена над значением в result (вроде так и должно работать) это чисто юзабилити 🙂

            • 0
              Виталий Ишкулов

              Parfeon, Но по сути у нас сейчас waitingOperand и выполняет функцию промежуточного результата. А result используется в контроллере.
              В модели нам все равно нужно иметь переменную для промежуточных результатов.
              Или я не так понял вопрос?

              • 0

                Виталий, Зачем промежуточная, если по сути можно выполнять операции непосредственно с переменной, которая служит для вывода результата.
                Т.е. изначально 0, человек нажал 3 (при отсутствии оператора, не выбирали ничего) тогда просто присваивает 3 значению result и тогда при нажатии на «=» не надо присваивать временную переменную result. Если человек выполнил последовательность «+» и «3», тогда соответственно result = result (в данный момент времени 0) + 3. В общем промежуточный не нужен, всёравно пока не нажат сброс, калькуляторы используют последнее значение вычислений.

                • 0
                  Виталий Ишкулов

                  Parfeon, О каком result идет речь? О локальной переменной в CalculatorViewController?
                  Сейчас она используется только для вывода результата, её можно убрать:
                  display.text = [NSString stringWithFormat:@»%g», [self.brain performOperation:operation]];

                  Либо то, что вы предлагаете, если я правильно понял, — сделать result инстанс-переменной в контроллере, а в модели убрать waitingOperand, а значение результата передавать из контроллера в модель как аргумент:
                  @interface CalculatorViewController : UIViewController {
                  ……
                  double result; //добавили инстанс-переменную в контроллер
                  }

                  — (IBAction)operationPressed:(UIButton *)sender {
                  ……
                  NSString *operation = sender.titleLabel.text;
                  result = [self.brain performOperation:operation withOperand:result]; //передаем result в brain
                  display.text = [NSString stringWithFormat:@»%g», result];
                  }

                  И в CalculatorBrain метод будет следующим:
                  — (double)performOperation:(NSString *)operation withOperand:(double)result;

                  Это будет работать, вопрос в том, что мы выиграли от того, что одну инстанс-переменную в модели заменили другой в контроллере, и при этом запутали код?

                  PS если вы имели в виду что-то другое, лучше приведите кусок кода, чтобы я вас лучше понял.)

  3. 0

    как компилить? что бы на айфоне запустить

    • 0
      Виталий Ишкулов

      ivan, Чтобы на айфоне запустить, нужно иметь подписку разработчика под iOS ($99 в год на сайте http://developer.apple.com), если её нет, то только на симуляторе можно запустить.
      Также существуют способы с помощью джейл-брейка запускать свои приложения.

  4. 0
    Королев Михаил

    iWoyo: @AppleInsider_ru что-то очень мне напоминает эта статья стэнфордский cs193p 🙂
    Ув. Виталий Ишкулов хотя бы коммент. в коде поставил.

  5. 0

    Vitalii ya s bolshim udovolstviem 4itayu vashi stat’i , u menya k Vam delovoe predlojenie , delo kasaetsa razrabotki app dlya musicantov , kakovim i yavlyayus k tomy je zvukoinjener i eshe mnogo 4ego delayu na etoi interestnoi nive , esli vas zainteresyet -obsudim , moi skype : tarasb… tel. +393202226260 (italia) e-mail : tarasmusica@hotmail.com , s uvajeniem taras

  6. 0

    Автор молодец, профессиональный перевод и оформление лекций Стенфордского университета (http://www.stanford.edu/class/cs193p/cgi-bin/drupal/). Не в упрек сказано, действительно человек ответственно и качественно перевел материал первых лекций курса, у меня идея перевести субтитры лекция на русский (с методикой и ПО я уже разобрался как это делать, осталось сделать перевод, найти время на это )))), к автору вопрос — делал ли он перевод остальных лекций и вообще вопрос по переводу, можно объединить усилия и сделать русский вариант вышеуказанных лекций.

    • 0
      Виталий Ишкулов

      dmitry3000, Спасибо, но здесь скорее не перевод, а использование материалов.
      Уведомления о комментариях не приходят, извиняюсь за столь поздний ответ. Переводами текстов/субтитров никогда особо не занимался, разве что во время учебы в универе)) Но заниматься локализацией приложений приходилось, времени отнимает жутко много. Хотя с субтитрами наверное будет полегче, не нужно постоянно проверять как это будет смотреться в интерфейсе и насколько понятен смысл контрола после перевода.)

  7. 0

    Класс! Будет еще?

  8. 0
    Михаил

    да нам неграмотным очень бы помогло
    Оформить статью намного сложнее чем написать текст к субтитрам
    субтитры бы очень помогли в освоении cs193p очень качественные лекции
    Автор молодец ничего не скажешь %)

  9. 0

    Дайте подсказку как реализовать сброс через кнопку С. Вроде как в ноли все сбрасываю, а все равно при действиях цифра->операция->цифра->’С’ выдает результат как при нажатии ‘=’

    • 0
      Виталий Ишкулов

      DeL, А что вы в нули сбрасываете?

      Можно добавить условие в метод
      — (double)performOperation:(NSString *)operation {
      if ([operation isEqual:@»√»])
      {
      operand = sqrt(operand);
      }
      else if ([@»C» isEqual:operation]) //очищаем все переменные и выводим 0 в качестве результата
      {
      operand = 0;
      waitingOperand = 0;
      waitingOperation = nil;
      }
      else
      {
      [self performWaitingOperation];
      waitingOperation = operation;
      waitingOperand = operand;
      }

      return operand;
      }

  10. 0

    Простите, не написал пожалуйста ) Пожалуйста, подскажите

  11. 0

    Спасибо за статью! Подскажите, плз, как реализовать кнопку «назад, т.е. убрать последний введенный знак?»

  12. 0

    Не знал что тут такие статьи были, хотя на тот момент уже и разработкой занимался и о сайте этом знал, хоть и не был постоянным читателем.

    Посмотрел на скриншоты — такая ностальгия…

Авторизуйтесь Чтобы оставить комментарий