Skip to main content
added 27 characters in body; edited title
Source Link
Jamal
  • 34.9k
  • 13
  • 133
  • 237

How can I refine this code so that it complies with MVC (specifically, separating view and model) App for typing chemical equations to the screen

Background

I am making an app that allows the user to type chemical equations (H2OH2O + NaCl) to the screen. The user taps, inside a tableviewcell, a custom textfield and types on a custom keyboard (both subclasses of uiviews). The vc doesn't have an actual instance of the keyboard, and the keyboard instead appears because it is the inputView property of the textfield. Also, the text field doesn't add text directly to itself, but instead places each compound inside a label, which is then added as its subview. Currently, the textfield stores the order of compounds internally, and the specific text is stored inside the labels. When the view controller needs the text, it creates a model class and takes the data from the textfield and stores it into the model.

According to mvcMVC, the views shouldn't be handling their own data, something that my textfield and label seriously infringe. I need help determining what parts of my code to transfer into the view controller and model. Some specific questions I have are

Keyboard --

It appears when textfield is tapped. When a key is pressed, it notifies its delegate, sometimes sending a key as a parameter.

Textfield complies to the KeyInput protocol. When an element on the keyboard is pressed consecutively, the subscript increases (H + H + O would result in H2O); similarly pressing the '+' consecutively will increase the charge of ions. This process is handled in the protocol methods, and knowledge of the last keys pressed are stored inside the actual labels. Additionally, the element order, charge, and physical state are all stored as separate strings as properties of the label, and the textfield appends all the strings to create the complete formula.

- (id)initWithCoder:(NSCoder *)coder {

self = [super initWithCoder:coder];
if (self) {
    
    self.userInteractionEnabled = YES;
    NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:@"FormulaKeyboard" owner:self options:nil];
    
    for (id object in bundle) {
        if ([object isKindOfClass:[FormulaKeyboard class]])
            keyboard = (FormulaKeyboard *)object;
    }   
    
    self.inputView = keyboard;
    keyboard.delegate = self;
    consecutiveElementCount = 0;
    chargeIndex = 4;
    self.equationOrder = [[NSMutableArray alloc] init];
    startingPoint = 5;
    subscriptList = [[NSArray alloc] initWithObjects:@"", @"₂", @"₃", @"₄", @"₅", @"₆", @"₇", @"₈", @"₉", @"₁₀", @"₁₁", @"₁₂", nil]; 
    
}
return self;
}



- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

[self becomeFirstResponder];
}


- (BOOL)canBecomeFirstResponder {

return YES;

} It complies to the KeyInput protocol. When an element on the keyboard is pressed consecutively, the subscript increases (H + H + O would result in H2O); similarly pressing the '+' consecutively will increase the charge of ions. This process is handled in the protocol methods, and knowledge of the last keys pressed are stored inside the actual labels. Additionally, the element order, charge, and physical state are all stored as separate strings as properties of the label, and the textfield appends all the strings to create the complete formula.

- (id)initWithCoder:(NSCoder *)coder {

self = [super initWithCoder:coder];
if (self) {
    
    self.userInteractionEnabled = YES;
    NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:@"FormulaKeyboard" owner:self options:nil];
    
    for (id object in bundle) {
        if ([object isKindOfClass:[FormulaKeyboard class]])
            keyboard = (FormulaKeyboard *)object;
    }   
    
    self.inputView = keyboard;
    keyboard.delegate = self;
    consecutiveElementCount = 0;
    chargeIndex = 4;
    self.equationOrder = [[NSMutableArray alloc] init];
    startingPoint = 5;
    subscriptList = [[NSArray alloc] initWithObjects:@"", @"₂", @"₃", @"₄", @"₅", @"₆", @"₇", @"₈", @"₉", @"₁₀", @"₁₁", @"₁₂", nil]; 
    
}
return self;
}



- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

    [self becomeFirstResponder];
    }


    - (BOOL)canBecomeFirstResponder {

        return YES;
}

#pragma mark -- KeyInput Protocol Methods

