欢送你浏览针对Windows Bridge for iOS上手系列博客的第一篇文章。Windows Bridge for iOS 是一个开辟源代码项目,你可以用它来创立Windows平台上一致的(UWP)使用,这些app可以运转于Windows10装备之上,而它们所运用的倒是iOS的API和Objective-C的代码。
今日,我们将在Xcode中构建一个容易的待处事项列表使用并运用Windows Bridge for iOS 来将其带到Windows10中来,要坚持将一切的代码都放到一个的代码库中,以利于项目在平台之间的完整可移植性。假如你想完整随着全部进程一步一步走一遍,可能需求做以下预备:
一台运转Windows 10的PC,而且装置了Visual Studio 2015 和 Windows Bridge for iOS。你可以从Windows Dev Center下载Visual Studio,而且在这里的GitHub上找到bridge的最新版本。
一台运转 Mac OS X 10.11 操纵系统的Mac电脑,而且装置了Xcode 7。假如你想要在实践的iOS装备上运转iOS项目,就还需求有一个付费的苹果开辟者账户.
假如你没有一台 PC 的话,可以从 Windows Bridge for iOS 的网站高低载我们的一个预先构建好的评价版本的虚拟机。依据你偏心运用的虚拟机情况如今对应包,然后你就能够在任什么时候间运转它了——这个包曾经包括了 Windows10,Visual Studio2015 和 iOS bridge。
假如你没有 Mac,可是又对在 Windows10 上运用 Objective-C 实行开辟觉得猎奇的话,依然可以去下载源代码,练习训练这全部进程而且在 Visual Studio 中编辑代码。
起首,下载初始的待处事项列表项目,可以在这里找到。翻开 ToDo.xcodeproj 文件,然后让我们来看看项目标架构。
在 Storyboard 编辑器中,我们有一个 UINavigationController 来充任根视图把持器,另有一个 UITableViewController 将作为我们的主屏界面来显示。由于全部使用就只要一个视图,所以导航把持器并非必需得有的,不外假如你想体验一下进一步改良这个项目标进程,那仍是为这个预留下空间比拟好,如许就便利前面再实行扩大了。
我们曾经构建了这个使用的大大多数工具,因而独一需求留意的就是“Clear” UIBarButtonItem,它曾经与 TDTableViewController 中的 clearAllTodos: 树立了一个 IBAction 的 outlet 联系关系。
如今让我们来看看Xcode中左边边栏中的类:
TDItem – 这是我们用来持有待处事项列表项的数据构造。
TDTableViewController – 这就是我们使用的大大多数营业逻辑的地点的地方。这个类集成自 UITableViewController ,治理着创立新的待处事项列表项和展现正在处置和曾经完成的待处事项这些功用。
TDTableViewCell – 这个类承继自 UITableViewCell ,而且为处置中和存档的待处事项都供给了结构。它运用了一个平移手势辨认器来添加删除功用,而且还保持了一个到其以后所展现的 TDItem. 的援用。它的拜托方法其父表格视图把持器,当一个格子被向左滑动(来实行待处事项删除)或许向右滑动(来实行待处事项的归档)时它就会被告诉到。
TDInputTableViewCell – 这个类也是集成自UITableViewCell,用来展现添加新的待处事项的输出框。它相似于 TDTableViewCell, 其拜托方是它的父表格视图把持器,当有新的待处事项被添加的时分会被告诉到。
TDLabel – 最初是 TDLabel,它承继自 UILabel,而且容易供给了一个让其全部文本上有根厚删除线的机制。
持续并在Xcode中的iOS模仿器中运转app,你会看到app启动了而且运转得很美丽:
测验考试一下添加一些待处事项,向右滑动来归档一个待处事项,另有向左滑动删除一个。假如你加入模仿器偏重新启动,会发明列表小时了;在我们把app带到Windows上时,会去反省一下存储会话数据的办法。
如今将项目标目次复制到一个主驱动器上,而且在你的Windows虚拟机上翻开。 (假如你运用的是Mac上的一个虚拟机,也能够将项目复制到一个Mac和Windows虚拟机都可以拜访到的同享目次。)
接下来,让我们将Xcode项目弄到Visual Studio 处理计划中。
在我们的 Windows 机械上,翻开 winobjc 目次并导航至 winobjc/bin ,你会发明一个叫做 vsimporter. 的文件。Vsimporter 是一个号令行Tools,它能将一个 Xcode 项目转换成一个 Visual Studio 处理计划。它会主动处置 Storyboards 和 Xibs,虽然今朝 Visual Studio 还并没有一个 Storyboard 编辑器,因而对 Storyboard 的修正如今还都必需在 Mac 长进行。 (这就是我们为何要事前会构建好大大多数的结构。)
在一个自力的文件阅读器窗口中翻开你的待处事项列表项目标目次。选择 File > Open command line prompt ,你会瞥见一个号令行窗口会显示出来。将 vsimporter 文件拖动到目次曾经切换到winobjc/bin 目次的号令行窗口之上,你应当会看到起全途径会显示出来。让号令行窗口处在取得核心的形态,然后敲下 Enter 键,然后再前往你的待处事项列表项目标目次,如今应当会包括一个新的 Visual Studio 处理计划文件。
双击方才创立的 Visual Studio 处理计划,Visual Studio 2015 就会启动。在侧边栏的 Visual Studio 处理计划阅读器里,你可以看到最顶层的处理计划文件,展看后就能够看到在 Xcode 中曾经很熟习的类构造。除扫尾文件在 Visual Studio 本人的目次中外,其他的构造都应当是一样的。
按下“F5”运转顺序,等候编译完成后,就胜利运转了。
我们用 Objective-C 写的的 iOS 使用就跑在了 Windows10 上。
你起首会留意到的是这个使用并没有适宜的缩放比例。Win10 系统运转在多种分歧装备上,要适应多种屏幕尺寸,为了包管杰出的用户体验,你的使用需求对所答应装备的设置装备摆设实行感知并反应。为了做到这点,我们将为这个使用生成一个 Category,即 app 拜托UIApplicationInitialStartupMode。
在处理计划阅读器中,双击 AppDelegate.m。 在最扫尾的 #import 底下,加入下面这些代码:
#ifdef WINOBJC @implementation UIApplication (UIApplicationInitialStartupMode) // Let WinObjC know how to render the app + (void) setStartupDisplayMode:(WOCDisplayMode*)mode { mode.autoMagnification = TRUE; mode.sizeUIWindowToFit = TRUE; mode.fixedWidth = 0; mode.fixedHeight = 0; mode.magnification = 1.0; } @end #endif
这里,我们运用了 #ifdef 和 #endif 预处置指令来反省 WINOBJC 符号能否曾经界说了,参加我们能包括 Windows 的特别代码。如许就坚持了代码库的可移植性, 由于 Windows 的特别代码在我们转头到 Xcode 中编辑而且在 iOS 上运转使用时会容易地被疏忽掉。
关于这个 WOCDisplayMode 工具属性的完好描绘(autoMagnification,sizeUIWindowToFit, fixedWidth 等等), 可以看看 GitHub上我们的项目维基的 SDK 一节的内容。
如今再次按下 F5 来运转使用,你会看到待处事项列表使用能正常的呼应窗口尺寸了。持续来添加一些待处事项,然后——
呃哦! 看起来我们发明了一个 bug:
略微深挖一下,我们会疾速地发明我们是在添加新的待处事项另有对他们实行归档时激发的问题。两种状况下,我们都运用了 UITableView的beginUpdates 和 endUpdates 实体办法挪用,他们能让我们修正依靠的数据构造并拔出和溢出表格视图中的行并包管全部进程的准确性。疾速的看一眼运转光阴志会显示出这些办法在 iOS bridge 中其实不支撑:
那该怎样办呢?
起首,确保你会在 GitHub 上提交发明的这个问题。GitHub 是通我们团队坚持相同的最好方法,以让我们了解你需求甚么Tools。假如你在 bridge 中找到了想要有却没有完成的 API,特征,或许是任何的问题,请让我们晓得。
接下来,我们异样可使用我们之前用来处理衬着问题的预处置指令来变通地处置这类状况。在 Visual Studio 中翻开 TDTableViewController.m,我们来改改 toDoItemDeleted:, toDoItemCompleted: 另有 toDoItemAdded: 办法:
- (void)toDoItemDeleted:(id)todoItem { #ifdef WINOBJC [_toDoItems removeObject:todoItem]; [self.tableView reloadData]; #else NSUInteger index = [_toDoItems indexOfObject:todoItem]; [self.tableView beginUpdates]; [_toDoItems removeObject:todoItem]; [self.tableView deleteRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:index inSection:TODO_SECTION]] withRowAnimation:UITableViewRowAnimationFade]; [self.tableView endUpdates]; #endif } - (void)toDoItemCompleted:(id)todoItem { #ifdef WINOBJC [_toDoItems removeObject:todoItem]; [_completedItems insertObject:todoItem atIndex:0]; [self.tableView reloadData]; #else NSUInteger index = [_toDoItems indexOfObject:todoItem]; [self.tableView beginUpdates]; [_toDoItems removeObject:todoItem]; [_completedItems insertObject:todoItem atIndex:0]; [self.tableView deleteRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:index inSection:TODO_SECTION]] withRowAnimation:UITableViewRowAnimationLeft]; [self.tableView insertRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:0 inSection:COMPLETE_SECTION]] withRowAnimation:UITableViewRowAnimationLeft]; [self.tableView endUpdates]; #endif } #pragma mark - TDInputTableViewCell delegate methods - (void)toDoItemAdded:(TDItem*) todoItem { #ifdef WINOBJC [_toDoItems insertObject:todoItem atIndex:0]; [self.tableView reloadData]; #else [self.tableView beginUpdates]; [_toDoItems insertObject:todoItem atIndex:0]; [self.tableView insertRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:0 inSection:TODO_SECTION]] withRowAnimation:UITableViewRowAnimationTop]; [self.tableView endUpdates]; #endif }
这个办法让我们可以轻松的在 Xcode 项目和 Visual Studio 处理计划中同享代码。当在 iOS 上运转使用的时分,我们仍是运用 beginUpdates 和 endUpdates 来数据格子的拔出和移除,可是在 Windows 上我们就只是容易的更新依靠的数据挪用,并挪用 reloadData,它会强迫让全部表格视图从头衬着。
按下 F5,你的 app 运转应当不会报错了。
如今,待处事项列表 app 假如不克不及影象你的添加的待处事项,那就其实不能完整投入运用,以后我们的使用把一切的工具都存储在内存外面,每次你启动它都得重新开端。我们固然可以做得更好一点。
由于我们曾经有了如许一个容易的运用场景,我们可使用属性列表序列化来将待处事项存储在一个 .plist 文件中。如许,我们就能够在每次创立、删除或许归档一个待处事项时写入文件,而且在 app 加载时读取这个文件。(愈加波动的一个完成就是每次一条数据发作变更时就将相干的变更写入文件,而不是全部待处事项的清单,不外容易起见,我们仍是会在一切的变更发作以后再全体写入文件。)
回到 TDTableViewController.m,在最底下添加下面这些办法:
- (void)writeToDosToDisk { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) { NSMutableArray *allItems = [[NSMutableArray alloc] init]; [_toDoItems enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { TDItem *item = obj; [allItems addObject:[item serialize]]; }]; [_completedItems enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { TDItem *item = obj; [allItems addObject:[item serialize]]; }]; NSArray *directories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documents = [directories firstObject]; NSString *filePath = [documents stringByAppendingPathComponent:@"todos.plist"]; if([allItems writeToFile:filePath atomically:YES]) { NSLog(@"Successfully wrote to dos to disk."); } }); } - (void)readToDosFromDisk { NSArray *directories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documents = [directories firstObject]; NSString *filePath = [documents stringByAppendingPathComponent:@"todos.plist"]; NSArray *loadedToDos = [NSArray arrayWithContentsOfFile:filePath]; [loadedToDos enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { NSDictionary *dict = obj; NSString *string = [[dict allKeys] firstObject]; BOOL complete = ((NSNumber*)[[dict allValues] firstObject]).boolValue; TDItem *toDo = [TDItem todoItemWithText:string isComplete:complete]; if(toDo.completed) { [_completedItems addObject:toDo]; } else { [_toDoItems addObject:toDo]; } }]; [self.tableView reloadData]; }
为了在一个属性清单中存储我们自界说的 TDItem 工具,我们需求将其转换成一个 NSDictionary。侥幸的是,我们的 TDItem 完成有一个 serialize 办法能做这件事儿并前往一个 NSDictionary,真是太巧了!
本文中的一切译文仅用于进修和交换目标,转载请务必注明文章译者、出处、和本文链接。 2KB翻译任务按照 CC 协定,假如我们的任务有进犯到您的权益,请实时联络我们。2KB项目(www.2kb.com,源码交易平台),提供担保交易、源码交易、虚拟商品、在家创业、在线创业、任务交易、网站设计、软件设计、网络兼职、站长交易、域名交易、链接买卖、网站交易、广告买卖、站长培训、建站美工等服务