Working with UITextView with SQLite iPhone App

Source

(c) kevin languedoc (klanguedoc)

This iPhone app demonstrates how to store information in a SQLite database using UITextView from one View Controller and then be able to shift forwards and backwards to view the stored data from another View Controller with another UITextView. The main feature of this app is to provide the code to shift forwards and backwards through SQLite data for display in the appropriate fields in a UIViewController.

In fact the viewing of the data is actually from a NSMutableArray which is populated when the app is launched using a SQL SELECT query. To provide the forward and backward transversal mechanism I used a variable to keep track of the current position in the array using two UIToolBarItems (Buttons) placed on an UIToolbar located at the bottom of the main View Controller.

To add new items to the app, you will need to click on the “Edit” tab in the Tabbed Navigator Controller. The second View Controller features an editable UITextField and an UITextView.

Create Project and Storyboard

Create a Single View application project from the available iOS templates. Once the project is created along with the Storyboard and the initial ViewController add the SQLite library to the Linked Libraries and Frameworks. To add the library elect the project root and in the Summary page on the right in the IDE, scroll down to the Linked Libraries and Frameworks section. Click on the “+” button in search for the library using the popover. Select the “libsqlite3.0.dylib” file and “Add” to add the library to the project. Next drag the library to the Frameworks group.

Also add a new UIViewController as subclass of the UIViewController. To add the file either select File-New-File or ctrl+N. In the template utility, select the Objective-C class. Name the UIViewController, readBioViewController and unselect the Xib option if it is selected. Once the files (.h and .m) create a new group called controllers and drag all the view controllers in the Project Navigator to the group. The logic will be added later.

Add a spacer to Toolbar to control UIToolBarItems
Add a spacer to Toolbar to control UIToolBarItems | Source
Designing an UIToolbar
Designing an UIToolbar | Source

Storyboard