- (void)newCompound {

//add plus sign
if (self.currentLabel != nil) {
    startingPoint = currentLabel.frame.origin.x + currentLabel.frame.size.width + 3;
    UILabel *plus = [[UILabel alloc] initWithFrame:CGRectMake(startingPoint, 5, 10, 10)];
    plus.text = @"+";
    plus.font = [UIFont systemFontOfSize:13];
    [plus sizeToFit];
    [self addSubview:plus];
    startingPoint = plus.frame.origin.x + plus.frame.size.width + 3;
}

self.currentLabel = [[FormulaLabel alloc] initWithFrame:CGRectMake(startingPoint, 2, 10, 22)]; 
self.currentLabel.elementFormula = [[NSMutableString  stringWithString:@""] retain];
self.currentLabel.charge = [[NSString alloc] initWithString:@""];
self.currentLabel.state = [[NSString alloc] initWithString:@""];
[self addSubview:self.currentLabel];
[equationOrder addObject:self.currentLabel];
}


- (void)addElement:(NSString *)currentElement {

if (self.currentLabel != nil) {
    
    if (![currentElement isEqualToString:self.currentLabel.lastElementPressed])  {
        // when new element is pressed 
        [self.currentLabel.elementFormula appendString:currentElement];
        self.currentLabel.lastElementPressed = currentElement;
        consecutiveElementCount = 1;
        subscriptLength = 0;
        [self.currentLabel.elementOrder addObject:currentElement];
        [self.currentLabel.subscriptOrder addObject:[subscriptList objectAtIndex:0]];
    }
    // don't increase subscript after 12 or of open parentheses
    else if (consecutiveElementCount < 12 && (![currentElement isEqualToString:@"("])) {                           consecutiveElementCount++;
        [self.currentLabel.subscriptOrder replaceObjectAtIndex:[self.currentLabel.subscriptOrder count] - 1 withObject:[subscriptList objectAtIndex:consecutiveElementCount - 1]];
    }
    
    NSRange range = NSMakeRange (([self.currentLabel.elementFormula length] - subscriptLength), subscriptLength);
    [self.currentLabel.elementFormula deleteCharactersInRange:range];
    NSString *tempSubscript = [subscriptList objectAtIndex:consecutiveElementCount - 1];
    [self.currentLabel.elementFormula appendString:tempSubscript];
    subscriptLength = [tempSubscript length];
    [self synthesizeFormula];
}
}


- (void)changeCharge:(NSString *)chargeIncrease {

if (self.currentLabel != nil) {

    NSArray *chargeList = [[[NSArray alloc] initWithObjects:@"⁴⁻", @"³⁻", @"²⁻", @"⁻", @"", @"⁺", @"²⁺", @"³⁺", @"⁴⁺", nil] autorelease];
    
    if ([chargeIncrease isEqualToString:@"+"] && chargeIndex < ([chargeList count] - 1)) 
       chargeIndex += 1;
    
      if ([chargeIncrease isEqualToString:@"-"] && chargeIndex > 0) 
        chargeIndex -= 1;
       
    self.currentLabel.charge = [chargeList objectAtIndex:chargeIndex];
    [self synthesizeFormula];
}
}

- (void) changeState:(NSString *)stateName {

if (self.currentLabel.state != nil) {
    self.currentLabel.state = stateName;
    [self synthesizeFormula];
}
}

- (void)deleteCharacter {

if (![self.currentLabel.elementFormula isEqualToString:@""] && (self.currentLabel != nil)) {
   
    NSString *lastElement = [self.currentLabel.elementOrder objectAtIndex:[self.currentLabel.elementOrder count] - 1];
    NSString *lastSubscript = [self.currentLabel.subscriptOrder objectAtIndex:[self.currentLabel.subscriptOrder count] - 1];
    int removeLength = [lastElement length] + [lastSubscript length];
    NSRange range = NSMakeRange([self.currentLabel.elementFormula length]- removeLength, removeLength);
    [self.currentLabel.elementFormula deleteCharactersInRange:range];
    [self.currentLabel.elementOrder removeObjectAtIndex:[self.currentLabel.elementOrder count]-1];
    [self.currentLabel.subscriptOrder removeObjectAtIndex:[self.currentLabel.subscriptOrder count]-1];
    [self synthesizeFormula];
}
}

