私は Xcode と Sqlite3 に少し慣れておらず、多くのチュートリアルに従い、SO に数え切れないほどの時間を費やしてきましたが、作成しようとしている単純な iPhone アプリを使用してデータベースに追加することにまだ行き詰まっています。アプリには、テキスト入力に使用されるテキスト フィールドを含むフォームがあり、データベースに保存したいと考えています。私のストーリーボードは、2 つの入力フィールドとボタン (「AddEvalFormButton」) で構成されています。
これが起こることです:
シミュレーターがリセットされていることを確認してから、アプリを実行します。前の画面のボタンを押して評価フォーム ページを取得するとすぐにクラッシュします (ViewDidLoad で何か問題が発生したと想定していますが、よくわかりません)。ただし、コンソールには次のように表示されます。「DBを作成できました」。(CreateOrOpenDB のブレークポイントもありました (以下のコードを参照): 0x1e4df: movl -12(%ebp), %edx <--これはまったくナンセンスです。この情報の使用方法に関するアイデアをいただければ幸いです)。
iOS シミュレーターを閉じて再度実行すると、今度はフォーム画面を開いてもクラッシュしません。テキスト フィールドにデータを入力できますが、AddEvalFormButton ボタンを押すとアプリがクラッシュします。今回はコンソールにメッセージはありません。
問題:
アプリがクラッシュする理由がわかりません
データが保存されていません: ターミナルを確認すると、データベースにテーブルが追加されていません。
私に関する限り (間違っている場合は訂正してください)、私が懸念しているディレクトリが 2 つあります。
1)私のXcodeプロジェクトのもの
2) ユーザー/ライブラリ/アプリケーション サポート/iPhone シミュレーター
EvaluationFormViewController.h、EvaluationFormViewController.m、EvalForm.h、および EvalForm.m の 4 つのファイルがあります。それらは次のようになります。
EvaluationFormViewController.h:
#import <UIKit/UIKit.h>
#import "sqlite3.h"
#import "EvalForm.h"
@interface EvaluationViewController : UIViewController <UIActionSheetDelegate, UITableViewDataSource, UITextFieldDelegate, UITextViewDelegate, UITableViewDelegate>
@property (weak, nonatomic) IBOutlet UIScrollView *scrollView;
@property (weak, nonatomic) IBOutlet UITextField *efq1Field;
@property (weak, nonatomic) IBOutlet UITextField *efq2Field;
- (IBAction)backgroundTouched:(id)sender;
- (IBAction)textfieldReturn:(id)sender;
- (IBAction)addEvalFormButton:(id)sender;
@end
EvaluationFormViewController.m:
#import "EvaluationViewController.h"
@interface EvaluationViewController ()
{
NSMutableArray *arrayOfEvalFormQs;
sqlite3 *evalFormDB;
NSString *dbPathString;
}
@property (nonatomic, readonly) CGPoint originalOffset;
@property (nonatomic, readonly) UIView *activeField;
@end
@implementation EvaluationViewController
@synthesize efq1Field, efq2Field;
//FROM TUTORIAL Cocoa W/Love; need the following instance variables
CGFloat animatedDistance;
//FROM TUTORIAL Cocoa W/Love; need the following instance constants
static const CGFloat KEYBOARD_ANIMATION_DURATION = 0.3;
static const CGFloat MINIMUM_SCROLL_FRACTION = 0.2;
static const CGFloat MAXIMUM_SCROLL_FRACTION = 0.8;
static const CGFloat PORTRAIT_KEYBOARD_HEIGHT = 216;
static const CGFloat LANDSCAPE_KEYBOARD_HEIGHT = 162;
- (IBAction)backgroundTouched:(id)sender {
[efq1Field resignFirstResponder];
[efq2Field resignFirstResponder];
}
//dismisses the keyboard when the return/done button is pressed for TextFields.
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
return YES;
}
//dismisses the keyboard when the return/done button is pressed for TextViews.
- (BOOL)textViewShouldReturn:(UITextView *)textView
{
[textView resignFirstResponder];
return YES;
}
//FROM TUTORIAL: www.cocoawithlove.com/2008/10/sliding-uitextfields-around-to-avoid.html
//Animate upwards when the text field is selected
//Get the rects of the text field being edited and the view that we're going to scroll. We convert everything to window coordinates, since they're not necessarily in the same coordinate space.
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
CGRect textFieldRect =
[self.view.window convertRect:textField.bounds fromView:textField];
CGRect viewRect =
[self.view.window convertRect:self.view.bounds fromView:self.view];
//So now we have the bounds, we need to calculate the fraction between the top and bottom of the middle section for the text field's midline:
CGFloat midline = textFieldRect.origin.y + 0.5 * textFieldRect.size.height;
CGFloat numerator =
midline - viewRect.origin.y
- MINIMUM_SCROLL_FRACTION * viewRect.size.height;
CGFloat denominator =
(MAXIMUM_SCROLL_FRACTION - MINIMUM_SCROLL_FRACTION)
* viewRect.size.height;
CGFloat heightFraction = numerator / denominator;
//Clamp this fraction so that the top section is all "0.0" and the bottom section is all "1.0".
if (heightFraction < 0.0)
{
heightFraction = 0.0;
}
else if (heightFraction > 1.0)
{
heightFraction = 1.0;
}
//Now take this fraction and convert it into an amount to scroll by multiplying by the keyboard height for the current screen orientation. Notice the calls to floor so that we only scroll by whole pixel amounts.
UIInterfaceOrientation orientation =
[[UIApplication sharedApplication] statusBarOrientation];
if (orientation == UIInterfaceOrientationPortrait ||
orientation == UIInterfaceOrientationPortraitUpsideDown)
{
animatedDistance = floor(PORTRAIT_KEYBOARD_HEIGHT * heightFraction);
}
else
{
animatedDistance = floor(LANDSCAPE_KEYBOARD_HEIGHT * heightFraction);
}
//Finally, apply the animation. Note the use of setAnimationBeginsFromCurrentState: — this will allow a smooth transition to new text field if the user taps on another.
CGRect viewFrame = self.view.frame;
viewFrame.origin.y -= animatedDistance;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:KEYBOARD_ANIMATION_DURATION];
[self.view setFrame:viewFrame];
[UIView commitAnimations];
}
//ANIMATE BACK AGAIN: The return animation is far simpler since we've saved the amount to animate.
- (void)textFieldDidEndEditing:(UITextField *)textField
{
CGRect viewFrame = self.view.frame;
viewFrame.origin.y += animatedDistance;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:KEYBOARD_ANIMATION_DURATION];
[self.view setFrame:viewFrame];
[UIView commitAnimations];
}
//SAME CODE AS ABOVE, BUT FOR TEXTVIEW
//FROM TUTORIAL: www.cocoawithlove.com/2008/10/sliding-uitextfields-around-to-avoid.html
//Animate upwards when the text field is selected
//Get the rects of the text field being edited and the view that we're going to scroll. We convert everything to window coordinates, since they're not necessarily in the same coordinate space.
- (void)textViewDidBeginEditing:(UITextView *)textView
{
CGRect textFieldRect =
[self.view.window convertRect:textView.bounds fromView:textView];
CGRect viewRect =
[self.view.window convertRect:self.view.bounds fromView:self.view];
//So now we have the bounds, we need to calculate the fraction between the top and bottom of the middle section for the text field's midline:
CGFloat midline = textFieldRect.origin.y + 0.5 * textFieldRect.size.height;
CGFloat numerator =
midline - viewRect.origin.y
- MINIMUM_SCROLL_FRACTION * viewRect.size.height;
CGFloat denominator =
(MAXIMUM_SCROLL_FRACTION - MINIMUM_SCROLL_FRACTION)
* viewRect.size.height;
CGFloat heightFraction = numerator / denominator;
//Clamp this fraction so that the top section is all "0.0" and the bottom section is all "1.0".
if (heightFraction < 0.0)
{
heightFraction = 0.0;
}
else if (heightFraction > 1.0)
{
heightFraction = 1.0;
}
//Now take this fraction and convert it into an amount to scroll by multiplying by the keyboard height for the current screen orientation. Notice the calls to floor so that we only scroll by whole pixel amounts.
UIInterfaceOrientation orientation =
[[UIApplication sharedApplication] statusBarOrientation];
if (orientation == UIInterfaceOrientationPortrait ||
orientation == UIInterfaceOrientationPortraitUpsideDown)
{
animatedDistance = floor(PORTRAIT_KEYBOARD_HEIGHT * heightFraction);
}
else
{
animatedDistance = floor(LANDSCAPE_KEYBOARD_HEIGHT * heightFraction);
}
//Finally, apply the animation. Note the use of setAnimationBeginsFromCurrentState: — this will allow a smooth transition to new text field if the user taps on another.
CGRect viewFrame = self.view.frame;
viewFrame.origin.y -= animatedDistance;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:KEYBOARD_ANIMATION_DURATION];
[self.view setFrame:viewFrame];
[UIView commitAnimations];
}
//ANIMATE BACK AGAIN: The return animation is far simpler since we've saved the amount to animate.
- (void)textViewDidEndEditing:(UITextView *)textView
{
CGRect viewFrame = self.view.frame;
viewFrame.origin.y += animatedDistance;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:KEYBOARD_ANIMATION_DURATION];
[self.view setFrame:viewFrame];
[UIView commitAnimations];
}
/**Keyboard dismissed when background is clicked or when return is hit**/
- (IBAction)textfieldReturn:(id)sender{
[sender resignFirstResponder];
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void) viewWillDisappear: (BOOL)animated{
[super viewWillDisappear:animated];
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc removeObserver:self name: UIKeyboardWillShowNotification object:nil];
[nc removeObserver:self name: UIKeyboardWillHideNotification object:nil];
}
- (void)viewWillAppear:(BOOL)animated
{
[[self view] setBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageNamed:@"136676912132100.gif"]]];
}
//RELEVANT CODE FOR SQLITE ISSUE BELOW:
- (void)viewDidLoad
{
[super viewDidLoad];
//relating to sqlite:
arrayOfEvalFormQs = [[NSMutableArray alloc]init];
[self createOrOpenDB];
}
- (void)createOrOpenDB
{
NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docPath = [path objectAtIndex:0];
dbPathString = [docPath stringByAppendingPathComponent:@"evalForm.db"];
char *error;
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:dbPathString]) {
const char *dbPath = [dbPathString UTF8String];
//creat db here
if (sqlite3_open(dbPath, &evalFormDB)==SQLITE_OK) {
const char *sql_stmt = "CREATE TABLE IF NOT EXISTS EVALFORMDB (ID INTEGER PRIMARY KEY AUTOINCREMENT, EFQ1 TEXT, EFQ2 TEXT)";
NSLog(@"You were able to create DB");
sqlite3_exec(evalFormDB, sql_stmt, NULL, NULL, &error);
sqlite3_close(evalFormDB);
}
else {
NSLog(@"Problem with createopenDB");
}
}
}
- (IBAction)addEvalFormButton:(id)sender {
char *error;
if (sqlite3_open([dbPathString UTF8String], &evalFormDB)==SQLITE_OK) {
NSString *inserStmt = [NSString stringWithFormat:@"INSERT INTO EVALFORM(EFQ1,EFQ2) values ('%s', '%s')",[self.efq1Field.text UTF8String], [self.efq2Field.text UTF8String]];
NSLog(@"I am in the loop");
const char *insert_stmt = [inserStmt UTF8String];
if (sqlite3_exec(evalFormDB, insert_stmt, NULL, NULL, &error)==SQLITE_OK) {
NSLog(@"Answer added");
//not really sure what this does:
EvalForm *evalForm = [[EvalForm alloc]init];
[evalForm setEfq1:self.efq1Field.text];
[evalForm setEfq2:self.efq2Field.text];
[arrayOfEvalFormQs addObject:evalForm];
}
else{
NSLog(@"Answer not added");
}
sqlite3_close(evalFormDB);
}
}
@end
EvalForm.h:
#import <Foundation/Foundation.h>
@interface EvalForm : NSObject
@property(nonatomic, strong)NSString *efq1;
@property(nonatomic, strong)NSString *efq2;
@end
EvalForm.m:
#import "EvalForm.h"
@implementation EvalForm
@end
できるだけ多くの情報を含めようとしました (やり過ぎかもしれません、申し訳ありません)。前述したように、私はまだ Xcode と SQLite に慣れていないので、自分のコードで基本的なものを見落としているかどうかはわかりません。私のコードが機能しない理由についての意見は大歓迎です。