Then open the Storyboard and add a second UIViewController. Then select both UIViewControllers and linked with them with a Tab Bar Controller by selecting “Editor-Embed In-Tab Bar Controller” from the Xcode menu. Change the label of the tab bar items by selecting each in turn and changing the label name in the Attributes inspector. In this app, I named them “View” and “Edit” for correspond to the UIViewController for viewing and the other one for editing. I also added an icon (30x30 png) to each, but this is optional. To add the icons, create an “images” group (right click on the “images” group in the project navigator and select “Add files to the project” menu item. Once the images are added, select the tab items in each UIViewController add the icon by selecting the appropriate icon in the image drop down field in the Attributes inspector.

In this sample app, the UIViewController on the left will be “display” view controller, so add a UITextField and an UITextView to the UIViewController and two UILabels. For the UILabel for the UITextField, name the label “Name” and the UILabel for the UITextView, name it “Bio” since this app will store and display the biographies of famous programmers. Also select the UITextView and UITextField and deselect the “enabled” and “enable user interaction” options in the Attributes inspector.

Select the UIViewController and open the Identity inspector in Xcode and select the readBioViewController class in the custom class field. Next click on the Editor Assitant (tuxedo icon in the toolbar) which will open the header file alongside the storyboard. Control+drag a connection from the UITextField to the header file between the @interface and @end directives. When you release the mouse and control buttons, a popover will appear. Name the the IBOutlet “displayName” repeat the process for the UITextView and name the corresponding IBOutlet “displayBio”

Next add a toolbar to the bottom of the UIViewController. A button (ToolbarItem) will be created at the same time. Change the label as in the screenshot by clicking on the item. Also add a second item from the Object library and also change the label as in the screenshot. To separate the two items, add a “Fixed Space Bar Button Item”. Drag it to push the second button the opposite side of the screen like in the screenshot. Next ctrl+drag a connection from the button on the left and change the connection type to “IBAction” and name it “stepBack” and repeat the steps for the button on the right and name it “stepForward”.

To finish up this UIController, ctrl+drag a connection from the UITextField and UITextView to the proxy object and select the delegate option. This is enable the app to dismiss the keyboard later.

Next repeat the process with the second (on the right) UIViewController. Again add an UITextField and UITextView to the UIController. Also add the same UILabels with the same names as in the screenshot. Also add a Toolbar. Rename the ToolbarIem to “Save”. This button will be used to insert the information into the SQLite database. Leave the UITextField and UITextView editable. With the UIViewController selected, open the Editor assistant and ctrl+drag a connection from the UITextField, naming the IBOutlet “insertName” and do likewise with the UITextField nd name it “insertBio”. Also crtl+drag a connection from the ToolBarItem to the header and change the connection type in the popover to IBAction and name it “saveBioInfo”

Close the header file by selecting the Standard editor. Next ctrl+drag a connection from the UITextField and UITextView to the proxy and select the delegate option.

At this point the UI and Storyboard are complete. In the remaining sections we will implement the code to make this app functional.

Add Tabbar Navigator Controller in Xcode IDE
Add Tabbar Navigator Controller in Xcode IDE | Source
The UILayout in Xcode IDE and Project Navigator showing the project structure
The UILayout in Xcode IDE and Project Navigator showing the project structure | Source

AppDelegate

The AppDelegate will be used to setup the database and table. This is dynamically. There is no need to create a SQLite database separately. You can of course as I have demonstrated in the past using SQL Manager (a FireFox addon). First add an import statement for the SQLite library to the header file, see AppDelegate.h code listing below. Also create a SQLite variable named “db” which will be used to create the database and subsequent table.

The next part will be done in the implementation file in the “didFinishWithOptions” method. Locate the “didFinishWithOptions” in the implementation file and add a char variable name emsg. This will be used by the sqlite3_exec function to capture any processing errors. Also create a boolean variable, "fileExist", to be used to check if the database file exist when the app launches because if not the database will be recreated each time the app is loaded, thus deleting all your stored data. Refer the AppDelegate.m code listing below to see how this is done.

Next we will use the NSSearchPathForDirectoriesInDomains method to obtain an array of directories. This method’s first parameter is the search path. We will use the NSDocumentDirectory constant to get all the directories under the Documents directory. Since this app only contains the main Documents directory (unless you create more), the array will only have one element. We will use the objectAtIndex:0 (first element in the array) to build the complete path to the database by appending the “DataBrowser.db" database name that we will store in this directory since the Documents directory is writable. Take a look at the AppDelegate.m code listing below to get a visual how this is done.

Then we will assign the boolean value to the fileExist boolean variable that we created earlier using the fileExistsAtPath method from the NSFileManager class. This method will return a true (YES) or false (NO) depending if the database file is found. When the app is first launched, this value will be NO or false which allow us to create the database in the next code statements.

If the fileExist value is false, the sqlite3_open SQLite function will be executed using the database path and SQLite db variable, thus dynamically creating the database file in the Documents directory or if it can’t, we log an error using the NSLog object. Once the database is successfully created and opened, we can execute the query to create the table that we will need to store the biography information from the app. Notice in the code below the “if not exists” statement that is included in the create statement. This will prevent the query from overwriting the table every time the app is launched. The table will contain two varchar columns: name and bio as in code listing AppDelegate.m below.

Finally the sqlite3_exec function is executed (this replaces or encapsulates the sqlite3_statement, sqlite3_step, sqlite3_finalize and sqlite3_close statements making the code much more streamlined.

This completes this section of the app. In the next section we will create a custom class called “Coder” to represent the data model. This class is part of the model pattern in MVC.

liAppDelegate.h

//
//  liAppDelegate.h
//  SQLite Data Browser
//
//  Created by Kevin Languedoc on 10/21/12.
//  Copyright (c) 2012 Kevin Languedoc. All rights reserved.
//

#import <UIKit/UIKit.h>
#import "sqlite3.h"

@interface liAppDelegate : UIResponder <UIApplicationDelegate>{
    sqlite3 * db;
}


@property (strong, nonatomic) UIWindow *window;

@end

liAppDelegate.m

//
//  liAppDelegate.m
//  SQLite Data Browser
//
//  Created by Kevin Languedoc on 10/21/12.
//  Copyright (c) 2012 Kevin Languedoc. All rights reserved.
//

#import "liAppDelegate.h"

@implementation liAppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    char *emsg;
    BOOL fileExist;
    
    //Get list of directories in Document path
    NSArray * dirPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    
    //Define new path for database in the documents directory because data cannot be written in the resource folder.
    NSString * documentPath = [[dirPath objectAtIndex:0] stringByAppendingPathComponent:@"DataBrowser.db"];
    
    fileExist = [[NSFileManager alloc] fileExistsAtPath:documentPath];
    
    if(fileExist){
        
        if(!(sqlite3_open([documentPath UTF8String], &db) == SQLITE_OK))
        {
            NSLog(@"An error has occured.");
            
        }else{
            
            const char *sqlTable = "create table if not exists Coders (Name varchar, Bio varchar)";
            
            if(sqlite3_exec(db, sqlTable, NULL, NULL, &emsg) != SQLITE_OK)
            {
                NSLog(@"There is a problem with statement");
                
            }
            
        }
        
    }
    return YES;
}

Note


If you are using a class prefix like my example, it will automatically be added to the class name when it is created. This is entirely optional.



Coder (liCoder)

Create a new group in the Project Navigator (click on the group to change the name) and name it “Model”. Then right click on the group and select “New File” from the context menu. Select the Objective-C class template and in the corresponding page of the utility,enter “Coder” in the name field and select the NSObject from the “subclass of” field. Leave the other options unchecked and select “next” button to add the custom class to the project.

Open the header file and add two instance variables, name and bio as NSString types. See the code listing below. Likewise open the implementation file and synthesize these two variables using the @synthesize directive. This will mimic the getter and setter methods. We will use this data model later in the DataDAO database interface custom class and in the Viewcontrollers.

liCoder.h

//
//  liCoder.h
//  SQLite Data Browser
//
//  Created by Kevin Languedoc on 10/21/12.
//  Copyright (c) 2012 Kevin Languedoc. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface liCoder : NSObject

@property(nonatomic,strong) NSNumber *index;
@property(nonatomic,strong) NSString *name;
@property(nonatomic,strong) NSString *bio;

@end

liCoder.m

//
//  liCoder.m
//  SQLite Data Browser
//
//  Created by Kevin Languedoc on 10/21/12.
//  Copyright (c) 2012 Kevin Languedoc. All rights reserved.
//

#import "liCoder.h"

@implementation liCoder
@synthesize name,bio,index;

@end

DataDAO

Before creating this custom class, create a new Group called DAO in the Project Navigator. The right click on the group and select “New File” from the context menu. Select the Objective-C class template and in the corresponding page of the utility,enter DataDAO in the name field and select the NSObject from the “subclass of” field. Leave the other options unchecked and select “next” button to add the custom class to the project.

Open the header file and add the sqlite3 library to the class using the import statement and create a sqlite3 variable as in the corresponding code listing below. Also add the Coder class to the header using the import statement. Then create a NSMutableArray instance variable called “bioList”, a Coder instance variable named “programmer” and two instance methods: insertData which takes two NSString arguments: name and bio, which will be used as input from the editable view controller and selectData with a NSMutableArray as a return type. The code for the header is in DataDAO.h code listing below.

In the implementation file, see the DataDAO.m code listing below, synthesize the variables as previously done. For the insertBio, start by getting the path of the database using the NSSearchPathForDirectoriesInDomains method and passing the NSDocumentsDirectory which will return only one element as before. Then use objectAtIndex to get a handle on that element in the array and append the name of the database, to be used to the database using the sqlite3_open function. If the database successfully opens, create a query of type char called insertSQL to insert data into the database, see the code listing DataDAO.m below. Also create a sql3_statement, both of which will be used by the sqlite3_prepare_v2 to execute the query. If the statement is ok, you will then use the sqlite3_bind_text, one for each argument to bind the argument values to the query statement and insert into the database.
Afterwards, use the sqlite3_step, sqlite3_finalize and sqlite3_close to actually execute the query and clean up the connection. This completes the insert process, next we will look at the selectBio.

The selectBio method follows the same pattern as the insertBio method to get the path of the Documents path and database filename. However in this method the NSMutableArray variable bioList is initialzed and a NSFileManager is created to manage the database path. Using the NSFileManager variable we check to make sure the database exists (you can also do this for the insertBio) method and if it exists, open the database using the sqlite3_open method. Afterwards we create simple SELECT * query to get all the data in the database using the sqlite3_statement and sqlite3_prepare_v2 functions. At this point we will loop through the results by checking if any results are in the database using the SQLITE_ROW boolean constant. For each iteration we will create a new instance of the Coder class which will be named programmer. We will assign the returned values to the name and bio properties of the Coder class and add this programmer object to the bioList mutable array. Remember to release the programmer object using the nil keyword.

When the rows in the results are exhausted, the database connection is cleaned up and the bioList is returned. This return value will be used later in the “display” view controller readBioViewController.

This completes this section of the app. In the next section we will create a add the logic to the view controllers to handle the interaction with the database. The view controllers are part of the View pattern in MVC.

DataDAO.h

//
//  DataDAO.h
//  SQLite Data Browser
//
//  Created by Kevin Languedoc on 10/21/12.
//  Copyright (c) 2012 Kevin Languedoc. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "sqlite3.h"
#import "liCoder.h"

@interface DataDAO : NSObject{
    sqlite3 *db;
}

@property(nonatomic, strong)NSMutableArray *bioList;
@property(nonatomic, strong)liCoder *programmer;

-(void)insertData:(NSString *)name :(NSString*)bio;
-(NSMutableArray *)selectData;

@end

DataDAO.m

//
//  DataDAO.m
//  SQLite Data Browser
//
//  Created by Kevin Languedoc on 10/21/12.
//  Copyright (c) 2012 Kevin Languedoc. All rights reserved.
//

#import "DataDAO.h"

@implementation DataDAO
@synthesize bioList, programmer;

-(void)insertData:(NSString *)name :(NSString*)bio{
    //Get list of directories in Document path
    NSArray * dirPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    
    //Define new path for database
    NSString * documentPath = [[dirPath objectAtIndex:0] stringByAppendingPathComponent:@"DataBrowser.db"];
    
    
    if(!(sqlite3_open([documentPath UTF8String], &db) == SQLITE_OK))
    {
        NSLog(@"An error has occurred.");
        return;
    }else{
        const char *insertSQL = "Insert into Coders (Name , Bio ) VALUES(?,?);";
        
        sqlite3_stmt *sqlStatement;
        if(sqlite3_prepare_v2(db, insertSQL, -1, &sqlStatement, NULL) != SQLITE_OK)
        {
            NSLog(@"Problem with prepare statement");
            return;
        }else{
            sqlite3_bind_text(sqlStatement, 1, [name UTF8String], -1, SQLITE_TRANSIENT);
            sqlite3_bind_text(sqlStatement, 2, [bio UTF8String], -1, SQLITE_TRANSIENT);;
            
            
            if(sqlite3_step(sqlStatement)==SQLITE_DONE){
                sqlite3_finalize(sqlStatement);
                sqlite3_close(db);
            }
            
        }
    }
    

}
-(NSMutableArray*)selectData{
    bioList = [[NSMutableArray alloc]init];
    int counter = 0;
    @try {
        NSFileManager *fileMgr = [NSFileManager defaultManager];
        //Get list of directories in Document path
        NSArray * dirPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        
        //Define new path for database in the documents directory because data cannot be written in the resource folder.
        NSString * dbPath = [[dirPath objectAtIndex:0] stringByAppendingPathComponent:@"DataBrowser.db"];
        
        BOOL success = [fileMgr fileExistsAtPath:dbPath];
        if(!success)
        {
            NSLog(@"Cannot locate database file '%@'.", dbPath);
        }
        if(!(sqlite3_open([dbPath UTF8String], &db) == SQLITE_OK))
        {
            NSLog(@"An error has occured.");
        }
        
        
        NSString * sqlQry = @"SELECT * FROM  Coders";
        
        sqlite3_stmt *sqlStatement;
        if(sqlite3_prepare_v2(db, [sqlQry UTF8String], -1, &sqlStatement, NULL) != SQLITE_OK)
        {
            NSLog(@"Problem with prepare statement: %d", sqlite3_errcode(db));
        }
        
        
        
        while (sqlite3_step(sqlStatement)==SQLITE_ROW) {
            programmer = [[liCoder alloc] init];
            
            programmer.name = [ NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement,0)];
            programmer.bio = [ NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement,1)];
            NSLog(@"This is the filename %@",programmer.name);
            [bioList addObject:programmer];
            programmer=nil;
        }
        sqlite3_finalize(sqlStatement);
        sqlite3_close(db);
    }
    @catch (NSException *exception) {
        NSLog(@"An exception occurred: %@", [exception reason]);
    }
    @finally {
        return bioList;
 
    }

}