- (void)synthesizeFormula {
NSMutableString *synthesizedFormula = [[[NSMutableString alloc] initWithString:self.currentLabel.elementFormula] autorelease];
[synthesizedFormula appendString:self.currentLabel.charge];
[synthesizedFormula appendString:self.currentLabel.state];
self.currentLabel.text = synthesizedFormula;
[self.currentLabel sizeToFit];
}

- (void)closeKeyboard {

[self resignFirstResponder];
}

Label 

Where the individual compounds are stored.

How can I refine this code so that it complies with MVC (specifically, separating view and model)

Background

I am making an app that allows the user to type chemical equations (H2O + NaCl) to the screen. The user taps, inside a tableviewcell, a custom textfield and types on a custom keyboard (both subclasses of uiviews). The vc doesn't have an actual instance of the keyboard, and the keyboard instead appears because it is the inputView property of the textfield. Also, the text field doesn't add text directly to itself, but instead places each compound inside a label, which is then added as its subview. Currently, the textfield stores the order of compounds internally, and the specific text is stored inside the labels. When the view controller needs the text, it creates a model class and takes the data from the textfield and stores it into the model.

According to mvc, the views shouldn't be handling their own data, something that my textfield and label seriously infringe. I need help determining what parts of my code to transfer into the view controller and model. Some specific questions I have are

Keyboard -- appears when textfield is tapped. When a key is pressed, it notifies its delegate, sometimes sending a key as a parameter.

Textfield complies to the KeyInput protocol. When an element on the keyboard is pressed consecutively, the subscript increases (H + H + O would result in H2O); similarly pressing the '+' consecutively will increase the charge of ions. This process is handled in the protocol methods, and knowledge of the last keys pressed are stored inside the actual labels. Additionally, the element order, charge, and physical state are all stored as separate strings as properties of the label, and the textfield appends all the strings to create the complete formula.

- (id)initWithCoder:(NSCoder *)coder {

self = [super initWithCoder:coder];
if (self) {
    
    self.userInteractionEnabled = YES;
    NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:@"FormulaKeyboard" owner:self options:nil];
    
    for (id object in bundle) {
        if ([object isKindOfClass:[FormulaKeyboard class]])
            keyboard = (FormulaKeyboard *)object;
    }   
    
    self.inputView = keyboard;
    keyboard.delegate = self;
    consecutiveElementCount = 0;
    chargeIndex = 4;
    self.equationOrder = [[NSMutableArray alloc] init];
    startingPoint = 5;
    subscriptList = [[NSArray alloc] initWithObjects:@"", @"₂", @"₃", @"₄", @"₅", @"₆", @"₇", @"₈", @"₉", @"₁₀", @"₁₁", @"₁₂", nil]; 
    
}
return self;
}



- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

[self becomeFirstResponder];
}


- (BOOL)canBecomeFirstResponder {

return YES;

}

#pragma mark -- KeyInput Protocol Methods

- (void)newCompound {

//add plus sign
if (self.currentLabel != nil) {
    startingPoint = currentLabel.frame.origin.x + currentLabel.frame.size.width + 3;
    UILabel *plus = [[UILabel alloc] initWithFrame:CGRectMake(startingPoint, 5, 10, 10)];
    plus.text = @"+";
    plus.font = [UIFont systemFontOfSize:13];
    [plus sizeToFit];
    [self addSubview:plus];
    startingPoint = plus.frame.origin.x + plus.frame.size.width + 3;
}

self.currentLabel = [[FormulaLabel alloc] initWithFrame:CGRectMake(startingPoint, 2, 10, 22)]; 
self.currentLabel.elementFormula = [[NSMutableString  stringWithString:@""] retain];
self.currentLabel.charge = [[NSString alloc] initWithString:@""];
self.currentLabel.state = [[NSString alloc] initWithString:@""];
[self addSubview:self.currentLabel];
[equationOrder addObject:self.currentLabel];
}


