1、创建BIDTinyPixDocument类
1 #import <UIKit/UIKit.h> 2 3 //创建文档类 4 @interface TinyPixDocument : UIDocument 5 //接收一对行和列索引作为参数 6 - (BOOL)stateAtRow:(NSUInteger)row column:(NSUInteger)column; 7 //指定的行和列设置特定的状态 8 - (void)setState:(BOOL)state atRow:(NSUInteger)row column:(NSUInteger)column; 9 //负责切换特定位置处的状态 10 - (void)toggleStateAtRow:(NSUInteger)row column:(NSUInteger)column; 11 12 @end
1 #import "TinyPixDocument.h" 2 3 //类扩展 4 @interface TinyPixDocument () 5 @property (nonatomic,strong) NSMutableData * bitmap; 6 @end 7 8 @implementation TinyPixDocument 9 10 //将每个位图初始化为从一个角延伸到另一个角的对角线图案。 11 - (id)initWithFileURL:(NSURL *)url 12 { 13 self = [super initWithFileURL:url]; 14 if (self) { 15 unsigned char startPattern[] = { 16 0x01, 17 0x02, 18 0x04, 19 0x08, 20 0x10, 21 0x20, 22 0x40, 23 0x80 24 }; 25 26 self.bitmap = [NSMutableData dataWithBytes:startPattern length:8]; 27 } 28 return self; 29 } 30 31 //实现读取单个位的状态的方法。实现这个方法只要从字节数组中获取相关字节,然后对其进行位移操作和AND操作,检查是否设置了给定位,相应的返回YES或NO。 32 - (BOOL)stateAtRow:(NSUInteger)row column:(NSUInteger)column 33 { 34 const char * bitmapBytes = [self.bitmap bytes]; 35 char rowByte = bitmapBytes[row]; 36 char result = (1 << column) & rowByte; 37 if (result != 0) { 38 return YES; 39 } else { 40 return NO; 41 } 42 } 43 //这个方法正好和上一个相反,用于为给定行和列的位置设置值。 44 - (void)setState:(BOOL)state atRow:(NSUInteger)row column:(NSUInteger)column 45 { 46 char *bitmapBytes = [self.bitmap mutableBytes]; 47 char *rowByte = &bitmapBytes[row]; 48 49 if (state) { 50 *rowByte = *rowByte | (1 << column); 51 } else { 52 *rowByte = *rowByte & ~(1 << column); 53 } 54 } 55 //辅助方法,外部代码使用该方法来切换单个单元的状态。 56 - (void)toggleStateAtRow:(NSUInteger)row column:(NSUInteger)column 57 { 58 BOOL state = [self stateAtRow:row column:column]; 59 [self setState:!state atRow:row column:column]; 60 } 61 62 //保存文档时调用 63 - (id)contentsForType:(NSString *)typeName error:(NSError **)outError 64 { 65 NSLog(@"saving document to URL %@", self.fileURL); 66 return [self.bitmap copy]; 67 } 68 //系统从存储区加载了数据,并且准备将这个数据提供给文档类的一个实例时,调用此方法。 69 - (BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName 70 error:(NSError **)outError 71 { 72 NSLog(@"loading document from URL %@", self.fileURL); 73 self.bitmap = [contents mutableCopy]; 74 return true; 75 }
2、主控制器代码
1 #import "BIDMasterViewController.h" 2 #import "BIDDetailViewController.h" 3 #import "BIDTinyPixDocument.h" 4 5 @interface BIDMasterViewController () <UIAlertViewDelegate> 6 7 @property (weak, nonatomic) IBOutlet UISegmentedControl *colorControl; 8 @property (strong, nonatomic) NSArray * documentFilenames; 9 @property (strong, nonatomic) BIDTinyPixDocument * chosenDocument; 10 11 @property (strong, nonatomic) NSMetadataQuery * query; 12 @property (strong, nonatomic) NSMutableArray * documentURLs; 13 14 @end 15 16 @implementation BIDMasterViewController 17 18 19 /* original 20 //接收一个文件名作为参数,将它和应用的Document目录的文件路径结合起来,然后返回一个指向该文件的URL指针。 21 - (NSURL *)urlForFilename:(NSString *)filename { 22 NSFileManager * fm = [NSFileManager defaultManager]; 23 NSArray * urls = [fm URLsForDirectory:NSDocumentDirectory 24 inDomains:NSUserDomainMask]; 25 NSURL * directoryURL = urls[0]; 26 NSURL * fileURL = [directoryURL URLByAppendingPathComponent:filename]; 27 return fileURL; 28 } 29 */ 30 31 - (NSURL *)urlForFilename:(NSString *)filename 32 { 33 // be sure to insert "Documents" into the path 34 NSURL * baseURL = [[NSFileManager defaultManager] 35 URLForUbiquityContainerIdentifier:nil]; 36 NSURL * pathURL = [baseURL URLByAppendingPathComponent:@"Documents"]; 37 NSURL * destinationURL = [pathURL URLByAppendingPathComponent:filename]; 38 return destinationURL; 39 } 40 41 /* original 42 43 //也用到了Document目录,用于查找代表现存文档的文件。该方法获取它所找到的文件,并将它们根据创建的时间来排序,以便用户可以以“博客风格”的顺序来查看文档列表(第一个文档是最新的)。文档文件名被存放在documentFilenames属性中,然后重新加载表视图(我们尚未处理)。 44 - (void)reloadFiles { 45 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, 46 NSUserDomainMask, YES); 47 NSString *path = paths[0]; 48 NSFileManager *fm = [NSFileManager defaultManager]; 49 50 NSError *dirError; 51 NSArray *files = [fm contentsOfDirectoryAtPath:path error:&dirError]; 52 if (!files) { 53 NSLog(@"Encountered error while trying to list files in directory %@: %@", 54 path, dirError); 55 } 56 NSLog(@"found files: %@", files); 57 58 files = [files sortedArrayUsingComparator: 59 ^NSComparisonResult(id filename1, id filename2) { 60 NSDictionary *attr1 = [fm attributesOfItemAtPath: 61 [path stringByAppendingPathComponent:filename1] 62 error:nil]; 63 NSDictionary *attr2 = [fm attributesOfItemAtPath: 64 [path stringByAppendingPathComponent:filename2] 65 error:nil]; 66 return [attr2[NSFileCreationDate] compare: attr1[NSFileCreationDate]]; 67 }]; 68 self.documentFilenames = files; 69 [self.tableView reloadData]; 70 } 71 */ 72 73 74 - (void)reloadFiles { 75 NSFileManager * fileManager = [NSFileManager defaultManager]; 76 // passing nil is OK here, matches first entitlement 77 NSURL * cloudURL = [fileManager URLForUbiquityContainerIdentifier:nil]; 78 NSLog(@"got cloudURL %@", cloudURL); // returns nil in simulator 79 80 self.query = [[NSMetadataQuery alloc] init]; 81 _query.predicate = [NSPredicate predicateWithFormat:@"%K like '*.tinypix'", 82 NSMetadataItemFSNameKey]; 83 _query.searchScopes = [NSArray arrayWithObject: 84 NSMetadataQueryUbiquitousDocumentsScope]; 85 [[NSNotificationCenter defaultCenter] 86 addObserver:self 87 selector:@selector(updateUbiquitousDocuments:) 88 name:NSMetadataQueryDidFinishGatheringNotification 89 object:nil]; 90 [[NSNotificationCenter defaultCenter] 91 addObserver:self 92 selector:@selector(updateUbiquitousDocuments:) 93 name:NSMetadataQueryDidUpdateNotification 94 object:nil]; 95 [_query startQuery]; 96 } 97 98 - (void)updateUbiquitousDocuments:(NSNotification *)notification { 99 self.documentURLs = [NSMutableArray array]; 100 self.documentFilenames = [NSMutableArray array]; 101 102 NSLog(@"updateUbiquitousDocuments, results = %@", self.query.results); 103 NSArray *results = [self.query.results sortedArrayUsingComparator: 104 ^NSComparisonResult(id obj1, id obj2) { 105 NSMetadataItem *item1 = obj1; 106 NSMetadataItem *item2 = obj2; 107 return [[item2 valueForAttribute:NSMetadataItemFSCreationDateKey] compare: 108 [item1 valueForAttribute:NSMetadataItemFSCreationDateKey]]; 109 }]; 110 111 for (NSMetadataItem *item in results) { 112 NSURL *url = [item valueForAttribute:NSMetadataItemURLKey]; 113 [self.documentURLs addObject:url]; 114 [(NSMutableArray *)_documentFilenames addObject:[url lastPathComponent]]; 115 } 116 117 [self.tableView reloadData]; 118 } 119 120 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 121 return 1; 122 } 123 124 - (NSInteger)tableView:(UITableView *)tableView 125 numberOfRowsInSection:(NSInteger)section { 126 return [self.documentFilenames count]; 127 } 128 129 - (UITableViewCell *)tableView:(UITableView *)tableView 130 cellForRowAtIndexPath:(NSIndexPath *)indexPath { 131 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"FileCell"]; 132 133 NSString * path = self.documentFilenames[indexPath.row]; 134 cell.textLabel.text = path.lastPathComponent.stringByDeletingPathExtension; 135 return cell; 136 } 137 138 - (IBAction)chooseColor:(id)sender { 139 NSInteger selectedColorIndex = [(UISegmentedControl *)sender selectedSegmentIndex]; 140 [self setTintColorForIndex:selectedColorIndex]; 141 142 // NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults]; 143 // [prefs setInteger:selectedColorIndex forKey:@"selectedColorIndex"]; 144 // [prefs synchronize]; 145 146 NSUbiquitousKeyValueStore * prefs = [NSUbiquitousKeyValueStore defaultStore]; 147 [prefs setLongLong:selectedColorIndex forKey:@"selectedColorIndex"]; 148 } 149 150 - (void)setTintColorForIndex:(NSInteger)selectedColorIndex { 151 UIColor *tint = nil; 152 switch (selectedColorIndex) { 153 case 0: 154 tint = [UIColor redColor]; 155 break; 156 case 1: 157 tint = [UIColor colorWithRed:0 green:0.6 blue:0 alpha:1]; 158 break; 159 case 2: 160 tint = [UIColor blueColor]; 161 break; 162 default: 163 break; 164 } 165 [UIApplication sharedApplication].keyWindow.tintColor = tint; 166 } 167 168 - (void)viewDidAppear:(BOOL)animated { 169 [super viewDidAppear:animated]; 170 171 // NSUserDefaults * prefs = [NSUserDefaults standardUserDefaults]; 172 // NSInteger selectedColorIndex = [prefs integerForKey:@"selectedColorIndex"]; 173 174 NSUbiquitousKeyValueStore * prefs = [NSUbiquitousKeyValueStore defaultStore]; 175 NSInteger selectedColorIndex = (int)[prefs longLongForKey:@"selectedColorIndex"]; 176 177 [self setTintColorForIndex:selectedColorIndex]; 178 [self.colorControl setSelectedSegmentIndex:selectedColorIndex]; 179 } 180 181 - (void)viewDidLoad 182 { 183 [super viewDidLoad]; 184 185 UIBarButtonItem * addButton = [[UIBarButtonItem alloc] 186 initWithBarButtonSystemItem:UIBarButtonSystemItemAdd 187 target:self 188 action:@selector(insertNewObject)]; 189 self.navigationItem.rightBarButtonItem = addButton; 190 [self reloadFiles]; 191 } 192 193 - (void)insertNewObject { 194 // get the name 195 UIAlertView *alert = 196 [[UIAlertView alloc] initWithTitle:@"Filename" 197 message:@"Enter a name for your new TinyPix document." 198 delegate:self 199 cancelButtonTitle:@"Cancel" 200 otherButtonTitles:@"Create", nil]; 201 alert.alertViewStyle = UIAlertViewStylePlainTextInput; 202 [alert show]; 203 } 204 205 - (void)alertView:(UIAlertView *)alertView 206 didDismissWithButtonIndex:(NSInteger)buttonIndex { 207 if (buttonIndex == 1) { 208 NSString *filename = [NSString stringWithFormat:@"%@.tinypix", 209 [alertView textFieldAtIndex:0].text]; 210 NSURL *saveUrl = [self urlForFilename:filename]; 211 self.chosenDocument = [[BIDTinyPixDocument alloc] initWithFileURL:saveUrl]; 212 [self.chosenDocument saveToURL:saveUrl 213 forSaveOperation:UIDocumentSaveForCreating 214 completionHandler:^(BOOL success) { 215 if (success) { 216 NSLog(@"save OK"); 217 [self reloadFiles]; 218 [self performSegueWithIdentifier:@"masterToDetail" 219 sender:self]; 220 } else { 221 NSLog(@"failed to save!"); 222 } 223 }]; 224 } 225 } 226 227 - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 228 if (sender == self) { 229 // if sender == self, a new document has just been created, 230 // and chosenDocument is already set. 231 232 UIViewController * destination = segue.destinationViewController; 233 if ([destination respondsToSelector:@selector(setDetailItem:)]) { 234 [destination setValue:self.chosenDocument forKey:@"detailItem"]; 235 } 236 } else { 237 // find the chosen document from the tableview 238 NSIndexPath * indexPath = [self.tableView indexPathForSelectedRow]; 239 NSString * filename = self.documentFilenames[indexPath.row]; 240 NSURL * docUrl = [self urlForFilename:filename]; 241 self.chosenDocument = [[BIDTinyPixDocument alloc] initWithFileURL:docUrl]; 242 [self.chosenDocument openWithCompletionHandler:^(BOOL success) { 243 if (success) { 244 NSLog(@"load OK"); 245 UIViewController *destination = segue.destinationViewController; 246 if ([destination respondsToSelector:@selector(setDetailItem:)]) { 247 [destination setValue:self.chosenDocument forKey:@"detailItem"]; 248 } 249 } else { 250 NSLog(@"failed to load!"); 251 } 252 }]; 253 } 254 }
3、创建BIDTinyPixView视图类,用于显示用户可编辑的网格。
1 #import <UIKit/UIKit.h> 2 @class BIDTinyPixDocument; 3 4 @interface BIDTinyPixView : UIView 5 @property (strong, nonatomic) BIDTinyPixDocument * document; 6 @end
1 #import "BIDTinyPixView.h" 2 #import "BIDTinyPixDocument.h" 3 4 typedef struct { 5 NSUInteger row; 6 NSUInteger column; 7 } GridIndex; 8 9 @interface BIDTinyPixView () 10 11 @property (assign, nonatomic) CGSize blockSize; 12 @property (assign, nonatomic) CGSize gapSize; 13 @property (assign, nonatomic) GridIndex selectedBlockIndex; 14 15 @end 16 17 @implementation BIDTinyPixView 18 19 - (id)initWithFrame:(CGRect)frame 20 { 21 self = [super initWithFrame:frame]; 22 if (self) { 23 // Initialization code 24 [self commonInit]; 25 } 26 return self; 27 } 28 29 - (id)initWithCoder:(NSCoder *)aDecoder { 30 self = [super initWithCoder:aDecoder]; 31 if (self) { 32 [self commonInit]; 33 } 34 return self; 35 } 36 37 - (void)commonInit{ 38 _blockSize = CGSizeMake(34, 34); 39 _gapSize = CGSizeMake(5, 5); 40 _selectedBlockIndex.row = NSNotFound; 41 _selectedBlockIndex.column = NSNotFound; 42 } 43 44 // Only override drawRect: if you perform custom drawing. 45 // An empty implementation adversely affects performance during animation. 46 - (void)drawRect:(CGRect)rect 47 { 48 // Drawing code 49 if (!_document) return; 50 51 for (NSUInteger row = 0; row < 8; row++) { 52 for (NSUInteger column = 0; column < 8; column++) { 53 [self drawBlockAtRow:row column:column]; 54 } 55 } 56 } 57 58 - (void)drawBlockAtRow:(NSUInteger)row column:(NSUInteger)column { 59 CGFloat startX = (_blockSize.width + _gapSize.width) * (7 - column) + 1; 60 CGFloat startY = (_blockSize.height + _gapSize.height) * row + 1; 61 CGRect blockFrame = CGRectMake(startX, startY, _blockSize.width, _blockSize.height); 62 UIColor *color = [_document stateAtRow:row column:column] ? 63 [UIColor blackColor] : [UIColor whiteColor]; 64 [color setFill]; 65 [self.tintColor setStroke]; 66 UIBezierPath *path = [UIBezierPath bezierPathWithRect:blockFrame]; 67 [path fill]; 68 [path stroke]; 69 } 70 71 - (GridIndex)touchedGridIndexFromTouches:(NSSet *)touches { 72 GridIndex result; 73 UITouch *touch = [touches anyObject]; 74 CGPoint location = [touch locationInView:self]; 75 result.column = 8 - (location.x * 8.0 / self.bounds.size.width); 76 result.row = location.y * 8.0 / self.bounds.size.height; 77 return result; 78 } 79 80 - (void)toggleSelectedBlock { 81 [_document toggleStateAtRow:_selectedBlockIndex.row 82 column:_selectedBlockIndex.column]; 83 [[_document.undoManager prepareWithInvocationTarget:_document] 84 toggleStateAtRow:_selectedBlockIndex.row column:_selectedBlockIndex.column]; 85 [self setNeedsDisplay]; 86 } 87 88 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 89 self.selectedBlockIndex = [self touchedGridIndexFromTouches:touches]; 90 [self toggleSelectedBlock]; 91 } 92 93 - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { 94 GridIndex touched = [self touchedGridIndexFromTouches:touches]; 95 if (touched.row != _selectedBlockIndex.row 96 || touched.column != _selectedBlockIndex.column) { 97 _selectedBlockIndex = touched; 98 [self toggleSelectedBlock]; 99 } 100 }
4、添加BIDDetailViewController内容
1 @interface BIDDetailViewController : UIViewController 2 3 @property (strong, nonatomic) id detailItem; 4 5 @end
1 #import "BIDDetailViewController.h" 2 #import "BIDTinyPixView.h" 3 4 @interface BIDDetailViewController () 5 @property (weak, nonatomic) IBOutlet BIDTinyPixView *pixView; 6 - (void)configureView; 7 @end 8 9 @implementation BIDDetailViewController 10 11 #pragma mark - Managing the detail item 12 13 - (void)setDetailItem:(id)newDetailItem 14 { 15 if (_detailItem != newDetailItem) { 16 _detailItem = newDetailItem; 17 18 // Update the view. 19 [self configureView]; 20 } 21 } 22 23 - (void)configureView 24 { 25 // Update the user interface for the detail item. 26 27 if (self.detailItem) { 28 self.pixView.document = self.detailItem; 29 [self.pixView setNeedsDisplay]; 30 } 31 } 32 33 - (void)viewDidLoad 34 { 35 [super viewDidLoad]; 36 // Do any additional setup after loading the view, typically from a nib. 37 [self configureView]; 38 } 39 //当用户按下返回按钮回到主列表时,文档实例将在没有进行任何保存操作的情况下被销毁,所以添加如下方法。
//一旦用户离开详情页面,就关闭文档,关闭文档会导致该文档被自动保存,保存工作发生在后台线程中。 40 - (void)viewWillDisappear:(BOOL)animated 41 { 42 [super viewWillDisappear:animated]; 43 UIDocument * doc = self.detailItem; 44 [doc closeWithCompletionHandler:nil]; 45 }
5、添加iCloud支持
创建授权文件
6、如何查询
@property (strong, nonatomic) NSMetadataQuery * query; @property (strong, nonatomic) NSMutableArray * documentURLs; - (void)reloadFiles { NSFileManager * fileManager = [NSFileManager defaultManager]; // passing nil is OK here, matches first entitlement NSURL * cloudURL = [fileManager URLForUbiquityContainerIdentifier:nil]; NSLog(@"got cloudURL %@", cloudURL); // returns nil in simulator self.query = [[NSMetadataQuery alloc] init]; _query.predicate = [NSPredicate predicateWithFormat:@"%K like '*.tinypix'", NSMetadataItemFSNameKey]; _query.searchScopes = [NSArray arrayWithObject: NSMetadataQueryUbiquitousDocumentsScope]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateUbiquitousDocuments:) name:NSMetadataQueryDidFinishGatheringNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateUbiquitousDocuments:) name:NSMetadataQueryDidUpdateNotification object:nil]; [_query startQuery]; } //实现查询完成时的那些通知 //查询的结果包含在一个NSMetadataItem对象的列表,从中我们可以获取文件URL和创建日期等数据项,我们根据创建日期来排列这些项,然后获取所有的URL以供之后使用。 - (void)updateUbiquitousDocuments:(NSNotification *)notification { self.documentURLs = [NSMutableArray array]; self.documentFilenames = [NSMutableArray array]; NSLog(@"updateUbiquitousDocuments, results = %@", self.query.results); NSArray *results = [self.query.results sortedArrayUsingComparator: ^NSComparisonResult(id obj1, id obj2) { NSMetadataItem *item1 = obj1; NSMetadataItem *item2 = obj2; return [[item2 valueForAttribute:NSMetadataItemFSCreationDateKey] compare: [item1 valueForAttribute:NSMetadataItemFSCreationDateKey]]; }]; for (NSMetadataItem *item in results) { NSURL *url = [item valueForAttribute:NSMetadataItemURLKey]; [self.documentURLs addObject:url]; [(NSMutableArray *)_documentFilenames addObject:[url lastPathComponent]]; } [self.tableView reloadData]; }
7、保存到哪里
1 - (NSURL *)urlForFilename:(NSString *)filename 2 { 3 // be sure to insert "Documents" into the path 4 NSURL * baseURL = [[NSFileManager defaultManager] 5 URLForUbiquityContainerIdentifier:nil]; 6 NSURL * pathURL = [baseURL URLByAppendingPathComponent:@"Documents"]; 7 NSURL * destinationURL = [pathURL URLByAppendingPathComponent:filename]; 8 return destinationURL; 9 }
8、将首选项保存到iCloud
1 - (IBAction)chooseColor:(id)sender { 2 NSInteger selectedColorIndex = [(UISegmentedControl *)sender selectedSegmentIndex]; 3 [self setTintColorForIndex:selectedColorIndex]; 4 5 // NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults]; 6 // [prefs setInteger:selectedColorIndex forKey:@"selectedColorIndex"]; 7 // [prefs synchronize]; 8 9 NSUbiquitousKeyValueStore * prefs = [NSUbiquitousKeyValueStore defaultStore]; 10 [prefs setLongLong:selectedColorIndex forKey:@"selectedColorIndex"]; 11 }
1 - (void)viewDidAppear:(BOOL)animated { 2 [super viewDidAppear:animated]; 3 4 // NSUserDefaults * prefs = [NSUserDefaults standardUserDefaults]; 5 // NSInteger selectedColorIndex = [prefs integerForKey:@"selectedColorIndex"]; 6 7 NSUbiquitousKeyValueStore * prefs = [NSUbiquitousKeyValueStore defaultStore]; 8 NSInteger selectedColorIndex = (int)[prefs longLongForKey:@"selectedColorIndex"]; 9 10 [self setTintColorForIndex:selectedColorIndex]; 11 [self.colorControl setSelectedSegmentIndex:selectedColorIndex]; 12 }