@end

liViewController

This method will controller to interaction with the editable view controller. In the header file add an import statement for the DataDAO class and create a DataDAO instance variable called “dao”. The other variables, editName and editBio as well as the insertBioInfo were created previously when we setup the IBAction and IBOutlets.

In the implementation file, synthesize the dao variable, then locate the insertBioInfo method to add the logic to save the data from the UITextField and UITextView to the database. The first thing to do is check to see if both UIControls have values to save otherwise raise the UIAlertView to notify the user that they need to enter values in both fields. If the validation is successfully passed, call the insertBio method of the dao object, passing the values of the fields. Finally reset the fields to an empty string to complete the operation.

To use the UIAlertView, add the UIAlertViewDelegate to the header file and also the UITextFieldDelegate and UITextViewDelegate as you can see in the code listing for the header file.

The UIAlertViewDelegate will provide the interaction with UIAlertView and the UITextFieldDelegate and UITextViewDelegate will allow us to implement the textFieldShouldReturn method of the UITextField and dismiss the keyboard. Likewise the shouldChangeTextInRange of the UITextView will be called when the "Return" or "Done" button is clicked on the keyboard to dismiss the keyboard. The code for both are in the implementation code listing below.

liViewController.h

liViewController.h

// SQLite Data Browser

//

// Created by Kevin Languedoc on 10/21/12.

// Copyright (c) 2012 Kevin Languedoc. All rights reserved.

//

#import <UIKit/UIKit.h>

#import "DataDAO.h"

@interface liViewController : UIViewController<UIAlertViewDelegate, UITextFieldDelegate, UITextViewDelegate>

@property (nonatomic, strong) DataDAO *dao;

@property (strong, nonatomic) IBOutletUITextField *insertName;

@property (strong, nonatomic) IBOutletUITextView *insertBio;

- (IBAction)saveBioInfo:(id)sender;

@end

liViewController.m

//
//  liViewController.m
//  SQLite Data Browser
//
//  Created by Kevin Languedoc on 10/21/12.
//  Copyright (c) 2012 Kevin Languedoc. All rights reserved.
//

#import "liViewController.h"

@interface liViewController ()

@end

@implementation liViewController
@synthesize insertName;
@synthesize insertBio;
@synthesize dao;


- (void)viewDidLoad
{
    [super viewDidLoad];
	// Do any additional setup after loading the view, typically from a nib.
}

- (void)viewDidUnload
{
    [self setInsertName:nil];
    [self setInsertBio:nil];

    [super viewDidUnload];
    // Release any retained subviews of the main view.
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}

- (IBAction)saveBioInfo:(id)sender {
     dao  = [[DataDAO alloc] init];
    if(![insertName.text isEqualToString:@""] || ![insertBio.text isEqualToString:@""]){
        [dao insertData:insertName.text :insertBio.text];
        [self insertName].text=@"";
        [self insertBio].text=@"";
    }else{
        UIAlertView *msg = [[UIAlertView alloc]initWithTitle:@"Error" message:@"You need to provide a name a description of this bio" delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil];
        
        [msg show];
    }
}
-(BOOL)textFieldShouldReturn:(UITextField *)textfield{
    if(textfield == self.insertName){
        [textfield resignFirstResponder];
    }else if (textfield == (UITextField*)self.insertBio){
        [textfield resignFirstResponder];
    }
    return YES;
}

- (BOOL)textView:(UITextView *)textView
shouldChangeTextInRange:(NSRange)range
 replacementText:(NSString *)text
{
    if ([text isEqualToString:@"\n"])
    {
        [textView resignFirstResponder];
    }
    return YES;
}
@end

Note


For large datasets this is not the best solution as it would add a performance impact on the app, but it is ok for smaller apps as this example app. In large datasets, it is better to fetch each record individually from the database.



readBioViewController

This final method in the app will fetch the stored data in the database using the forward and backwards keys in the UIViewController. Open the header file and add the DataDAO class using the import statement as usual. Also import the Coder class. Create a NSMutableArray to store the values from the database. Add an int variable, stepper, to keep track of where we are in the array index. Create an instance variable for the DataDAO class, again called dao and an instance variable for the Coder class, called programmer. The other variables are the IBOutlets and IBActions that were previously created as well as the instance methods for the ToolbarItems.Take a look at the code listing for the header file below.

In the implementation, whose code is provided in the code listing following the header code, synthesize the variables that we created, omitting the IBOutlet variables. Locate the viewDidLoad method of the view controller and initialize the DATADao object as well as the Coder object. Set the stepper int variable to 0 and initialize the bioArray variable and fill the array using the selectBioInfo method of the dao DataDAO object.