- (void)addElement:(NSString *)currentElement {

if (self.currentLabel != nil) {
    
    if (![currentElement isEqualToString:self.currentLabel.lastElementPressed])  {
        // when new element is pressed 
        [self.currentLabel.elementFormula appendString:currentElement];
        self.currentLabel.lastElementPressed = currentElement;
        consecutiveElementCount = 1;
        subscriptLength = 0;
        [self.currentLabel.elementOrder addObject:currentElement];
        [self.currentLabel.subscriptOrder addObject:[subscriptList objectAtIndex:0]];
    }
    // don't increase subscript after 12 or of open parentheses
    else if (consecutiveElementCount < 12 && (![currentElement isEqualToString:@"("])) {                           consecutiveElementCount++;
        [self.currentLabel.subscriptOrder replaceObjectAtIndex:[self.currentLabel.subscriptOrder count] - 1 withObject:[subscriptList objectAtIndex:consecutiveElementCount - 1]];
    }
    
    NSRange range = NSMakeRange (([self.currentLabel.elementFormula length] - subscriptLength), subscriptLength);
    [self.currentLabel.elementFormula deleteCharactersInRange:range];
    NSString *tempSubscript = [subscriptList objectAtIndex:consecutiveElementCount - 1];
    [self.currentLabel.elementFormula appendString:tempSubscript];
    subscriptLength = [tempSubscript length];
    [self synthesizeFormula];
}
}


- (void)changeCharge:(NSString *)chargeIncrease {

if (self.currentLabel != nil) {

    NSArray *chargeList = [[[NSArray alloc] initWithObjects:@"⁴⁻", @"³⁻", @"²⁻", @"⁻", @"", @"⁺", @"²⁺", @"³⁺", @"⁴⁺", nil] autorelease];
    
    if ([chargeIncrease isEqualToString:@"+"] && chargeIndex < ([chargeList count] - 1)) 
       chargeIndex += 1;
    
      if ([chargeIncrease isEqualToString:@"-"] && chargeIndex > 0) 
        chargeIndex -= 1;
       
    self.currentLabel.charge = [chargeList objectAtIndex:chargeIndex];
    [self synthesizeFormula];
}
}

- (void) changeState:(NSString *)stateName {

if (self.currentLabel.state != nil) {
    self.currentLabel.state = stateName;
    [self synthesizeFormula];
}
}

- (void)deleteCharacter {

if (![self.currentLabel.elementFormula isEqualToString:@""] && (self.currentLabel != nil)) {
   
    NSString *lastElement = [self.currentLabel.elementOrder objectAtIndex:[self.currentLabel.elementOrder count] - 1];
    NSString *lastSubscript = [self.currentLabel.subscriptOrder objectAtIndex:[self.currentLabel.subscriptOrder count] - 1];
    int removeLength = [lastElement length] + [lastSubscript length];
    NSRange range = NSMakeRange([self.currentLabel.elementFormula length]- removeLength, removeLength);
    [self.currentLabel.elementFormula deleteCharactersInRange:range];
    [self.currentLabel.elementOrder removeObjectAtIndex:[self.currentLabel.elementOrder count]-1];
    [self.currentLabel.subscriptOrder removeObjectAtIndex:[self.currentLabel.subscriptOrder count]-1];
    [self synthesizeFormula];
}
}

- (void)synthesizeFormula {
NSMutableString *synthesizedFormula = [[[NSMutableString alloc] initWithString:self.currentLabel.elementFormula] autorelease];
[synthesizedFormula appendString:self.currentLabel.charge];
[synthesizedFormula appendString:self.currentLabel.state];
self.currentLabel.text = synthesizedFormula;
[self.currentLabel sizeToFit];
}

- (void)closeKeyboard {

[self resignFirstResponder];
}

Label Where the individual compounds are stored.

App for typing chemical equations to the screen

I am making an app that allows the user to type chemical equations (H2O + NaCl) to the screen. The user taps, inside a tableviewcell, a custom textfield and types on a custom keyboard (both subclasses of uiviews). The vc doesn't have an actual instance of the keyboard, and the keyboard instead appears because it is the inputView property of the textfield. Also, the text field doesn't add text directly to itself, but instead places each compound inside a label, which is then added as its subview. Currently, the textfield stores the order of compounds internally, and the specific text is stored inside the labels. When the view controller needs the text, it creates a model class and takes the data from the textfield and stores it into the model.

