本文作于前几天,由于今天的突发新闻,已作了修订。
自从Elop那篇着火的平台备忘录出炉,Nokia的杯具就已经造成,唯一令人不解的就是:Elop为什么还没有被开除?
距离这个备忘录事件已经过去两年半多,基于WinPhone平台的Lumia手机也出过好几款了,但是看看竞争对手们这两年半发生了些什么?不论是市场份额、机型(Android)、应用数量、还是平台的改进程度,都远远超过WinPhone——打这么长的名字的确挺麻烦,但我不愿意用缩写,因为通常我说到WP是指WordPress。
Elop当年说过不选择Android的理由是因为当时HTC和三星已经做大,Nokia后来加入这个红海难以脱颖而出,所以选择了全新的WinPhone,并且他相信微软做软件比Nokia更专业。
只可惜微软做软件的确更专业,但遗憾的是没有能够跟上移动世界的变化节奏。当年我吐槽《微软挪鸡鸭》的时候,还有很多微软粉来喷我,现在不知道他们都在哪里?
当然我也不是说Nokia马上就要死,但现在WinPhone的情况跟两年半前的Symbian相比,真没好多少。如果Nokia当初没有放弃Symbian,现在的情况也不会更糟到哪里去。既然当年可以着一次火,现在其实也是一个着火的好时候。
再着一次火从Nokia决绝地放弃Symbian来看,估计是微软下的手,要求Nokia只做WinPhone,就现在的情况来看,跟我两年半前那篇预计的差不多,微软在这次的合作中得到了大部分好处,甚至自己卖起了Surface平板——幸好这货卖得不好(这货也被我吐槽过《Surface,送我都不要》),不然现在估计已经有Surface手机了。但是Nokia呢?给人剩下的印象就是:
拍照很NB
这跟Symbian时代给人的印象——抗摔——有什么分别?
我不知道它们之前的排它性协议时效多长,但我真的觉得Nokia应该准备后路,不应该再在WinPhone这一棵树上吊死。
那么它还有什么选择?
现在再选Android?当然不可能,那跟找死也没有什么分别了——还不如两年半前来呢。
WebOS?现在还有多少人记得它?虽然市场上二手Veer跟当年相比也没有跌价多少——折旧率远远低于Lumia 920之类——但毕竟是大势已去,现在状况基本等同于Symbian。
ChromeOS?现在有厂商在这用这货吗?Web真到了能代替本地应用的时候了?恐怕还需要假以时日吧。
还有什么?
Ubuntu Touch去年我介绍过一个比较Geek的东西:Ubuntu for Android。我甚至还试了它的前身,那个Moto发明的WebTop,的确有一定的可用性,只是因为仍然太不成熟,可用性还是较差的。
但是现在情况不同了,Ubuntu已经在此基础上有了进一步的发展,那就是Ubuntu Touch,以及相应的硬件(计划明年发布):Ubuntu Edge。
8月初,我在G4PCC(我和几位Go4Pro.org的朋友一起搞的一年一度的小规模技术交流会)上看到了Mike那台刷了Ubuntu Touch的Nexus4,把玩了一把,感觉还是很不错的。
其实Ubuntu从桌面转向移动的准备工作已经做了很久。早在09年左右的时候,Ubuntu推出了NBR(NetBookRemix)版本,专门为小屏幕桌面设计了Unity UI。到了11年的时候,Unity已经成为Ubuntu的标配。
虽然早期的Unity的确不好用,但是经过这两年的发展,已经很不错了。而移动版的Ubuntu Touch也是采用了类似Unity的界面。
Ubuntu Touch既然叫Touch,当然是完全针对触摸屏设计的,大部分操作都信赖于按键,而是通过从边缘滑入的操作进行。包括那个Unity的侧边栏,都是通过边缘滑动显示的,还有正在运行的进程列表也是。
当然它最大的优点就是移动和桌面的一体化——插上键鼠外接显示器即成为全功能桌面版的Ubuntu。像WinPhone、WindowsRT、Window8那种精神分裂的东西完全是拍马也赶不上的(除非是新开发的兼容应用,大部分Windows应用根本不能运行在WindowsRT上,更不要说WinPhone了。
但是Ubuntu Touch不同,它可以运行所有Ubuntu桌面应用(当然不包括硬件相关的,比如需要特定硬件或使用X86指令集的),不存在这种分裂。另一方面,它也有一套自己的开发机制,基于QT和一种新的类似脚本的开发语言,而且以这种模式开发的应用本身就是兼容的,可以直接运行在未来版本的桌面Ubuntu上。
与其它的桌面移动一体方式相比,我觉得Ubuntu这条路是最有前途的。
Ubuntu Edge和Nokia虽然我很看好Ubuntu Touch,但必须承认,移动的世界是依赖于硬件的,不同于桌面,有标准的PC硬件平台。所以Ubuntu搞了Edge计划,打算制造手机。
可惜的是,他们原本计划为Edge筹资的行动最后还是失败了……他们的目标是3200万美元,但最后只筹到1200万美元——虽然这已经是众筹的历史最高纪录了。
所以我觉得Nokia应该来和Ubuntu合作,比竟当年Nokia曾经拥有QT,从技术角度说,Nokia和Ubuntu Touch比和WinPhone应该有更亲近的关系。
当然,这一切的前提都是在于Nokia没有中木马的情况下……
更新消息前几天微软宣布包耳膜将在一年内退休,结果微软的股价大涨7%。我戏称要是Nokia宣布Elop在一个月内退休,估计股价能涨70%……
某Nokia员工评论说:Nokia的股票现在都是地板价了,翻几番都有可能……
所以木马对于Nokia有多可恶,真是地球人都知道。
最新修订今天的震撼性消息就是微软以71.7亿美元收购Nokia设备与服务部门(即手机部门,之后Nokia将仅剩网络和地图部门)。
估计木马的剧本大概是这样的:
三年前的一个晚上,包耳膜对木马说:我们要做手机,你帮我把NOKIA搞来,我让你接班。木马接令而去,几个月后丢出燃烧的平台备忘录,三年后终于大功告成,包耳膜宣布一年内退休,木马圆满回归,等待他的将是微软帝国的王位。
我早在两年半前就说过:
这事(指Nokia制造WinPhone)对于微软来说是一件好事,赚大发了,但对于NOKIA来说,无疑是北欧巨人倒下的第一步。
不幸言中。
之前用Text Kit写Reader的时候,在分页时要计算一段文本的尺寸大小,之前使用了NSString类的sizeWithFont:constrainedToSize:lineBreakMode:方法,但是该方法已经被iOS7 Deprecated了,而iOS7新出了一个boudingRectWithSize:options:attributes:context方法来代替:
很碍眼的黄色警告标志。
先来看看iOS7 SDK包中关于boudingRectWithSize:options:attributes:context方法的定义:
// NOTE: All of the following methods will default to drawing on a baseline, limiting drawing to a single line. // To correctly draw and size multi-line text, pass NSStringDrawingUsesLineFragmentOrigin in the options parameter. @interface NSString (NSExtendedStringDrawing) - (void)drawWithRect:(CGRect)rect options:(NSStringDrawingOptions)options attributes:(NSDictionary *)attributes context:(NSStringDrawingContext *)context NS_AVAILABLE_IOS(7_0); - (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options attributes:(NSDictionary *)attributes context:(NSStringDrawingContext *)context NS_AVAILABLE_IOS(7_0); @end
关于该方法,NSAttributedString其实也有一个同名的方法:
@interface NSAttributedString (NSExtendedStringDrawing) - (void)drawWithRect:(CGRect)rect options:(NSStringDrawingOptions)options context:(NSStringDrawingContext *)context NS_AVAILABLE_IOS(6_0); - (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options context:(NSStringDrawingContext *)context NS_AVAILABLE_IOS(6_0); @end该方法在iOS6就可以使用了。
关于该类,有一篇关于NSAttributedString UIKit Additions Reference翻译的文章:http://blog.csdn.net/kmyhy/article/details/8895643
里面就说到了该方法:
boundingRectWithSize:options:context:
返回文本绘制所占据的矩形空间。
- (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options context:(NSStringDrawingContext *)context
参数
size
宽高限制,用于计算文本绘制时占据的矩形块。
The width and height constraints to apply when computing the string’s bounding rectangle.
options
文本绘制时的附加选项。可能取值请参考“NSStringDrawingOptions”。
context
context上下文。包括一些信息,例如如何调整字间距以及缩放。最终,该对象包含的信息将用于文本绘制。该参数可为 nil 。
返回值
一个矩形,大小等于文本绘制完将占据的宽和高。
讨论
可以使用该方法计算文本绘制所需的空间。size 参数是一个constraint ,用于在绘制文本时作为参考。但是,如果绘制完整个文本需要更大的空间,则返回的矩形大小可能比 size 更大。一般,绘制时会采用constraint 提供的宽度,但高度则会根据需要而定。
特殊情况
为了计算文本块的大小,该方法采用默认基线。
如果 NSStringDrawingUsesLineFragmentOrigin未指定,矩形的高度将被忽略,同时使用单线绘制。(由于一个 bug,在 iOS6 中,宽度会被忽略)
兼容性
- iOS 6.0 以后支持。
声明于
NSStringDrawing.
另外,关于参数(NSStringDrawingOptions)options
typedef NS_ENUM(NSInteger, NSStringDrawingOptions) { NSStringDrawingTruncatesLastVisibleLine = 1 << 5, // Truncates and adds the ellipsis character to the last visible line if the text doesn't fit into the bounds specified. Ignored if NSStringDrawingUsesLineFragmentOrigin is not also set. NSStringDrawingUsesLineFragmentOrigin = 1 << 0, // The specified origin is the line fragment origin, not the base line origin NSStringDrawingUsesFontLeading = 1 << 1, // Uses the font leading for calculating line heights NSStringDrawingUsesDeviceMetrics = 1 << 3, // Uses image glyph bounds instead of typographic bounds } NS_ENUM_AVAILABLE_IOS(6_0);
NSStringDrawingTruncatesLastVisibleLine:
如果文本内容超出指定的矩形限制,文本将被截去并在最后一个字符后加上省略号。如果没有指定NSStringDrawingUsesLineFragmentOrigin选项,则该选项被忽略。
NSStringDrawingUsesLineFragmentOrigin:
绘制文本时使用 line fragement origin 而不是 baseline origin。
The origin specified when drawing the string is the line fragment origin and not the baseline origin.
NSStringDrawingUsesFontLeading:
计算行高时使用行距。(译者注:字体大小+行间距=行距)
NSStringDrawingUsesDeviceMetrics:
计算布局时使用图元字形(而不是印刷字体)。
Use the image glyph bounds (instead of the typographic bounds) when computing layout.
简单写了一个Demo来看看该方法的使用,并比较了一下各个options的不同,首先是代码:
NSAttributedString *attrStr = [[NSAttributedString alloc] initWithString:textView.text]; textView.attributedText = attrStr; NSRange range = NSMakeRange(0, attrStr.length); NSDictionary *dic = [attrStr attributesAtIndex:0 effectiveRange:&range]; // 获取该段attributedString的属性字典 // 计算文本的大小 CGSize textSize = [textView.text boundingRectWithSize:textView.bounds.size // 用于计算文本绘制时占据的矩形块 options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading // 文本绘制时的附加选项 attributes:dic // 文字的属性 context:nil].size; // context上下文。包括一些信息,例如如何调整字间距以及缩放。该对象包含的信息将用于文本绘制。该参数可为nil NSLog(@"w = %f", textSize.width); NSLog(@"h = %f", textSize.height);再看看不同的options下控制台的输出结果:
NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading 2013-09-02 21:04:47.470 BoudingRect_i7_Demo[3532:a0b] w = 322.171875 2013-09-02 21:04:47.471 BoudingRect_i7_Demo[3532:a0b] h = 138.000015 NSStringDrawingUsesLineFragmentOrigin // The specified origin is the line fragment origin, not the base line origin 2013-09-02 17:35:40.547 BoudingRect_i7_Demo[1871:a0b] w = 318.398438 2013-09-02 17:35:40.549 BoudingRect_i7_Demo[1871:a0b] h = 69.000000 NSStringDrawingTruncatesLastVisibleLine // Truncates and adds the ellipsis character to the last visible line if the text doesn't fit into the bounds specified. Ignored if NSStringDrawingUsesLineFragmentOrigin is not also set. 2013-09-02 17:37:38.398 BoudingRect_i7_Demo[1902:a0b] w = 1523.408203 2013-09-02 17:37:38.400 BoudingRect_i7_Demo[1902:a0b] h = 13.800000 NSStringDrawingUsesFontLeading // Uses the font leading for calculating line heights 2013-09-02 17:40:45.903 BoudingRect_i7_Demo[1932:a0b] w = 1523.408203 2013-09-02 17:40:45.905 BoudingRect_i7_Demo[1932:a0b] h = 13.800000 NSStringDrawingUsesDeviceMetrics // Uses image glyph bounds instead of typographic bounds 2013-09-02 17:42:03.283 BoudingRect_i7_Demo[1956:a0b] w = 1523.408203 2013-09-02 17:42:03.284 BoudingRect_i7_Demo[1956:a0b] h = 13.800000
其中如果options参数为NSStringDrawingUsesLineFragmentOrigin,那么整个文本将以每行组成的矩形为单位计算整个文本的尺寸。(在这里有点奇怪,因为字体高度大概是13.8,textView中大概有10行文字,此时用该选项计算出来的只有5行,即高度为69,而同时使用NSStringDrawingUsesFontLeading | NSStringDrawingUsesLineFragmentOrigin却可以得出文字刚好有10行,即高度为138,这里要等iOS7官方的文档出来再看看选项的说明,因为毕竟以上文档是iOS6的东西)
如果为NSStringDrawingTruncatesLastVisibleLine或者NSStringDrawingUsesDeviceMetric,那么计算文本尺寸时将以每个字或字形为单位来计算。
如果为NSStringDrawingUsesFontLeading则以字体间的行距(leading,行距:从一行文字的底部到另一行文字底部的间距。)来计算。
各个参数是可以组合使用的,如NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingTruncatesLastVisibleLine。
根据该方法我调整了一下Reader的分页方法:(主要是将被iOS7 Deprecated的sizeWithFont:constrainedToSize:lineBreakMode:方法改成了boudingRectWithSize:options:attributes:context:方法来计算文本尺寸)
/* 判断是否需要分页和进行分页动作 */ -(BOOL)paging { /* 获取Settings中设定好的字体(主要是获取字体大小) */ static const CGFloat textScaleFactor = 1.; // 设置文字比例 NSString *textStyle = [curPageView.textView tkd_textStyle]; // 设置文字样式 preferredFont_ = [UIFont tkd_preferredFontWithTextStyle:textStyle scale:textScaleFactor]; //设置prferredFont(包括样式和大小) NSLog(@"paging: %@", preferredFont_.fontDescriptor.fontAttributes); // 在控制台中输出字体的属性字典 /* 设定每页的页面尺寸 */ NSUInteger height = (int)self.view.bounds.size.height - 40.0; // 页面的高度 /* 获取文本的总尺寸 */ NSDictionary *dic = preferredFont_.fontDescriptor.fontAttributes; CGSize totalTextSize = [bookItem.content.string boundingRectWithSize:curPageView.textView.bounds.size options:NSStringDrawingUsesLineFragmentOrigin attributes:dic context:nil].size; NSLog(@"w = %f", totalTextSize.width); NSLog(@"h = %f", totalTextSize.height); /* 开始分页 */ if (totalTextSize.height < height) { /* 如果一页就能显示完,直接显示所有文本 */ totalPages_ = 1; // 设定总页数为1 charsPerPage_ = [bookItem.content length]; // 设定每页的字符数 textLength_ = [bookItem.content length]; // 设定文本总长度 return NO; // 不用分页 } else { /* 计算理想状态下的页面数量和每页所显示的字符数量,用来作为参考值用 */ textLength_ = [bookItem.content length]; // 文本的总长度 NSUInteger referTotalPages = (int)totalTextSize.height / (int)height + 1; // 理想状态下的总页数 NSUInteger referCharactersPerPage = textLength_ / referTotalPages; // 理想状态下每页的字符数 // 输出理想状态下的参数信息 NSLog(@"textLength = %d", textLength_); NSLog(@"referTotalPages = %d", referTotalPages); NSLog(@"referCharactersPerPage = %d", referCharactersPerPage); /* 根据referCharactersPerPage和text view的高度开始动态调整每页的字符数 */ // 如果referCharactersPerPage过大,则直接调整至下限值,减少调整的时间 if (referCharactersPerPage > 1000) { referCharactersPerPage = 1000; } // 获取理想状态下的每页文本的范围和pageText及其尺寸 NSRange range = NSMakeRange(referCharactersPerPage, referCharactersPerPage); // 一般第一页字符数较少,所以取第二页的文本范围作为调整的参考标准 NSString *pageText = [bookItem.content.string substringWithRange:range]; // 获取该范围内的文本 NSLog(@"%@", pageText); NSRange ptrange = NSMakeRange(0, pageText.length); NSDictionary *ptdic = [[bookItem.content attributedSubstringFromRange:ptrange] attributesAtIndex:0 effectiveRange:&ptrange]; CGSize pageTextSize = [pageText boundingRectWithSize:curPageView.textView.bounds.size options:NSStringDrawingUsesLineFragmentOrigin attributes:ptdic context:nil].size; // 若pageText超出text view的显示范围,则调整referCharactersPerPage NSLog(@"height = %d", height); while (pageTextSize.height > height) { NSLog(@"pageTextSize.height = %f", pageTextSize.height); referCharactersPerPage -= 2; // 每页字符数减2 range = NSMakeRange(0, referCharactersPerPage); // 重置每页字符的范围 ptdic = [[bookItem.content attributedSubstringFromRange:range] attributesAtIndex:0 effectiveRange:&range]; CGSize pageTextSize = [pageText boundingRectWithSize:curPageView.textView.bounds.size options:NSStringDrawingUsesLineFragmentOrigin attributes:ptdic context:nil].size; pageText = [bookItem.content.string substringWithRange:range]; // 重置pageText pageTextSize = [pageText boundingRectWithSize:curPageView.textView.bounds.size options:NSStringDrawingUsesLineFragmentOrigin attributes:ptdic context:nil].size; // 获取pageText的尺寸 } // 根据调整后的referCharactersPerPage设定好charsPerPage_ charsPerPage_ = referCharactersPerPage; NSLog(@"cpp: %d", charsPerPage_); // 计算totalPages_ totalPages_ = (int)bookItem.content.length / charsPerPage_ + 1; NSLog(@"ttp: %d", totalPages_); // 计算最后一页的字符数,防止范围溢出 charsOfLastPage_ = textLength_ - (totalPages_ - 1) * charsPerPage_; NSLog(@"colp: %d", charsOfLastPage_); // 分页完成 return YES; } }这样就看不到碍眼的黄色警告标志了。
重要的是,由于该方法计算文本的尺寸更为准确,所以可以使得分页后页与页之间的连贯性好了很多,而且每页的空间利用率都提高了很多,每页的文字几乎铺满了整个页面。
最近手头上资金不足,想自己搞一下app赚点广告费,解决一下自己的零花钱问题,各位有没有做自己的app呢?打算用有米广告,各位觉得怎么样?
你们的收益是如何呢?能在这里晒晒吗?