The stepBack and stepForward are exactly the same except that in the stepForward, the stepper variable in incremented with each call and stepper is decremented in the stepBack method. In each method, we first check if the stepper value is within the bounds of the array, if not we exit the procedure with the return statement. Otherwise we get the Coder object at the current index and assign it to the programmer variable. Finally we populate the fields in the storyboard.

readBioViewController.h

//
//  readBioViewController.h
//  SQLite Data Browser
//
//  Created by Kevin Languedoc on 10/21/12.
//  Copyright (c) 2012 Kevin Languedoc. All rights reserved.
//

#import <UIKit/UIKit.h>
#import "DataDAO.h"
#import "liCoder.h"

@interface readBioViewController : UIViewController{
    int stepper;
}

@property(nonatomic,strong)DataDAO *dao;
@property(nonatomic,strong)liCoder *coder;


@property (strong, nonatomic) IBOutlet UITextField *displayName;
@property (strong, nonatomic) IBOutlet UITextView *displayBio;

@property (strong, nonatomic) NSMutableArray *bioArray;

- (IBAction)stepBack:(id)sender;
- (IBAction)stepForward:(id)sender;


@end

readBioViewController.m

//
//  readBioViewController.m
//  SQLite Data Browser
//
//  Created by Kevin Languedoc on 10/21/12.
//  Copyright (c) 2012 Kevin Languedoc. All rights reserved.
//

#import "readBioViewController.h"

@interface readBioViewController ()

@end

@implementation readBioViewController
@synthesize displayName;
@synthesize displayBio;
@synthesize bioArray;
@synthesize dao,coder;
@synthesize arrayIndex;


- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
	// Do any additional setup after loading the view.
    dao = [[DataDAO alloc] init];
    coder = [[liCoder alloc] init];
    bioArray = [[NSMutableArray alloc] init];
    bioArray = [dao selectData];
    arrayIndex = [[NSNumber alloc]initWithInt:0];
    stepper=0;
    
}


- (void)viewDidUnload
{
    [self setDisplayName:nil];
    [self setDisplayBio:nil];
    [super viewDidUnload];
    // Release any retained subviews of the main view.
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}

- (IBAction)stepBack:(id)sender {
    NSLog(@"%u",[bioArray count]);
    if(stepper<0 || stepper < [bioArray count]){
        [self displayName].text=@"";
        [self displayBio].text = @"";
        return;
    }else{
        coder = [bioArray objectAtIndex:stepper];
        stepper--;
        [self displayName].text=coder.name;
        [self displayBio].text = coder.bio;
    }
        
}

- (IBAction)stepForward:(id)sender {
      NSLog(@"%u",[bioArray count]);
 
    if(stepper<0 || stepper < [bioArray count]){
        [self displayName].text=@"";
        [self displayBio].text = @"";
        return;
    }else{
        coder = [bioArray objectAtIndex:stepper];
        stepper++;
        [self displayName].text=coder.name;
        [self displayBio].text = coder.bio;
        
    }
}
@end

That is it for this example that demonstrates how to interactively fetch results from a SQLite database and also how to use the UITextView and how to dismiss the keyboard from both of these UIControls.

More by this Author


Comments

No comments yet.

    Sign in or sign up and post using a HubPages Network account.

    0 of 8192 characters used
    Post Comment

    No HTML is allowed in comments, but URLs will be hyperlinked. Comments are not for promoting your articles or other sites.


    Click to Rate This Article
    working