According to MVC, the views shouldn't be handling their own data, something that my textfield and label seriously infringe. I need help determining what parts of my code to transfer into the view controller and model. Some specific questions I have are

Keyboard

It appears when textfield is tapped. When a key is pressed, it notifies its delegate, sometimes sending a key as a parameter.

Textfield

It complies to the KeyInput protocol. When an element on the keyboard is pressed consecutively, the subscript increases (H + H + O would result in H2O); similarly pressing the '+' consecutively will increase the charge of ions. This process is handled in the protocol methods, and knowledge of the last keys pressed are stored inside the actual labels. Additionally, the element order, charge, and physical state are all stored as separate strings as properties of the label, and the textfield appends all the strings to create the complete formula.

- (id)initWithCoder:(NSCoder *)coder {

self = [super initWithCoder:coder];
if (self) {
    
    self.userInteractionEnabled = YES;
    NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:@"FormulaKeyboard" owner:self options:nil];
    
    for (id object in bundle) {
        if ([object isKindOfClass:[FormulaKeyboard class]])
            keyboard = (FormulaKeyboard *)object;
    }   
    
    self.inputView = keyboard;
    keyboard.delegate = self;
    consecutiveElementCount = 0;
    chargeIndex = 4;
    self.equationOrder = [[NSMutableArray alloc] init];
    startingPoint = 5;
    subscriptList = [[NSArray alloc] initWithObjects:@"", @"₂", @"₃", @"₄", @"₅", @"₆", @"₇", @"₈", @"₉", @"₁₀", @"₁₁", @"₁₂", nil]; 
    
}
return self;
}



- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

    [self becomeFirstResponder];
    }


    - (BOOL)canBecomeFirstResponder {

        return YES;
}

#pragma mark -- KeyInput Protocol Methods

- (void)newCompound {

//add plus sign
if (self.currentLabel != nil) {
    startingPoint = currentLabel.frame.origin.x + currentLabel.frame.size.width + 3;
    UILabel *plus = [[UILabel alloc] initWithFrame:CGRectMake(startingPoint, 5, 10, 10)];
    plus.text = @"+";
    plus.font = [UIFont systemFontOfSize:13];
    [plus sizeToFit];
    [self addSubview:plus];
    startingPoint = plus.frame.origin.x + plus.frame.size.width + 3;
}

self.currentLabel = [[FormulaLabel alloc] initWithFrame:CGRectMake(startingPoint, 2, 10, 22)]; 
self.currentLabel.elementFormula = [[NSMutableString  stringWithString:@""] retain];
self.currentLabel.charge = [[NSString alloc] initWithString:@""];
self.currentLabel.state = [[NSString alloc] initWithString:@""];
[self addSubview:self.currentLabel];
[equationOrder addObject:self.currentLabel];
}


- (void)addElement:(NSString *)currentElement {

if (self.currentLabel != nil) {
    
    if (![currentElement isEqualToString:self.currentLabel.lastElementPressed])  {
        // when new element is pressed 
        [self.currentLabel.elementFormula appendString:currentElement];
        self.currentLabel.lastElementPressed = currentElement;
        consecutiveElementCount = 1;
        subscriptLength = 0;
        [self.currentLabel.elementOrder addObject:currentElement];
        [self.currentLabel.subscriptOrder addObject:[subscriptList objectAtIndex:0]];
    }
    // don't increase subscript after 12 or of open parentheses
    else if (consecutiveElementCount < 12 && (![currentElement isEqualToString:@"("])) {                           consecutiveElementCount++;
        [self.currentLabel.subscriptOrder replaceObjectAtIndex:[self.currentLabel.subscriptOrder count] - 1 withObject:[subscriptList objectAtIndex:consecutiveElementCount - 1]];
    }
    
    NSRange range = NSMakeRange (([self.currentLabel.elementFormula length] - subscriptLength), subscriptLength);
    [self.currentLabel.elementFormula deleteCharactersInRange:range];
    NSString *tempSubscript = [subscriptList objectAtIndex:consecutiveElementCount - 1];
    [self.currentLabel.elementFormula appendString:tempSubscript];
    subscriptLength = [tempSubscript length];
    [self synthesizeFormula];
}
}


