
从事软件行业近二十年
我的博客即将入驻“云栖社区”,诚邀技术同仁一同入驻。
Android Studio 中 build.gradle 中 dependencies 下的 comile 后面的内容的来源 compile 'io.reactivex.rxjava2:rxandroid:2.0.1' compile 'io.reactivex.rxjava2:rxjava:2.1.0' 答案来自于:http://mvnrepository.com/ 你知道的名字应该是 rxandroid,而它依赖 rxjava,如果你想使用 rxjava 的最新版本,而不是 rxandroid 默认指定的依赖版本,那么就加上它的 compile. <!-- https://mvnrepository.com/artifact/io.reactivex.rxjava2/rxandroid --> <dependency> <groupId>io.reactivex.rxjava2</groupId> <artifactId>rxandroid</artifactId> <version>2.0.1</version> </dependency> <!-- https://mvnrepository.com/artifact/io.reactivex.rxjava2/rxjava --> <dependency> <groupId>io.reactivex.rxjava2</groupId> <artifactId>rxjava</artifactId> <version>2.1.0</version> </dependency>
Eclipse 使用 Maven 构建动态 Web 工程,默认无 java 目录的解决方法 太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循“署名-非商业用途-保持一致”创作公用协议 转载请保留此句:太阳火神的美丽人生 - 本博客专注于 敏捷开发及移动和物联设备、绿色教育的研究:iOS、Android、Html5、Arduino、pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作。 两年前,在病重的那些日子里,把有关 Maven 构建 Spring MVC 项目的细节扒了个底朝天,尤其各种细节的官方文档出处。 现在看来还不够,有很多细节还没有记录下来,接下来,有空再继续把玩个够,技术这东西,就得玩成象掰指节响一样熟练才有意思。 这回高大尚,弄了个试用版的 Myeclipse 2014,据说,也证实过了,Myeclipae 2015 对于 Spring MVC 的 web jar 包莫名的不兼容,好吧 下载2017,前途未补,还是降下来吧。 新建工程,又是一个小儿科,可是还要更熟,直到闭眼睛都能操作出来才好: 。。。。。。 截图不能直接粘上来,很是倒运,那就不折腾了,直入主题,一句话的事儿,本是从别人那里看到了,反复验证后,贴点图算是自已的了,没想到还是盗取的结果,也好,总比盗而不言强得多,至少我在传播吧。 新建工程,src/main 下只有 resources 和 webapp,没有 java 目录。 打开工程的构建路径,选中 JRE System Library,点右边的 Edit...,在弹出窗口中切换任意选项,均会使 java 目录自动生成。 当然建议点那个 Installed JREs,构建本机 JDK 的 JRE 系统库,然后选中,据说 JDK 的能看源码, JRE 的不能,真假不知。 那么说的那位哥,你说的是这个吗? 据说这里能配源码!噢,好像源码已经在本地了。 据说这里还能配文档!噢,这个是在线的,不妨下一个到本地来试试,然后改一下: 回归正题,看看那个 java 回来是什么样子, 真是闲得无聊,手工建个 java 目录就完事了呗?! 不,拿 IDE 和这老些自动化插件,然后再手动建个目录,感觉有点太祟。 好了,到此结束,下次再有心情,再继续垒泥坝,一点一点地垒,很有意思,也很多。 补充 jre 的不能配置源码的截图: 但文档是有的。 另外,你也可以给他配一个,具体调试时能否进到源码中,这个未实践证实,证实过的朋友不妨留个言。
iOS Swift _Nullable 与 Android 注解帮助编译时检查 - 两家好像步调开始一致一段时间了 太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循“署名-非商业用途-保持一致”创作公用协议 转载请保留此句:太阳火神的美丽人生 - 本博客专注于 敏捷开发及移动和物联设备研究:iOS、Android、HTML5、Arduino、pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作。 首先声明个人看法,在 Android 系统运行时中,通过注入方式给 XML 布局中界面元素引出实例,一样要写东西,相对来讲也没简化多少代码量,确将编译时的负担转嫁给了运行时,这种懒可能与程序员的天生懒促使进步是背道而驰的,如果手机真的到了 Spring MVC 在服务器上运行资源丰厚的地步,这个元可厚非,然而,就连我的 E550 换了三星 固态,内存加到 12 G,运行都要偶尔出现卡顿,更何况手机乎?当然不至于这么邪乎,但是微量的积累,对于持于集成不断宠大的工程来说,灾难是注定的,而解决办法只能是自断肱股。 下面列出个点,后续扩展吧,之前也总是这样,最终没的时间扩展来说,等想到时,已经没有当时的激性了,最终不了了之。 编译时检查! 待续。。。
XCode 8 Take a screenshot on a device If you need a launch image for your app, you can use Xcode to launch an app on a device and capture a screenshot. For iOS and tvOS apps, connect a device to your Mac before you begin. For watchOS apps, connect an iPhone paired with an Apple Watch. 1、Take a screenshot using the Debug menu Choose Debug > View Debugging > Take Screenshot of Active Device. The new screenshot appears on the desktop. 2、Take a screenshot using the Devices window Open the Devices window (choose Window > Devices). In the left column, select your device, and in the detail pane, click the Take Screenshot button. The new screenshot appears on the desktop.When generating your app’s launch images, always capture images from devices with the appropriate screen size and resolution. For a universal iOS app, this means capturing screenshots both from an iPad and from an iPhone or iPod touch. If you are submitting your app to the App Store, capture all required screenshots that you upload later to iTunes Connect. 看巴懂的留言,这个估计都能看懂,留个便签,方便查询。
iOS 10 中导航控制器出栈入栈连续操作存在的问题 太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循“署名-非商业用途-保持一致”创作公用协议 转载请保留此句:太阳火神的美丽人生 - 本博客专注于 敏捷开发及移动和物联设备研究:iOS、Android、HTML5、Arduino、pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作。 [iOS][兼容性]iOS7 pushViewController,popViewControllerAnimated存在的问题 出现如上链接中提到的问题,懒得再描述了。 其给出的解决办法 dispatch_async(dispatch_get_main_queue(), ^{ [currentViewController PushViewController:viewController animation:animation]; }); 实践,这种办法没有效果,倒是利用延时之前解决过 push 同时隐藏 tabbar 时下方闪黑的问题: __block UIViewController *weakSelf = self; dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.01 * NSEC_PER_SEC)); dispatch_after(delayTime, dispatch_get_main_queue(), ^{ [weakSelf performSelectorOnMainThread:@selector(delayPush:) withObject:viewController waitUntilDone:YES]; }); currentController.extendedLayoutIncludesOpaqueBars = NO; currentController.navigationController.navigationBar.translucent = NO; currentController.edgesForExtendedLayout = UIRectEdgeNone;//UIRectEdgeTop | UIRectEdgeBottom | UIRectEdgeLeft | UIRectEdgeRight; } - (void)delayPush:(UIViewController *)controller { [self pushViewController:controller animated:YES]; } @end 只不过,我这是在主线程延时执行。 是否把它的 dispatch_async 换成我的 dispatch_after 并指定个延时时间会有效果。 下面列一下我的解决办法。 我是想把前面的 Controller 都静默 pop 掉,然后再 push 一个,这样最后这个 controller 返回时,是首页。 分析 push 与 pop ,最终都是对 UINavigationController 的 viewcontrollers 数组进行管理并配以相应动画,这样的出栈、入栈操作,可以直接对 viewcontrollers 进行。 UIViewController *mainController = self.navigationController.viewControllers[0]; NSArray *controllers = [NSArray arrayWithObjects:mainController, controller, nil]; [self.navigationController setViewControllers:controllers animated:YES]; 好了,问题就这样解决了,把所有的变化集中在 viewcontrollers 的变化中,一次性交给 UINavigationController 进处理。 补充: 实践证明,上面的猜测是不可以的 [self.navigationController popToRootViewControllerAnimated:NO]; dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)); dispatch_after(delayTime, dispatch_get_main_queue(), ^{ [self.navigationController pushViewController:controller animated:YES]; }); 还是得用上面重构 viewcontrollers 来调整。
从 iOS AutoLayout 到识别关系链的渐进过程,说开去! 太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循“署名-非商业用途-保持一致”创作公用协议 转载请保留此句:太阳火神的美丽人生 - 本博客专注于 敏捷开发及移动和物联设备研究:iOS、Android、HTML5、Arduino、pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作。 本想就近几天彻底研究实践得到的结果作进一步的展示, 然后确被 AutoLayout 洗了脑,终于明白了自动布局系统犹生人生的各中局, 局中的关系是基本固定的,但仍有微妙变化的, 需要你去识别出来,只要关系链找得准, 那么形成一个稳定的布局不是问题, 然而一不小心,就容易漏掉了哪里,或撞破了哪层关系, 呜呼,好生繁复。 然而,自动布局也是有原则的,那么人生过场中各中棋局何偿不是, 只是没有认识到,这是局,需要破,冒冒失失地往里闯而已, 一直以这种蛮劲来解决这种问题,孰料,局间之巧力遍布,似万千柔丝绑缚,看似轻巧,确真的个愈挣愈紧。 对于 AutoLayout 懂与不懂,不要紧了,先学会看局吧,看懂了,局即已破,信手捻来。看不破即入,结果可想而知。 后续补充识局绝技,自动布局就是局,慢慢来过,且不可心急蛮闯!
iOS6~10 UITableViewCell 之行高算式精要 太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循“署名-非商业用途-保持一致”创作公用协议 转载请保留此句:太阳火神的美丽人生 - 本博客专注于 敏捷开发及移动和物联设备研究:iOS、Android、HTML5、Arduino、pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作。 简言之,自动布局耳。 iOS 2.0 引入 @property(nonatomic) CGFloat rowHeight; iOS 7.0 引入 @property(nonatomic) CGFloat estimatedRowHeight; const CGFloat UITableViewAutomaticDimension; Requests that UITableView use the default value for a given dimension. 初始设置 - (void)viewDidLoad { [super viewDidLoad]; self.tableView.estimatedRowHeight = 100; self.tableView.rowHeight = UITableViewAutomaticDimension; 行高代理方法实现 -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { // 用 cell 标识从缓存中提取 cell 实例,如果没有,系统自动按 storyboard 中 cell 原型或自注山的 cell 创建 NSString *identifier = @"main_cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier]; // 在自动计算高度前,先填充 cell 的内容 // 禁止将 AutoresizingMask 转换成约束 cell.contentView.translatesAutoresizingMaskIntoConstraints = NO; // 表视图的外框宽度 CGFloat contentViewWidth = CGRectGetWidth(self.tableView.bounds); // 使用该宽度构建表视图单元的内容视图的宽度约束 NSLayoutConstraint *widthFenceConstraint = [NSLayoutConstraint constraintWithItem:cell.contentView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:contentViewWidth]; // 约束表单元内容视图的宽度 [cell.contentView addConstraint:widthFenceConstraint]; // 确保表单元内容视图中元素布局自顶向下连贯,内容视图的顶边和底边有约束,左右一样要连贯,才能计算出正确的值,否则为 0 或 10001,也许还有别的 CGFloat fittingHeight = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingExpandedSize].height; // 高度由自动布局用 systemLayoutSizeFittingSize 算完了,约束可以拿下去了 [cell.contentView removeConstraint:widthFenceConstraint]; // 需要分隔线并打开时,按屏幕映射系数把这一个单位的实际像素加上去。 //return fittingHeight+2*1/[UIScreen mainScreen].scale; return fittingHeight; } 总结: 其中注释已详述,关键两点, 一是表单元的内容视图没有四边固定约束,所以会缺少约束,而其宽度一般为表宽,故加此约束, 二是高度由内容撑起,其包裹内容,以确定上下边的约束,故内容视图上下边也一定要有约束。(补充:一定要让内容视图的顶、底直接或间接相连以约束) 原考虑直接 controll-drag 建立表单元内容视图与外部各层容器的约束,以确保其宽度为明确的,但实际是不允许这样做的,所以得从代码中加入,否则 systemLayoutSizeFittingSize 计算得到的值为10001 或 0(网友结果,本人未遇)。 另外, 可以使用网上流行的 FDTemplateLayoutCell 再有约束封装开源库 Masonry 近期翻译了官方有关自动布局多篇文档,官方建议的 Stack View,也许作为如 Android 线性布局类似的工具,也许会有更多版本提供吧,不如干脆采用 Android 同样的布局算了,可是面子又过意不去,何如?
AndroidSocketClient socket client server简易封装 Import JitPack Add it in your project's build.gradle at the end of repositories: repositories { // ... maven { url "https://jitpack.io" } } Step 2. Add the dependency in the form dependencies { compile 'com.github.vilyever:AndroidSocketClient:3.0.3' } Updates 3.0.3 提升readByLength按长度读取的速度 issue #26 3.0.2 修复初次连接失败时,因CountDownTimer导致崩溃的问题 issue #24 3.0.1 修复包头验证bug,by zzdwuliang 增加地址检测的详细log 3.0.0 支持ReadToData和ReadToLength自动读取以下两种结构 常见包结构1:【包头(可选)】【正文】【包尾】 常见包结构2:【包头(可选)】【余下包长度(正文加包尾长度)(此部分也可做包头)(此部分长度固定)】【正文】【包尾(可选)】 Usage app模块下包含简单的使用demo 请一定设置读取策略socketClient.getSocketPacketHelper().setReadStrategy(); 远程端连接信息配置 socketClient.getAddress().setRemoteIP(IPUtil.getLocalIPAddress(true)); // 远程端IP地址 socketClient.getAddress().setRemotePort("21998"); // 远程端端口号 socketClient.getAddress().setConnectionTimeout(15 * 1000); // 连接超时时长,单位毫秒 默认String编码配置 /** * 设置自动转换String类型到byte[]类型的编码 * 如未设置(默认为null),将不能使用{@link SocketClient#sendString(String)}发送消息 * 如设置为非null(如UTF-8),在接受消息时会自动尝试在接收线程(非主线程)将接收的byte[]数据依照编码转换为String,在{@link SocketResponsePacket#getMessage()}读取 */ socketClient.setCharsetName(CharsetUtil.UTF_8); // 设置编码为UTF-8 固定心跳包配置 /** * 设置自动发送的心跳包信息 */ socketClient.getHeartBeatHelper().setDefaultSendData(CharsetUtil.stringToData("HeartBeat", CharsetUtil.UTF_8)); /** * 设置远程端发送到本地的心跳包信息内容,用于判断接收到的数据包是否是心跳包 * 通过{@link SocketResponsePacket#isHeartBeat()} 查看数据包是否是心跳包 */ socketClient.getHeartBeatHelper().setDefaultReceiveData(CharsetUtil.stringToData("HeartBeat", CharsetUtil.UTF_8)); socketClient.getHeartBeatHelper().setHeartBeatInterval(10 * 1000); // 设置自动发送心跳包的间隔时长,单位毫秒 socketClient.getHeartBeatHelper().setSendHeartBeatEnabled(true); // 设置允许自动发送心跳包,此值默认为false 动态变化心跳包配置 /** * 设置自动发送的心跳包信息 * 此信息动态生成 * * 每次发送心跳包时自动调用 */ socketClient.getHeartBeatHelper().setSendDataBuilder(new SocketHeartBeatHelper.SendDataBuilder() { @Override public byte[] obtainSendHeartBeatData(SocketHeartBeatHelper helper) { /** * 使用当前日期作为心跳包 */ byte[] heartBeatPrefix = new byte[]{0x1F, 0x1F}; byte[] heartBeatSuffix = new byte[]{0x1F, 0x1F}; SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); byte[] heartBeatInfo = CharsetUtil.stringToData(sdf.format(new Date()), CharsetUtil.UTF_8); byte[] data = new byte[heartBeatPrefix.length + heartBeatSuffix.length + heartBeatInfo.length]; System.arraycopy(heartBeatPrefix, 0, data, 0, heartBeatPrefix.length); System.arraycopy(heartBeatInfo, 0, data, heartBeatPrefix.length, heartBeatInfo.length); System.arraycopy(heartBeatSuffix, 0, data, heartBeatPrefix.length + heartBeatInfo.length, heartBeatSuffix.length); return data; } }); /** * 设置远程端发送到本地的心跳包信息的检测器,用于判断接收到的数据包是否是心跳包 * 通过{@link SocketResponsePacket#isHeartBeat()} 查看数据包是否是心跳包 */ socketClient.getHeartBeatHelper().setReceiveHeartBeatPacketChecker(new SocketHeartBeatHelper.ReceiveHeartBeatPacketChecker() { @Override public boolean isReceiveHeartBeatPacket(SocketHeartBeatHelper helper, SocketResponsePacket packet) { /** * 判断数据包信息是否含有指定的心跳包前缀和后缀 */ byte[] heartBeatPrefix = new byte[]{0x1F, 0x1F}; byte[] heartBeatSuffix = new byte[]{0x1F, 0x1F}; if (Arrays.equals(heartBeatPrefix, Arrays.copyOfRange(packet.getData(), 0, heartBeatPrefix.length)) && Arrays.equals(heartBeatSuffix, Arrays.copyOfRange(packet.getData(), packet.getData().length - heartBeatSuffix.length, packet.getData().length))) { return true; } return false; } }); socketClient.getHeartBeatHelper().setHeartBeatInterval(10 * 1000); // 设置自动发送心跳包的间隔时长,单位毫秒 socketClient.getHeartBeatHelper().setSendHeartBeatEnabled(true); // 设置允许自动发送心跳包,此值默认为false 自动按包尾分割信息读取数据的发送配置 /** * 根据连接双方协议设置自动发送的包尾数据 * 每次发送数据包(包括心跳包)都会在发送包内容后自动发送此包尾 * * 例:socketClient.sendData(new byte[]{0x01, 0x02})的步骤为 * 1. socketClient向远程端发送包头(如果设置了包头信息) * 2. socketClient向远程端发送正文数据{0x01, 0x02} * 3. socketClient向远程端发送包尾 * * 使用{@link com.vilyever.socketclient.helper.SocketPacketHelper.ReadStrategy.AutoReadToTrailer}必须设置此项 * 用于分隔多条消息 */ socketClient.getSocketPacketHelper().setSendTrailerData(new byte[]{0x13, 0x10}); /** * 根据连接双方协议设置自动发送的包头数据 * 每次发送数据包(包括心跳包)都会在发送包内容前自动发送此包头 * * 若无需包头可删除此行 */ socketClient.getSocketPacketHelper().setSendHeaderData(CharsetUtil.stringToData("SocketClient:", CharsetUtil.UTF_8)); /** * 设置分段发送数据长度 * 即在发送指定长度后通过 {@link SocketClientSendingDelegate#onSendingPacketInProgress(SocketClient, SocketPacket, float, int)}回调当前发送进度 * * 若无需进度回调可删除此二行,删除后仍有【发送开始】【发送结束】的回调 */ socketClient.getSocketPacketHelper().setSendSegmentLength(8); // 设置发送分段长度,单位byte socketClient.getSocketPacketHelper().setSendSegmentEnabled(true); // 设置允许使用分段发送,此值默认为false /** * 设置发送超时时长 * 在发送每个数据包时,发送每段数据的最长时间,超过后自动断开socket连接 * 通过设置分段发送{@link SocketPacketHelper#setSendSegmentEnabled(boolean)} 可避免发送大数据包时因超时断开, * * 若无需限制发送时长可删除此二行 */ socketClient.getSocketPacketHelper().setSendTimeout(30 * 1000); // 设置发送超时时长,单位毫秒 socketClient.getSocketPacketHelper().setSendTimeoutEnabled(true); // 设置允许使用发送超时时长,此值默认为false 自动按包尾分割信息读取数据的接收配置 /** * 设置读取策略为自动读取到指定的包尾 */ socketClient.getSocketPacketHelper().setReadStrategy(SocketPacketHelper.ReadStrategy.AutoReadToTrailer); /** * 根据连接双方协议设置的包尾数据 * 每次接收数据包(包括心跳包)都会在检测接收到与包尾数据相同的byte[]时回调一个数据包 * * 例:自动接收远程端所发送的socketClient.sendData(new byte[]{0x01, 0x02})【{0x01, 0x02}为将要接收的数据】的步骤为 * 1. socketClient接收包头(如果设置了包头信息)(接收方式为一直读取到与包头相同的byte[],即可能过滤掉包头前的多余信息) * 2. socketClient接收正文和包尾(接收方式为一直读取到与尾相同的byte[]) * 3. socketClient回调数据包 * * 使用{@link com.vilyever.socketclient.helper.SocketPacketHelper.ReadStrategy.AutoReadToTrailer}必须设置此项 * 用于分隔多条消息 */ socketClient.getSocketPacketHelper().setReceiveTrailerData(new byte[]{0x13, 0x10}); /** * 根据连接双方协议设置的包头数据 * 每次接收数据包(包括心跳包)都会先接收此包头 * * 若无需包头可删除此行 */ socketClient.getSocketPacketHelper().setReceiveHeaderData(CharsetUtil.stringToData("SocketClient:", CharsetUtil.UTF_8)); /** * 设置接收超时时长 * 在指定时长内没有数据到达本地自动断开 * * 若无需限制接收时长可删除此二行 */ socketClient.getSocketPacketHelper().setReceiveTimeout(120 * 1000); // 设置接收超时时长,单位毫秒 socketClient.getSocketPacketHelper().setReceiveTimeoutEnabled(true); // 设置允许使用接收超时时长,此值默认为false 自动按包长度信息读取的发送配置 /** * 设置包长度转换器 * 即每次发送数据时,将包头以外的数据长度转换为特定的byte[]发送到远程端用于解析还需要读取多少长度的数据 * * 例:socketClient.sendData(new byte[]{0x01, 0x02})的步骤为 * 1. socketClient向远程端发送包头(如果设置了包头信息) * 2. socketClient要发送的数据为{0x01, 0x02},长度为2(若设置了包尾,还需加上包尾的字节长度),通过此转换器将int类型的2转换为4字节的byte[],远程端也照此算法将4字节的byte[]转换为int值 * 3. socketClient向远程端发送转换后的长度信息byte[] * 4. socketClient向远程端发送正文数据{0x01, 0x02} * 5. socketClient向远程端发送包尾(如果设置了包尾信息) * * 此转换器用于第二步 * * 使用{@link com.vilyever.socketclient.helper.SocketPacketHelper.ReadStrategy.AutoReadByLength}必须设置此项 * 用于分隔多条消息 */ socketClient.getSocketPacketHelper().setSendPacketLengthDataConvertor(new SocketPacketHelper.SendPacketLengthDataConvertor() { @Override public byte[] obtainSendPacketLengthDataForPacketLength(SocketPacketHelper helper, int packetLength) { /** * 简单将int转换为byte[] */ byte[] data = new byte[4]; data[3] = (byte) (packetLength & 0xFF); data[2] = (byte) ((packetLength >> 8) & 0xFF); data[1] = (byte) ((packetLength >> 16) & 0xFF); data[0] = (byte) ((packetLength >> 24) & 0xFF); return data; } }); /** * 根据连接双方协议设置自动发送的包头数据 * 每次发送数据包(包括心跳包)都会在发送包内容前自动发送此包头 * * 若无需包头可删除此行 */ socketClient.getSocketPacketHelper().setSendHeaderData(CharsetUtil.stringToData("SocketClient:", CharsetUtil.UTF_8)); /** * 根据连接双方协议设置自动发送的包尾数据 * 每次发送数据包(包括心跳包)都会在发送包内容后自动发送此包尾 * * 若无需包尾可删除此行 * 注意: * 使用{@link com.vilyever.socketclient.helper.SocketPacketHelper.ReadStrategy.AutoReadByLength}时不依赖包尾读取数据 */ socketClient.getSocketPacketHelper().setSendTrailerData(new byte[]{0x13, 0x10}); /** * 设置分段发送数据长度 * 即在发送指定长度后通过 {@link SocketClientSendingDelegate#onSendingPacketInProgress(SocketClient, SocketPacket, float, int)}回调当前发送进度 * * 若无需进度回调可删除此二行,删除后仍有【发送开始】【发送结束】的回调 */ socketClient.getSocketPacketHelper().setSendSegmentLength(8); // 设置发送分段长度,单位byte socketClient.getSocketPacketHelper().setSendSegmentEnabled(true); // 设置允许使用分段发送,此值默认为false /** * 设置发送超时时长 * 在发送每个数据包时,发送每段数据的最长时间,超过后自动断开socket连接 * 通过设置分段发送{@link SocketPacketHelper#setSendSegmentEnabled(boolean)} 可避免发送大数据包时因超时断开, * * 若无需限制发送时长可删除此二行 */ socketClient.getSocketPacketHelper().setSendTimeout(30 * 1000); // 设置发送超时时长,单位毫秒 socketClient.getSocketPacketHelper().setSendTimeoutEnabled(true); // 设置允许使用发送超时时长,此值默认为false 自动按包长度信息读取的接收配置 /** * 设置读取策略为自动读取指定长度 */ socketClient.getSocketPacketHelper().setReadStrategy(SocketPacketHelper.ReadStrategy.AutoReadByLength); /** * 设置包长度转换器 * 即每次接收数据时,将远程端发送到本地的长度信息byte[]转换为int,然后读取相应长度的值 * * 例:自动接收远程端所发送的socketClient.sendData(new byte[]{0x01, 0x02})【{0x01, 0x02}为将要接收的数据】的步骤为 * 1. socketClient接收包头(如果设置了包头信息)(接收方式为一直读取到与包头相同的byte[],即可能过滤掉包头前的多余信息) * 2. socketClient接收长度为{@link SocketPacketHelper#getReceivePacketLengthDataLength()}(此处设置为4)的byte[],通过下面设置的转换器,将byte[]转换为int值,此int值暂时称为X * 3. socketClient接收长度为X的byte[] * 4. socketClient接收包尾(如果设置了包尾信息)(接收方式为一直读取到与包尾相同的byte[],如无意外情况,此处不会读取到多余的信息) * 5. socketClient回调数据包 * * 此转换器用于第二步 * * 使用{@link com.vilyever.socketclient.helper.SocketPacketHelper.ReadStrategy.AutoReadByLength}必须设置此项 * 用于分隔多条消息 */ socketClient.getSocketPacketHelper().setReceivePacketLengthDataLength(4); socketClient.getSocketPacketHelper().setReceivePacketDataLengthConvertor(new SocketPacketHelper.ReceivePacketDataLengthConvertor() { @Override public int obtainReceivePacketDataLength(SocketPacketHelper helper, byte[] packetLengthData) { /** * 简单将byte[]转换为int */ int length = (packetLengthData[3] & 0xFF) + ((packetLengthData[2] & 0xFF) << 8) + ((packetLengthData[1] & 0xFF) << 16) + ((packetLengthData[0] & 0xFF) << 24); return length; } }); /** * 根据连接双方协议设置的包头数据 * 每次接收数据包(包括心跳包)都会先接收此包头 * * 若无需包头可删除此行 */ socketClient.getSocketPacketHelper().setReceiveHeaderData(CharsetUtil.stringToData("SocketClient:", CharsetUtil.UTF_8)); /** * 根据连接双方协议设置的包尾数据 * * 若无需包尾可删除此行 * 注意: * 使用{@link com.vilyever.socketclient.helper.SocketPacketHelper.ReadStrategy.AutoReadByLength}时不依赖包尾读取数据 */ socketClient.getSocketPacketHelper().setReceiveTrailerData(new byte[]{0x13, 0x10}); /** * 设置接收超时时长 * 在指定时长内没有数据到达本地自动断开 * * 若无需限制接收时长可删除此二行 */ socketClient.getSocketPacketHelper().setReceiveTimeout(120 * 1000); // 设置接收超时时长,单位毫秒 socketClient.getSocketPacketHelper().setReceiveTimeoutEnabled(true); // 设置允许使用接收超时时长,此值默认为false 手动读取的发送配置 /** * 设置分段发送数据长度 * 即在发送指定长度后通过 {@link SocketClientSendingDelegate#onSendingPacketInProgress(SocketClient, SocketPacket, float, int)}回调当前发送进度 * * 若无需进度回调可删除此二行,删除后仍有【发送开始】【发送结束】的回调 */ socketClient.getSocketPacketHelper().setSendSegmentLength(8); // 设置发送分段长度,单位byte socketClient.getSocketPacketHelper().setSendSegmentEnabled(true); // 设置允许使用分段发送,此值默认为false /** * 设置发送超时时长 * 在发送每个数据包时,发送每段数据的最长时间,超过后自动断开socket连接 * 通过设置分段发送{@link SocketPacketHelper#setSendSegmentEnabled(boolean)} 可避免发送大数据包时因超时断开, * * 若无需限制发送时长可删除此二行 */ socketClient.getSocketPacketHelper().setSendTimeout(30 * 1000); // 设置发送超时时长,单位毫秒 socketClient.getSocketPacketHelper().setSendTimeoutEnabled(true); // 设置允许使用发送超时时长,此值默认为false 手动读取的接收配置 /** * 设置读取策略为手动读取 * 手动读取有两种方法 * 1. {@link SocketClient#readDataToData(byte[], boolean)} )} 读取到与指定字节相同的字节序列后回调数据包 * 2. {@link SocketClient#readDataToLength(int)} 读取指定长度的字节后回调数据包 * * 此时SocketPacketHelper中其他读取相关设置将会无效化 */ socketClient.getSocketPacketHelper().setReadStrategy(SocketPacketHelper.ReadStrategy.Manually); 常用回调配置 // 对应removeSocketClientDelegate socketClient.registerSocketClientDelegate(new SocketClientDelegate() { /** * 连接上远程端时的回调 */ @Override public void onConnected(SocketClient client) { SocketPacket packet = socketClient.sendData(new byte[]{0x02}); // 发送消息 packet = socketClient.sendString("sy hi!"); // 发送消息 socketClient.cancelSend(packet); // 取消发送,若在等待发送队列中则从队列中移除,若正在发送则无法取消 } /** * 与远程端断开连接时的回调 */ @Override public void onDisconnected(SocketClient client) { // 可在此实现自动重连 socketClient.connect(); } /** * 接收到数据包时的回调 */ @Override public void onResponse(final SocketClient client, @NonNull SocketResponsePacket responsePacket) { byte[] data = responsePacket.getData(); // 获取接收的byte数组,不为null String message = responsePacket.getMessage(); // 获取按默认设置的编码转化的String,可能为null } }); 发送状态回调配置 // 对应removeSocketClientSendingDelegate socketClient.registerSocketClientSendingDelegate(new SocketClientSendingDelegate() { /** * 数据包开始发送时的回调 */ @Override public void onSendPacketBegin(SocketClient client, SocketPacket packet) { } /** * 数据包取消发送时的回调 * 取消发送回调有以下情况: * 1. 手动cancel仍在排队,还未发送过的packet * 2. 断开连接时,正在发送的packet和所有在排队的packet都会被取消 */ @Override public void onSendPacketCancel(SocketClient client, SocketPacket packet) { } /** * 数据包发送的进度回调 * progress值为[0.0f, 1.0f] * 通常配合分段发送使用 * 可用于显示文件等大数据的发送进度 */ @Override public void onSendingPacketInProgress(SocketClient client, SocketPacket packet, float progress, int sendedLength) { } /** * 数据包完成发送时的回调 */ @Override public void onSendPacketEnd(SocketClient client, SocketPacket packet) { } }); 接收状态回调配置 // 对应removeSocketClientReceiveDelegate socketClient.registerSocketClientReceiveDelegate(new SocketClientReceivingDelegate() { /** * 开始接受一个新的数据包时的回调 */ @Override public void onReceivePacketBegin(SocketClient client, SocketResponsePacket packet) { } /** * 完成接受一个新的数据包时的回调 */ @Override public void onReceivePacketEnd(SocketClient client, SocketResponsePacket packet) { } /** * 取消接受一个新的数据包时的回调 * 在断开连接时会触发 */ @Override public void onReceivePacketCancel(SocketClient client, SocketResponsePacket packet) { } /** * 接受一个新的数据包的进度回调 * progress值为[0.0f, 1.0f] * 仅作用于ReadStrategy为AutoReadByLength的自动读取 * 因AutoReadByLength可以首先接受到剩下的数据包长度 */ @Override public void onReceivingPacketInProgress(SocketClient client, SocketResponsePacket packet, float progress, int receivedLength) { } }); License Apache License Version 2.0
ThinkPad E550 安装 WIN7 的启动 U 盘制作 太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循“署名-非商业用途-保持一致”创作公用协议 转载请保留此句:太阳火神的美丽人生 - 本博客专注于 敏捷开发及移动和物联设备研究:iOS、Android、Html5、Arduino、pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作。 此文有几个点: 1、 E550 强捆了 WIN8 的分区格式,所以需要在 BIOS 中把 UEFI ONLY 改成 兼容模式,这样 WIN7 使用的老败也能支持了; 2、制作启动 U 盘!UltraISO 的制作方式用了快十年了,这次失败,已经预料到会是分区识别问题。找到一专用工具 http://www.ushendu.org/ 话不多说,选择下载 UEFI 版,然后就制作吧。 WIN 7 下载链接提供两个: thunder://QUFodHRwOi8vZG93bi02NC5naG9zdDc3Ny5jb20vZ2hvc3Rjai9XaW5kb3dzIDcgWDY0Lmlzb1pathunder://QUFodHRwOi8vdzc0LnpodWFuZ3hpdG9uZy5jb206ODA4LzIwMTYwNy9GUUhZX0dIT1NUX1dJTjdfU1AxX1g2NF9WMjAxNl8wNy5pc29aWg== 至于 UltraISO 如何制作成功,这个我想就无需这么怀旧了,学会放下,才能轻装前行,始终想着我们的目标是: 1、把新入手的 250 G 的三星 850 SSD 系统做好,要 WIN7 的系统。(果断没选 750,因为那是简版,性能和寿命都减了的) 2、那么就要先准备好 WIN7 的启动 U 盘。(现在装系统好像没个 8G U 盘还真不方便,低压 U 的本子一般都不配光驱,都往薄了靠,最后都没达到 MBA 的薄) 所以目标的 WIN7 启动盘,至于用什么工具就不重要了。 再有盘做好了,还得把下载到的 WIN7 的 GHOST 版 ISO 拷到 U 盘里,原带的不太好用,而且可能是 WIN8 的,装了 WIN8 就等着无休止的 WIN10 免费升级轰炸吧。
Web 2.0 大环境下 JavaScript 的 MVC 赶潮风儿 太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循“署名-非商业用途-保持一致”创作公用协议 转载请保留此句:太阳火神的美丽人生 - 本博客专注于 敏捷开发及移动和物联设备研究:iOS、Android、Html5、Arduino、pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作。 首先,我们来一起把 Web 应用开发的前后端职责做一个相对平衡的划分,能者多劳是另一回事,暂且不论。 其次,很想知道参考资料是做什么用的?是需要背下来,然后开发时完全用脑袋编码,还是用时找到并能正确使用!答案其实已然都知道,不过,嘘。。。 最后,如何灵活运用 MVC 思想在 Html、CSS、JavaScript 间构建软链接,解耦前端页面展现与后端 JavaScript 业务逻辑。 Web 2.0 与 Web 1.0 的根本区别在哪里? 服务端模板引擎与Web端 JS 的 DOM 操作又有什么联系? 模块化为什么会很容易地进行单元测试与功能复用? 服务端的三层架构如何在 Web 浏览器中与 MVC 共舞? 好了,先留下这些个待详述 问题点,等有时间的时侯再逐一补充, 最关键的是,每一部分的实现,抽象提炼,最终得到的东西,想开源出来, 那么每一个做同类项目的开发人员或团队,就不用再纠结了, 遵循约定重于配置的原则, 只要在特定的位置做特定的事情, 即简便,又利于团队内成员共同理解,甚至更多人轻松了解。
微信公众号中 JavaScript 获取用户周边的标志性建筑列表 太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循“署名-非商业用途-保持一致”创作公用协议 转载请保留此句:太阳火神的美丽人生 - 本博客专注于 敏捷开发及移动和物联设备研究:iOS、Android、Html5、Arduino、pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作。 1、使用微信 JS-SDK wx.getLocation 方法获取用户当前坐标,此处测试直接给定,可以使用百度坐标拾取器在地图上选点 2、新建 test.html 文件,可将如下内容拷入, <!-- 引入高德地图 JavaScript API 的 Key --> <script type="text/javascript" src="http://webapi.amap.com/maps?v=1.3&key=你 javascript api key"></script> <!-- 搜周边并返回 Json 列表 --> <script type="text/javascript"> // 加载搜索插件 - 这个不需要依赖地图; AMap.service(["AMap.PlaceSearch"], function() { // 插件加载完成,构建搜索对象并指定参数 var placeSearch = new AMap.PlaceSearch({ typ:'大厦', city: "0451", //城市 }); // 使用微信 JS-SDK wx.getLocation 方法获取用户当前坐标,此处测试直接给定,可以使用百度坐标拾取器在地图上选点 var cpoint = [126.645821,45.733826]; // 搜索哈尔滨用户当前位置方圆 200 米范围内的所有‘大厦’类型的周边物,结果以 JSON 返回 placeSearch.searchNearBy('', cpoint, 200, function(status, result) { alert(JSON.stringify(result) ); }); }); </script> 3、使用浏览器打开以上页面文件,会弹出的窗口,其中的 json 数据可以全选复制到 http://www.bejson.com/ 进行格式化,如下: { "info": "OK", "poiList": { "pois": [ { "id": "B0FFGBY2I0", "name": "启点工作室", "type": "生活服务;生活服务场所;生活服务场所", "location": { "J": 45.73307, "D": 126.64724000000001, "lng": 126.64724, "lat": 45.73307 }, "address": "文景街14-20号", "tel": "", "distance": 138 } ], "count": 1, "pageIndex": 1, "pageSize": 10 } }
Jquery 中为后生成或插入的 Html 元素先设定响应事件处理方法 太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循“署名-非商业用途-保持一致”创作公用协议 转载请保留此句:太阳火神的美丽人生 - 本博客专注于 敏捷开发及移动和物联设备研究:iOS、Android、Html5、Arduino、pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作。 在 Jquery 1.9 之前,可以使用这种方法,Jquery 1.9 开始,使用 on 整合了 bind、live、delegate 。 $(".class").live("click", function () { msgId = $(this).attr("id"); alert(msgId); }
<c:forEach>详解 <c:forEach>标签的语法定义如下所示。 <c:forEach var="name" items="expression" varStatus="name" begin="expression" end="expression" step="expression"> body content </c:forEach> <c:forEach>标签具有以下一些属性:l var:迭代参数的名称。在迭代体中可以使用的变量的名称,用来表示每一个迭代变量。类型为String。l items:要进行迭代的集合。对于它所支持的类型将在下面进行讲解。l varStatus:迭代变量的名称,用来表示迭代的状态,可以访问到迭代自身的信息。l begin:如果指定了items,那么迭代就从items[begin]开始进行迭代;如果没有指定items,那么就从begin开始迭代。它的类型为整数。l end:如果指定了items,那么就在items[end]结束迭代;如果没有指定items,那么就在end结束迭代。它的类型也为整数。l step:迭代的步长。 <c:forEach>标签的items属性支持Java平台所提供的所有标准集合类型。此外,您可以使用该操作来迭代数组(包括基本类型数组)中的元素。它所支持的集合类型以及迭代的元素如下所示:l java.util.Collection:调用iterator()来获得的元素。l java.util.Map:通过java.util.Map.Entry所获得的实例。l java.util.Iterator:迭代器元素。l java.util.Enumeration:枚举元素。l Object实例数组:数组元素。l 基本类型值数组:经过包装的数组元素。l 用逗号定界的String:分割后的子字符串。l javax.servlet.jsp.jstl.sql.Result:SQL查询所获得的行。 不论是对整数还是对集合进行迭代,<c:forEach>的varStatus属性所起的作用相同。和var属性一样,varStatus用于创建限定了作用域的变量(改变量只在当前标签体内起作用)。不过,由varStatus属性命名的变量并不存储当前索引值或当前元素,而是赋予javax.servlet.jsp.jstl.core.LoopTagStatus类的实例。该类包含了一系列的特性,它们描述了迭代的当前状态,如下这些属性的含义如下所示:l current:当前这次迭代的(集合中的)项。l index:当前这次迭代从0开始的迭代索引。l count:当前这次迭代从1开始的迭代计数。l first:用来表明当前这轮迭代是否为第一次迭代,该属性为boolean类型。l last:用来表明当前这轮迭代是否为最后一次迭代,该属性为boolean类型。l begin:begin属性的值。l end:end属性的值l step:step属性的值下面就来看一个个基本的例子,表格隔行背景色变化<c:forEach var="item" items="${contents}" varStatus="status"> <tr <c:if test="${status.count%2==0}">bgcolor="#CCCCFE"</c:if> align="left"> xxx </tr></c:forEach> 努力生活,珍惜一切,知足最快乐!young joy..
JavaScript 检查一个 JSON 对象中是否对存指定的 Key 太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循“署名-非商业用途-保持一致”创作公用协议 转载请保留此句:太阳火神的美丽人生 - 本博客专注于 敏捷开发及移动和物联设备研究:iOS、Android、Html5、Arduino、pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作。 Actually, checking for undefined-ness is not an accurate way of testing whether a key exists. What if the key exists but the value is actually undefined? 实际上,测试未定义这种结果确实不是一个比较精确的检测键是否存在的有效方法。 var obj = { key: undefined }; obj["key"] != undefined false, but the key exists! 上面的结果是 false ,但这个键确是存在的。 You should instead use the in operator: 你应该换用 in 操作: "key" in obj true, regardless of the actual value 结果是 true,不考虑实际值 If you want to check if a key doesn't exist, remember to use parenthesis: 如果你想要检查一个键是否不存在,记得使用取反:!("key" in obj) true if "key" doesn't exist in object 如果键存在于这个对象里,那么结果是 true !"key" in objERROR! Equivalent to "false in obj" 这个有错!等于 "false in obj" Or, if you want to particularly test for properties of the object instance (and not inherited properties), use hasOwnProperty: 或者,如jusi你特别想检测一下对象实际的属性(并非继承的属性),那么使用 hasOwnProperty: obj.hasOwnProperty("key") true 这个测试的结果是 true
Jquery 获取和设置元素属性值 太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循“署名-非商业用途-保持一致”创作公用协议 转载请保留此句:太阳火神的美丽人生 - 本博客专注于 敏捷开发及移动和物联设备研究:iOS、Android、Html5、Arduino、pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作。 $(this).attr(key); 获取节点属性名的值,相当于getAttribute(key)方法 $(this).attr(key, value); 设置节点属性的值,相当于setAttribute(key,value)方法 var diywidgetid = $('#w_1').attr('diywidgetid'); 获取节点属性的值 $(this).val(); 获取某个元素节点的value值,相当于$(this).attr(“value”); $(this).val(value); 设置某个元素节点的value值,相当于$(this).attr(“value”,value); $(".class").live("click", function () { msgId = $(this).attr("id"); alert(msgId); }
eclipse到Android Studio的项目迁移 标签: android项目迁移android studioeclipse 2015-08-21 14:18 629人阅读 评论(0) 收藏 举报 分类: Android(31) 工具(14) 版权声明:本文为博主原创文章,未经博主允许不得转载。 目录(?)[+] 一直以来,公司开发都是用eclipse,但是随着我们应用不断成长,项目结构越来越庞大,项目间依赖关系变得很复杂,用eclipse管理显得非常吃力,经常一个同事更改依赖项目之后,别人在更新,都会出现问题,因为这些事情浪费很多时间。最终决定迁移到Android Studio。但是迁移的过程中还是遇到了很多问题,通过这篇博客,把迁移过程中遇到的问题,以及每个问题的解决方案,记录一下,也希望能帮助到有同样需求的同学。 这里就不详细介绍从eclipse导入到Android Studio的过程了。 1.乱码 用eclipse开发时编码用的是GBK,而android studio中使用的是UTF-8,所以在build的时候,项目中的温度符号,以及一些中文注释就会出现乱码。 解决方案 统一编码,我们把编码都改成UTF-8. Android studio通过如下方式改项目和文件的编码: [html] view plain copy print? File -> Other Settings -> Default Settings -> 搜索File Encodings ->改三个地方的编码(IDE Encoding,Project Encoding,Default encoding for properties files) 2. 重复图片 我们有一个Library是从主项目中抽取出来的,抽取的过程中很多图片从主项目中copy出来之后,忘记删除了,导入到Android studio之后,build的过程就会出错,不过错误信息中有提示重复的文件,我们只需要根据提示把不用的图片删掉。 3. Unable to execute dex: method ID not in [0, 0xffff]: 65536 对于这个问题,google官方给出了解释,http://developer.android.com/tools/building/multidex.html,大概意思就是:当你的应用以及应用依赖的library达到一定的Size之后,在build的时候就会提示这个错误,说明你的应用已经达到了Android app构建架构的限制大小。android虚拟机限制了每一个DEX文件能够引用的method的个数为65536, 它包括Android Framework,Library,以及自己项目中的methods总和。 解决方案 给我们的应用生成多个DEX文件,需要在build.gradle,文件中配置: [html] view plain copy print? android { defaultConfig { ... multiDexEnabled = true } } 另外,还要使用MultiDexApplication类,如果你的应用中已经有Application类,需要让它继承自MultiDexApplication,再实现attachBaseContext方法,代码如下: [java] view plain copy print? protected void attachBaseContext(Context base) { super.attachBaseContext(base); MultiDex.install(this); } 4. OutOfMemoryError: GC overhead limit exceeded 在build.gradle文件中做如下配置: [html] view plain copy print? android { ... dexOptions { incremental true javaMaxHeapSize "4g" } } 5. java.util.zip.ZipException: duplicate entry during packageAllDebugClassesForMultiDex 报错信息如下: [html] view plain copy print? Error:Execution failed for task ':ezweather_original:packageAllDebugClassesForMultiDex'. > java.util.zip.ZipException: duplicate entry: android/support/annotation/ColorRes.class 解决方案,在build.gradle中添加如下配置: [html] view plain copy print? configurations { all*.exclude group: 'com.android.support', module: 'support-v4' } 这时候app终于能运行起来了...
癌之所以为绝症,是因为药方找错了方向 太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文禁止转载 本文禁止转载 有了佛性,你就会放下特想要的,比如烧烤,那么你就不会被那些脏东西染了肠胃,也就不会得肠胃病了。 这可以说成,佛性可以治肠胃病吗? 但有研究表明,肠胃病容易反复,是因为人的情绪,那么这个就比较容易直接解释佛性可以治肠胃病了, 因为,佛性就是管控人的情绪的良药。 拒小范围调查,已了解有近十例癌症病人,在发现癌之前的两到三年,有过极端的情绪, 更以姚贝娜的生前自述,以说明这个问题, 以下摘自这里,建议详读,不管是为了自已还是你的亲人 那么佛能让人放下、放开、放松, 人们的心态左右其所看到的世界的颜色, 心态好,周围世界的颜色是鲜亮的, 心态差,周围世界的颜色是灰色的, 大多数人都是两种状态交错,且以前者占绝大多数时间, 而后者居多的不存在,因为这样状态的人早已经被淘汰掉了, 而大自然新发明了一种不需要经过多代就可以进行优胜劣汰的工具:癌! 心态不好,如果一直在占据着你,对不起,你危险了。 调整心态,学会释怀, 而如今的九零后,自打出生就有这种免疫力, 不信你看看针对七零后、八零后、九零后的那个调查的微信文章, 说得很清楚,九零后什么都无所谓的, 但不知这种先天的免疫力是从何而来的, 但至少是六零后、七零后、八零后需要接种的疫苗。 是的, 癌,就是一种瘟疫,它不以病原体进行传播, 它更高级,通过社会风气、人文、情绪进行传播, 情绪的变化虽然不能致病, 但情绪变化的结果可是致病的要因。 所以,想要不被传染上癌, 那么你吃的药不是片剂、冲剂或丸剂, 而是散剂,佛性是散状的,是的,你不妨也感受一下,是散状的。 癌,并不会固定要长在你哪里, 而是你那里最弱,就长在哪里, 女人得乳腺癌,不是乳的错,而是这里最弱! 不吸烟的男人得肺癌, 不是烟的错, 是肾主水,男人易肾劳过度,包括房事、熬夜、过度操劳, 而肺只有在水份充足时,才会健康, 十男九亏,当然缺水,肺即处于缺水润的状态, 自然肺对于男人来说是最弱的! 至于,心与肺、肝与肾、人体各脏器从中医角度能很好的解释其关联性, 中医不光治本,其研究的也是内部机理,而不象西只研究运行方式这种表象的东西。 其实,并非仅有佛性能治癌,只不过佛性如抗生素一样,更直接而已,这是“西医”, 治标不治本,回头还会犯的,但至少眼下急用救命不可缺。 真正要治本,那么得请“中医”来,找出根源,从源头施治, 那么,中华传统文化,儒、道、易、法任何一家都能根治这病, 不知道有没有听者明白了, 其实就是我们需要章法、信念。 不过,确实也不用太着急了,现时正提倡国粹,学习传统,我们见到希望了。 我们国家传统文学中,其实并不是简单的认几个字、云几句诗那么简单, 其中蕴含的宝藏是几千年自然形成的,包罗万象, 至少有一剂根治癌的药方,而其方源于无形, 也许真正到了那一天,也没人看到药方, 但确没人再得癌了。 癌,这种病, 真是千古奇病, 都说无药可医,是绝症, 然而真正原因确是,治这种病的药,不是普通的药, 以普通药的角度来治就误入歧途了, 我们假设一下,如果把古人整体搬到现在来, 他们会得癌吗? 很明显不会,因为他们不会跟自已过不去, 而我们时下的人们更易得癌,是因为跟自已过不去, 而且章法零乱,迷失, 相比之下,那被人鄙视的“三从四德”都好过迷失。 然而,我们本该不迷失的,最简单,拿一本《三字经》都是至宝,关键在于得信。 当然了, 要想有佛性, 当然得学佛了, 学佛要靠悟, 因为佛代表着智慧, 她不是知识、不是能力,超过知识、超过能力, 智慧,你懂的, 不懂? 百度一下好了。 以下摘录百度百科《心经》 般若,即智慧的意思。 佛学经典只是说了这一件事情,佛即是大智慧,大智慧就是佛的终极所述。 所以,佛的核心就是要让人们有智慧,并以智慧的方式来解决问题。 比如,遇事纠结就是不智慧,佛会说放下它,如果你暂时解决不了的话,待到能解决时再去考虑。 其实,这种思想不仅在佛学中有,在卡耐基先生的著作中也有论述。 佛不仅仅这一点点,博大精神着呢, 随着社会实践,人们也在不断发展和进化佛学, 当然,不排除时下会有以佛名为恶之事,其实已与佛无任何关系,只是挂着佛的外衣而已。 佛在历史的不同阶段,都会以不同的方式展现在人们面前, 所以,真正的佛学才是我们所要追求的,也是人们在社会实践中不断发展和进化的。
VS 2012 标配 Asp.net MVC 和 Entity Framework - mybatisnet 可能就显得路子野 了 太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循“署名-非商业用途-保持一致”创作公用协议 转载请保留此句:太阳火神的美丽人生 - 本博客专注于 敏捷开发及移动和物联设备研究:iOS、Android、Html5、Arduino、pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作。 以下是 MyBatis.net 的代码托管地址,现在已经归档了,估计是真没人用它。 http://code.google.com/p/mybatisnet/source/checkout 那么微软的最新 VS 2015 也真是没谁了, 集成移动开发包括 iOS 和 Anroid,而且社区版是免费的, 真看出微软使出吃奶劲儿翻身儿了。 其实,微软这些年一直要按苹果的路子走, 可惜这正中了诺基亚的老坑了, 不放弃一些东西,就是拖累,无法真正轻装前行。 目前从 VS2015 看, 微软开发工具轻装上阵,分出多个版本,该免费的免费, 还有他那 Web 窗体也逐渐被流行的 MVC 所替代(Asp.Net Mvc 组件,早已内置 VS 开发环境,而且 Nuget 随时可以更新最新的组件), Entity Framework 作为 ORMapping 组件完美与 Asp.Net Mvc 组合应用, 当然了,微软也有 Spring 作为 MVC 和 ORMapping 这几层的粘合剂: http://springframework.net/ 可是最新的更新是 2012-12-11,好像很久没有更新了。 相对来讲,autofac 还算不错。 技术世界总是翻云覆雨,更迭相对比较频繁,不过服务端技术还算可以接受,有两三年的过渡期。 相比来讲,移动前端的更迭确不那么美观,至少一年就要学习很多新技术、新知识,当然在这个领域的急速上升期,这是必然的, 而且,能在这一领域跟得上的,也都得到了相应的回报。 对厂家来说,不断的升级,才能带来新的购买,这是利益驱动点。 同时,能参与到这一过程的,必然付出也较多,当然也就回报也多, 如果有一天,您觉得跟不上了, 那么,转一下吧,做做服务端或 HTML5 Web 前端, 可是,你要意识到,移动前端世界的更迭之快,可能某一天你会发现,你又是新手了。 直到某一天,移动前端也发展到了一个平和期,不再有更多创新的时侯, 更深层次的领域应用就开始爆发了, 而如今的 互联网+ 只是在探路期,每一个行业的深层次需求是巨量的, 尤其还没有与物联结合,当然物联一直处于概念期,也正是等待着前期的积累, 积累未足,过早涉足物联网,就会产生涝得涝死,旱得旱死的状况, 总有某些方面不称手,不匹配,不成产业链的稀缺感产生。 传小米正在研发自主手机芯片 http://tech.qq.com/a/20160101/020994.htm http://mobile.it168.com/a2016/0128/1883/000001883026.shtml 个人揣测,小米不仅是要给自已的手机自已的芯, 小米看到的可能正是物联网世界的触角:传感器芯片。
Spring 官方文档彩蛋 太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循“署名-非商业用途-保持一致”创作公用协议 转载请保留此句:太阳火神的美丽人生 - 本博客专注于 敏捷开发及移动和物联设备研究:iOS、Android、Html5、Arduino、pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作。 近期查看 Spring 官方文档,我都是这样做的, 第一步,先打开 spring 官网文档链接页面 http://spring.io/docs,这个是我存起来的,在站点顶部也很容易找到 第二步,点参考文档条目 第三步,在众多的 spring 项目中,找到 并点开右面的三横,出现如下菜单项 其中,我们点 GA 项后面的参考文档 这时,你会看到浏览器地址栏的地址如下: 于是乎,我就打开了官方的文档,而且仅此而已。 之前,我想把它保存到我的手机里,用我的 5.5 寸大巴掌随时学习一下,然而各种保存,各种 pdf 均苦逼到家,终于,找到一个谷歌浏览器上的工具,能将整页保存到一个单独文件中,这用来保存其它分页的超爽,可是这个大页面啊,真是没谁了。。。 唠叨到这里,你知道我想要的了吧,那你知道怎么解决吗? 聪明人儿,先别知声,我来点破吧 还得看这个地址: http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/ 最后一个单词 htmlsingle 代表啥意思? 那么为什么要建立它呢? 这时,我们用马列主义的XXX理论,可以分析到,如果没有其它的,那么就直接存这个了,正因为有其它形式的文档,才建了这个目录来区别保存, 得嘞,走起 http://docs.spring.io/spring/docs/current/spring-framework-reference 再往上,或再往下,您自个看着办吧,反正俺是下到了,那叫一个美,美,,糖心地大罗贝。。。(赵丽荣老师的小品出来了,别害怕,多么可敬的人儿啊!) 值此新春佳节到来之际,祝所有朋友们新春快乐,全家幸福,身体安康,天天开心快乐没愁事儿!
看了这个,你觉得 Win8 和 Win10 又有什么差别吗? 太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循“署名-非商业用途-保持一致”创作公用协议 转载请保留此句:太阳火神的美丽人生 - 本博客专注于 敏捷开发及移动和物联设备研究:iOS、Android、Html5、Arduino、pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作。 看了这个,你觉得 Win8 和 Win10 又有什么差别吗? 以下摘自:微软已准备好停止对 Windows 8 的技术支持,你会被逼升级吗? 我宁可用 Win7,只不过还没琢磨如何把 E550 降到 Win7,或者 Xp更爽,只不过 64 位对于我这 12G 内存还是首选的,当然是 Win7 了。 (这图片是咋儿也上传不上来了。。。戳上面链接吧) 什么理由让你如此的顾头不顾腚? Win8 用户还会为了那多出来的两年支持而决定升到 Win10 以消耗自已的硬件性能吗? 当然了,希望这个转载地址中所说的是真实可信。
微软终于上道儿了:操作系统不买帐,拿更新的扩展功能最低版本来限制 太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循“署名-非商业用途-保持一致”创作公用协议 转载请保留此句:太阳火神的美丽人生 - 本博客专注于 敏捷开发及移动和物联设备研究:iOS、Android、Html5、Arduino、pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作。 以下是独立于 VS2015 运行的 Android 模拟器,使在 PC 上运行安卓应用成功很自然的事情,贡献! 然而,下面的提示略有不爽,可是一想,大家都不买人家操作系统 win10 的帐,人家也得想点办法不是。 细看提示,发现它说的是:该计算机不支持 windows phone 模拟器,它需要 64 位版本的 win 8.1pro 和一台支持 Hyper-V 的电脑。 两个信息,一是 win 8.1,但不知 pro 是否重要;二是 Hyper-V (微软虚拟机教程)_, 实际查看,我的系统中没有这玩意儿,算了,回头再看看如保处理。 微软 Visual Studio 2015 官方中文正式版完整ISO镜像下载 - 免费社区版 以下摘自:微软安卓 Android 模拟器独立安装版下载 - Visual Studio Emulator for Android 当然,现在成熟的 Android 模拟器已经有很多,我们曾推荐过的 Genymotion、Andy、ARC Welder 、BlueStacks 等都是不错的,而微软的 VS Emulator for Android 只是一个新选择,希望微软在日后可以加强并完善,将其变成更具实用性的 Android 模拟器吧。
Visual Studio 外请版本管理插件 - AnkhSVN 太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循“署名-非商业用途-保持一致”创作公用协议 转载请保留此句:太阳火神的美丽人生 - 本博客专注于 敏捷开发及移动和物联设备研究:iOS、Android、Html5、Arduino、pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作。 Visual Studio 有自已的版本管理系统,叫做 TFS(Team Foundation Server), 而我们常用 SVN,当然时下流行 GIT,多种原因,还是先 SVN 吧, 这叫做入乡随俗吧。 但是,使用 Visual Studio 和 Tortoise SVN 进行代码版本管理,这是我等xxx绝不能容忍的,太累了呀 浏览器搜到如下可用插件,其中 AnkhSVN 在 0.x 版本时就用过,当时还算稳定,只不过有些功能不完善,自已下了原码,参照着改了些功能,爽哉乎,不爽矣! 多不闲聊,工作时间,贴图和链接,还有网友评价,我还是选 AnkhSVN https://www.visualsvn.com/visualsvn/download/ https://ankhsvn.open.collab.net/ StackOverflow 的问答结果很具权威性,08、09 刚做苹果和安卓开发的兄弟们,遇见它,都如见了救星一样,和国外大牛们一起奋战的感觉就是好。 http://stackoverflow.com/questions/1252672/ankhsvn-vs-visualsvn AnkhSVN 安装完成后,打开 Visual Studio 2012,在选项管理窗口中找到如下: 更改源代码管理插件为 AnkhSVN : 打开 SVN 服务器工程如下: 新鲜出炉的提示还热乎着呢: 行了,下面就是隐私了,记得 https:// 的 SVN 地址(我想你也是用的 Visual SVN Server 吧)确认证书。
从实体和关系角度看 PowerDesigner 设计数据库模型 太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循“署名-非商业用途-保持一致”创作公用协议 转载请保留此句:太阳火神的美丽人生 - 本博客专注于 敏捷开发及移动和物联设备研究:iOS、Android、Html5、Arduino、pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作。 还是从主体和关系出发,我们进行数据库建模。 和面向对象的 UML 建模有类似之处,只不过数据库建模是表间关联及最小范畴的界定。 当然五个泛式在数据库设计过程中的指导作用是不可或缺的,然而,如果你真的完全遵照五个范式设计出了数据库模型,那么你的设计本身就失败了! 因为那个结果一定是不太好用的,趋于极致的往往就是不合常理的。 适当的冗余,对于时间与空间的权衡,才是真正的数据库设计所遵循的至上法则,这其中无不时刻涉及实际业务环境因素,因地置宜,因时不同,因人而异! 马列矛盾理论告诉我们:数据库设计就是‘主体’和‘关系’的设计 这一篇中传达了主体和关系的概念。 在我所涉及到的数据库设计工具中,最喜欢 MySql WorkBench,其次是 ERWin,最后才是 PowerDesigner。 而 ERWin 当年就很难下到,现在下到了,居然没有 SQLSERVER2000的 DBLib 无法连接 SQLSERVER2008,这真有点扯了,那也是没办法的事情,有空儿再试试。 目前只能求其次,用 PowerDesigner 了。 相对 MySQL WorkBench 来讲,表的自增长 id,还是 MySQL WorkBench 做得好些,每个表都是 id,插入其它表做外键时,自动将表名加在 id 前。 即然开始用 PowerDesigner 了,那么就看看它的好吧,一步一步来做设计。 首先,我们得了解 PowerDesigner 中有概念模型、逻辑模型和物理模型,可以在概念模型中设计,并逐步生成后续两者,再由物理模型生成建库脚本或直接创建库。 反过来,从现有数据库反向生成物理模型,也是可以的。 上面 PowerDesigner 中表的设计,Name 和 Code 暂时都使用英文,因为我还不知道 Name 用中文,最后生成的数据库如何用 Code 中的英文。 这里的 Convert name into codes 选项勾后,也不起作用,不知是何原因。有了解的朋友,敬请指教。 从上面看出,表名 student ,自增长 id 也得加上表名 student_id,主要是这个名字全局唯一,所以都用 id 就会冲突。 下面 school 和 student 两表的关系是依赖关系,即学生因上了某所学校而称之为学生。 但实际用时,为了降低约束,不让 school_id 在 student 表中作为主键,那么 Dependent 不勾选,Mandatory 表示强制,即一个学生必须有一个学校,而学校至少有一个学生,下图中默认允许学校没学生了,新开张的吧 上面这种关系线,是一对多的关系,线即代表外键,在逻辑图和物理图中会生成这个外键 。 另一种是,一条关系线代表一种多对多的关系,会转换成一张表。 多出一张 relationship_2 的表,其中关联的两张表的主键形成了复合主键。 最终,核心在于考虑好表间的关系,这才是主要的,至于这个工具如何用,那都是其次了。
全球癌症热点图:患癌负担转至发展中国家托尔在一次采访中说道,至于低收入国家的癌症患病率为什么越来越高, “或许是与采用西方的生活方式有关。” 他说道,“人们的运动越来越少。体力劳动减少,交通工具的使用增多,人们还可以吃到更美味但并不是那么健康的食物。”
转自 学习ASP.NET MVC系列: 学习ASP.NET MVC(一)——我的第一个ASP.NET MVC应用程序 学习ASP.NET MVC(二)——我的第一个ASP.NET MVC 控制器 学习ASP.NET MVC(三)——我的第一个ASP.NET MVC 视图 学习ASP.NET MVC(四)——我的第一个ASP.NET MVC 实体对象 学习ASP.NET MVC(五)——我的第一个ASP.NET MVC CURD页面 学习ASP.NET MVC(六)——我的第一个ASP.NET MVC 编辑页面 学习ASP.NET MVC(七)——我的第一个ASP.NET MVC 查询页面 学习ASP.NET MVC(八)——“Code First Migrations ”工具 学习ASP.NET MVC(九)——“Code First Migrations ”工具使用示例
医可救众生?佛可救众生! 太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循“署名-非商业用途-保持一致”创作公用协议 转载请保留此句:太阳火神的美丽人生 - 本博客专注于 敏捷开发及移动和物联设备研究:iOS、Android、Html5、Arduino、pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作。 检查已过,良性为终; 然,苟得余生,更使奋力,释我所能,暖吾所栖。 唤,迷途知醒,勿贪勿急,行自然循序之力,方能安然, 足矣。 一段外国人说的中国方言古语,看得懂吗?将就看吧,也非常感谢你能来看,谢谢。 请不要评论,谢谢。 ---------------------------- 占位,希望在有生之年,能至少把这篇写完。 癌,是很可怕的! 然而,又不可怕,因为只要你找到了它的克星。 也许我找到了,但不知时间是否还够用。 那就是心态。 而保持良好心态,儒学、道学。。。都绕了很大的圈子,唯有佛学很直接,空与色,想与不想,虽然改变不了实际存在,而确能改变它在你心中的心象,而直正对你有影响的并不是真实存在之物,而是形成的这份心象。 还有关于我们这一代人看九零后的“无所谓”心态,我想为这一代人更正一下,他们是对的,只有他们这种心态才能适应这个时代。 而我们这些生在红旗下的一代,过份的责任心,不仅解决不了问题,只会产生无名的纠结,而自已都还不知道。 放下,放下纠结,尽力就好了。 再有,我一直抱着学习佛学的态度,一直认为很多信佛是迷信。 今天,再次更正,其实信佛就像背诵古诗词一样,只有浸润到你的每一个细节,才能改变你的心态,这可能就是量变和质变的关系,也可以用“书读百遍其意自现”来解释吧。 三年前,我学了乒乓球,锻炼濒临垮塌的身体,同时我开始学习佛法,仅心经和金刚经,那时觉得佛法很好,但我错过了一个机会,就是让它浸润我的机会,只有浸润,才能达到改变。 近三年,与很多信佛之人交流,他们对我理解的佛表示赞同,然而理解只讲给别人听,确没有浸润自已,何以渡自已,连自已都渡不了,更何以渡别人! 有些事情其实就是去做,形成下意识,打乒乓球也一样,我用三年时间研究各种动作和姿势,但是比赛始终接不到球,近一个月,我放下了用脑袋打球,就是练,练成下意识,再比赛时,拍子一挥,球就过去了,叫好声不断。是的,人与动物的区别,就是人有思维,而思维就如软件一样,能力大,但速度相比更件要慢得多,还是还给硬件去做吧,练成下意识,下意识就是硬件,有些紧急情况下,你能做出来的事情,让你自已都惊奇,因为那不受你的思维控制,是下意识控制完成的。 人们一直在挖掘大脑,一直在锻炼思维,其实下意识是最该锻炼的,而下意识又是潜意识的一部分,是本能动作,所谓本能就是练多了形成习惯了。而潜意识所能做的事情,更复杂,包括思维的方式,即想问题的方式,比如遇见一件事,你是本能地从阳光的一面去解读,还是从阴暗面儿去解读,这其实不是思维的事情,思维只负责去想,而以什么方向去想,是潜意识的事情,这个与下意识还不同,这可以叫做思维下意识,更形象地说,思维下意识是一块电版,你插上什么电版,供电后,你的思维就按什么方式去想问题。 然而,心脏是否参与人的智慧部分,这个似乎有定论了,但多少会有影响,尤其血液,这个还没考虑过。 说到这里,就是想说,人的思维是一把双刃剑,用不好,让自已气血郁结,什么病都来了。需要的时侯再用,再用,用完就放起来,这就是思维的作用。 人们更多的是靠潜意识在支配着做事情,而在什么时侯该掉换方向了,这个是思维的事情,两者互相影响和改变着。 学佛,一定要“迷信着”学,即泛在化地影响自已才好。然而不能让这件事情成为影响你正常生活、学习、工作的负面因素。 现在这个时代,我敢这样去说,是因为不怕会教唆别人,你想想,大家都拿着手机,被手机迷的乱了套了,还不如被佛迷呢,至少佛在教人向善,佛在教人轻松。 佛祖说,人要渡河,找到一条船渡到彼岸,就不要再为船该如何处理去担忧,放下它,继续走自已的路。 佛祖说,学佛都要这样,更何况其它事情,佛祖是告诫人们信佛要有时有晌,放在心里就好了,不能没完没了,像玩手机一样,“废寝忘食”,那就 不好了。 佛祖说,放下屠刀立地成佛,无论什么时侯,只要不再纠结,把它放下,马上就成佛了。 佛祖说,世间有无数佛,只要能做到放下二字,而佛祖是因为第一个做到了,才称为佛祖。 佛祖说,刚出生的婴儿都是佛,而“如来”法名即是已达到刚出生婴儿的境界。 佛法无边,我佛慈悲,善哉善哉!
给俺的 CSDN 博客加背景音乐 - 高大尚的《心经》背景音乐 太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循“署名-非商业用途-保持一致”创作公用协议 转载请保留此句:太阳火神的美丽人生 - 本博客专注于 敏捷开发及移动和物联设备研究:iOS、Android、Html5、Arduino、pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作。 尚不知其它博客能否,反正高大尚的 CSDN 能做到,如下图: 源码如下: <audio src="http://你的.mp3" controls="controls" autoplay="autoplay" loop="loop"> Your browser does not support the HTML5 audio tag. </audio> 具体该组件的每个属性的用法与含义参照这里,截图如下: 以下转载自这里,需要的朋友,可以自行更换赏悦。 《金刚经》读诵唱诵MP3专辑全系列 [MP3音频格式] 下载地址(下载方法:鼠标移到集数的下载地址上面,然后点右键选“使用迅雷下载”或者选“目标另存为”):===========================================《金刚经》仁炟法师率众念诵===========================================《金刚经》佛光山唱诵===========================================《金刚经》僧众木鱼唱诵===========================================《金刚经》慧律法师唱诵===========================================《金刚经》寺院版唱诵===========================================《金刚经》文殊讲堂唱诵===========================================《金刚经》慧平法师读诵===========================================《金刚经》比丘尼木鱼版===========================================《金刚经》伴奏唱诵上 《金刚经》伴奏唱诵下===========================================《金刚经》衍轮法师粤语读诵===========================================《金刚经》释达忍法师唱诵===========================================《金刚经》风柯月渚读诵===========================================《金刚经》黄慧音音乐伴奏演唱=========================================== 相关下载:济群法师《金刚经》 《金刚经》佛光山唱诵梦参老和尚《金刚经》2007 《金刚经》慧律法师唱诵《金刚经》聆志居士读诵 海涛法师《企业金刚经》大寂法师-金刚经 成观法师《金刚经释义》星云大师《金刚经大义》 心定法师《实用的金刚经》妙华法师《金刚心是怎么样练成的》 达忍法师《金刚经》唱诵济群法师《金刚经》要义(音频) 妙华法师《悟入金刚经》界诠法师《金刚经大意》 静波法师《金刚经》8000部佛教视频软件下载
iOS 中 Storyboard 与 Xib 间控制器跳转 - 简化整理完整版 太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循“署名-非商业用途-保持一致”创作公用协议 转载请保留此句:太阳火神的美丽人生 - 本博客专注于 敏捷开发及移动和物联设备研究:iOS、Android、Html5、Arduino、pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作。 按以下四种情况分别说明,应该不缺啥了吧! Xib.controller <=> Xib.controller Storyboard.controller <=> Storyboard.controller Storyboard.controller <=> Xib.controller Xib.controller <=> Storyboard.controller ========== 补充: Storyboard.controller <=> Storyboard.controller 还有一种特例,就是同一个 Storyboard 中的 controller 间跳转。 ========= 再补充: 复用单元的构建,xib 中或 Storyboard 中。 =============================================================== 终极简化两步: 一是获取到视图控制器实例,只有两种情况,xib 和 storyboard,代码直接构建不算手画界面部分不在讨论范畴: A、从 xib :AaaViewController *aaaViewController = [[AaaViewController alloc] initWithNibName:@"AaaViewController" bundle:nil]; B、从 storyboard :假设 BbbViewController 视图控制器在 www.storyboard 中,并且其 storyboardid 为 BbbViewController UIStoryboard *wwwSB = [UIStoryboard storyboardWithName:@"www" bundle:nil]; BbbViewController *bbbViewController = [wwwSB INSTANTIATEvIEWcONTROLLERwITHiDENTIFIER:@"BbbViewController"]; a、简化一:如果当前控制器和要压入的控制器在同一个 storyboard 的话,可以省略构建 UIStoryboard ,直接使用 self.storyboard 替代 wwwSB, 一个 storyboard 控制器被加载,其 storyboard 被默认保存为属性便于使用。 b、简化二:当 storyboard 中的视图控制器没有设置 storyboardid 时,而在该 storyboard 中的控制器间使用 segue 进行了关联,那么可以执行 segue 完成跳转, 当然 segue 也需要设置 identifier : [self performSegueWithIdentifier:@"SegueToPersonCenter" sender:sender]; 框架内部直接实例化了这个 identifier 对应的 segue 跳转到的视图控制器,那么就会有问题出现了,以往可以自已实例化,然后设置目标控制器的属性,现在怎么办? 看下面这个回调方法,在跳转前会被执行,给一个设置的机会: - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { } 这里的 sender 这里的 sender 就是上面的传入参数 sender,也可以在控制器的别名分类中设置私有属性暂存参数,然后在此回调中使用。 c、简化三:如果是刚开始为了快速构建控制器间的流转,也即草图阶段,不关心细节,那么可能连 segue 都不用,直接在按钮上往目标控制器上一拉选,点按钮就跳转了。 二是将此视图控制器压入导航控制器堆栈或模态呈现: 这个确实没什么可说的,与常规一样,也列一下吧: [self.navigationController pushViewController:aaaViewController animated:YES]; [self presentViewController:bbbViewController animated:YES completion:^{}]; 描述完成,基本没有遗漏了吧,关键的东西都在第一步中,视图控制器的实例化与设置上, 相对来讲,越简便的方法越受束缚,越麻烦的方法,给你的自由度越高,苹果在这方面面面俱到了。 可以所有的控制器在一个 storyboard 中,使用 segue 跳转
有人在Google Webmaster Help里问在URL里使用竖线(也就是“|”,比如类似www.guao.hk/backup|you|forever这样的URL地址)是不是挺傻的,因为他发现在Google Keyword Tool里添加带有竖线的URL地址,Google会给他一个无效网站的错误提示。在Google里搜索inurl:|也是找不到任何东西的。 Google的JohnMu回复了他: 我通常会抓取并索引所有可访问和有效的URL,也许你会在我们的索引里找到很多带竖线的URL,但这并不是一个好主意,就像在URL里使用空格一样,这些字符会带来各种问题。所以就我个人来讲,为降低风险最好不要这么干。 我隐约记得早年间经常有人用竖线作为URL里字符之间的分割符,现在应该基本绝迹了。 Via Search Engine Roundtable 本站文章除注明转载外,均为本站原创编译转载请注明:文章转载自:谷奥——探寻谷歌的奥秘 [http://archives.guao.hk]本文标题:Google 不推荐在 URL 里使用竖线本文地址:http://archives.guao.hk/posts/dont-use-pipes-in-your-urls-says-google.html 本文短网址:http://guao.cc/Jtm 作者: musiXboy 分类: Google技巧 标签: Google Keyword Tool, Google Search
[ISUX译]iOS 9人机界面指南(三):iOS 技术 UI规范 summer 2015-11-29 3247浏览 0评论 专为0基础小白量身打造的UI设计入门课程(ps,ai软件+图标技巧),在线学习2个月包教会(公开课3位师傅),拜师费1500,随到随学,可插班。抢名额请加qq群:429369013咨询。 本文译自苹果官方人机界面指南 iOS Human Interface Guidelines ,由腾讯ISUX设计师翻译整理,非发文者一人之作。 文章索引 3.1 3D触摸(3D Touch) 3.1.1 轻压和重压(Peek and Pop) 3.1.2 主屏幕快捷操作(Home Screen Quick Actions) 3.2 Live Photos 3.3 钱包(Wallet) 3.4 苹果的移动支付平台(Apple Pay) 3.5 研究型应用程序(Research Apps) 3.6 应用扩展(App Extensions) 3.6.1 今天部件(Today Widgets) 3.6.2 分享和动作扩展(Share and Action Extensions) 3.6.3 图片编辑扩展(Photo Editing Extensions) 3.6.4 文档提供者扩展(Document Provider Extensions) 3.6.5 自定义输入法(Custom Keyboards) 3.7 HomeKit 3.8 多任务处理(Multitasking) 3.9 通知(Notifications) 3.10 社交媒体(Social Media) 3.11 iCloud 译者注:本文译自苹果官方人机界面指南 iOS Human Interface Guidelines (2015年10 月21日),由腾讯ISUX设计师翻译整理,非发文者一人之作。译文首发于ISUX博客,如在阅读过程中发现错误与疏漏之处,欢迎不吝指出。后续章节会陆续更新,敬请期待。 3.1 3D触摸(3D Touch) 3D Touch 给 iOS 9 用户提供了一个新的交互维度。在所支持3DTouch的设备上,人们可以通过按压应用的图标去快速选择应用定制的操作。在应用内,人们可以使用多种按压操作去获取一个项目的预览,可以在独立的视图里打开一个项获取相关操作。(了解更多在你的代码中如何添加3D Touch支持,参阅 Adopting 3D Touch on iPhone .) 3.1.1 轻压和重压(Peek and Pop) 轻压让用户可以在不离开他们当前环境的情况下预览一个项和执行相关操作。支持轻压的该项会在轻压后给出一个小矩形视图作为反馈。 在Safari中的一个轻压视图 在Safari轻压中的快速操作 轻压(Peek): 当用户按压在一个支持轻压的项上时出现轻压,用户手指抬起后会消失 当用户在轻压视图下再更加重一点的按压称之为重压,重压可以查看该项的详细视图 当用户在轻压视图中向上滑动,可以提供与该项相关的快速操作 当用户轻轻按压在屏幕,支持轻压的这个项会展示一个你提供的矩形视图,示意可以进行下一步交互操作。那个视图应该够大,这样才能让用户手指不会混淆内容,这个视图应该足够细节,这样可以让用户选择是否去更加重一点按压从而转换到轻压视图。 重要 你在应用中始终如一提供轻压和重压的体验是至关重要的。如果你在有些地方支持轻压和重压,在某些地方不支持,用户有可能认为你的应用或者他们的设备出现了问题。 使用轻压去为该项提供一个生动的,内容丰富的预览。当轻压能够给用户提供关于该项的足够信息,从而帮助他们扩展当前的任务,这样做是最好的。例如,在用户决定好是在Safari中打开信息中的网页还是分享这个链接给朋友之前,用户可以使用轻压预览信息中URL的页面。在表单视图中,轻压可以给用户提供一个行项的详细内容。 为每个轻压提供重压。虽然一个轻压可以提供给用户所需要的大部分信息,但是你应该可以让用户过渡到重压,从而让用户放开当前正在进行的任务,转移专注力到该项上来。重压的内容应该与用户点击该项后的内容一致。 不要为同样一个项授予轻压和编辑菜单(Edit menu)两个功能。当同一个项的这两个功能都启用的时会很混乱。(获取更多编辑菜单信息,参看 Edit Menu.) 在轻压操作里,避免展现类似按钮的界面元素。如果用户抬起手指去点击像按钮的元素,轻压会消失。 如果可能,提供轻压快捷操作。 在轻压里,用户可以向上滑动去显示该项的相关操作。例如,Mail里的轻压快捷操作包括回复全部,转发和删除信息。并不是每个轻压都需要快捷操作,但是如果你已经为该项提供定制的点击并长按的操作,那么最好在轻压里提供相同的操作从而替代点击并长按操作。(注意在网页视图中,轻压快速操作是自动提供的。) 不要将轻压作为唯一开启该项的指定操作的方式。不是每一个设备都支持轻压和重压,一些用户可能选择关掉3D Touch, 因此在你的应用中去寻找其他方式实现轻压的功能是非常重要的。当你的应用在较旧的设备上运行时,可以把轻压的快捷操作映射到一个视图里,让用户通过点击并长按获得。 3.1.2 主屏幕快捷操作(Home Screen Quick Actions) 主屏幕快捷操作可以在主屏幕给用户呈现方便的、有用的、应用特定的操作。 Camara的主屏幕快捷操作 Mail的主屏幕快捷操作 主屏幕快捷操作: 当用户在主屏幕采用比点击且长按更重的按压,按压在应用图片上时,出现屏幕快捷操作 它会显示一个你提供的短标题,一个图标和可选的副标题 它不支持其他定制的内容 它可以随着你应用的更新,更新显示的信息 使用主屏幕快捷操作去开启引人注目的、高价值的任务。例如,Maps可以让用户不需要打开Maps,通过在当前位置附近搜索就可以获得回家的方向。一个应用至少需要把一个有用的任务放在主屏幕快捷操作里;你可以提供最多四个快捷操作。 避免使用主屏幕快捷操作去减少应用里导航的内容。如果用户访问你应用的重要区域非常困难和耗时,那么首先去修改你的应用的导航,这样做是可以让所有用户都获益的。接着,可以去为有用的深层次链接提供主屏幕快捷操作,从而开启这些有用的、创造性的任务。 不要把主屏幕快捷操作作为通知用户的一种方式。iOS用户期望以其他方式接收应用中的信息(更多信息参看 Notifications)。 为主屏快捷操作提供一个简洁的标题(可有副标题)和一个模板的图标。标题应该直接传达这个操作的结果;例如,“回家的方向”,“新建联系人”,和“新建信息”。你也可以提供一个副标题给用户更多上下文信息。例如,Mail使用一个副标题在主屏快捷操作的重要位置去告诉用户有未读信息。 不要把你的应用名字或者无关的信息放在标题和副标题里,同时要考虑到使用本地化的用语。 保持标题的简洁不会被切断从而帮助用户快速理解操作是非常重要的。如果你提供的副标题一行显示不全,系统会截断;如果你没有副标题,系统会把一行展示不完全的长标题以两行展现。 你可以从很多系统提供的模板图标中选择图标,你也可以创作定制的模板图标。更多关于图标尺寸、内边距和定位的详细引导信息,可以下载主屏快速操图标模板 https://developer.apple.com/design/downloads/Quick-Action-Guides.zip。更多关于设计模板图标的信息,参看Template Icons。 系统会自动安排图标在快速操作列表中的位置是在左侧或者在右侧,这依赖于你的应用的图标在用户主屏幕的位置。(摒除图标在列表中的位置,在自左向右的语言中文字总是左对齐。)这里有主屏快捷操作的多种展现方式的例子。 3.2 Live PhotosLive Photos 让用户在丰富的声音和动作环境下,捕捉和再现他们喜爱的回忆。从iOS 9开始,相机(Camera)应用可以捕捉附加的内容(拍照之前和结束后的声音和额外的画面)为传统的、静止的图片增加生活气息。 在iOS9.1及之后的版本中,你的应用可以让用户享受和分享Live Photos。这个指引可以帮助你给用户提供更好的体验。 在不支持Live Photo的环境中,把Live Photo像传统照片一样展示。不要在支持Live Photos的环境中,自定一种与Live Photo相似的体验。 不要分开展现Live Photo的附加画面和声音。要让用户在不同的应用中体验Live Photos时,有一致的视觉呈现和交互方式。把Live Photo拆分开展现是一个很坏的体验。 确保用户可以区分Live Photo 和传统静止图片。在用户分享照片时,帮助他们做好区分是特别重要的。最好在用户查看一个LivePhoto的时候,展现一点移动作为提示。万一这个提示没有起到作用,你可以在LivePhoto上展示一个系统提供的标记。LivePhoto不要展现一个像视频里回放按钮的界面元素。 注意 上图这种情况,不支持像照片应用里全屏浏览滑动切换照片时的显示的 把用户所做的调整应用到Live Photo的所有帧中。如果你的应用可以让用户为照片添加滤镜或者调整,应确保它可以作用于整个LivePhoto中。如果你不支持调整用户想分享的 LivePhoto的所有内容,要让他们知道可以以传统照片的方式分享。 让用户在决定分享前,可以预览这个Live Photo的所有内容。如果你的应用UI可以让用户选择照片分享,要为他们提供一个把Live Photo作为传统照片分享的方式。 如果你使用系统提供的标记,应该把它放在每个LivePhoto上同样的位置。标记可以放在一个不会影响用户查看照片的角落。确保在你的应用中采用一致的方式添加标记,这样可以让用户依靠它去识别LivePhoto。iOS有两种方式提供标记: 覆盖。这种覆盖的方式包含一个阴影,适合覆盖在照片上 纯色。这种纯色的方式(无阴影)可以被用来创建一个可调色的图片模板 iOS也提供了很多纯色标记,其中,图片上一个删除线代表现在的LivePhoto被当做是一个传统的照片 在用户下载一个Live Photo的时候给他们一个好的体验。尤其重要的是,用户需要知道他们正在下载的是一个LivePhoto,他们需要知道什么时候可以播放它。如果你为一个Live Photo展示一个未播放的进度指示器,确保这个指示器与你的应用中其他的下载体验一致。 3.3 钱包(Wallet) Wallet应用是帮助用户查看和管理各种数字化票券的,他们是一些物理个体的数字展现,例如登机牌、优惠券、会员卡、奖励卡和各种票。Wallet也可以让人们添加他们的信用卡、借记卡和储值卡结合Apple Pay使用。你可以在应用中创建一个票券,分发给用户,并且在有更改时进行更新。 使用PassKit框架可以方便地用自定义内容来收集一个票券和使用户票券库中的票券。(想要学习Passbook技术的核心概念和PassKit接口的使用方法,请参考Passbook Programming Guide。)以下几点可以帮助你创建一个用户乐意保留并使用的票券。 设计一个在各个设备上呈现很好票券。当你选择一个票券的样式——比如登机牌,优惠券,票据,奖励卡或者通用的票券——你会获得一个特别的布局和一系列区域去处理(更加详细关于不同票券的样式,参看Pass Style Sets the Overall Visual Appearance)。这个系统在各个设备上合理展示你的票券,所以正确使用票券的区域是非常重要的。例如,在Apple Watch上,条状图(strip)和缩略图(thumbnail)图片是不显示的,所以你不希望把基本的信息放到这些元素里。更多Apple Watch票券的布局,参看Designing Passes for Apple Watch. 使用合适的票券区域展现文本。在你的票券中使用允许VoiceOver的用户获取票券中的所有信息的区域,保持你的票券外观的一致性。避免将文本嵌入图片或使用自定义的字体也是一个很好的方法,因为不是所有的图标会展示到所有的设备上,这样对用户来说阅读这样的文字会有困难。 避免使用识别一个设备的语言。你不能预料到哪些用户将会查看你的票据的设备,因此你不想使用可能在一个特别设备上不起作用的语言。比如,文字告诉用户“滑动去查看”内容,当这个发生在Apple Watch上将会不起作用。 尽可能,不要只是简单复制已有的物理票券。Wallet 已经建立了有美感的设计,票券也都配合这种设计以看起来更好。所以不要只是复制物理票券的外观,抓住这次机会设计一个符合Wallet 组成和功能的干净简洁的票券样式。 对放在票券正面的信息精挑细选。用户期望扫一眼票券就能快速获得他们需要的信息,所以票券正面的信息应该是整洁且易读的。如果有用户可能会需要的额外信息,将它们放到票券的背面要比挤在正面好得多。注意,Apple Watch上的票券没有背面。 通常情况下,避免使用纯白色背景。通常,一个好看的票券应该使用鲜艳的纯色背景或者使用强烈的,充满活力的图片作为背景。当然,在设计背景时还要确保内容的可读性。 在商标文本区域显示你的公司名称。所有票券的商标文本区域的文字都使用了统一的字体。为了避免和其他票券发生冲突,还是建议您在商标文本区域输入文字,不要使用自定义字体。 使用白色的公司商标。商标图片放置在票券左上角公司名称的旁边。最好提供一个白色的,单色的,不包含文字的商标。如果你想要增强商标的效果,又想要与文字风格匹配的话,可以增加一个在y轴方向上有1像素偏移,有1像素模糊和透明度为35%的黑色阴影效果。 如果可以的话,使用矩形的条形码。类似于PDF417这样的长方形条形码比正方形二维码更适合票券的布局。如下右图所示,正方形的二维码会使两边有空白区域并且会在垂直方向上使上下方内容变得拥挤。 为性能去优化图片。因为用户通常会通过电子邮件或者Safari接收票券,所以让下载的越快越好是非常重要的。为了提高用户体验,使用能满足视觉效果的最小的图片文件。 在合适的时候更新票券以增强其效用。即使一个票券代表的是一个并不会改变的物理实体,数字的票券也可以通过映射真实世界的一些事件来提供更好的用户体验。例如,当某个航班延误时你可以更新登机牌上的信息,这样用户就能够通过查看电子登机牌来获得当前的信息。 3.4 苹果的移动支付平台(Apple Pay) Apple Pay是苹果公司面向iOS移动设备推出的一种简单、安全、个人的移动支付方式。当用户在购买实体商品和服务时时,可以使用Apple Pay快速、安全地提供个人联系方式、收货地址以及付款信息。 通过用Apple Pay支付,用户无需每次购物都要创建账号或填一遍个人信息。Apple Pay显著加快了支付流程,有助于消除前期的各种信息登记,进而为用户的“无障碍”选购过程提供更好的体验。欲了解更多信息,请参阅 Apple Pay Programming Guide. Apple Pay的用户界面非常清晰、简洁高效、低调。它包含三个界面元素,各出现在不同的上下文情境中。 按钮。Apple Pay的按钮用来告诉用户,他们可以在当前的情境下(比如商品页面)完成购买。当用户点击了Apple Pay的按钮,立即显示支付上拉菜单(见下文) 开始帮助用户完成支付流程。用户通过“设置Apple Pay”的选项Apple Pay的相关银行卡信息绑定操作。通过调用PKPaymentButton API口可以找到这两个按钮(想要了解更多信息,请查阅PKPaymentButton Class Reference)。有关使用Apple Pay支付按钮的更多详情,请参阅Identity Guidelines. Apple Pay支付标识。当用户需要在授权支付之前选择付款方式并敲定其他信息时,他们期望看到Apple Pay的支付标识。Apple Pay的支付标识应该同其他付款方式以相同或类似的格式显示。 支付上拉菜单。在用户提交订单以及完成相关支付之前,Apple Pay会显示一个包含了联系方式、收货地址以及与结账相关付款信息的支付上拉菜单。尽管用户依然可以在支付上拉菜单里做些微调,比如选择不同的送货方式,但他们不用做出重大改变或输入其他信息。当用户看到该支付上拉菜单,他们应该能够立即完成交易并授权付款。 对于可以使用Apple Pay付费的用户,Apple Pay的用户界面应当始终显示。如果用户的移动设备支持Apple Pay,并且他们已经激活了相关可用的银行卡因此可以通过将Apple Pay设为默认支付方式来满足用户的期望。 如果用户无法使用Apple Pay服务,就不要显示任何Apple Pay的用户界面。如果用户使用的设备不支持Apple Pay,仍强行将其作为一个支付方式选项,可能会对用户造成混淆。但是,如果用户使用的设备是支持Apple Pay,但没有绑定任何信用卡或借记卡,你可以在界面中显示“设置Apple Pay”的按钮。 当用户点击了Apple Pay的按钮,立即显示支付上拉菜单。当用户决定使用Apple Pay来结账时,如果还要迫使用户经历额外步骤,会使支付流程显得复杂,增加不必要的矛盾,并可能会让用户感到沮丧受挫。当用户点击了Apple Pay按钮,不要显示其他警告或模态对话框视图。如果用户可以提供像打折或促销代码之类的信息,请在用户点击Apple Pay按钮之前找到一种方式来接收该信息。 Apple Pay按钮与其他可见的支付按钮应保持相同的尺寸大小或更大。将Apple Pay按钮放置在醒目的位置,可以帮助用户轻松找到它。 使用Handoff功能帮助用户完成在Apple Watch上发起的购买。 Apple Watch佩戴者可以在商店完成支付,但他们无法完成由Apple Watch第三方应用程序调用的支付行为。当Apple Watch佩戴者发起了由第三方应用程序调用的支付行为,则显示一条消息告诉他们请在iPhone上完成支付。为了更好的用户体验,还可以使用Handoff功能深层链接到你的iOS应用程序上,并立即显示包含预设好的相应付款信息的支付上拉菜单。 有关使用Apple Pay支付按钮以及Apple Pay支付标识的更多信息指南,请参阅 Apple Pay Identity Guidelines. 3.4.1 自定义支付上拉菜单 (Customizing the Payment Sheet) 根据完成交易付款所需要了解的信息,以及所要传达给用户关于本次购物的信息,来自定义Apple Pay支付上拉菜单所要显示的内容。 支付上拉菜单仅显示对完成交易付款有必要的信息。如果Apple Pay支付上拉菜单显示了些无关的信息,用户可能会感到困惑或焦虑。举个例子,如果商品是在线交付或通过电子方式完成,需要联系人的电子邮件地址是有意义的,而不是收货地址。在这种情况下,要求收货地址可能给用户产生会有什么东西将意外被派件到家中或公司的错觉,或许还可能导致他们对个人隐私被访问产生不必要的担忧。 支付上拉菜单允许用户可以选择更换送件或取件方式。用户可以从你在支付上拉菜单中设定的几种交付方式中随意选择一种。通过用文本标签控件、报价以及可选的第二行预计到达日期,来具体描述一种收货方式。另外,你还可以设定交付方式为“派件”或“取件”,让用户指定一个可接收快递送货上门或需要运输服务取件的位置。 使用并排项来描述周期性付款和一些购买费用的小计。 并排项包含了一个标签文本和花费数值。并排项被用来: 表明用户授权一个定期付款项目,比如“每月订阅 $19.99” 告知用户关于额外费用,比如“礼品包装 $5.00”和“税费 $4.53” 显示优惠券或折扣信息带来的减价,比如“周五特价 -$2.00” 描述某个项目需要按实际计费,比如运输服务“时间&距离 …” 不要用并排项来显示所要购买的商品的构成清单。 创建并排项标签时,尽可能显示在同一行。并排项标签应该具体、容易理解。如果行条目标签字符数过长,那么很难让你的用户一看就懂。 商户名称需要紧紧跟随在同一行的“Pay”字符后面作为一个整体。确保所使用的商户名称以及相应的开销支出,必须与用户检查自己的信用卡或银行对账单时的数据保持一致。这一点很重要,因为它有助于用户确信他们的开销支出是无误的。如果你的应用程序只是作为中间媒介,而不是最终的商户支付,请明确向用户表明这个具体说明“付款给 最终的商户名称(通过 你的应用程序名称)。 如果总价花费在支付授权时还不明确,请向用户传达有可能会有额外费用的信息。举个例子,一次汽车旅程从支付授权时刻起到驶向最终目的地,它的开销报价可能会受行车距离或时间的影响变化。或者,一个客户可能想要给点小费在商品已派件之后。对于这样的情况,在支付上拉菜单中给予一个非常明确的解释说明是很有必要的。当你使用一个并排项来配置最终总价的更新信息时,总价金额会自动显示为“总价待定”。另外,如果你是预授权支付一个具体金额的订单,确保支付上拉菜单准确地反映了这一信息。 3.4.2 简化结账流程(Streamlining the Checkout Process) Apple Pay使得购物变得快速、简单,对此人们会欣然接受的。更少的步骤和更少的需要用户手动输入的信息,使得整个结账流程更好。 始终使用Apple Pay的最新数据信息。假设用户一直保持Apple Pay个人信息的完整性和时刻更新,那么不要依赖于任何先前收集的信息。即使你以前已收集过用户的联系方式、交付方式和支付信息,也要在结账时获取来自Apple Pay的最新信息。在结账环节,尽量避免用户输入本可以从Apple Pay获取的任何信息。 使用Apple Pay加快购买。对于单个商品项目的购买,让用户只需通过点击商品页面上的Apple Pay支付按钮,即可显示支付上拉菜单并进行即时结算。购买单个商品时,无需采取额外的步骤,也无需将商品添加到购物车,用户喜欢在应用程序中体验到这种便利性。对于多个商品被添加到购物车中会使用相同的交付方式被送到相同地址的情况,一旦用户有意向支付时,会通过显示支付上拉菜单的快速结账流程来支持。 在显示支付上拉菜单前需提前收集好赎回代码或促销代码。因为在Apple Pay支付上拉菜单中没有办法输入这些代码,请务必在显示支付上拉菜单之前收集好相关代码。 如果人们可以将购买的商品派送到不同地方,或以不同的速度发货,请在显示支付上拉菜单之前提前收集好该信息。对于这种极端情况,你需要在显示支付上拉菜单之前得到发货信息,因为在支付上拉菜单中没有办法来指定多种交付方式和地址。一般情况下,在支付上拉菜单中务必收集到交付方式和地址信息。 显示订单确认页面或致谢页面。在交易完成时,通过使用订单确认页,以这种直接的用户体验来显示关于商品能派送到的预计时间以及用户如何跟进订单状态的信息。 如果合适的话,请在你的订单确认页上提到Apple Pay。尽管在你的确认页面上提到Apple Pay不是必要的,如果你愿意选,可以使用其中的一种格式: “Visa 1234(Apple Pay)” “用Apple Pay已完成付款” 3.5 研究型应用程序(Research Apps) 研究型应用程序可以让苹果用户充分利用iOS移动设备的便利性,参与到各种研究性学习中来。通过调用开源代码ResearchKit,使用预设的几种界面视图和转场动画,可以很轻易为你的研究和参与者自定义一个美观易用的研究型应用程序,这些资源都可以在苹果的开源代码ResearchKit项目中调用。要想了解如何使用ResearchKit来为你的研究开发一个研究型应用程序,请查阅researchkit.org. 重要 这些规范准则仅供参考之用,并不构成任何法律意见。对于与你的研究型应用程序发展以及任何法律适用的相关建议,你应该向律师咨询。 通常情况下,一个研究型应用程序是由ResearchKit定制化的界面视图以及应用程序本身具体设定的界面视图组成,可归纳为三种主要的体验: 参与者的就位培训(Onboarding) 研究性调查(Study-specific investigation) 管理条目信息(Management items) 设计你的研究型应用程序时务必要遵循以下每个部分的规范准则,将有助于你的用户参与者感到舒服和保持参与感。 3.5.1 参与者的就位培训(Onboarding) 就位培训的体验包含了一系列向潜在参与者介绍研究内容以及征询他们同意的环节。完成这些以后,参与者通常不会再重新访问这些就位培训的内容环节: 你应该按如图所示的这个顺序呈现就位培训的各个体验环节,也就是按介绍指引、适任、知情同意,以及授权访问数据。 创建一个具有号召性用语的介绍指引。指引环节应该帮助人们了解更多关于你的研究以及告诉他们如何成为一名参与者。指引环节最好也能向那些现有的参与者提供快捷登录的入口以便继续正在进行的研究。 尽快确认招募的用户是否合格。适任环节呈现在指引环节之后、知情同意环节之前(如果参与者并不适合该研究则没有必要让其查看知情同意环节)。请确认所呈现的适任资质要求对于你的研究来说是必要的。请使用简单、直白的语言描述这些要求,并让用户可以很容易就输入相关信息。 在得到参与者的同意之前,确保他们已充分了解你的研究内容。ResearchKit有助于让知情同意流程显得简洁、友好,同时还允许将你同意的任何法律规定或由机构审查委员会和伦理审查委员会所设定的规定纳入其中。(如果你的应用程序涉及到进行人体生物学相关的研究,必须确保你的应用程序符合现有的苹果应用商店规范指南,并获得参与者的许可。)通常情况下,知情同意环节包含了: 说明这项研究是如何工作的 确保参与者了解研究内容以及各自的责任 获得参与者的许可 将冗长的同意书分解成易理解消化的小节。每个小节可以只覆盖研究内容的一个方面,比如数据采集、数据应用、潜在好处、可能的风险、时间承诺、如何撤出等等。每个小节请使用简洁、直白的语言来说明一个高度概括的内容。如果有必要,提供一个查看详情的按钮便于参与者了解该小节更详细的解释。应该让他们在同意参与之前,就查看完全部知情同意内容。 通过一个小测验来测试参与者的理解情况是有意义的。在获得参与者允许的情况下,你可以选择向每个参与者询问相同的问题。 你的研究必须获得参与者的同意,如果合适,还可以收集一些联系人信息。参与者同意参与研究后,他们需要提供个人签名以及联系方式,最后会收到一个确认对话框。对于这些信息记录,大多数的研究型应用程序会向参与者电邮一份PDF格式的同意书。 参与者需要对这个确认自愿参与研究的告警对话框给予响应 参与者可以提供他们的个人签名在知情同意流程中 如果你需要访问参与者的设备或数据必须得到他们的许可。必须解释清楚你的研究型应用程序为什么需要访问他们的位置信息、健康应用程序或其他数据,并且确保避免向参与者索要对你研究并非至关重要的数据。同样地,如果你需要向参与者发送通知提醒也要获得参与者的许可。 让参与者准备授权访问数据,比如健康应用程序的数据 让参与者自己选择他们愿意与你共享的数据 3.5.2 具体研究的调查(Study-Specific Investigation) 为了从参加者获得数据输入,你的研究可能会使用情况调查、活动任务,或两者的组合。根据你的研究的体系结构,参与者可能会在每个环节多次或仅需完成一次交互即可。 问卷调查的设计应该能让参与者专注参与其中。 ResearchKit可以很容易就呈现多种答案类型的调查问题,比如对错、多选、日期和时间、比例计算,以及开放式文本填写。当你使用ResearchKit的界面视图来创建一项调查,请遵循以下准则,来保证好的用户体验: 告诉参与者总共有多少道问题,以及完成调查预计需要花费的时间 每屏只呈现一道问题 显示给参与者当前调查的进度 调查应该尽可能简短。几个简短的调查往往好于一个冗长的调查 对于需要额外解释的问题,问题描述请用标准字体大小,然后解释文字用略小的字体大小 调查结束时要告知参与者 ResearchKit提供了许多用于调查环节的可自定义界面视图。这里有一些样例。 使得活动任务容易理解。活动任务需要参与者参与到一次活动中来,比如对着麦克风语音、手指在屏幕上完成点击、行走散步,以及执行一次记忆力测试。请按照以下几点准则来鼓励参与者执行活动任务,并给与他们成功的绝佳机会: 请用简洁易懂的语言来描述如何执行本次任务。 如果任务必须在特定的时间或特定情况下进行,请务必明示。 确保参与者可以分辨出任务何时结束。 以下是ResearchKit所支持的两个活动任务样例。 3.5.3 管理条目信息(Management Items) ResearchKit提供了个人档案的界面视图来让参与者可以管理他们的个人信息。此外,创建一个可以激励用户并能让他们追踪他们在研究中的进度的界面视图是个不错的选择。在大多数情况下,参加者应该能够随时访问这两个模块。 使用个人档案来帮助参与者管理个人信息和与你研究相关的数据。个人档案界面视图允许参与者在研究进程期间可以编辑相应数据,比如体重或睡眠习惯,并且可以提醒他们即将到来的活动任务。你同样可以在个人档案中给予参与者一种简单的方式离开该研究、查看知情同意书,以及查看该应用程序的隐私政策。 使用仪表盘概览视图来激励参与者,并呈现进度。一个概览视图可以让你与参与者对信息一览无余并鼓励他们继续参与。如果你的研究内容合适的话,你可以使用该概览视图给予参与者丰富的反馈,比如每日进度、每周评估、具体活动的结果,以及同其他参与者的汇总结果进行对比。 3.6 应用扩展(App Extensions) 应用扩展可以延伸应用的使用范围。当用户使用其他应用时,应用扩展使得用户仍能使用你应用的核心功能。举个例子,当人们在Safari中浏览网页时,他们可以使用你的分享扩展来发送一张图片或一篇文章到你的社交网站上。或者当使用Photos(照片)应用时,人们可能会使用你的图片编辑扩展来为一张图片加上一个滤镜效果。(在这些场景中,Safari和照片应用承载用户使用扩展的场景,因而被称为宿主应用(host apps)。) 你需要提交包含应用扩展的完整iOS应用到App Store(包含扩展的应用被称为容器应用(containing app))。在你的容器应用中启用扩展之后,人们就可以在使用其他应用时,使用扩展来执行快速任务。例如,在邮件中浏览某个商品时,人们可以不用离开邮件应用就使用你的动作扩展来把商品添加到购物清单中。 表 22-1 列举了可以多个创建的iOS应用扩展类型。 以下指南适用于所有类型的应用扩展,针对特定类型应用扩展的指南请参阅后续章节。(如果想了解如何开发、调试和发布一个扩展,请参阅App Extension Programming Guide.) 确保是单任务。应用扩展并不是应用的精简版,它帮助用户在有全局目标的上下文中完成狭义范围内的有限任务。例如,动作扩展可以为用户提供一种不同的方式来查看当前内容。 保证用户的交互是有限和流畅的。好的应用扩展应该只需几步点击就可以帮助人们完成任务,这样他们就能尽快回到之前的场景中。例如,分享扩展只需一次点击即可完成一张图片的分享。 将容器应用及其应用扩展的名称保持一致。一个容器应用中如果有多个扩展,需要使用不同的名称,你需要确保用户能够理解你的扩展和应用之间的关系。人们会在很多不同的情况下遇到扩展,如果他们当下没有认出来,那么他们就未必会信任这些扩展。 大部分情况下,复用容器应用的图标。显示用户熟悉的图标是获得用户信任的另一种方式。请注意,对于动作扩展来说,你应该使用单色版本的容器应用图标(详见分享和动作扩展)。 重要:和设计图标和图形一样,不要重复使用iOS的图标和图片,不要为苹果的产品和设计再设计一套图片。 避免在扩展上显示模态视图。很多扩展默认以模态视图来显示,所以应避免再叠加模态视图。尽管有时候用户可能会在扩展上遇到警告框,但是在设计扩展的流程时,应避免出现模态视图。 3.6.1 今天部件(Today Widgets) 人们会在通知中心的今天区域中查看今天部件(Today widgets)。因为人们会设置今天区域以显示他们最关注的信息,所以在此进行设计可以有效帮助你的部件在这些用户最重要的信息中占据一席之地。 设计与通知中心风格一致的外观。当使用通知中心的默认边距和背景时,你的今天部件就会给用户以统一的体验。为获得最佳的结果,你应该重点关注你的内容而不是背景或者其他的,尤其应该避免绘制一片纯色背景。 注意:iOS会自动在自定义的部件内容上方显示应用的图标和标题(图标会显示在标题前面的空白处)。 将部件内容与标题对齐。当你的部件内容与标题对齐时,人们就可以很简单地浏览今天视图中他们想要的部件。遵守今天视图中的边距规范,并将内容约束在如图的部件内容区内。 一般情况下,使用白色的系统字体来显示文本。在通知中心默认背景下白色文字会看起来较好。对于二级文本,可以使用系统提供的vibrant外观样式(查看notificationCenterVibrancyEffect了解更多)。 提供通知中心式的体验。人们访问通知中心来获取简要的更新或者执行一个非常简单的任务,所以今天部件最好只显示适量的信息和进行有限的互动,特别是: 避免用户在部件中需要滚动或纵向移动来查看全部的信息。部件可以通过纵向扩展来显示更多的信息,但若部件的高度超过通知中心的高度就不是一种好的体验了,因为这样会干扰其他部件的查看 避免使用横向扫动或拖曳,因为这会干扰在通知中心进行导航 尽可能使用户只需一步操作就完成任务或打开你的应用(注意,在今天部件中键盘是不可用的) 优化性能以便人们可以即刻获得有用的信息。可以考虑在本地缓存信息,以便当有更新时就可显示最近信息。人们只希望在今天视图中花很少的时间,如果部件使用内存不当,iOS就可能会终止它 在适当情况下,让人们点击你的今天部件来打开你的应用。因为今天部件提供了专一的体验,所以就能有效引导人们去到你的应用以获取更多信息或功能。最好不要显示“打开应用”按钮,而是应该让你的整个今天部件都可被点击来打开应用。你也可以让用户点击部件中的UI对象,以打开你的应用并跳转到关于此UI对象的视图中。举个例子,日历部件显示了今天的事件,如果用户想要获得某个事件的更多信息,他们可以点击部件中的事件来打开日历应用进行查看。 注意:虽然从部件打开应用的方式对用户来说还不错,但继续在部件中提供有用且及时的信息依然是很重要的。人们可不一定会欣赏一个功能只是打开应用的今天部件。 如果可能,在今天部件中让人们知道他们需要登录来获取有用的信息。如果你的今天不见需要人们登录查看信息,展示一个信息去鼓励他们登录和解释什么样的内容将会被呈现。例如,如果你的时间部件即将到来的预约是用户登录后展现的,你可能需要让用户“登录我的应用去查看即将到来的预约”。 不要制作一个今天不见需要打开除了你自己应用外的应用。一个模拟iOS主屏的行为的时间部件不会为你的用户提供有用的功能。 3.6.2 分享和动作扩展(Share and Action Extensions) 人们通过点击应用中的动作按钮(Action button)来使用分享和动作扩展。在通过动作按钮显示的动作视图控制器(activity view controller)中,动作扩展被列在底部,分享扩展被列在动作扩展之上。人们可以使用更多(More)按钮来管理显示在动作视图控制器中的分享和动作扩展。 分享或动作扩展通常被认为是在当前用户场景下用来输入内容之用。例如,当在Safari中阅读一篇文章时,用户可能会点击动作按钮并使用一个分享扩展来发送这篇文章到分享网站上,也可能会使用一个动作扩展来查看这篇文章的翻译。 注意:在动作视图控制器中,iOS只会显示支持当前内容类型的动作扩展。例如,当用户当前内容是视频时,iOS就不会显示支持文本的动作扩展。 尽可能在分享扩展中使用系统提供的UI。系统所提供的撰写视图控制器 (compose view controller) 提供给用户一种一致的体验,并能自动支持一些常用任务,例如预览和确认标准项,同步内容,查看动画,以及完成一封邮件。欲知更多关于使用系统提供的撰写视图控制器,请参见 App Extension Programming Guide中的Share. 如果上传需要一定时间,那就应考虑在分享扩展的容器应用中显示上传进度。无论分享的文件有多大,人们都期待在点击扩展中的发送或分享按钮后,能立即返回他们之前的场景。你需要让进度状态随时更新,但是人们不想每次上传完毕后都收到通知,并且也无法自动重启扩展。在这种场景下,在容器应用中显示上传进度是一种解决方案,这样容器应用就可以在后台处理任务,并在遇到问题时发送通知。 动作扩展使用单色的应用图标。( 不同的是,分享扩展则应该使用其容器应用的彩色应用图标。) 要为动作扩展设计图标时,你可能需要从创建一个应用图标的模版开始着手。如有需要,可以专注图标所特有的元素来进行简化设计。 如果你在容器应用中提供了多个动作扩展,那么最好为他们设计一套图标,且确保这套图标中的每一个看起来都与容器应用的图标是有关联的。 3.6.3 图片编辑扩展(Photo Editing Extensions) 当人们在照片(Photos)中查看图片或视频时,可以使用图片编辑扩展。一般来说,图片编辑扩展能帮助用户筛选图片或进行一些其他的图片或视频编辑。在用户确认之后,编辑后的内容就会出现在照片应用中。 照片应用提供了一个模态视图来显示图片编辑扩展的自定义UI。当用户在确认对图片或视频的编辑时选择了取消(你必须要在代码上保证存在这个行为),照片应用还可以显示一个确认视图。 避免在图片编辑扩展中使用导航栏。如图所示,承载扩展的模态视图已经包含了导航栏,若再增加另一个导航栏,既会占据更多你的界面空间,还会使用户产生困扰。(照片应用默认会以全屏高度来显示你的视图,所以你的内容会出现在内建的导航栏之下。) 如果可以,让用户能够预览编辑结果。尽可能让用户在关闭扩展返回照片应用之前看到他们编辑的成果。 3.6.4 文档提供者扩展(Document Provider Extensions) 文档提供者扩展帮助人们在其他各种应用中查阅你的应用所管理的文档。在宿主应用(host app)中,文档采集视图控制器(document picker view controller)会显示你的扩展所提供的UI(想要了解更多有关文档采集视图控制器的内容,请查阅UIDocumentPickerViewController Class Reference). 注意:文档提供者扩展由两个不同的部分组成:文档采集视图控制器扩展和文件提供者扩展。文档采集视图控制器扩展包括了你的自定义UI,文件提供者扩展实现对文件的访问。为了简单起见,本节所使用的术语文档提供者扩展(Document Provider extension)是为了表述扩展中文档采集视图控制器部分的UI和体验。 避免在文档提供者扩展中使用导航栏。iOS会显示扩展的自定义UI,而自定义UI又包含在文档采集视图控制器中基于导航栏的界面之中。所以,在内建导航栏之下再显示第二个导航栏会使用户感到困惑,并且还会占据原本你的内容区域。(文档采集视图控制器默认会以全屏高度来显示你的视图,所以你的内容会出现在内建的导航栏之下。) 3.6.5 自定义输入法(Custom Keyboards) 人们在整个系统中使用带有自定义输入法的输入法扩展来替换iOS的自带输入法。在启用输入法扩展之后,除了受保护的文本区域(例如密码输入区)和手机键盘区(例如联系人中的电话号码区)外,当人们点击任何文本输入区域后就能使用自定义输入法。 为用户提供明显的方式来切换输入法。人们对于iOS的输入法切换按钮很熟悉,他们会期望在你的输入法中也有类似的体验。 如果可能,在你的容器应用中包括一个教程。如果必要,使用你的自定义键盘的容器应用去给人们讲解如何启用和使用你的键盘。不要把这个信息直接放在键盘本身,因为它可能让人们尝试使用这个键盘时感到困惑。 3.7 HomeKit 通过HomeKit,用户能够方便地在家中使用iOS设备上的智能家居应用来操控家中相关联的设备(无论这些设备制造商是谁)。最好的智能家居应用集成HomeKit和iOS系统来帮助用户: 创建家居环境、房间和区域 添加、寻找和移动家居设备(如灯泡或温度调节装置) 定义能够使一组多个家居设备响应的行为 管理用户 用Siri来操控他们的家居设施 想要了解如何在你的应用中使用HomeKit,可参阅HomeKIt Developer Guide。下面的指南可以帮助你做出一个容易上手、令人愉悦的智能家居程序。 不要想当然地认为你的设备会是用户所设置的首个设备。你的应用除了能让用户很容易就能创建家居环境、房间和区域,还需要让用户能方便地将你的设备接入之前已经设置好了的区域中。 让添加新设备变得简单。不要强迫用户在添加设备之前注册账号。最好让你的应用能自动发现新的设备并将他们显著地展示在用户界面上。确保所展示的信息足够充分让用户可以轻易辨识出该家居设备。 帮助用户辨认他们正在调节的设备。给用户一个能够帮助他们从物理属性辨认设备的控制器。例如,你可以让用户通过闪一下灯泡来确认他们正在调节的是他们想要调节的那个。 让用户能够通过多种方式来搜寻设备。当天的时间、季节和用户当前的位置会在特定的时刻成为判别某些设备是否重要的影响因素。因此,你的应用应该允许用户能在家中按类型、名称、或者位置的方式来搜寻设备。 为家中已接入的设备提供推荐的操作集。操作集允许用户设定在某种情景下让多个家居设备按照特定的方式行动。例如,一个“离开”操作集可以将房屋内的温度调低、关闭电灯和锁上所有房门。你的应用可以向用户推荐一些已经设定好了的操作集或者让用户创建自定义操作集。当用户能够基于房间或区域去创建自定义操作集时,让用户可以从你推荐的设备列表中进行选择,通常能使用户获得更好的体验。 使用友好的交谈式语言让你的应用平易近人、易于使用。智能家居概念可能会懵到用户,应避免使用他们可能不理解的缩写和技术术语。例如,HomeKit是指代API的专用技术术语,它就不应该在你的应用中使用。 注意:如果你是苹果MFi认证许可商,请访问MFi门户网站查看设备包装的命名及消息通知的规则。 与Siri互动。通过Siri,使用一个简单的陈述句就能控制执行复杂的操作。Siri能够识别操作集、房屋、房间和区域的名称,并且能够理解像“Siri,把前门关了”、“Siri,把楼上的灯关了”和“Siri,把多媒体房的温度调高一点”这样的陈述。遵循以下准则能帮助你为用户提供使用Siri操控设备时的良好体验: 使用Siri能够识别的功能名称,而非设备名称。一个设备可能提供多种功能(例如,一个既有风扇功能又有照明功能的风扇吊灯),因此,帮助用户区分这些功能是很重要的。最佳方案是让用户在一系列不包含公司名称及型号的限定的名称中进行选择,并且允许他们以后编辑。你所推荐的名称应该使用规范的、容易理解的词语来描述功能,并可选择是否包含家中的位置信息,例如“客厅灯”或者“车库门”。你还可以让用户指定一种控制插座开关的通用口令,例如“Siri,把灯关了”,来控制所有的灯具和其相关的设备 当用户配置操作集的时候,告诉用户如何通过Siri去操控它。举个例子,当“电影”这个操作已经确认配置完毕时,让用户知道他可以通过跟Siri说“Siri,把家调成电影模式”这样的话来激活这个操作。 注意,当用户单独对Siri说出某操作的名称时,同样也能激活那个操作。Siri能够识别系统预置以及用户自定义的操作集,这些已配置的操作集至少包含一项操作 帮助用户设置触发机制。在iOS9中,HomeKit支持触发机制:当满足特定的时间、地点或其他设备的行为的条件时激活操作的方式。比如用户可以设置一个当太阳落山且车库门打开时,就打开厨房灯操作的触发机制。设置一个包含多个项目的条件关系容易使人感到混乱,因此,将你的设置界面做得简单易用至关重要。举例来说,使用与人们平常说话一样的表达方式来展示项目、属性和逻辑,就更容易使人理解。 3.8 多任务处理(Multitasking) 多任务处理让人们在屏幕上(在合适的iPad模式上)查看多个应用,可以在最近使用的应用之间进行快速切换。在iOS9,中,人们可以使用多任务处理UI(下图所示)去选择最近使用的应用。 能否在多任务处理中处理好取决于能否在设备中与其他应用和谐共存。从更高的层面来说,这意味着所有的应用都应: 仔细调整资源使用避免占用太多CPU,内存,屏幕空间和其他资源 处理好中断或来自其他应用的声音 停止和重启,即快速平滑地从后台切换到前台 不在前台时应恪守己任 下述指南细则可以帮助你的应用在专注应用切换的多任务处理中取得成功。更多合格的iPad模式下关于多任务环境中运行的信息,参阅 Adopting Multitasking Enhancements on iPad. 准备好被打断,并恢复。多任务处理增加了后台应用中断你的应用的可能性。其他特性,诸如广告出现和更快的应用切换,也会造成更频繁地打断。越快速和越精确地保存应用当前状态,人们就可以越快地重新运行应用,并从之前离开的页面继续使用。你可以通过利用UIKit的状态保存和恢复来为用户提供无缝的重新开始的体验(查看Preserving Your App’s Visual Appearance Across Launches了解更多信息)。 确保你的UI可以处理两倍高度的状态栏。两倍高度的状态栏会在诸如通话、录音和共享等过程中出现。在未作处理的应用中,状态栏的额外高度会引起布局问题,如UI被向下挤压或者被遮住。在多任务处理环境中,使两倍高状态栏显示正常是格外重要的,因为它可能会出现在更多的应用当中。 准备好暂停需要人们注意或主动参与的活动。例如,如果你的应用是一款游戏或媒体观看应用,你需要确保你的用户从应用切换走时,不会丢失任何内容或事件。当人们切换回游戏或媒体播放器时,他们希望能继续之前的体验,就好像他们从未离开过应用。 确保音频行为合适。当你的应用正在运行时,多任务处理会使得其他媒体活动更可能地同时进行,也会有更多可能性使你的音频不得不暂停,并恢复处理中断。查看声音来帮助你确保你的音频能满足人们的期望,并与设备中的其他音频和平共处。 适度使用本地通知。应用可以在特定时间发送本地通知,无论应用是在暂停中还是运行中亦或是根本就没有运行。为了达到最好的用户体验,应避免用过多的通知来骚扰人们,并遵循通知中创建通知内容的指南。 必要时,在后台完成用户的任务。当人们开始一个任务时,他们通常会期望即使已经从应用中切换走了任务仍能够完成。如果你的应用在执行用户任务途中,并且这个任务不需要额外的用户交互,那么你就应该在应用挂起之前就在后台完成任务。 3.9 通知(Notifications) 通知为人们提供即时的重要信息和功能。人们能在多种情况下收到通知,例如在锁屏界面中,或者在使用应用时,或者访问通知中心时。 通知中心有两种视图:通知(Notifications )和今天(Today)。 今天视图显示了一组可编辑的部件。今天部件是一个应用扩展,显示了少量及时和重要的信息或功能,这些信息或功能则是由用户所关注的应用所提供。举例来说,日历部件只显示了今天的事件。点击日历部件中的一个事件可以唤起日历应用,并打开该事件,用户接下来可以编辑该事件或管理其他的事件。想要了解更多关于设计今天部件的内容,请参见今天部件。 通知视图会显示用户感兴趣的应用所发出的最近通知。用户可以在设置(Settings)中来设置是否在通知中心显示该应用的通知。 iOS应用可以使用通知来让人们知道一些有趣的事情是什么时候发生的,例如: 收到一条消息 事件即将发生 有新的数据可下载了 某些状态发生了变化 在iOS8及之后的版本中,应用可以定义用户在通知中的操作。例如,用户可以在待办事项应用的通知中就标记该事项已完成,而无需额外打开应用。 iOS定义了两种类型的通知。 本地通知(local notification)由应用安排待发送,最终通过iOS发送到同一设备中,无论该应用当前是否正在后台运行。例如,日历或待办事项应用可以安排一条本地通知来提醒人们一个即将到来的会议或者日期。 远程通知(remote notification)(也称为推送通知(push notification))是由应用的远程服务器通过苹果推送通知服务来发送的,这类通知最终会被推送到所有安装了该应用的设备。例如,一款在线竞技类的游戏,用户可以和其他玩家竞赛的,可以更新所有玩家的最新状态。 注意:应用扩展可能会要求远程通知必须发送到它的容器应用。在这种场景下,容器应用常常会在后台运行来处理通知。想要了解更多关于应用扩展的内容,请参见应用扩展。 如果当你的应用正在后台运行时收到了本地或远程的通知,你就应该以你的应用所特有的方式将信息传达给你的用户。 为了确保用户能够自定义他们的通知体验,你应该尽可能多地支持以下的通知类型: 横幅(Banner) 警告框(Alert) 小气泡(Badge) 声音(Sound) 注意:在iOS8及之后的版本中,你必须对所有你想发送给用户的通知类型进行注册。当你第一次进行注册动作时,用户会遇到一个警告框,他们可以在其中操作来决定允许或拒绝所有来自你的应用的通知。不管用户选择的结果是什么,他们应始终能访问应用的设置来更改此项设置,或者设置他们想要接收的通知类型。 横幅(banner)是一个小而透明的视图,会出现在屏幕顶部并在几秒后消失。用户还可以看到在锁屏当中的横幅以及在通知中心中以通知形式出现的横幅。在横幅中,iOS会显示通知的内容和应用的小图标(欲了解更多关于小图标的内容,请参见 App Icon)。用户点击横幅来隐藏显示并切换到发送通知的应用。 除了默认的点击动作之外,当用户轻扫横幅时,你还可以定义两个动作按钮。点击通知动作按钮来隐藏横幅的显示并启动你的应用(可能是在后台)来执行动作。 通知警告框是显示在屏幕上的标准警告框视图,需要用户操作后才会隐藏。当用户点击Options按钮后,你需要提供并显示通知消息以及任何一个默认动作,或最多四个特定动作。警告框的背景样式不能做修改。 当用户点击警告框中的一个默认或自定义动作按钮时,iOS会同时隐藏警告框并运行你的应用(可能是在后台)。点击关闭或确定按钮会隐藏警告框而不打开应用。 小气泡(badge)是一个显示未读通知数量的红色小圆(小气泡显示在应用图标的右上角)。小气泡的大小和颜色不能做修改。 横幅、警告框和小气泡这三种通知都可以使用自定义或系统提供的声音。 在通知中谨慎使用具破坏性的动作。要确定用户有足够的上下文来避免意想不到的后果。为了帮助用户区分你所定义的破坏性动作,iOS会用红色来显示它。有时候,在应用执行破坏性动作之前,应该请求用户进行确认。举个例子,如果在锁屏的横幅(banner)中提供了一个破坏性动作,那么就应确保只有设备的主人才能执行该动作(你需要在代码上实现这一需求)。 为每个动作按钮提供自定义标题。创建一个简短的标题来描述清楚将要发生的动作。例如,游戏可能会使用“Play”作为标题来表明,点击这个按钮会打开应用来进行游戏。确保标题: 使用标题样式的大小写(title-style capitalization) 足够简短,能不被截断地显示在按钮内(也应确保测试各种语言文字的标题显示正常) 不要为同一个事件重复发送通知。用户可以选择处理通知项;通知项在用户未处理前会一直显示。如果为同一事件重复发送通知,通知中心列表中会满是通知,用户就有可能会关闭你的应用的通知 不要在通知消息中包含你的应用名称。自定义信息会在警告框和横幅中显示,也会在通知中心中以通知的形式显示。你无需在自定义信息中显示你的应用名称,因为iOS会在显示信息的同时自动显示应用名称。 为了使本地或远程通知信息更有作用,你应该: 专注于信息而不是用户的行为。避免告诉人们点击哪个按钮或如何打开你的应用 足够简短,一两行就可以显示完整。较长的信息对于用户来说很难进行快速阅读,也会造成在警告框中需要滚动才能查看完整 使用句式大小写(sentence-style capitalization),并配以合适的结束语句符号。可能的时候,可以使用一个整句 注意:如有必要,iOS会缩短你的消息以便能在各种通知发送样式下显示;为了最好的效果,你不应主动缩减你的消息。 保持小气泡的内容是最新的。当用户注意到新信息时,即时更新小气泡非常重要,这样用户就不会觉得收到了额外的通知。注意,当小气泡为0时也会移除通知中心中所有对应的通知项。 重要:不要使用小气泡做通知以外的用途。记住,用户能够关闭应用的小气泡,所以你无法确定他们一定能看到小气泡中的内容。 当收到通知时,提供用户可以选择听到的音效。当人们没有在看屏幕的时候,可以通过音效获取他们的注意。例如,日历应用可能会在显示警告框的同时播放一个音效来提醒人们一个即将到来的事件。再如,协作任务管理应用可能会在小气泡更新时播放一个音效来告知某个远程协同的同事已经完成了某个任务。 你可以提供自定义的音效,或者使用内置的警告音。如果你创建了自定义音效,请确保它是简短的、有特色的并且是经由专业制作的。(想要了解更多关于音效的技术需求,请参阅Local and Remote Notification Programming Guide中的Preparing Custom Alert Sounds。)注意,当通知发送后,你无法以编程方式来触发设备的震动,因为用户对于警告框是否伴随震动拥有支配权。 3.10 社交媒体(Social Media) 人们会期望在任何场景下都可以使用他们喜爱的社交媒体帐号。iOS以人们喜欢的方式将社交媒体的交互与你的应用进行了整合。 注意:当用户点击动作按钮时,他们会得到一个如上图的动作视图控制器。想要了解更多关于这个视图控制器的内容,请参见Activity View Controller。 动作视图控制器的中间一行显示了用户启用的和系统提供的分享应唨扩展。想要了解更多关于设计分享扩展的内容,请参见 Share and Action Extensions。 考虑在你的应用中为用户提供一种简便的方式来撰写邮件。用户有可能会启用分享扩展以便能在任何地方都可以发送内容。但是你也可以使用系统提供的撰写视图控制器来呈现给用户,他们可以在其中进行编辑操作。你可以在显示给用户进行编辑之前,预先加载具有自定义内容的撰写视图(在你呈现给用户之后,只有用户可以编辑这些自定义内容)。想要了解更多关于社交框架(Social framework)的编程界面,包括SLComposeViewController类,请参见 Social Framework Reference. 如果可能,避免要求用户登录进入一个社交媒体账户。社交框架(Social framework)会和帐号框架(Accounts framework)一起来支持一个单点登录模式,所以你可以获得授权来访问用户的帐号,而无需要求他们来重新授权。如果用户还没有登录进入一个帐号,你可以显示UI来让他们进行登录。 3.11 iCloudiCloud 可以让用户随时随地用不同的设备访问他们想要的内容。将iCloud集成到应用中,用户不用进行同步操作就可以在不同场景下使用不同的设备访问并编辑私人信息。 为了提供这种体验,你可能需要重新检查你的应用中现有的信息,尤其是用户自建内容的存储、访问和展示方式。想要了解如何使用iCloud,请参考iCloud Design Guide. iCloud用户体验的一个基本方向是透明性:理想情况下,用户不需要知道他们的信息存储在什么地方,也不需要去思考当前浏览的信息是哪个版本的。以下几点可以帮助你创建用户期望的iCloud体验。 如果可能,让用户方便地在你的应用中启用iCloud。在iOS设备上,用户可以在设置中登录iCloud账户,因此多半用户会期望应用可以自动启用iCloud。但是如果你觉得用户可能需要自主选择是否使用你应用的云服务,你可以在用户第一次进入应用时提供一个简单的选项来进行设置。大多数情况下,这个选项应该为:是否将所有内容上传到云端。 尊重用户的iCloud空间。一定要记住iCloud空间是用户花钱买来的有限资源。你应该使用iCloud来存储用户自己创建和可理解的信息,避免将可再生的应用资源和内容存储在云端。同样要记住,当用户登录了iCloud账户时,你的应用的文件夹内容也会自动备份到云端。所以为了节省用户云端空间,你最好只挑选必要的信息存储于文件夹中。 避免让用户自己选择在iCloud上存储哪些文件。一般地,用户会期望他们在意的所有信息都能够通过iCloud访问到。实际上大多数用户都不需要进行个人文件存储的管理,所以你的应用也可以不用考虑这个问题。为了提供更好的用户体验,你可能想要重新构建处理和展示内容的方式,这样就可以给用户提供更多的文件管理功能。 决定哪种类型的信息需要存储在云端。除了存储用户自建的文件和内容,你还可以存储少量的其他信息在云端,例如用户当前的状态,用户的偏好设置等等。你可以使用iCloud的关键值存储来保存这类信息。例如,用户使用你的应用看了一个杂志,你可以使用iCloud的关键值存储来保存用户浏览到的位置,这样用户在别的设备上重新打开这个杂志时就能从上次离开的地方继续浏览了。 如果你使用iCloud的关键值存储来保存用户的偏好设置,确保用户在每个设备上都是想这样设置的。例如,有些偏好设置在工作环境中比在家里要更好用。在某些情况下,将偏好设置保存在应用服务器上要比保存在云端更合理,这样偏好设置就不会受iCloud的限制。 确保iCloud无法使用时应用的行为是合理的。例如,用户退出iCloud账户,关闭应用的iCloud或者进入飞行模式时,iCloud都是无法使用的。在这些情况下,用户都进行了某些操作来禁止iCloud服务,所以你的应用可以不用再进行提醒。但是,需要告诉用户在打开iCloud之前,当前做的修改在其他设备上都无法看到。 避免给用户创建“本地”文件的选项。不管你的应用是否支持iCloud,都不应该给用户提供因设备而区分的文件系统。相反,你应该希望用户关注通过iCloud访问文件的普适性。 在合适的时候自动更新信息。最好不需要用户来确认他们正在访问的是最新的内容。但是,也需要在用户设备存储空间和带宽限制之间做出平衡。如果你的用户要使用非常大的文件,那么让他们自己选择是否要从云端下载一个更新的文件可能更合适。如果需要这样做的话,可以设计一种方式来指出当前在云端有一个该文件的最新版本。当用户选择更新时,如果下载时间较长最好给用户明显的反馈。 告知用户删除某文件的后果。当用户从有iCloud服务的应用上删除文件的时候,这个文件同样会从用户的iCloud账号和其他设备上删除。所以最好在执行删除操作之前告知用户删除的后果,让用户进行确认。 必要时尽可能早地告知用户冲突问题。使用iCloud编程接口,你需要在不打扰到用户的情况下解决大多数不同版本之间的冲突问题。但在有些情况下,你需要尽可能早地检测出冲突问题来避免用户在错误版本上浪费太多时间。你需要设计一种自然的方式来告诉用户有冲突存在,接着给用户提供方便的方式来区分不同版本以及做出决策。 确保在搜索中包括用户在云端的信息。使用iCloud的用户趋向于认为云端的信息是普遍可访问的,所以他们会期望搜索结果中也有云端的信息。如果你的应用允许用户搜索他们的信息,确保你使用了将搜索扩展到iCloud账户的接口。 本章英文原文访问地址:iOS Human Interface Guidelines 译文地址:腾讯ISUX 转载请注明:学ui网 » [ISUX译]iOS 9人机界面指南(三):iOS 技术
[ISUX译]iOS 9人机界面指南(二):设计策略 雪糕 2015.11.09 文章索引 2.1 设计原则(Design Principles) 2.1.1 美学完整性(Aesthetic Integrity) 2.1.2 一致性(Consistency) 2.1.3 直接操作(Direct Manipulation) 2.1.4 反馈(Feedback) 2.1.5 隐喻(Metaphors) 2.1.6 用户控制(User Control) 2.2 从概念到产品(From Concept to Product) 2.2.1 定义应用(Define Your App) 2.2.2 为任务量身订制界面(Tailor Customization to the Task) 2.2.3 原型 & 迭代(Prototype & Iterate) 2.3 案例学习:从桌面到iOS(Case Study: From Desktop to iOS) 2.3.1 iPad版Keynote应用(Keynote on iPad) 2.3.2 iPhone版邮件应用(Mail on iPhone) 2.3.3 iOS系统内的网页内容(Web Content in iOS) 译者注:本文译自苹果官方人机界面指南 iOS Human Interface Guidelines (2015年10 月21日),由腾讯ISUX设计师翻译整理,非发
[ISUX译]iOS 9人机界面指南(一):UI设计基础 raina 2015.10.29 文章索引 1.1 为iOS而设计(Designing for iOS) 1.1.1 设计跟随内容 (Defer to Content) 1.1.2 保证清晰(Provide Clarity) 1.1.3 用深度层次来进行交流(Use Depth to Communicate) 1.2 iOS应用解析(iOS App Anatomy) 1.3 适应性和布局(Adaptivity and Layout) 1.3.1 为自适应而开发(Build In Adaptivity) 1.3.2 在不同环境提供良好体验(Provide a Great Experience in Each Environment) 1.3.3 使用布局来沟通(Use Layout to Communicate) 1.4 启动与停止(Starting and Stopping) 1.4.1 即时启动(Start Instantly) 1.4.2 时刻准备好停止(Always Be Prepared to Stop) 1.5 导航(Navigation) 1.6 模态情境(Modal Contexts) 1.7 交互性与反馈(Interactivity and Feedback) 1.7.1 可交互元素吸引用户点击(Interactive Elements Invite Touch) 1.7.2 用户所知道的标准手势(Users Know the Standard Gestures) 1.7.3 反馈有助于理解(Feedback Aids Understanding) 1.7.4 输入信息的方式要简单(Inputting Information Should Be Easy) 1.8 动画(Animation) 1.9 品牌推广(Branding) 1.10 颜色与字体(Color and Typography) 1.10.1 色彩有助于增进沟通(Color Enhances Communication) 1.10.2 优秀的排版提供清晰的传达(Great Typography Enables Clear Communication) 1.11 图标和图形(Icons and Graphics) 1.11.1 应用图标(The App Icon) 1.11.2 小图标(Small Icons) 1.11.3 图形(Graphics) 1.12 术语和措辞(Terminology and Wording) 1.13 与iOS的整合(Integrating with iOS) 1.13.1 正确使用标准UI元素(Use Standard UI Elements Correctly) 1.13.2 弱化文件和文档处理(Downplay File and Document Handing) 1.13.3 必要时提供可配置选项(Be Configurable If Necessary) 1.13.4 充分利用iOS技术(Take Advantage of iOS Technologies) 译者注:本文译自苹果官方人机界面指南 iOS Human Interface Guidelines(2015年10 月21日),由腾讯ISUX设计师翻译整理,非发
读后有感 - UI设计师必知:线框图、原型和视觉稿 太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循“署名-非商业用途-保持一致”创作公用协议 转载请保留此句:太阳火神的美丽人生 - 本博客专注于 敏捷开发及移动和物联设备研究:iOS、Android、Html5、Arduino、pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作。 线框图:草图,重点在交互的目标上,即业务如何交互完成,是不依赖于平台的,如同数据库设计中的概念模型。这一部分应该是上一层次业务流的具体化,也仅是具体化而已。 原型图: 针对效果图来说,它叫原型图,但针对草图来说,它是选择了特定平台的界面组件的,或可叫适配图或选型图,选择适当的组件来承载交互所要传递的数据。 视觉稿: 又叫效果图,这是美工所要做的事情,在保证交互需求以及承载数据的组件的选择前提下,如何美观地布局、设计、美化,产生良好的美感用户体验。 以上有否与以下转载文章内容不同之处? 当然,我是通篇拜读过的,好几天过去了,有点忘记了, 以上内容也是结合留下的记忆结合实践总结出来的, 有不妥的话,这个欢迎交流,谢谢。 UI设计师必知:线框图、原型和视觉稿 经验 陌北默 2014-07-31 6467浏览 0评论 UI设计大师们常常画漂亮的线框图,原型,视觉稿,超赞,超令人感叹。 但是你分得清这3个概念,它们的用途吗?是否也常常傻傻分不清呐,今天就彻底的了解下它们吧。 前言:本文为译稿 1,对三个常见概念进行了剖析。有删节,部分段落重组。 背景:两三年以前,我发现很多搞信息技术的朋友们(非设计师)交付设计时,用着上面列出的词汇。他们以为线框图 (Wireframe)、原型 (Prototype) 和视觉稿 (Mockup) 是一个东西:表达自己创意的线框手绘设计稿。 混淆概念便带来麻烦:他们分不清用户体验设计师的作品,常常感到困惑。「这按钮他妈的怎么点不了?」「我不知道这个地方可以按啊!」类似这样的问题在用户体验的项目中屡见不鲜。误把线框图当成原型,有点像建筑里分不清蓝图(指导如何建筑施工的方案)和演示厅。你完全可以试着在演示厅里小住一会(它的妙处就在于能直观传达房间之美),但你没法舒服地躺在蓝图上;蓝图不过是一张纸罢了。 在建筑学上,演示厅和蓝图服务于不同的交流对象: 蓝图,即施工方案,详细描述该如何建造建筑 演示厅,给未来的居民提供测试和体验的机会 演示厅和蓝图的共同之处是,它们都代表着最终的产品,即建筑(房屋)。线框图、原型和视觉稿亦是如此,这些文档都是最终产品的不同展现方式。 不管你信不信,我向用户体验设计团队授课的第一件事,就是告诉他们分清这三个概念。因为,这实在太重要了。 接下来,让我们详细讨论三者的区别,以后你就懂得在什么样的场合下用什么词汇了。 线框图 1、什么是线框图? 线框图 (Wireframe) 是低保真的设计图,当明确表达: 内容大纲(什么东西) 信息结构(在哪) 用户的交互行为描述(怎么操作) 线框图不仅仅是无意义的线和框的集合;好吧,虽然看上去是的,囧。你可以把线框图理解为设计图的骨干与核心,它承载着最终产品所有重要的部分。 线框图可以帮你平衡保真度与速度。绘图时不用在意细枝末节,但必须表达出设计思想,不能漏掉任何重要的部分。就像给项目以及一起协作的团队成员(开发工程师、视觉设计师、文案作者和项目经理),开辟了一条辅助理解设计的通道。说得再明白点,你是在设计城市地图,地图上能展现出每一条街道,只不过绘制上做了简化。看地图能看出城市的框架,但无法一览城市的美感。 绘制线框图,重点是「快」。大多数时间请和团队成员沟通,多做思考。审美上的视觉效果则应当尽可能简化。黑白灰是经典用色,你也可以用蓝色代表超链接。如果你在准备线框图时花了大把时间(比如,选择图标、上传图片),请换个简单的方式(比如,使用占位符:一个画×的图片,再加上合适的描述文字)。我们习惯把线框图称为低保真设计图。 切记,好的线框图能像水晶一样,清晰明了地表达设计创意,在成员中无缝传达其思想。 2、何时使用线框图 线框图常常用来作项目说明。鉴于其静态设计,一次只能通过一张界面演示交互,因此,务必附上说明。(只要有必要,简短描述或附在复杂的技术文档里,都成) 也因为绘制起来快速、简单,它也经常用于非正式场合,比如团队内部交流。要是开发问起一个东西怎么做,回复时不妨附上一张迅速绘制的线框图。线框图难以充当用户测试的材料;倒也能收集些反馈,如果你更关心用户意见,而非测试方法。 虽然近些年遭人闲话,但线框图在整个设计过程中发挥着惊人的效果,在复杂项目的初始阶段必不可少。 原型 1、什么是原型 原型 (Prototype) ,常常和线框图混淆,是中等保真的设计图,代表最终产品,模拟交互设计。原型允许用户: 从界面上,体验内容与交互 像最终产品一样,测试主要交互 原型应该尽可能模拟最终产品,就算长得不是一模一样(绝对不能是灰色线框设计)。交互则应该精心模块化,尽量在体验上和最终产品保持一致。 原型背后的逻辑不要依赖交互形式。减少制作原型的成本,加快开发速度。 2、何时使用原型 原型常用于做潜在用户测试。在正式介入开发阶段前,以最接近最终产品的形式考量产品可用性。 如你所想,原型一般难以成为上好的文档;因为它得让「读者」费一些力气来理解界面。但从另一个角度来看,作为界面,原型的直观和易懂倒使它成为最高效的设计文档。 请注意,相对其它交流媒介,原型成本高、费时。我建议绘制原型后,能在接下来的开发阶段复用起来。(唔,你可能得亲自编写 HTML 和样式表代码)对于简单的项目来说,相当好用。(一旦考虑「复用」,必将增加绘制成本,偏离原型的初衷。并不建议复用。——译者注) 若设计得当,与用户测试相结合,原型是物超所值的。 视觉稿 1、什么是视觉稿 视觉稿 (Mockup) 是高保真的静态设计图。通常来说,视觉稿就是视觉设计的草稿或终稿。优秀的视觉稿: 表达信息框架,静态演示内容和功能 帮助团队成员以视觉的角度审阅项目 人们常年分不清什么是视觉稿,什么是线框图,和某些软件公司的名字不无关系。噗~ 2、何时使用视觉稿 如果你想从股东手里获得认可,视觉稿尤其管用;收集用户反馈也相当好使。 把它添到设计文档里去吧,绝对是画龙点睛之笔。 总结 原文地址:http://designmodo.com/wireframing-prototyping-mockuping/译文地址:http://cuikai-wh.com/blog/2460
泱泱大中华,美丽我的家 - 俗晒网速,感受幸福 太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循“署名-非商业用途-保持一致”创作公用协议 转载请保留此句:太阳火神的美丽人生 - 本博客专注于 敏捷开发及移动和物联设备研究:iOS、Android、Html5、Arduino、pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作。 还记得那一篇转载《2015中国宽带目标完成:8M以上占56%》, 今天,2015 结束还有8天, 刚刚在上个月联通全面撤换光纤,由 4M 宽带提速到 12M 光纤,幸福生活从此开始了。 由于两年套餐到期,本月又去换套餐,发现继续办 12M 光纤近 140元的月费,还不如 20M 近 160元的套餐。 有人说我不识数,不,这里有个事儿忘透露,20M的免两部手机和固话两年的来电显示,所以差这20块钱就抹平了。 这回不会再说我傻了吧,俺小学没毕业,不会算帐儿,但俺机道去问啊! 自黑真有趣。。。 好了,看看俺的速度吧 下载速度2.3M,那么按早些年宽带安装员的算法,乘上10,那就是23M,我去的了,我赚了3M耶,oh year! 泰山不是堆的,火车不是推的, 再回看前面地址中转载的内容,是不是没有吹呀, 这国家强,生活咋就这么幸福呢呀! 注:本文俗晒享受幸福生活片断,拒绝评论,感谢 CWTV/CXTV/CTYV/WTCV, 哦了个去呀地,我这儿还没写完呢呀,你咋就下完了呐! 。。。 我们地大中国呀, 好大的一个家, 经过那个多少那个风吹和雨打, 我们地大中国呀, 好大的一个家, 永远那个永远美丽又强大。 天天这心情,啥病都没了,不信,你也试试。 想想为了一颗蛋而欢呼的那个广告,愚意颇深,让我们一起作阿 Q 吧! 注在最前面的一句话:歉俗勿看,谢谢。
占位 自动转载器那小子,你转完了没? 转完了,我开写了哈! Block,就两个事儿,一个是引用,一个是实例,除了实现处,其它地方都是引用。 以此思路,再继续看看引用和实现的定义方式吧。 参考官方文档。 后补 ========== 最近在忙一件大事,好事有结果了,而确没有明确结果。 今天又用到 block 构建复用架构,发现短短的一两周时间,就有点忘记 block 的用法了。 看来真得整理一下了。 ========== 引用 和 实现 引用即名称,返回类型是要有的 实现可以没有,它的内部返回值就是返回类型,再有它所处的位置也决定了它的类型,不是员工,是老板! (mac 版啥时侯能直接粘帖图片呢!) 细想想,和 C 语言的指针有啥区别? 定义带 block 参数的消息: - (void)interfaceCall:(NSString *)interfaceType params:(id)paramDic businessSuccess:(void(^)(AFHTTPRequestOperation *httpOp))successHandler businessFail:(void (^)(AFHTTPRequestOperation *httpOp))failHandler netFail:(void (^)(AFHTTPRequestOperation *httpOp))netFailHandler { } 发送带 block 参数的消息: NSMutableDictionary *paramDic = [NSMutableDictionary dictionaryWithObjectsAndKeys:username, @"username", password, @"password", studentid, @"studentid", email, @"email", nil]; [self interfaceCall:@"regist" params:paramDic businessSuccess:^(AFHTTPRequestOperation *httpOp) { } businessFail:^(AFHTTPRequestOperation *httpOp) { } netFail:^(AFHTTPRequestOperation *httpOp) { }];
合理利用“泛在式” - 手机促使人群分化 太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循“署名-非商业用途-保持一致”创作公用协议 转载请保留此句:太阳火神的美丽人生 - 本博客专注于 敏捷开发及移动和物联设备研究:iOS、Android、Html5、Arduino、pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作。 过去,我们坐在教室里,在课堂上学习书本知识。 而书本知识,只是知识的一个抽象,只是很小的一部分。 那么我们可以这样说, 用整块的时间在学习散乱的知识,是这样的,好些学科,一样学一点儿。 这是以学习为学习目的的学习。 而如今,人们步入社会后,时间很少,大家总想多一项技能, 这样,就会为了这一个目标,抓紧任何时间去学习、了解、实践、思考、总结。 那么我们可以这样说, 用零散的时间在学习整个的知识,是这样的,就这一项技能,好多块时间从好多个方面去了解、理解和掌握、练习。 这是以内容为目的的学习,学习这个行为是自我感知很少的,而能感知到的是学到的内容,想要学到的内容。 佛说布施不可有布施之象, 也即学习不能有学习的形态出现在脑海里, 脑海里出现的应该是学习的内容才对, 如果整天脑海里出现的都是自已学习的苦逼样子, 那么这种学习怎么样也不会苦到哪儿去的, 因为学习的苦,在于旁观者看来是苦,而在于学习者本身感受,是美哉、爽哉、意犹未尽,哪里有什么苦。 真正的苦,在于为了做到这个样子而坚持着,这才叫苦。 回归到泛在式,那么泛在式学习,即上面所述。 然而,任何事物都有两面性,泛在式同样是把双刃剑。 泛在式学习以手机这类移动随身设备而兴起,以前随身带书的人,其实也是泛在式, 只不过那时泛在式的人很少,都被当成老学究,大傻子,其实我很献幕那样的快乐生活,傻即是福! 而如今,由于手机这类移动设备以及配备的花式各样的 App,更配以没事儿就响的通知,这个真不亚于当年的短信增值时代乐章,整整响了十年。 尤以微信、QQ为首,更配以打车、网购、订餐趋之, 如今俺在上班时基本不敢把手机调出声音来, 不然的话,一响就忍不住要看一眼,一上午啥都别想专心做, 这就是所说的泛在式,是我们被人家 App 给泛在式了,借用的是我们的手机,跑着的是我们的流量,唔呼衰哉。 对于泛在式,我想说,合理地主动地去支配、管理, 要泛在式地做一些事情,而不要被泛在式了。 好了,书说到此,其实好像跟什么手机呀、什么 App 呀,都没啥子关系, 关键还是在于人自已,是否能很好地自我管理, 而泛在式的出现,使这一能力出现了分水岭, 越能自我管理的人群,越能得到泛在式的优势和提升; 越缺失自我管理的人群,越容易被泛在式,随波逐流,浪费青春。 两极分化从此开始了,当然这也是业余8小时最大的区别, 更有甚者,上班8小时都会受到影响, 尚不知睡觉8小时如何。。。 好了,有感一发,您也就一看,莫做过多解读,字面上的内容足以传递充足的信息。
iOS夯实:ARC时代的内存管理 什么是ARC Automatic Reference Counting (ARC) is a compiler feature that provides automatic memory management of Objective-C objects. Rather than having to think about retain and release operations [^1] [^1]: Transitioning to ARC Release Notes ARC提供是一个编译器的特性,帮助我们在编译的时候自动插入管理引用计数的代码。最重要的是我们要认识到ARC的本质仍然是通过引用计数来管理内存。因此有时候如果我们操作不当,仍然会有内存泄露的危险。下面就总结一下ARC时代可能出现内存泄露的场景。 内存泄露类型 循环引用 基于引用计数的内存管理机制无法绕过的一个问题便是循环引用(retain cycle)(Python同样也采用了基于引用计数的内存管理,但是它采用了另外的机制来清除引用循环导致的内存泄露,而OC和Swift需要我们自己来处理这样的问题[^2]) 对象之间的循环引用:使用弱引用避免 block与对象之间的循环引用: 会导致Block与对象之间的循环引用的情况有: self.myBlock = ^{ self.someProperty = XXX; }; 对于这种Block与Self直接循环引用的情况,编译器会给出提示。 但是对于有多个对象参与的情况,编译器便无能为力了,因此涉及到block内使用到self的情况,我们需要非常谨慎。(推荐涉及到self的情况,如果自己不是非常清楚对象引用关系,统一使用解决方案处理) someObject.someBlock = ^{ self.someProperty = XXX; }; //还没有循环引用 self.someObjectWithBlock = someObject; // 导致循环引用,且编译器不会提醒 解决方案: __weak SomeObjectClass *weakSelf = self; SomeBlockType someBlock = ^{ SomeObjectClass *strongSelf = weakSelf; if (strongSelf == nil) { // The original self doesn't exist anymore. // Ignore, notify or otherwise handle this case. } [strongSelf someMethod]; }; 我们还有一种更简便的方法来进行处理,实际原理与上面是一样的,但简化后的指令更易用。 @weakify(self) [self.context performBlock:^{ // Analog to strongSelf in previous code snippet. @strongify(self) // You can just reference self as you normally would. Hurray. NSError *error; [self.context save:&error]; // Do something }]; 你可以在这里找到@weakify,@strongify工具:MyTools_iOS [^2]: How does Python deal with retain cycles? NSTimer 一般情况下在action/target模式里 target一般都是被weak引用,除了NSTimer。 + (NSTimer *)timerWithTimeInterval:(NSTimeInterval)seconds target:(id)target selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)repeats 在官方文档中: targetThe object to which to send the message specified by aSelector when the timer fires. The timer maintains a strong reference to this object until it (the timer) is invalidated. Timer Programming Topics : A timer maintains a strong reference to its target. This means that as long as a timer remains valid, its target will not be deallocated. As a corollary, this means that it does not make sense for a timer’s target to try to invalidate the timer in its dealloc method—the dealloc method will not be invoked as long as the timer is valid. 举一个例子,一个Timer的Target是ViewController. 这个时候,如果我们是在dealloc方法里让timer invalidate,就会造成内存泄露. 事实上,timer是永远不会被invalidate.因为此时VC的引用计数永远不会为零。因为Timer强引用了VC。而因为VC的引用计数不为零,dealloc永远也不会被执行,所以Timer永远持有了VC. 因此我们需要注意在什么地方invalidate计时器,我们可以在viewWillDisappear里面做这样的工作。 Swift's ARC 在Swift中,ARC的机制与Objective-C基本是一致的。 相对应的解决方案: 对象之间的循环引用:使用弱引用避免 protocol aProtocol:class{} class aClass{ weak var delegate:aProtocol? } 注意到这里,aProtocol通过在继承列表中添加关键词class来限制协议只能被class类型所遵循。这也是为什么我们能够声明delegate为weak的原因,weak仅适用于引用类型。而在Swift,enum与struct这些值类型中也是可以遵循协议的。 闭包引起的循环引用: Swift提供了一个叫closure capture list的解决方案。 语法很简单,就是在闭包的前面用[]声明一个捕获列表。 let closure = { [weak self] in self?.doSomething() //Remember, all weak variables are Optionals! } 我们用一个实际的例子来介绍一下,比如我们常用的NotificationCenter: class aClass{ var name:String init(name:String){ self.name = name NSNotificationCenter.defaultCenter().addObserverForName("print", object: self, queue: nil) { [weak self] notification in print("hello \(self?.name)")} } deinit{ NSNotificationCenter.defaultCenter().removeObserver(self) } } Swift的新东西 swift为我们引入了一个新的关键词unowned。这个关键词同样用来管理内存和避免引用循环,和weak一样,unowned不会导致引用计数+1。 那么几时用weak,几时用unowned呢? 举上面Notification的例子来说: 如果Self在闭包被调用的时候有可能是Nil。则使用weak 如果Self在闭包被调用的时候永远不会是Nil。则使用unowned 那么使用unowned有什么坏处呢? 如果我们没有确定好Self在闭包里调用的时候不会是Nil就使用了unowned。当闭包调用的时候,访问到声明为unowned的Self时。程序就会奔溃。这类似于访问了悬挂指针(进一步了解,请阅读Crash in Cocoa) 对于熟悉Objective-C的大家来说,unowned在这里就类似于OC的unsafe_unretained。在对象被清除后,声明为weak的对象会置为nil,而声明为unowned的对象则不会。 那么既然unowned可能会导致崩溃,为什么我们不全部都用weak来声明呢? 原因是使用unowned声明,我们能直接访问。而用weak声明的,我们需要unwarp后才能使用。并且直接访问在速度上也更快。(这位国外的猿说:Unowned is faster and allows for immutability and nonoptionality. If you don't need weak, don't use it.) 其实说到底,unowned的引入是因为Swift的Optional机制。 因此我们可以根据实际情况来选择使用weak还是unowned。个人建议,如果无法确定声明对象在闭包调用的时候永远不会是nil,还是使用weak来声明。安全更重要。 延伸阅读:从Objective-C到Swift 参考链接:shall-we-always-use-unowned-self-inside-closure-in-swif what-is-the-difference-between-a-weak-reference-and-an-unowned-reference
iOS夯实:内存管理 最近的学习计划是将iOS的机制原理好好重新打磨学习一下,总结和加入自己的思考。 有不正确的地方,多多指正。 目录: 基本信息 旧时代的细节 新时代 基本信息 Objective-C 提供了两种内存管理方式。 MRR (manual retain-release) 手动内存管理这是基于reference counting实现的,由NSObject与runtime environment共同工作实现。 ARC (Automatic Reference Counting)自动引用技术ARC使用了基于MBR同样的reference counting机制,区别在于系统在编译的时候帮助我们插入了合适的memory management method。 Good Practices能有效的避免内存相关问题 基于内存,主要有两种错误 清空或覆盖了还在使用的内存这种清空通常会导致应用崩溃,甚至用户数据遭到改写 没有清空已经不需要的内存会导致内存泄露会导致系统性能下降,应用遭到系统终止 实际上,我们不应该只从reference counting的角度来管理内存,这样会让我们纠结于底层细节。我们应该站在object ownership and object graphs的角度来管理内存 可以用一幅这样的图来阐明: 旧时代的细节 一.基本内存管理准则 基本的内存管理准则:Cocoa为我们提供了这些准则 You own any object you create我们通过名字前缀为“alloc”, “new”, “copy”, or “mutableCopy”的方法创建对象 You can take ownership of an object using retain通过retain来获取对象的ownership,使用retain主要有两种场景 在accessor method 或 init method。来获取你想存储的对象的所有权 在某些场景里避免一个对象被移除,我们可以对它进行retain When you no longer need it, you must relinquish ownership of an object you own当我们需要释放一个对象所有权时,我们通过对它发送release或autorelaese消息。 You must not relinquish ownership of an object you do not own不要释放你没有拥有的对象所有权 关于dealloc: NSObject为我们提供了一个dealloc方法,当对象被销毁时,系统会自动调用它。这个方法的作用主要是清空对象自身内存与它所持有的资源。例如: @interface Person : NSObject @property (retain) NSString *firstName; @property (retain) NSString *lastName; @property (assign, readonly) NSString *fullName; @end @implementation Person - (void)dealloc [_firstName release]; [_lastName release]; [super dealloc]; } @end 需要强调的是:永远不要自己调用dealloc方法在dealloc的最后需要调用super class的dealloc 二.实践中的内存管理准则 1. 使用存取方法来简化内存管理。 如果代码中都是一堆retain release,必然不是一个好的情况。在存取方法里面进行retain和release的操作能够简化内存管理。例如: - (void)setCount:(NSNumber *)newCount { [newCount retain]; [_count release]; // Make the new assignment. _count = newCount; } 2. 使用存取方法来设置Property value 对比如下代码,第一种使用了存取方法来设置,第二种直接对实例变量操作。显然我们应该采用第一种,使用第二种情况,简单的情况还好,如果情况一旦复杂,就非常容易出错。并且直接对实例变量操作,不会引发KVO通知。 - (void)reset { NSNumber *zero = [[NSNumber alloc] initWithInteger:0]; [self setCount:zero]; [zero release]; } - (void)reset { NSNumber *zero = [[NSNumber alloc] initWithInteger:0]; [_count release]; _count = zero; } 3. 不要在初始化方法和dealloc方法中使用Accessor Methods 苹果在《Advanced Memory Management Programming Guide》指出: Don’t Use Accessor Methods in Initializer Methods and deallocThe only places you shouldn’t use accessor methods to set an instance variable are in initializer methods and dealloc. To initialize a counter object with a number object representing zero, you might implement an init method as follows: - init { self = [super init]; if (self) { _count = [[NSNumber alloc] initWithInteger:0]; } return self; } 唯一不需要使用Accessor Methods的地方是initializer和dealloc. 在苹果官方文档中没有解释为什么。经过一番查阅后,最主要的原因是此时对象的状况不确定,尚未完全初始化完毕,而导致一些问题的发生。 例如这个类或者子类重写了setMethod,里面调用了其他一些数据或方法,而这些数据和方法需要一个已经完全初始化好的对象。而在init中,对象的状态是不确定的。 举个例子,一个子类重写了set方法,在里面进行了一些子类特有的操作,而此时如果父类在init直接使用Accessor Methods,就会导致问题的发送。 其它一些问题还有,像会触发KVO notification等。[^1] [^2] 总之,记住在开发中记住这个principle最重要。 在开发中,我发现还是有人因为疏忽或不知道而直接在init方法里面使用self.property = XXX来进行赋值,留下了一些隐患。Swift去除了直接和instance variable打交道的途径。统一使用property进行管理,并对init进行严格的规定,提升了安全性,解决了人为因素导致的错误。欢迎对Swift有兴趣的同学继续阅读。从Objective-C到Swift [^1]: stackoverflow[^2]: objc-zen-book 4. 使用弱引用来避免引用环 我们都知道在ARC,弱引用通过声明property的attribute为weak来实现。 而在MRR中则是通过引用对象,但是不retain它来实现(A weak reference is a non-owning relationship where the source object does not retain the object to which it has a reference) 在Cocoa中,典型需要使用弱引用的有delegate,data source, notification observer 我们需要注意处理好弱引用对象,如果对已经被销毁的对象发送信息,则会导致crash.通常被弱引用的对象当它将被销毁时,负责通知其它object. 像notification center保存了observer的弱引用,因此当被弱引用observer准备结束生命周期时,observer需要通知notification center,unregister自己。 4. 不要让你正在使用的对象被移除 观察以下两种代码,第一种因为没有retain,对象可能会被移除,而第二种是正确的写法 heisenObject = [array objectAtIndex:n]; [array removeObjectAtIndex:n]; // heisenObject could now be invalid. heisenObject = [[array objectAtIndex:n] retain]; [array removeObjectAtIndex:n]; // Use heisenObject... [heisenObject release]; 5. Collections类拥有其收集的的对象的所有权 例如NSArray,dictionary等。他们负责其收集的对象的所有权,因此我们不需要retain存进去的对象。例如下面的allocedNumber就不需要retain了。 for (i = 0; i < 10; i++) { NSNumber *allocedNumber = [[NSNumber alloc] initWithInteger:i]; [array addObject:allocedNumber]; [allocedNumber release]; } 6. 最后,以上这些ownership policy是基于retain count实现的 当你创建一个对象,它的retain count为1。 当你对一个对象发送retain信息,它的retain count +1 当你对一个对象发送release信息,他的retain count -1当你对一个对象发送autorelease信息,在当前autorelease pool block结束时,retain count -1 当一个对象的retain count 降至0,它就被dealloced了。 三.使用Autorelease Pool block autorelease pool 为我们提供了一个机制,避免当我们解除一个对象所有权时,对象被立刻销毁(例如从一个方法里返回一个对象) 一个autorelease pool block对象是这样子的: @autoreleasepool { // Code that creates autoreleased objects. } 在block的最后,所有收到过autorelease消息的对象都会接收到release消息。 在ARC的新时代里面,autorelease pool block主要用于处理避免内存峰值,只是我们不需要再手动添加autorelease的代码了 例如以下这个例子(有点生编硬造,主要为了阐明一下)如果我们没有添加autoreleasepool,我们最后可需要释放10000*10000个对象,而不是每次循环都分别释放掉10000个对象 - (void)useALoadOfNumbers { for (int j = 0; j < 10000; ++j) { @autoreleasepool { for (int i = 0; i < 10000; ++i) { NSNumber *number = [NSNumber numberWithInt:(i+j)]; NSLog(@"number = %p", number); } } } } 新时代 详情亲看:iOS夯实:ARC时代的内存管理
iOS 开发中的争议(一) Mar 15th, 2015 打算分享一些有争议的话题,并且表达一下我的看法。这是该系列的第一篇,我想讨论的是:类的成员变量应该如何定义? 在 Objective-C 的语言的早期,类的私有成员变量是只能定义在 .h 的头文件里面的。像如下这样: 1 2 3 4 @interface ViewController : UIViewController { @private NSInteger _value; } 之后,苹果改进了 Objective-C,允许在 .m 里面添加一个特殊的匿名 Category,即没有名字的 Category,来实现增加类的成员变量。像如下这样: 1 2 3 4 5 @interface ViewController () @property (nonatomic) NSInteger value; @end 这样的好处是,这些变量在头文件中被彻底隐藏起来了,不用暴露给使用者。 接着,在 2013 年的 WWDC 中,苹果进一步改进了 Objective-C,允许在 .m 的@implementation 中直接添加类的私有成员变量。像如下这样: 1 2 3 @implementation ViewController { NSInteger _value; } 于是,大家对于如何定义私有的成员变量上就产生的分歧。许多人喜欢用匿名的 Category 的方式来定义私有成员变量。但是,我个人更推荐在 @implementation 中直接添加类的私有成员变量。下面我做一些解释。 ---------- 部分转载,完整原文可点击标题跳过去,以下是转载标注内容: 到这里,我知道了,这个我确实见过,但不知为什么在要实现中声明变量,因为 2013 年我正埋头 OpenGLES 的苦海中,就是那两年对这个领域的好奇,让我疲惫不堪,并且在 iOS 的发展路上落下了几课,两年才缓过来,包括身体和技术结构。
不要在init和dealloc函数中使用accessor Aug 10th, 2011 Objective-C 2.0 增加了 dot syntax,用于简单地调用成员变量的 accessor。相当于 java 的 getter 和 setter。因为正常情况下,写一个 accessor 对于初学者来说,还是挺容易犯错的。比如有一个 NSString * 的成员变量叫 name。一个错误的写法是: 1 2 3 -(void)setName:(NSString *)newName { name = newName; } Java 同学肯定想不通上面的代码有什么错吧?原因是 Objective-C 需要自己负责内存的释放。所以需要在改变 reference 之前,将原对象 release,对新的对象,也需要 retain 一下,代码就改成这样: 1 2 3 4 -(void)setName:(NSString *)newName { [name release]; name = [newName retain]; } 初学者可能以为这样就对了,其实还是有错,如果 newName 和 name 的指向的是同一个对象,并且这个对象 retain count 只有 1 的话。那么 name release 之后,这个对象就被回收掉了。所以应该改成: 1 2 3 4 5 6 -(void)setName:(NSString *)newName { if (name != newName) { [name release]; name = [newName retain]; } } 这样才算是一个正确的 set 函数,Java 同学肯定被吓到了,虽然知道这么写,但这比 Java 麻烦多了。于是,Objective-C 允许程序员使用 @property + @synthesize 关键字来自动生成这些代码(注:Xcode 现在会默认自动添加 @synthesize 关键字)。于是 Objective-C 的程序员幸福了。大部分时候根本就不用写 getter 和 setter。 但是需要小心,Objective-C 的 accessor 不能在 init 和 dealloc 函数中使用!如果你在 dealloc 中这么写,就有问题: 1 2 3 4 -(void)dealloc { self.name = nil; [super dealloc] } 苹果在它的开发者文档库中有一个专门讲 cocoa 的内存管理的文章:http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.pdf 文章的第 16 页有一节题目是:Don’t Use Accessor Methods in Initializer Methods and dealloc 文章说:你唯一不应该用 Accessor 的地方是 init 函数和 delloc 函数。在 init 函数中,对于一个 _count 成员变量应该像下面这样赋值: 1 2 3 4 5 6 7 -(id)init { self = [super init]; if (self) { _count = [[NSNumber alloc] initWithInteger:0]; } return self; } 对于一个带参数的 init 函数,你应该实现成下面这样: 1 2 3 4 5 6 7 - (id)initWithCount:(NSNumber *)startingCount { self = [super init]; if (self) { _count = [startingCount copy]; } return self; } 对于在 dealloc 中,对应的写法应该是调 release: 1 2 3 4 - (void)dealloc { [_count release]; [super dealloc]; } 但是比较郁闷的是,文章最后没有说为什么不能!去 stackoverflow 上搜了一下,比较不靠谱的说法是这样少一次函数调用,更快。比较靠谱的说法是:在 init 和 dealloc 中,对象的存在与否还不确定,所以给对象发消息可能不会成功。 顺便说一下 , 当发现这个文章的时候,我们的代码中已经有了很多这样错误用法。虽然程序没有出现严重的内存问题,但是为了保险,还是打算一行一行改掉,后来我想,这个能不能用 vim 搞定呢?于是我进 vim,用 qa 启动宏录制,然后输入 :%s/self./[/g 再输入:%s/= nil/release]/g 再输入 q, 这样就可以用 @a 来启动刚刚录制的宏来做替换了。相当方便。 Posted by 唐巧 Aug 10th, 2011 iOS 关注我的「iOS开发」微信公众号,每天获得精选的 iOS 开发文章和创业心得:
专访 YYKit 作者 Ibireme: 开源大牛是怎样炼成的 Nov 26th, 2015 版权说明 本文为InfoQ中文站特供稿件,首发地址为:文章链接。如需转载,请与InfoQ中文站联系。 前言 第一次听到 ibireme 这个名字,是看到他在 微博上分享 了 YYText 开源库。当时我第一眼见到 YYText 的功能示意 GIF 图时(下图所示),就被它丰富的功能吸引了。YYText 应该是我见到过的功能最强大的基于 CoreText 的排版框架了。 令人惊讶的是,YYText 虽然代码量很大(超过一万行),但它只是 ibireme 的作品之一。ibireme 利用业余时间完成了 YYKit 工具库,包括: YYModel — 高性能的 iOS JSON 模型框架。 YYCache — 高性能的 iOS 缓存框架。 YYImage — 功能强大的 iOS 图像框架。 YYWebImage — 高性能的 iOS 异步图像加载框架。 YYText — 功能强大的 iOS 富文本框架。 YYKeyboardManager — iOS 键盘监听管理工具。 YYDispatchQueuePool — iOS 全局并发队列管理工具。 YYAsyncLayer — iOS 异步绘制与显示的工具。 YYCategories — 功能丰富的 Category 类型工具库。
以下转载内容很不错,后续补充从官方文档疏理出来的脉络,确实很好的用法。 Storyboard 跳转 和 传值 写在前面:因为苹果推 Storyboard 而且 目前来看, Apple Watch 也是用 Storyboard 就知道, 明天应用估计都是 Storyboard 的天下了.(水平有限, 不对之处在所难免, 望海涵)很多人似乎还是在用 XIB, 对 Storyboard 如何进行跳转 似乎 懵懵懂懂...好吧, 鉴于 早上群里, 有人问 怎么跳转, 怎么传值 等等问题. 就做下总结, 同时为大家 提供一些方法和参考.------------------1. 最简单的方法拖拽, 这个就不用多解释了吧. 直接拖拽到另一个视图控制器, 选择 show, 就行了.2. 利用 Segue 方法 (这里主要是 方法1 的传值)连好线, 点击 连线中间部分, 设置 Identifier.然后 调用 performSegueWithIdentifier 方法.(注: Demo 里面, 是直接将 TableViewController 和 SecondViewController 进行连线, 而不是 点击 Cell 的 indicator 进行连线)执行以下 方法, 就可以进行 跳转操作了. 复制代码performSegueWithIdentifier("SecondSegue", sender: self) 如何传值?很简单, 需要调用 prepareForSegue 方法 (因为这里是 父视图 -> 子视图 传值, 所以要用 destinationViewController) 复制代码 override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { var theSegue = segue.destinationViewController as SecondViewController theSegue.text = "Pass" } (注: 这里, Swift 的 自动补全可能失效, 所以 如果没有出现 destinationViewController, 没关系往后打.)这里的 text 是我在 子视图中 声明的变量, 用来设置 Label 的 值.PS: 一般, 我们都用使用连线的方式, 这里告诉大家另一个方式, 就是用 viewWithTag. 我先前在 Label 控件 Tag 中设置为 100. 当然你也可以使用连线, 使用 viewWithTag 可以在我们自定义 Cell 的时候 方便用到, 而不需要创建单独的 Cell class.3. 利用 self.storyboard利用 self.storyboard 方法, 就不需要进行 连线, 一样可以进行视图之间的跳转. 但是必须要设置 Storyboard ID.然后利用 如下方法进行 跳转 和 传值 复制代码 var thirdVC = self.storyboard?.instantiateViewControllerWithIdentifier("ThirdViewController") as ThirdViewController thirdVC.text = "Pass" self.navigationController?.pushViewController(thirdVC, animated: true) 因为是在 同一个 Storyboard 里面进行跳转, 所以 self.storyboard 就可以满足需求.不明白? 看最后一个方法:4. 利用 UIStoryboardXIB 方法, 我们需要 用到 nibName, 同样 如果我们想要 分离多个 sence 在不同的Storyboard 里面, 怎么办?这个时候, 就不能使用 self.storyboard 了.而是: 复制代码 var storyboard = UIStoryboard(name: "New", bundle: nil) var newVC = storyboard.instantiateViewControllerWithIdentifier("NewViewController") as NewViewController newVC.text = "Pass" self.navigationController?.pushViewController(newVC, animated: true) 是不是 和 XIB 有异曲同工的感觉, 这样就可以把 Storyboard 分几个, 每个里面放几个 Sence.这样的好处就是, 当你需要做多个不同功能的模块的应用, 分离到不同的 Storyboard 中, 彼此互不影响.相对于 XIB 来说, 每个文件夹 只需要 1 个 Storyboard 文件, 和 Swfit 文件就可以了.[ 此帖被dongeyoung在2014-11-23 04:55重新编辑 ]
Xcode 7 你能不能再抗扎腾点儿呢 - 尤其自个儿强制升级后,没事儿就关闭 太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循“署名-非商业用途-保持一致”创作公用协议 转载请保留此句:太阳火神的美丽人生 - 本博客专注于 敏捷开发及移动和物联设备研究:iOS、Android、Html5、Arduino、pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作。 作为苹果公司的最新系统 iOS 9,同样带来了不少变化。 适配新版本嘛,这个多年来一直在做,无可厚非,跟上形势,进得了厅堂(通过苹果应用商店审核)。 然而,好多新特性是需要集成开发环境配合的,当然你自个改也可以,如果你真的很不嫌费事,而且有大把时间。 但不知 Xcode 6,是否会提示下载 iOS 9,反正我上周重做系统前,没发现这个。 那么估且就这样认为吧, Xcode 7 是你适配 iOS 9 的必备,虽然很多特性是可以在发布时禁掉的。 。。。 N 多罗列 。。。 终于用 Xcode 7 针对模拟器开始了新的征程。 可是,某一天,发现 Xcode 7 彩色法轮经常转起,是不是魔界有大事发生,这是什么征照?!呢。。。 。。。 也许是开得太久,需要重启一下吧, 重启 Xcode 7 ,发现它一直提示自动升级中,一小时后,还是这样的提示。。。 重启 iMac,终于能打开 Xcode 7 了,开始干活咯。。。 咔嚓,异常关闭! 我的 Xcode 7 呀,你玩毛呐! 之后。。。 无尽的伤痛。。。 下一个版本无论是啥,俺都要升级! 而且, 为么什么这么一个烂版本, 还要强制自动升级, 乔布斯。。。,你上来看看吧, 没你管着,他们竟瞎整啊! 。。。 啥,没听清 噢, 你也管不了啦,那真没招了, 爱咋咋地吧 也许把机器内存翻一倍能彩轮不现吧?! 这时代磁盘也是屏颈,固态能解决大磁盘与内存之间的瓶颈。 马上订一块 128 G 固态,把家里的 R400 老爷机上的 128 G SATA 换掉。。。
iOS HLS 流媒体文件打散问题 - 只有声音无影像 太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循“署名-非商业用途-保持一致”创作公用协议 转载请保留此句:太阳火神的美丽人生 - 本博客专注于 敏捷开发及移动和物联设备研究:iOS、Android、Html5、Arduino、pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作。 HTTP Live Streaming (HLS)上面是官方的专栏页,在其中下载相应的工具包,其中有一个 mediafilesegmenter ,使用参数如下: sudo mediafilesegmenter -f ./112 ./33.mp4 -t 1 ./112 是当前 Web 服务器根目录下新建的 112 目录,也可以是其它路径 ./33.mp4 是待打散的视频文件 启用 Mac 默认 Web 方法,后补 iOS 支持的视频格式,后补 Android 4.0 之后支持 HLS,另可引入维它命! ======= 生病中,很想把这篇刚彻彻底研究出的思路整理出来,要不然就忘了,无耐。。。
Mac OS X 10.10 启用 Web 服务器 转载 2015-01-25 17:07:22 原文: Get Apache, MySQL, PHP and phpMyAdminworking on OSX 10.9 Mavericks Mac OS X 10.9 依旧预装了 Apache,但是已经不能在 「系统偏好设置」中的「Web 共享」来开启了,需要手动通过命令行开启。 启动Apache 启动:sudo apachectl start 停止:sudo apachectl stop 重启:sudo apachectl restart 查看 Apache 版本 httpd -v 浏览器打开 http://127.0.0.1 可以看到 Itworks! 的页面 文件根目录 系统级的根目录 http://localhosts/ 对应的文件目录是: /Library/WebServer/Documents/ 系统级根目录默认没有开启目录列表,开启方法:编辑 /etc/apache2/httpd.conf 文件搜索找到 将 OptionsFollowSymLinksMultiviews 修改为 OptionsIndexes FollowSymLinks Multiviews 用户级根目录 另一个 Web根目录默认是 ~/Sites ,10.9中你需要手动创建这个Sites目录。 检查这个目录下是否有 username.conf 文件 /etc/apache2/users/ 如果没有,则需要新建一个,username 需要是你的账户名字,建议使用终端创建这个文件: cd /etc/apache2/users sudo vi username.conf 贴入以下内容,注意修改 username 为你的账户名字 Options Indexes MultiViews FollowSymLinks AllowOverride All Order allow,deny Allow from all Require all granted 这个文件的权限应该是: -rw-r--r-- 1 root wheel 298 Jun 28 16:47username.conf 如果不是,请修改 sudo chmod 644 username.conf 编辑 /etc/apache2/httpd.conf 文件,删除下列这些代码前的注释符号: # Include /private/etc/apache2/extra/httpd-userdir.conf LoadModule authz_core_module libexec/apache2/mod_authz_core.so LoadModule authz_host_module libexec/apache2/mod_authz_host.so LoadModule userdir_module libexec/apache2/mod_userdir.so 编辑 /etc/apache2/extra/httpd-userdir.conf 文件,删除下列这些代码前的注释符号: # Include /private/etc/apache2/users/*.conf 重启 Apache sudo apachectl restart 这时,这个网址应该已经可以用了: http://localhost/~username/ 启用重定向 .htaccess sudo vi /etc/apache2/httpd.conf 删除 AllowOverride all 前的注释 # PHP OSX 10.9 已经预装了 PHP 5.4.17,编辑 httpd.conf sudo vi /etc/apache2/httpd.conf 取消这一行前边的注释符号 # LoadModule php5_modulelibexec/apache2/libphp5.so 重启 Apache sudo apachectl restart 查看 Apache 信息 MySQL OS X 10.9 需要单独安装 MySQL,下载地址 ,选择 Mac OS X ver. 10.7(x86, 64-bit), DMGArchive 。(下载无需注册,点击下边小字部分的「_ No thanks, justtake me to the downloads!_」即可) 三个文件都需要安装。其中第二个会在「系统偏好设置」中添加一个 MySQL 设置项:开机自动启动、启动/关闭 MySQL 命令行启动 MySQL sudo /usr/local/mysql/support-files/mysql.serverstart 查看 MySQL 版本 /usr/local/mysql/bin/mysql -v 添加 mysql 别名到 PATH 里: cd ; vi .bash_profile 添加: export PATH="/usr/local/mysql/bin:$PATH" 保存退出,然后执行 source ~/.bash_profile 之后就可以直接使用 mysql 命令mysql -v 使用 \q 可以退出 mysql模式 设置 MySQL 密码 修改 mysql root 账户密码: /usr/local/mysql/bin/mysqladmin -u root password'yourpasswordhere' 注意使用单引号包裹密码 修复 2002 MySQL Socket 错误 sudo mkdir /var/mysql sudo ln -s /tmp/mysql.sock/var/mysql/mysql.sock phpMyAdmin 安装前必须先如上操作修复 2002 MySQL Socket 错误。 下载 phpMyAdmin ,解压后放在 ~/Sites 目录下,新建 config 文件夹 mkdir ~/Sites/phpmyadmin/config 修改权限 chmod o+w ~/Sites/phpmyadmin/config 打开 http://127.0.0.1/~username/phpmyadmin/ 输入 mysql 的用户名和密码就可以登陆进去了。 然后删除 /config 目录。 phpMyAdmin可能会提示:配置文件现在需要一个短语密码。此时修改文件:phpMyAdmin/libraries/config.default.php找到:$cfg['blowfish_secret']= '';修改为:$cfg['blowfish_secret'] ='rpsh.net'; (rpsh.net 可以为任意字符) 访问 http://127.0.0.1/~username/phpmyadmin/ 就可以管理你的 mysql 了。 权限 为了方便程序在 ~/Sites 目录下读写 sudo chmod -R a+w ~/Sites/testsite 若担心安全问题,可以使用 _www 权限,若这样做当需要 admin 权限需做验证: sudo chown -R _www ~/Sites/testsite
Xcode 7.0 官方免费的真机开发 太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循“署名-非商业用途-保持一致”创作公用协议 转载请保留此句:太阳火神的美丽人生 - 本博客专注于 敏捷开发及移动和物联设备研究:iOS、Android、Html5、Arduino、pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作。 苹果开发需要跟进的另一篇文档:What's New in Xcode 关于免费的真机开发描述如下: 这个确实是在网上搜集料时无意发现的,说实话,我没有看 Xcode 更新的习惯,拿来就用,立马上手。 可是,这个隐藏的功能,是看不见地,也许偿试可能会发现,那确是个概率问题。 以后,不光要看 What's New in iOS , Android x.0 Updates ,还得看这个。 一览众山小的感觉, 不对, 是 运筹帷幄之中,决胜千里之外 的感觉。 大了, 提纲挈领 现实一些 参考: 【官方方法】xcode7免证书真机调试
iOS 9 适配,我咋还没遇到这么多坑呢呀 太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循“署名-非商业用途-保持一致”创作公用协议 转载请保留此句:太阳火神的美丽人生 - 本博客专注于 敏捷开发及移动和物联设备研究:iOS、Android、Html5、Arduino、pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作。 iOS 9 适配,我咋还没遇到这么多坑呢呀 (后面仨字儿,谁会多?!。。。真不会呀,那你想想小沈阳,你就会了) 接下来扯点正经的,转一篇别人的贴,俺这原创是指俺挨个截图转载地呀,也废了不少功夫 iOS 9 适配,我咋还没遇到这么多坑呢呀 有两种可能: 一是还没做到那一部分,当然遇不到了; 二是提前知道有这个坑绕开了,列出个坑表儿,做查坑的; 其实,我也掉里了,是 iOS 7 时的一个坑,掉这一次,就长心眼儿了, 先把坑尽可能查出来,这样即使有漏掉的坑,掉下去的机率也要少得多,即使掉里了,跳坑所用时间也不会太久。
iOS 8 引入的 UIActionSheet 的替代品 - UIAlertController 太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循“署名-非商业用途-保持一致”创作公用协议 转载请保留此句:太阳火神的美丽人生 - 本博客专注于 敏捷开发及移动和物联设备研究:iOS、Android、Html5、Arduino、pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作。 样式和代码如下: 换作 Alert 样式来试试: 只需将上面的 UIAlertControllerStyleActionSheet 替换成 UIAlertControllerStyleAlert 即可,其它完全不用动。即使将 "取消" action 先加,它也依旧躺在最底下,待遇平平,不像 actionsheet 中,人家 取消 自个儿一个办公室 一般闲职都一个办公室,真正干活的堆一个办公室,交流起来方便 添加个文本输入框: