段老师的视频教程,采用都是比较通用是 Demo,方便以后代码复用。也是从项目实践经验来教授相关知识如 FMDB,这几节课获益匪浅。
Demo 源码见 Github,并添加了相关注释。
28. 沙盒机制
介绍几种数据持久化的方案,选择合适项目的方案:
- 沙盒机制
- NSFileManager
- Plist、Archive数据归档 * SQLite3应用
- NSUserDefaults
沙盒目录结构 NSDocumentDirectory
:
- Application Bundle/
- Documents/
- Library/Caches/
- Library/Perferences/
- tmp/
29. NSFileManager & Plist
存取文档的方法封装一个通用的类BLUtility.h/m
,方便调用。以下代码是测试保存文本文档。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
+ (NSString*) getPathWithinDocumentDir:(NSString*)aPath { NSString *fullPath = nil; NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); if ([paths count] > 0) { fullPath = (NSString *)[paths objectAtIndex:0]; if ([aPath length] > 0) { fullPath = [fullPath stringByAppendingPathComponent:aPath]; } } return fullPath; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
- (void)writeFileButtonClicked:(id)sender { NSString *string = @"ABC副经理"; NSString *stringFilePath = [BLUtility getPathWithinDocumentDir:@"string.text"]; [BLUtility createDirectory:stringFilePath lastComponentIsDirectory:NO]; [string writeToFile:stringFilePath atomically:YES encoding:NSUTF8StringEncoding error:nil]; }
|
30. Archive 数据归档和 NSUserDefaults
转化为 NSData,二进制的数据,利于存储和传输。
遵循 NSCoding 协议,即可实现编码和解码的类,进一步对其归档和解档。也用到了封装的类BLUtility.h/m
。本例中文档保存到 NSUserDefaults:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
- (id)initWithCoder:(NSCoder *)aDecoder { self = [super init]; if (self) { self.userName = [aDecoder decodeObjectForKey:@"userName"]; self.email = [aDecoder decodeObjectForKey:@"email"]; self.password = [aDecoder decodeObjectForKey:@"password"]; self.age = [aDecoder decodeIntForKey:@"age"]; } return self; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
+ (NSObject *) unarchiverObject:(NSData *)archivedData withKey:(NSString *)key { if(archivedData == nil) { return nil; } NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:archivedData]; NSObject *object = [unarchiver decodeObjectForKey:key]; [unarchiver finishDecoding]; return object; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
- (void) readButtonClicked:(id)sender { NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; NSInteger test = [userDefaults integerForKey:@"integer"]; NSLog(@"test = %li", test); NSString *userDataPath = [BLUtility getPathWithinDocumentDir:UserDataName]; if ([userDataPath length] > 0 && [[NSFileManager defaultManager] fileExistsAtPath:userDataPath]) { NSData *userData = [NSData dataWithContentsOfFile:userDataPath]; BLUser *user = (BLUser *)[BLUtility unarchiverObject:userData withKey:@"UserData"]; _userNameTextField.text = user.userName; _emailTextField.text = user.email; _passwordTextField.text = user.password; _ageTextField.text = [NSString stringWithFormat:@"%ld", user.age]; } }
|
###延伸阅读:
31. SQLite3
通过 FMDB 封装,得以 OBjective-C 访问 SQLite,完成常用增删改查。从语法和数据体积实现轻量级存储方案。这个方案稍后用在习题“同学录”上实践一下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
|
#import <Foundation/Foundation.h> #import "FMDatabase.h" #import "BLPoem.h"
@interface BLPoemDB : NSObject {
FMDatabase *_db; }
- (BOOL) createPoemTable;
- (BOOL) addPoem:(BLPoem *)poem;
- (NSMutableArray *) getAllPoems;
- (NSMutableArray *) getFavoritesPoems;
- (BOOL) setFavorite:(BOOL)favorite favoriteId:(NSInteger)poemId;
- (BOOL) deletePoemWithPoemId:(NSInteger)poemId;
@end
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
|
#import "BLPoemDB.h" #import "BLUtility.h"
#define BLPOEMDBNAME @"BLPoemDB.sqlite"
@implementation BLPoemDB
-(id) init { self = [super init]; if (self) { NSString *dbPath = [BLUtility getPathWithinDocumentDir:BLPOEMDBNAME]; NSFileManager *fileManager = [NSFileManager defaultManager]; BOOL existFile = [fileManager fileExistsAtPath:dbPath]; if (existFile == NO) { NSString *poemDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:BLPOEMDBNAME]; [fileManager copyItemAtPath:poemDBPath toPath:dbPath error:nil]; } _db = [[FMDatabase alloc] initWithPath:dbPath]; if ([_db open] == NO) { return nil; } } return self; }
- (BOOL) createPoemTable { [_db beginTransaction]; BOOL success = [_db executeUpdate:@"CREATE TABLE IF NOT EXISTS POEMTABLE (" @"ID INTEGER PRIMARY KEY NOT NULL," @"POEMNAME TEXT NOT NULL," @"POETNAME TEXT NOT NULL," @"POEMCONTENT TEXT NOT NULL," @"WHETHERFAVORITE BOOL NOT NULL);"]; [_db commit]; if(!success || [_db hadError]) { [_db rollback]; return NO; } return YES; }
- (BOOL) addPoem:(BLPoem *)poem { FMResultSet *rs = [_db executeQuery:@"SELECT ID FROM POEMTABLE WHERE ID=?", [NSNumber numberWithInteger:poem.poemId]]; if(rs && [rs next]) { [rs close]; return YES; } [rs close]; [_db beginTransaction]; NSString *bookMark = nil; BOOL success = [_db executeUpdate:@"INSERT OR IGNORE INTO POEMTABLE (ID,POEMNAME,POETNAME,POEMCONTENT,WHETHERFAVORITE) VALUES (?,?,?,?,?);", [NSNumber numberWithInteger:poem.poemId], poem.poemName, poem.poetName, poem.poemContent, [NSNumber numberWithBool:poem.whetherFavorite], bookMark]; [_db commit]; if(!success || [_db hadError]) { [_db rollback]; return NO; } return YES; }
- (NSMutableArray *) getAllPoems { NSMutableArray *result = [[NSMutableArray alloc] init];
FMResultSet *rs = [_db executeQuery:@"SELECT * FROM POEMTABLE"]; while([rs next]) { BLPoem *poem = [[BLPoem alloc] init]; poem.poemId = [rs intForColumn:@"ID"]; poem.poemName = [rs stringForColumn:@"POEMNAME"]; poem.poetName = [rs stringForColumn:@"POETNAME"]; poem.poemContent = [rs stringForColumn:@"POEMCONTENT"]; poem.whetherFavorite = [rs boolForColumn:@"WHETHERFAVORITE"]; [result addObject:poem]; } [rs close]; return result; }
- (NSMutableArray *) getFavoritesPoems { NSMutableArray *result = [[NSMutableArray alloc] init]; FMResultSet *rs = [_db executeQuery:@"SELECT * FROM POEMTABLE WHERE WHETHERFAVORITE=?", [NSNumber numberWithBool:YES]]; while([rs next]) { BLPoem *poem = [[BLPoem alloc] init]; poem.poemId = [rs intForColumn:@"ID"]; poem.poemName = [rs stringForColumn:@"POEMNAME"]; poem.poetName = [rs stringForColumn:@"POETNAME"]; poem.poemContent = [rs stringForColumn:@"POEMCONTENT"]; poem.whetherFavorite = [rs boolForColumn:@"WHETHERFAVORITE"]; [result addObject:poem]; } [rs close]; return result; }
- (BOOL) setFavorite:(BOOL)favorite favoriteId:(NSInteger)poemId { if (_db == nil) { return NO; } [_db beginTransaction]; BOOL result = [_db executeUpdate:@"UPDATE POEMTABLE SET WHETHERFAVORITE=? WHERE ID=?", [NSNumber numberWithBool:favorite], [NSNumber numberWithInteger:poemId]]; [_db commit]; return result; }
- (BOOL) deletePoemWithPoemId:(NSInteger)poemId { [_db beginTransaction]; BOOL success = [_db executeUpdate:@"DELETE FROM POEMTABLE WHERE ID=?", [NSNumber numberWithInteger:poemId]]; [_db commit]; if(!success || [_db hadError]) { [_db rollback]; return NO; } return YES; }
- (void) dealloc { [_db close]; _db = nil; }
@end
|
延伸阅读:
32. 多线程和多媒体
简单介绍了常见多媒体的播放:
- UIImagePickerViewController
- UIActionSheet (👈名字谐音)
- AVAudioPlayer
- SystenSounID
- MPMoviePlayerViewController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| #import <AVFoundation/AVFoundation.h> #import <AudioToolbox/AudioToolbox.h> #import <MediaPlayer/MediaPlayer.h>
@interface BLOneViewController ()<UIAlertViewDelegate, UIActionSheetDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate> { AVAudioPlayer *audioPlayer; SystemSoundID systemSoundId; }
#pragma mark - Play Music button action
- (IBAction)palyMusic:(UIButton *)sender {
if (!audioPlayer) { NSString *mp3Path = [[NSBundle mainBundle] pathForResource:@"yishengsuoai" ofType:@"mp3"]; NSURL *mp3Url = [[NSURL alloc] initFileURLWithPath:mp3Path]; audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:mp3Url error:NULL]; audioPlayer.numberOfLoops = -1; [audioPlayer prepareToPlay]; } if ([audioPlayer isPlaying]) { [audioPlayer stop]; sender.titleLabel.text = @"Play Music"; }else { [audioPlayer play]; sender.titleLabel.text = @"Pasue Music"; } }
|
33. 硬件访问、CALayer & Core Graphics
33.1. gravity and user acceleration
Useage: shaking device to feedback or 360° image
1 2 3 4 5 6 7 8 9 10 11 12 13
| NSOperationQueue *diviceMotionQueue = [[NSOperationQueue alloc] init]; [self.motionManager startDeviceMotionUpdatesToQueue:diviceMotionQueue withHandler:^(CMDeviceMotion *motion, NSError *error) { NSLog(@"\n \n-------------------------------------------------------------------------->\n \n 1 - attitude: %@ \n 2 - rotation rate x: %f, y: %f, z: %f,\n \n 3 - gravity x: %f, y: %f, z: %f \n \n 4 - user acceleration x: %f, y: %f, z: %f \n \n 5 - magnetic field accuracy: %d, x: %f, y: %f, z: %f \n \n <--------------------------------------------------------------------------\n \n" , motion.attitude, motion.rotationRate.x, motion.rotationRate.y, motion.rotationRate.z, motion.gravity.x, motion.gravity.y, motion.gravity.z, motion.userAcceleration.x, motion.userAcceleration.y, motion.userAcceleration.z, motion.magneticField.accuracy, motion.magneticField.field.x, motion.magneticField.field.y, motion.magneticField.field.z); }];
|
Search the definiton of each new word, really 😄😄😄.
Do you believe that, At least I don’t think so.
Output:
1 2 3 4 5 6 7 8 9 10 11 12 13
| --------------------------------------------------------------------------> 1 - attitude: CMAttitude Pitch: 0.751292, Roll: 0.555726, Yaw: 22.198534 2 - rotation rate x: 0.001511, y: -0.000795, z: 0.002454, 3 - gravity x: 0.009698, y: -0.013112, z: -0.999867 4 - user acceleration x: -0.002603, y: 0.000783, z: -0.014095 5 - magnetic field accuracy: -1, x: 0.000000, y: 0.000000, z: 0.000000 <--------------------------------------------------------------------------
|
33.2. CALayer
Modifying the Layer’s Appearance Properties.
Lots of properties really similar to Sketch.
33.3. Core Graphics
CGContextRef: An opaque type that represents a Quartz 2D drawing environment.
Path on CGContextRef vs Vector on Artboard in Sketch, by learn Sketch really make sense of those abstract concepts.
34. 自定义手势、Block & GCD
34.1. 手势
尽量设计使用常用的手势
设计一个小 Demo,实现:Shake Your iPhone To Send Feedback Email To Me😄
34.2. Block
比较简洁的函数,就在当前就可以调用。
34.3. GCD
Group queue:并行执行多任务比较牛。
35. APNS、Core Date、URL Scheme、单元测试、APP发布流
35.1. APNS
稍后添加到 Classmates
35.2. Core Data
核心概念:
1 2 3 4 5 6 7 8
| @property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
@property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
|
稍后把 Classmate 整个 Core Data 的版本
35.3. URL Scheme
就是一个通过个地址访问系统软件或第三方软件,如地图和 Launch Center Pro。iOS 9之后估计有更多可能性。
35.4. 单元测试或断点测试
35.5. App 发布流程