- (void)changeCharge:(NSString *)chargeIncrease {

if (self.currentLabel != nil) {

    NSArray *chargeList = [[[NSArray alloc] initWithObjects:@"⁴⁻", @"³⁻", @"²⁻", @"⁻", @"", @"⁺", @"²⁺", @"³⁺", @"⁴⁺", nil] autorelease];
    
    if ([chargeIncrease isEqualToString:@"+"] && chargeIndex < ([chargeList count] - 1)) 
       chargeIndex += 1;
    
      if ([chargeIncrease isEqualToString:@"-"] && chargeIndex > 0) 
        chargeIndex -= 1;
       
    self.currentLabel.charge = [chargeList objectAtIndex:chargeIndex];
    [self synthesizeFormula];
}
}

- (void) changeState:(NSString *)stateName {

if (self.currentLabel.state != nil) {
    self.currentLabel.state = stateName;
    [self synthesizeFormula];
}
}

- (void)deleteCharacter {

if (![self.currentLabel.elementFormula isEqualToString:@""] && (self.currentLabel != nil)) {
   
    NSString *lastElement = [self.currentLabel.elementOrder objectAtIndex:[self.currentLabel.elementOrder count] - 1];
    NSString *lastSubscript = [self.currentLabel.subscriptOrder objectAtIndex:[self.currentLabel.subscriptOrder count] - 1];
    int removeLength = [lastElement length] + [lastSubscript length];
    NSRange range = NSMakeRange([self.currentLabel.elementFormula length]- removeLength, removeLength);
    [self.currentLabel.elementFormula deleteCharactersInRange:range];
    [self.currentLabel.elementOrder removeObjectAtIndex:[self.currentLabel.elementOrder count]-1];
    [self.currentLabel.subscriptOrder removeObjectAtIndex:[self.currentLabel.subscriptOrder count]-1];
    [self synthesizeFormula];
}
}

- (void)synthesizeFormula {
NSMutableString *synthesizedFormula = [[[NSMutableString alloc] initWithString:self.currentLabel.elementFormula] autorelease];
[synthesizedFormula appendString:self.currentLabel.charge];
[synthesizedFormula appendString:self.currentLabel.state];
self.currentLabel.text = synthesizedFormula;
[self.currentLabel sizeToFit];
}

- (void)closeKeyboard {

[self resignFirstResponder];
}

Label 

Where the individual compounds are stored.

Source Link
Mahir
  • 361
  • 1
  • 10

How can I refine this code so that it complies with MVC (specifically, separating view and model)

Background

I am making an app that allows the user to type chemical equations (H2O + NaCl) to the screen. The user taps, inside a tableviewcell, a custom textfield and types on a custom keyboard (both subclasses of uiviews). The vc doesn't have an actual instance of the keyboard, and the keyboard instead appears because it is the inputView property of the textfield. Also, the text field doesn't add text directly to itself, but instead places each compound inside a label, which is then added as its subview. Currently, the textfield stores the order of compounds internally, and the specific text is stored inside the labels. When the view controller needs the text, it creates a model class and takes the data from the textfield and stores it into the model.

According to mvc, the views shouldn't be handling their own data, something that my textfield and label seriously infringe. I need help determining what parts of my code to transfer into the view controller and model. Some specific questions I have are

  • what class should implement the keyboard's protocol
  • whether the vc or the textfield should add labels as the textfield's subviews
  • how much data should be kept in the model, and how much (if any) can remain in the textfield/labels. -- how the vc should exchange info between model and textfields

enter image description here

Currently, the code is organized as follows:

Keyboard -- appears when textfield is tapped. When a key is pressed, it notifies its delegate, sometimes sending a key as a parameter.

@protocol KeyInput <UITextInputTraits>

- (void) newCompound;
-(void) closeKeyboard;
- (void) addElement:(NSString*) currentElement;
- (void) changeCharge:(NSString*) chargeIncrease;
- (void) changeState:(NSString*) stateName;
- (void) deleteCharacter;

@end

@interface FormulaKeyboard : UIView {

id <KeyInput> delegate; 
UIButton *element1;
}

@property (nonatomic, retain) id <KeyInput> delegate;

-(IBAction) addCompound:(id)sender;   //calls [delegate newCompound]
-(IBAction) close:(id)sender;      
- (IBAction) deletePressed:(id)sender;
-(IBAction) element1Pressed:(id)sender; // calls [delegate addElement:@"Na"]
-(IBAction) element2Pressed:(id)sender;
-(IBAction) element3Pressed:(id)sender;
-(IBAction) element4Pressed:(id)sender;
-(IBAction) openParenPressed:(id)sender;  // rest are similar
-(IBAction) closedParenPressed:(id)sender;
-(IBAction) plusPressed:(id)sender;
-(IBAction) minusPressed:(id)sender;
-(IBAction) solidPressed:(id)sender;
-(IBAction) liquidPressed:(id)sender;
-(IBAction) gasPressed:(id)sender;
-(IBAction) aqueousPressed:(id)sender;

Textfield complies to the KeyInput protocol. When an element on the keyboard is pressed consecutively, the subscript increases (H + H + O would result in H2O); similarly pressing the '+' consecutively will increase the charge of ions. This process is handled in the protocol methods, and knowledge of the last keys pressed are stored inside the actual labels. Additionally, the element order, charge, and physical state are all stored as separate strings as properties of the label, and the textfield appends all the strings to create the complete formula.

- (id)initWithCoder:(NSCoder *)coder {

self = [super initWithCoder:coder];
if (self) {
    
    self.userInteractionEnabled = YES;
    NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:@"FormulaKeyboard" owner:self options:nil];
    
    for (id object in bundle) {
        if ([object isKindOfClass:[FormulaKeyboard class]])
            keyboard = (FormulaKeyboard *)object;
    }   
    
    self.inputView = keyboard;
    keyboard.delegate = self;
    consecutiveElementCount = 0;
    chargeIndex = 4;
    self.equationOrder = [[NSMutableArray alloc] init];
    startingPoint = 5;
    subscriptList = [[NSArray alloc] initWithObjects:@"", @"₂", @"₃", @"₄", @"₅", @"₆", @"₇", @"₈", @"₉", @"₁₀", @"₁₁", @"₁₂", nil]; 
    
}
return self;
}



- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

[self becomeFirstResponder];
}


- (BOOL)canBecomeFirstResponder {

return YES;

}

#pragma mark -- KeyInput Protocol Methods

- (void)newCompound {

//add plus sign
if (self.currentLabel != nil) {
    startingPoint = currentLabel.frame.origin.x + currentLabel.frame.size.width + 3;
    UILabel *plus = [[UILabel alloc] initWithFrame:CGRectMake(startingPoint, 5, 10, 10)];
    plus.text = @"+";
    plus.font = [UIFont systemFontOfSize:13];
    [plus sizeToFit];
    [self addSubview:plus];
    startingPoint = plus.frame.origin.x + plus.frame.size.width + 3;
}

self.currentLabel = [[FormulaLabel alloc] initWithFrame:CGRectMake(startingPoint, 2, 10, 22)]; 
self.currentLabel.elementFormula = [[NSMutableString  stringWithString:@""] retain];
self.currentLabel.charge = [[NSString alloc] initWithString:@""];
self.currentLabel.state = [[NSString alloc] initWithString:@""];
[self addSubview:self.currentLabel];
[equationOrder addObject:self.currentLabel];
}


- (void)addElement:(NSString *)currentElement {

if (self.currentLabel != nil) {
    
    if (![currentElement isEqualToString:self.currentLabel.lastElementPressed])  {
        // when new element is pressed 
        [self.currentLabel.elementFormula appendString:currentElement];
        self.currentLabel.lastElementPressed = currentElement;
        consecutiveElementCount = 1;
        subscriptLength = 0;
        [self.currentLabel.elementOrder addObject:currentElement];
        [self.currentLabel.subscriptOrder addObject:[subscriptList objectAtIndex:0]];
    }
    // don't increase subscript after 12 or of open parentheses
    else if (consecutiveElementCount < 12 && (![currentElement isEqualToString:@"("])) {                           consecutiveElementCount++;
        [self.currentLabel.subscriptOrder replaceObjectAtIndex:[self.currentLabel.subscriptOrder count] - 1 withObject:[subscriptList objectAtIndex:consecutiveElementCount - 1]];
    }
    
    NSRange range = NSMakeRange (([self.currentLabel.elementFormula length] - subscriptLength), subscriptLength);
    [self.currentLabel.elementFormula deleteCharactersInRange:range];
    NSString *tempSubscript = [subscriptList objectAtIndex:consecutiveElementCount - 1];
    [self.currentLabel.elementFormula appendString:tempSubscript];
    subscriptLength = [tempSubscript length];
    [self synthesizeFormula];
}
}


- (void)changeCharge:(NSString *)chargeIncrease {

if (self.currentLabel != nil) {

    NSArray *chargeList = [[[NSArray alloc] initWithObjects:@"⁴⁻", @"³⁻", @"²⁻", @"⁻", @"", @"⁺", @"²⁺", @"³⁺", @"⁴⁺", nil] autorelease];
    
    if ([chargeIncrease isEqualToString:@"+"] && chargeIndex < ([chargeList count] - 1)) 
       chargeIndex += 1;
    
      if ([chargeIncrease isEqualToString:@"-"] && chargeIndex > 0) 
        chargeIndex -= 1;
       
    self.currentLabel.charge = [chargeList objectAtIndex:chargeIndex];
    [self synthesizeFormula];
}
}

- (void) changeState:(NSString *)stateName {

if (self.currentLabel.state != nil) {
    self.currentLabel.state = stateName;
    [self synthesizeFormula];
}
}

- (void)deleteCharacter {

if (![self.currentLabel.elementFormula isEqualToString:@""] && (self.currentLabel != nil)) {
   
    NSString *lastElement = [self.currentLabel.elementOrder objectAtIndex:[self.currentLabel.elementOrder count] - 1];
    NSString *lastSubscript = [self.currentLabel.subscriptOrder objectAtIndex:[self.currentLabel.subscriptOrder count] - 1];
    int removeLength = [lastElement length] + [lastSubscript length];
    NSRange range = NSMakeRange([self.currentLabel.elementFormula length]- removeLength, removeLength);
    [self.currentLabel.elementFormula deleteCharactersInRange:range];
    [self.currentLabel.elementOrder removeObjectAtIndex:[self.currentLabel.elementOrder count]-1];
    [self.currentLabel.subscriptOrder removeObjectAtIndex:[self.currentLabel.subscriptOrder count]-1];
    [self synthesizeFormula];
}
}

- (void)synthesizeFormula {
NSMutableString *synthesizedFormula = [[[NSMutableString alloc] initWithString:self.currentLabel.elementFormula] autorelease];
[synthesizedFormula appendString:self.currentLabel.charge];
[synthesizedFormula appendString:self.currentLabel.state];
self.currentLabel.text = synthesizedFormula;
[self.currentLabel sizeToFit];
}

- (void)closeKeyboard {

[self resignFirstResponder];
}

Label Where the individual compounds are stored.

@interface FormulaLabel : UILabel {

NSMutableString *elementFormula;
NSString *charge;
NSString *state;
NSString *lastElementPressed;
NSMutableArray *elementOrder;
NSMutableArray *subscriptOrder;

}

@property (nonatomic, retain) NSMutableString *elementFormula;
@property (nonatomic, retain) NSString *charge;
@property (nonatomic, retain) NSString *state;
@property (nonatomic, retain) NSString *lastElementPressed;
@property (nonatomic, retain) NSMutableArray *subscriptOrder;
@property (nonatomic, retain) NSMutableArray *elementOrder;