diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000..09344ee4eb Binary files /dev/null and b/.DS_Store differ diff --git a/CNAME b/CNAME index 523fdc8124..59e77ccbd4 100644 --- a/CNAME +++ b/CNAME @@ -1 +1 @@ -zyuanming.github.io +www.myhanson.com diff --git a/README.md b/README.md index 9b83c4924b..1c93fa383a 100755 --- a/README.md +++ b/README.md @@ -1,6 +1,24 @@ -##说明 +## 说明 + +* 此博客 fork 自 [zyuanming](https://github.com/zyuanming/zyuanming.github.io),感谢。 +* 这个库是这篇微信公众号文章 [十分钟在Github搭建自己的个人博客](http://mp.weixin.qq.com/s/ICOeB7ILLKIKMBiRGdVWnA) 中提到的原仓库,因为本人博客现在已经转向 Hexo,故此仓库不再维护。 + +`For English:` + * This is fork from [zyuanming](https://github.com/zyuanming/zyuanming.github.io). Thanks. + * This repo is mentioned in this blog from wechat platform [十分钟在Github搭建自己的个人博客](http://mp.weixin.qq.com/s/ICOeB7ILLKIKMBiRGdVWnA). Now, I switch to Hexo, this repo no longer maintain. + +## License + +请勿随意使用以下目录里的原创内容: +* _posts/ +* assets/image + +I own the copyright to the following directories and their contents. You may not reuse anything therein without my permission: +* _posts/ +* assets/image + +All other directories and files are MIT Licensed. -此博客 fork 自 [陈素封](http://cnfeat.com),感谢。 diff --git a/_config.yml b/_config.yml index f7d8368788..8017f74e87 100755 --- a/_config.yml +++ b/_config.yml @@ -1,16 +1,16 @@ # Site settings -title: 读立写生 +title: Hanson header-img: "img/facebook.jpg" tagline: "cn" -description: "剑尖|读而立·写而生" +description: "Hanson|Think Different" baseurl: "" -url: "http://zyuanming.github.io" +url: "http://zyphs21.github.io" # About/contact owner: - name: " 剑尖" - email: zyuanming@outlook.com - bio: "写就是写的报酬" + name: "张远平" + email: yuanpingzhang123@gmail.com + bio: "" # Data gavatar: http://dreamofbook.qiniudn.com/Az..png @@ -18,10 +18,10 @@ favicon: img/favicon.png douban_username: twitter_username: -github_username: zyuanming -weibo_username: 小剑尖 -facebook_username: -zhihu_username: +github_username: zyphs21 +weibo_username: 汉森HS +facebook_username: +zhihu_username: zhang-yi-sen-46 # Build settings # use Github Flavored Markdown !important @@ -31,7 +31,7 @@ highlighter: pygments permalink: pretty paginate: 8 exclude: ["less","node_modules","Gruntfile.js","package.json","README.md"] -relative_permalinks: true + # http://en.wikipedia.org/wiki/List_of_tz_database_time_zones timezone: Asia/Shanghai @@ -44,16 +44,16 @@ defaults: type: "posts" values: layout: "post" - author: "剑尖" + author: "hanson" header-img: "img/facebook.jpg" # We don't want posts without a header image, that whould mean white on white # Comments comments : provider : duoshuo duoshuo : - short_name : 剑尖 + short_name : hanson disqus : - short_name : 剑尖 + short_name : hanson # Analytics and webmaster tools stuff goes here google_analytics: @@ -61,4 +61,4 @@ google_verify: # https://ssl.bing.com/webmaster/configure/verify/ownership Option 2 content= goes here bing_verify: -# Links to include in footer navigation \ No newline at end of file +# Links to include in footer navigation diff --git a/_posts/.DS_Store b/_posts/.DS_Store new file mode 100644 index 0000000000..5008ddfcf5 Binary files /dev/null and b/_posts/.DS_Store differ diff --git "a/_posts/2014-10-25-\345\233\276\347\211\207\344\277\241\346\201\257.md" "b/_posts/2014-10-25-\345\233\276\347\211\207\344\277\241\346\201\257.md" deleted file mode 100644 index 5fa6492074..0000000000 --- "a/_posts/2014-10-25-\345\233\276\347\211\207\344\277\241\346\201\257.md" +++ /dev/null @@ -1,49 +0,0 @@ ---- -layout: post -title: 图片信息 -date: 2014-10-25 -categories: blog -tags: [图像] -description: 写不是义务,写本身就是写的报酬。 - ---- - -## 颜色空间 - -用RGB模式表示颜色是颜色空间的其中一种分类,它是众多存储颜色方法中的一种,另一种颜色空间是灰阶空间。所有的图形只有黑色和白色。我这里推荐一篇翻译文章:[IOS中图形图像处理第一部分:位图图像原图修改][1],虽然不是很详细,但是起码讲解很生动。 - -## 颜色深度 - -颜色深度标准通常有以下几种: - -8位色,每个像素所能显示的彩色数为2的8次方,即256种颜色。 - -16位增强色,16位彩色,每个像素所能显示的彩色数为2的16次方,即65536种颜色。 - -24位真彩色,每个像素所能显示的彩色数为24位,即2的24次方,约1680万种颜色。 - -32位真彩色,即在24位真彩色图像的基础上再增加一个表示图像透明度信息的Alpha通道。 - -例如:一个使用16位存储的图片,可能5位表示红色,5位表示绿色,5位表示蓝色,,一位是alpha。在这种情况下,它要么表示透明要么不是。一个使用32位存储的图片,每8位表示红绿蓝,和alpha通道。在这种情况下,就不光可以表示透明还是不透明,alpha通道还可以表示256级的半透明度。 - -下面举例RGB颜色空间下的不同的存储格式: - -1. A8R8G8B8 : a,r, g ,b都占用了8位,一个像素占用4个字节 - -2. A1R5G5B5 : 16位,一个像素占用两个字节,alpha通道用1位表示 - -3. ARGB4444 : 16位,一个像素占用两个字节,alpha通道占了4位 - -4. RGB888 : 没有alpha通道,不透明图片,一个像素占用三个字节,24位真彩色 - -5. RGB565 : 一个像素占用两个字节,16位真彩色 - -为了优化显示效率和减少内存占用,我们可以根据app的实际应用情景,使用合适的颜色深度的图片。这在游戏中更是一种经常使用的优化方法。 - -## alpha通道 - -alpha通道在不同的图片颜色深度下,保存的信息不同 - -通常,alpha通道是一个8位的灰度通道,该通道用256级灰度来记录图像中的透明度信息,定义透明、不透明和半透明区域,其中黑表示全透明,白表示不透明,灰表示半透明。 - - [1]: http://www.tairan.com/archives/7449 \ No newline at end of file diff --git "a/_posts/2014-11-01-\350\256\276\350\256\241\346\250\241\345\274\217-\345\244\226\350\247\202\346\250\241\345\274\217.md" "b/_posts/2014-11-01-\350\256\276\350\256\241\346\250\241\345\274\217-\345\244\226\350\247\202\346\250\241\345\274\217.md" deleted file mode 100644 index ed2cba176f..0000000000 --- "a/_posts/2014-11-01-\350\256\276\350\256\241\346\250\241\345\274\217-\345\244\226\350\247\202\346\250\241\345\274\217.md" +++ /dev/null @@ -1,187 +0,0 @@ ---- -layout: post -title: 设计模式:外观模式 -date: 2014-11-01 -categories: blog -tags: [设计模式] -description: 写不是义务,写本身就是写的报酬。 - ---- - -当涉及到设计模式时,你可能会问: - -> 为什么在编程中需要使用设计模式? -> -> 我们的代码不用设计模式也能正常工作。 - -为此,我想反问:“你愿意住在一个豪华的家,还是一个只是简单的四面墙的地方?”毕竟,无论哪种,都可以满足我们的需求。 - -一般情况下,我们会选择一个豪华的家,因为它提供了更好的设施,需要更少的维护,保养也不用那么麻烦,因为基础架构已经存在。 - -同样的道理也适用于编程:采用设计模式的代码是很容易理解,易于维护,易于扩展的。 - -在这系列的教程中,我们将覆盖一些对我们在编程中非常有用的设计模式。你讲了解它们的优点和缺点,以及在什么情况下去使用它们。 - -在这系列的教程中,我将使用PHP语言来说明设计模式;但是设计模式实际上是一个可以应用到任何编程语言中的概念。它只是根据你选择的语言,改变相关的语法而已。 - -设计规则将分为下面四个分类: - -* 创建模式 - -* 结构模式 - -* 行为模式 - -* 并行模式 - -在这篇文章中,我们将了解外观模式。它属于结构模式的范畴,因为它处理的是如何使你的代码的结构易于理解和长期保持良好的可维护性。 - -## 外观模式 - -### UML - -![][1] - -### 问题 - -让我们假设,你需要按顺序执行一些操作,在你应用中的多个地方都会请求同样的动作。你必须一遍一遍地把相同的代码拷贝到不同的地方。你完成了这些操作后,但是过了一段时间,你发现在这段代码中需要修改一些东西。 - -你发现问题的所在了吗?我们需要在所有出现这段代码的地方都一一进行修改。这非常痛苦,不是吗? - -### 解决方案 - -作为一种解决方案,你应该做的是创造一个主控制器,它处理所有的重复代码。在需要调用这段代码的地方,我们将只需提供所需的参数给这个主控制器,让它来完成操作。 - -现在,如果我们需要修改一些东西,那我们只需要修改这个主控制器里面的代码,而不需要在所有使用这段过程的地方修改。 - -### 例子 - -在本文中,我们使用简单的例子来说明问题。假设你被赋予了规划你朋友婚礼的任务。如果你自己一个人做所有事情,想象一下你需要搞定的东西。这将导致很高的错误率发生,忽略了某些可能极大地影响你朋友婚礼的东西。 - -这种情况下,你需要一个婚礼策划者,来确保所有的工作都得到良好的管理,很少出错。 - -这里,你作为一个客户端来启动这个过程,然后这个婚礼策划师作为你的一个“门面”,根据你的方向来完成工作。 - -## 代码例子 - -这里,我们将看到多个例子,这些对于网页来说都是非常平常的例子。我们将看到外观设计模式产品结账过程中的实现,但是在看到完美的代码之前,让我们先看看有问题的代码。 - -一个简单的结账过程由下面的步骤组成: - -1. 添加产品到购物车。 - -2. 计算运费 - -3. 计算折扣 - -4. 生成订单 - -### 问题: - - // Simple CheckOut Process - $productID = $_GET['productId']; - - $qtyCheck = new productQty(); - - if($qtyCheck->checkQty($productID) > 0) { - - // Add Product to Cart - $addToCart = new addToCart($productID); - - // Calculate Shipping Charge - $shipping = new shippingCharge(); - $shipping->updateCharge(); - - // Calculate Discount Based on - $discount = new discount(); - $discount->applyDiscount(); - - $order = new order(); - $order->generateOrder(); - } - - -在上面的代码中,你将发现这个结账过程包括了多个需要被生成的对象来完成整个结账操作。想象一下你需要在多个地方实现这个过程。如果是这样的话,当这些代码需要修改时将会是一个问题。最后是把这个过程统一放到一个地方。 - -### 解决方法 - -我们将使用外观模式来实现之前的操作,使得代码更易管理和扩展。 - - class productOrderFacade { - - public $productID = ''; - - public function __construct($pID) { - $this->productID = $pID; - } - - public function generateOrder() { - - if($this->qtyCheck() > 0) { - - // Add Product to Cart - $this->addToCart(); - - // Calculate Shipping Charge - $this->calulateShipping(); - - // Calculate Discount if any - $this->applyDiscount(); - - // Place and confirm Order - $this->placeOrder(); - - } - - } - - private function addToCart () { - /* .. add product to cart .. */ - } - - private function qtyCheck() { - - $qty = 'get product quantity from database'; - - if($qty > 0) { - return true; - } else { - return true; - } - } - - - private function calulateShipping() { - $shipping = new shippingCharge(); - $shipping->calculateCharge(); - } - - private function applyDiscount() { - $discount = new discount(); - $discount->applyDiscount(); - } - - private function placeOrder() { - $order = new order(); - $order->generateOrder(); - } - } - - -现在,我们创建了产品订单的一个外观,我们所需要做的,就像下面这样调用: - - // Note: We should not use direct get values for Database queries to prevent SQL injection - $productID = $_GET['productId']; - - // Just 2 lines of code in all places, instead of a lengthy process everywhere - $order = new productOrderFacade($productID); - $order->generateOrder(); - - -现在,想象一下,当你需要修改这个订单流程的代码时,只需要在外观中修改即可。 - -### 总结 - -简单地说,外观模式就是把一系列的操作封装到一个统一的接口下。 - - [1]: http://images.cnitblog.com/blog/406864/201411/102008521475001.png \ No newline at end of file diff --git "a/_posts/2014-11-03-\350\256\276\350\256\241\346\250\241\345\274\217-\351\200\202\351\205\215\345\231\250\346\250\241\345\274\217.md" "b/_posts/2014-11-03-\350\256\276\350\256\241\346\250\241\345\274\217-\351\200\202\351\205\215\345\231\250\346\250\241\345\274\217.md" deleted file mode 100644 index 3a2a9b2eac..0000000000 --- "a/_posts/2014-11-03-\350\256\276\350\256\241\346\250\241\345\274\217-\351\200\202\351\205\215\345\231\250\346\250\241\345\274\217.md" +++ /dev/null @@ -1,193 +0,0 @@ ---- -layout: post -title: 设计模式:适配器模式 -date: 2014-11-03 -categories: blog -tags: [设计模式] -description: 写不是义务,写本身就是写的报酬。 - ---- - -> 本文翻译自:[Design Patterns: The Adapter Pattern][1] - -在[上一篇文章][2]中,我们介绍了外观模式如何只用一个简单的外观类来简化任何庞大而复杂的系统工作。 - -在这篇文章中,我们将继续讨论设计模式---适配器模式。 当你的代码依赖于一些外部的API或者其它的代码改变很频繁时,可以使用这个特定的模式来解决问题。这种模式属于“结构模式”的范畴,因为它教导我们的代码和类如何构造来更容易地管理和扩展。 - -再次重申,设计模式对于传统的类并没有什么新东西,相反,它展示给我们一种更好地构造我们的类,控制它们的行为和管理它们的创建的方式。 - -> 注意,下面的例子,都是用PHP语言来说明的 - -### 问题 - - class Twitter { - - public function __construct() { - // Your Code here // - } - - public function send($msg) { - // Posting to Twitter // - echo $msg; - } - } - - $twitter = new Twitter(); - $twitter->send('Posting on Twitter'); - - -在上面的代码中,你可以看到,我们正在利用Twitter类简单地发送消息。在这里,我们直接创造Twitter API的对象,并在Twitter类上发布tweet。你会在很多地方使用这些代码。 - -前段时间,Twitter把这个send API方法名改为了 sendTweet。这清楚的表明,对于像我们这样使用这个 send 方法的开发者来说是个大问题。具体来说,我们需要改变所有的send 方法名 为 sendTweet。 想象一下我们需要修改的大量的代码,以及需要对每个功能重新测试一遍需要花费的时间。 - -### 解决办法 - -解决这个问题的一个方法是使用适配器模式。 - -根据维基百科上: - -> 在软件工程中,适配器模式是一种软件设计模式,它允许通过另一个接口使用一个现有的类的接口。它经常被用来使现有的类与其它类一起合作,而无需修改其源代码。 - -这种情况下,我们应该创建一个包装接口来让它变成可能。我们将不改变任何外部类库的代码,因为我们控制不了它们,而且这些外部类库也会随时改变。 - -让我们深入到现在的代码,它显示了运行中的适配器模式: - - // Concrete Implementation of Twitter Class - class Twitter { - - public function __construct() { - // Your Code here // - } - - public function send($msg) { - // Posting to Twitter // - echo $msg; - } - } - - // Simple Interface for each Adapter we create - interface socialAdapter { - public function send($msg); - } - - class twitterAdapter implements socialAdapter { - - private $twitter; - - public function __construct(Twitter $twitter) { - $this->twitter = $twitter; - } - - public function send($msg) { - $this->twitter->send($msg); - } - } - - -查看上面的代码,你应该可以知道我们没有改变主的 Twitter 类,而是为我们的 social adapter 创建了一个接口,以及一个给Twitter的适配器。 - -随后,我们将使用这个适配器类,而不是直接使用Twitter类。当创建一个适配器类时,我们将一个主的Twitter类作为参数传递进去,所以这个适配器类有一个主类的引用,它可以调用主的Twitter类的方法。 - -让我们看看我们如何能够直接利用这个方法: - - // Client Code - $twitter = new twitterAdapter(new Twitter()); - $twitter->send('Posting to Twitter'); - - -现在想象一下Twitter把send 方法改为了 sendTweet方法。然后我们仅仅需要在twitterAdapter中改变相应的名称即可。看一下下面的代码,仅仅一个改变: - - class twitterAdapter implements socialAdapter { - - private $twitter; - - public function __construct(Twitter $twitter) { - $this->twitter = $twitter; - } - - public function send($msg) { - $this->twitter->sendTweet($msg); - } - } - - -### 添加一个新的适配器 - -在这一点上,我们已经看到我们如何使用适配器设计模式克服了上述情况。现在,它很容易添加一个新类依赖于现有的适配器上。比方说,Facebook有一个状态更新的API。 - -同样的,我们应用一个跟Twitter适配器模式一样的适配器,而不是直接使用Facebook类。 - - // Concrete Implementation of Twitter Class - class Facebook { - - public function __construct() { - // Your Code here // - } - - public function updateStatus($msg) { - // Posting to Facebook // - echo $msg; - } - } - - // Facebook Adapter - class facebookAdapter implements socialAdapter { - - private $facebook; - - public function __construct(Facebook $facebook) { - $this->facebook = $facebook; - } - - public function send($msg) { - $this->facebook->updateStatus($msg); - } - } - - - // Client Code - $facebook = new facebookAdapter(new Facebook()); - $facebook->send('Posting to Facebook'); - - -如你所看到的,应用了同样的原则。你定义了一个可用于第三方类的方法,如果一个依赖改变了它的API,你只需要改变这个依赖类,而不用暴露它的外部的接口。 - -### 总结 - -一个伟大的应用程序正在不断挂接到其它库和API,所以我建议我们实行适配器的方法,这样当一个第三方API或库改变了它的代码,我们就不会遇到任何麻烦。 - -我已经尽了最大努力提供一个基本的,但有用的例子来证明适配器设计模式,但如果您有其他意见或问题,请不要犹豫,将它们添加下面的饲料中。 - -### 更新(个人添加) - -在作者发布这篇文章后,有一个回复指出了,前面的适配器中,不应该在创建时,传递特定的第三方类(如 new Twitter(),new Facebook())。也就是不要在适配器中,加入如下的构造方法,这样不是很优雅。 - - public function __construct(Facebook $facebook) { - - -因为这样,我们的适配器就依赖于特定的外部类了。回复者还贴出了自己的代码: - - interface SocialNetworkAdapter { - - public function send($message); - - public function read($postId); // just an example - - } - - class TwitterAdapter implements SocialNetworkAdapter { ... } - - class FacebookAdapter implements SocialNetworkAdapter { ... } - - class LinkedinAdapter implements SocialNetworkAdapter { ... } - - class SocialNetworksHolder { - public function add(SocialNetworkAdapter $adapter) {} - public function sendToAll() {} - } - - -我们直接通过SocialNetworksHolder 来处理所有的发送请求。 - - [1]: http://code.tutsplus.com/tutorials/design-patterns-the-adapter-pattern--cms-22262 - [2]: http://code.tutsplus.com/tutorials/design-patterns-the-facade-pattern--cms-22238 \ No newline at end of file diff --git "a/_posts/2014-11-06-Objective-C\345\256\217\345\256\232\344\271\211\347\232\204\346\224\266\351\233\206.md" "b/_posts/2014-11-06-Objective-C\345\256\217\345\256\232\344\271\211\347\232\204\346\224\266\351\233\206.md" deleted file mode 100644 index 5e1f76ed31..0000000000 --- "a/_posts/2014-11-06-Objective-C\345\256\217\345\256\232\344\271\211\347\232\204\346\224\266\351\233\206.md" +++ /dev/null @@ -1,52 +0,0 @@ ---- -layout: post -title: Objective-C 宏定义的收集 -date: 2014-11-06 -categories: blog -tags: [iOS] -description: 写不是义务,写本身就是写的报酬。 - ---- - -> 本文翻译自:[Collection of Objective-C Macros][1] - -下面你将看到一些关于Objective-C 宏定义的收集,你也可以把你收集的[回复][2]给我(谢谢你!) - - // 度数 转为 弧度 - #define degreesToRadians(x) (M_PI * x / 180.0) - - // 使定时器失效 - #define UA_invalidateTimer(t) [t invalidate]; t = nil; - - // 设备信息 - #define UA_isIPad (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) - #define UA_isIPhone (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) - #define UA_isRetinaDevice ([[UIScreen mainScreen] respondsToSelector:@selector(scale)] && [[UIScreen mainScreen] scale] >= 2) - #define UA_isMultiTaskingSupported ([[UIDevice currentDevice] respondsToSelector:@selector(isMultitaskingSupported)] && [[UIDevice currentDevice] isMultitaskingSupported]) - - - // 线程 - #define UA_runOnMainThread if (![NSThread isMainThread]) { dispatch_sync(dispatch_get_main_queue(), ^{ [self performSelector:_cmd]; }); return; }; - - // 颜色 - #define UA_rgba(r,g,b,a) [UIColor colorWithRed:r/255.0f green:g/255.0f blue:b/255.0f alpha:a] - #define UA_rgb(r,g,b) UA_rgba(r, g, b, 1.0f) - - // 调试和日志 - #ifdef DEBUG< - #define UA_log( s, ... ) NSLog( @"<%@:%d> %@", [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __LINE__, [NSString stringWithFormat:(s), ##__VA_ARGS__] ) - #else - #define UA_log( s, ... ) - #endif - - #define UA_logBounds(view) UA_log(@"%@ bounds: %@", view, NSStringFromRect([view bounds])) - #define UA_logFrame(view) UA_log(@"%@ frame: %@", view, NSStringFromRect([view frame])) - - // 其它的 - #define NSStringFromBool(b) (b ? @"YES" : @"NO") - #define UA_SHOW_VIEW_BORDERS YES - #define UA_showDebugBorderForViewColor(view, color) if (UA_SHOW_VIEW_BORDERS) { view.layer.borderColor = color.CGColor; view.layer.borderWidth = 1.0; } - #define UA_showDebugBorderForView(view) UA_showDebugBorderForViewColor(view, [UIColor colorWithWhite:0.0 alpha:0.25]) - - [1]: http://iosdevelopertips.com/general/some-good-macros-in-ios.html - [2]: http://iosdevelopertips.com/submit-tips \ No newline at end of file diff --git "a/_posts/2014-11-11-Radare-\351\200\206\345\220\221\345\267\245\347\250\213\346\241\206\346\236\266.md" "b/_posts/2014-11-11-Radare-\351\200\206\345\220\221\345\267\245\347\250\213\346\241\206\346\236\266.md" deleted file mode 100644 index 41cac5f866..0000000000 --- "a/_posts/2014-11-11-Radare-\351\200\206\345\220\221\345\267\245\347\250\213\346\241\206\346\236\266.md" +++ /dev/null @@ -1,48 +0,0 @@ ---- -layout: post -title: Radare - 逆向工程框架 -date: 2014-11-11 -categories: blog -tags: [逆向工程] -description: 写不是义务,写本身就是写的报酬。 - ---- - -> 本文翻译自:[Radare – The Reverse Engineering Framework][1] - -Radare开始作为一个简单的命令行界面的十六进制编辑器,支持64位偏移量以从磁盘中搜索和恢复数据。它已演变成由一个以十六进制编辑器为中心所组成的项目,包括组装/分解器,代码分析,脚本功能,分析和图形化代码与数据,并容易与unix集成。从本质上讲,它已经成为一个逆向工程框架,插件等等。 - -radare2本身就是这个十六进制编辑器和调试器的核心。允许从不同的IO访问,如磁盘,网络,内核插件,远程设备,调试过程中打开任何类型的文件,并像纯文本文件一样处理。 - -![][2] - -它实现了一个先进的命令行界面,有关移动文件,分析数据,拆卸,二进制补丁,数据对比,查找,替换,可以使用Ruby,Python和Lua和Perl脚本。 - -### 功能 - -* CLI 和虚拟模式 -* 抽取和粘贴 -* Perl/Python 脚本支持 -* 修补磁盘上的虚拟基地址 -* 类似 vi 的环境和 重复命令 (3x) -* x86-linux/bsd 和 arm-linux 下的调试器 -* 数据书签 (flags) -* 脚本 (还没有任何分支和条件) -* 自身的魔法数据库 (rfile) -* 小/大尾数法转换 -* 数据搜索 -* 在 arm, x86 和 ppc 上显示外部参照(xrefs) -* 数据类型查看 -* 数据块查看 -* 虚拟模式命令 - -你可以在这里下载 radare: - -[radare2-0.9.7.tar.xz][3] - -或者来[这里][4]查看更多信息 - - [1]: http://www.darknet.org.uk/2014/11/radare-reverse-engineering-framework/ - [2]: http://images.cnitblog.com/blog/406864/201411/112222406311973.png - [3]: http://www.radare.org/get/radare2-0.9.7.tar.xz - [4]: http://www.radare.org/ \ No newline at end of file diff --git "a/_posts/2014-11-12-SSH \347\256\241\351\201\223-\344\275\277\347\224\250SSH\350\277\233\350\241\214\347\253\257\345\217\243\350\275\254\345\217\221.md" "b/_posts/2014-11-12-SSH \347\256\241\351\201\223-\344\275\277\347\224\250SSH\350\277\233\350\241\214\347\253\257\345\217\243\350\275\254\345\217\221.md" deleted file mode 100644 index 5fcb441e6b..0000000000 --- "a/_posts/2014-11-12-SSH \347\256\241\351\201\223-\344\275\277\347\224\250SSH\350\277\233\350\241\214\347\253\257\345\217\243\350\275\254\345\217\221.md" +++ /dev/null @@ -1,116 +0,0 @@ ---- -layout: post -title: SSH 管道 - 使用SSH进行端口转发 -date: 2014-11-12 -categories: blog -tags: [Linux] -description: 写不是义务,写本身就是写的报酬。 - ---- - -> 本文翻译自:[SSH Tunnel – Port Forwarding With SSH][1] - -SSH 有很多功能,SSH 管道就是其中一个。SSH管道是两台机器间的安全连接,经常被称为“SSH隧道”,或者“端口转发”。 - -使用SSH,我们可以绑定本地计算机的指定端口到远程计算机上的指定端口。这将在这些机器之间创建加密的SSH 管道,并允许通过本地主机套接字地址直接与远程主机通信。我们可以使用SSH管道来保护不安全的连接或绕过防火墙的不同限制。 - -在我们创建我们的第一个SSH管道之前,你可以在你机器上的命令行下输入 “ssh” 命令来测试是否已经安装了SSH。如果你运行的是CentOS 6 迷你版本的系统,那么你可能需要安装 openssh-clients 软件包(Ubuntu 用户需要安装 openssh-client 软件包)。 - -这里有三种类型的端口转发,以及使用SSH管道的三种方式: - -* 本地端口转发(允许通过中间SSH服务器,从本地套接字地址访问目的套接字地址) - -* 远程端口转发(允许通过中间SSH服务器套接字地址,从远程位置访问localsocket地址) - -* 动态端口转发(SOCKS代理服务器 - 在本文中不涉及详细信息!) - -我每天都使用“SSH管道”(本地端口转发),因为我的一个客户的环境被设计为只能在SSH服务的22端口上访问工作站的Linux服务器。所有其他基础设施设备都通过这个工作站才能访问,所以使用SSH管道是直接访问不同服务的最佳路径。 - -![](http://images.cnitblog.com/blog/406864/201411/122159095225925.png) - -## SSH 管道 - 本地端口转发 - -本地端口转发,您可以从本地机器连接到远程计算机,即使你不能通过本地机器直接连接到远程机器。为了达到这个目的,你需要一个可以通过SSH访问的中间服务器,并且这个服务器可以访问这些远程的机器。这个中间服务器可以驻留在您的本地网络内,并受到不同的防火墙策略影响或者是在你的本地网络之外。 - -### 例子 \#1: - -我们可以通过SSH的22端口访问一个工作站机器(用户名:wsuser,主机名:workstation),工作站机器的后面是一个应用服务器(主机名:appserver),在8080端口上运行着Apache Tomcat。我们不能通过本地机器在8080端口上访问这个Apache Tomcat的管理员页面,但是这个工作站机器可以访问Apache Tomcat 网页的8080端口。因此,我们可以创建一个SSH 管道,通过这个工作站,把我们本地机器的本地8080端口转发到这个应用服务器。 - -我们可以在我们的本地机器上运行下面的命令来实现这个需求: - - ssh -f wsuser@workstation -L 8080:appserver:8080 -N - -等到这个工作站机器的SSH 服务器认证成功后,连接就会被建立,当我们在本地机器上打开浏览器,输入 http://localhost:8080 就可以访问到那个应用服务器的Apache Tomcat 网页。 - - -### 例子 \#2: - -假设我们的情况跟例子1一样,除了一个不同,那就是在工作站机器( 用户名: wsuser, 主机名: workstation)和应用服务器(用户名: appuser, 主机名: appserver)之间有一个防火墙,只允许通过SSH在22端口上从工作站机器访问应用服务器,意味着工作站机器不能直接访问Apache Tomcat 的8080端口。 - -还有一种从本地机器访问Apache Tomcat 管理员网页的方法,但是我们需要通过SSH进行两跳。 - -1. SSH 连接到工作站机器 - - ssh wsuser@workstation - -2. 当连接到工作站机器,通过SSH,把8080端口转发到应用服务器 - - ssh -f appuser@appserver -L 8080:localhost:8080 -N - -3. 下一步需要通过SSH,把本地机器的8080端口转发到工作站机器 - - ssh -f wsuser@workstation -L 8080:localhost:8080 -N - -瞧,当我们在本地机器上打开浏览器,输入 http://localhost:8080 就可以访问到那个应用服务器的Apache Tomcat 网页了。 - -## SSH 管道 - 远程端口转发 - -远程端口转发的实现与本地端口转发的实现一样。通过本地端口转发,我们可以使用一个有SSH服务的中间机器,从本地访问远程的机器。而通过远程端口转发,我们可以使用一个有SSH服务的中间机器,从远程访问本地机器。当然,为了实现这个,我们需要通过SSH连接到一个中间机器。当我们没有路由器的管理权限,不能在路由器级别上配置端口转发时,远程端口转发就显得非常有用,SSH管道就可以完成这种伎俩。 - -在我们开始远程端口转发之前,我们必须重新配置一个中间的机器上的SSH服务器来启用它。我们必须编辑“/ etc/ SSH/ sshd_config文件”,把以下选项取消注释并更改为“yes”: - - GatewayPorts yes - -当然,我们需要重启SSH服务。 - -### 例子 \#1: - -我们在本地的8080端口上运行了Apache Tomcat 服务。我们希望某个朋友,不是在本地网络上的,可以在8080端口上访问我们的Apache Tomcat管理员页面,帮助我们配置或者部署新的应用程序。幸运的是,我们有一个WebServer(用户名:myuser, 主机名:webserver),管理着一些网页,可以通过因特网访问的。同时也可以通过SSH,从我们本地机器访问这个WebServer。我们将配置一个远程端口转发,让我们的朋友可以通过这个WebServer来访问运行在我们本地机器上的Apahce Tomcat服务。 - -我们可以通过下面的代码来实现: - - ssh -f myuser@webserver -R 8080:localhost:8080 -N - -瞧,我们的朋友就可以通过WebServer 的8080端口来访问我们本地机器上运行的Apache Tomcat 服务了。 - -正如我们刚到的,这个命令与配置本地端口转发的命令唯一的不同就是把 “-L” 变成了“-R”。 - -## SSH 管道 - 动态端口转发 - -动态端口转发会把你的机器变成一个SOCKS代理。SOCKS代理可以代理所有通过网络或者因特网的请求。但程序通常必须被配置为使用SOCKS代理。 SOCKS代理可以用下面的命令开启: - - ssh -C -D 1080 localmachine - -其中,-c选项启用压缩,-D选项指定动态端口转发和1080是标准的SOCKS代理服务器的端口。下一步将是重新配置你的Web浏览器在端口1080使用127.0.0.1作为SOCKS代理。 - -使用动态端口转发和配置浏览器以使用本地SOCKS代理服务器将加密所有访问您的Web浏览器的流量,使您的连接安全。 - -## SSH 管道 – GeekPeek 提示 - -如果你是在一个非默认端口上运行SSH服务器,运行SSH服务时,需要通过 “-p” 来指定端口。 - - ssh -f wsuser@workstation -p 22222 -L 8080:appserver:8080 -N - -在进行本地端口转发或者远程端口转发之前,仔细检查那些已经在中间服务器上使用的端口,您可以使用netstat命令和grep命令来检查要转发的端口。 - - netstat -anp |grep 8080 - -不要忘了在进行远程端口转发之前,重新配置SSH 服务,已经重启SSH服务器。 - - GatewayPorts yes - -“-f”选项要求SSH运行在后台,“-N”选项告诉SSH不要执行远程命令。如果你不想SSH进入后台,可以移除这两个选项。 - -请确保你的iptables的配置与你配置的端口转发规则兼容! - -[1]: http://geekpeek.net/ssh-tunnel-port-forwarding-ssh/ \ No newline at end of file diff --git "a/_posts/2014-12-05-\346\234\211\345\274\271\346\200\247\345\217\257\344\274\270\345\261\225\347\232\204UICollectionView\345\244\264\351\203\250\350\247\206\345\233\276.md" "b/_posts/2014-12-05-\346\234\211\345\274\271\346\200\247\345\217\257\344\274\270\345\261\225\347\232\204UICollectionView\345\244\264\351\203\250\350\247\206\345\233\276.md" deleted file mode 100644 index 0ae0323449..0000000000 --- "a/_posts/2014-12-05-\346\234\211\345\274\271\346\200\247\345\217\257\344\274\270\345\261\225\347\232\204UICollectionView\345\244\264\351\203\250\350\247\206\345\233\276.md" +++ /dev/null @@ -1,144 +0,0 @@ ---- -layout: post -title: 有弹性,可伸展的UICollectionView 头部视图 -date: 2014-12-05 -categories: blog -tags: [iOS] -description: 写不是义务,写本身就是写的报酬。 - ---- - -> 本文翻译自:[Stretchy UICollectionView headers][1] - -滚动视图的反弹效果可能是iOS中最具特色的效果之一。虽然最初只是华而不实,但随着时间的推移,实际上它已经发挥了一些功能用途,像下拉刷新。另一个很好地应用滚动视图的反弹效果的,就是我最近看到的弹性头部视图。 - -![][2] - -这个效果非常好,当你向下拉动滚动视图时,可以在顶部和底部查看更多的图片内容。你可能已经在[Twitter][3]的iOS应用和[Airbnb][4]清单中看到类似的效果了。这种效果很容易实现,今天我将向您展示如何去做。 - -让我们开始吧。首先,创建一个\[UICollectionViewFlowLayout\]\[3\]的子类。 - - #import - - @interface StretchyHeaderCollectionViewLayout : UICollectionViewFlowLayout - @end - - -如果你曾经摆弄自定义过UICollectionView的布局,那么你可能已经意识到它们非常强大的。这是我们的实现: - - #import "StretchyHeaderCollectionViewLayout.h" - - @implementation StretchyHeaderCollectionViewLayout - - - (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds { - // This will schedule calls to layoutAttributesForElementsInRect: as the - // collectionView is scrolling. - return YES; - } - - - (UICollectionViewScrollDirection)scrollDirection { - // This subclass only supports vertical scrolling. - return UICollectionViewScrollDirectionVertical; - } - - - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect { - - UICollectionView *collectionView = [self collectionView]; - UIEdgeInsets insets = [collectionView contentInset]; - CGPoint offset = [collectionView contentOffset]; - CGFloat minY = -insets.top; - - // First get the superclass attributes. - NSArray *attributes = [super layoutAttributesForElementsInRect:rect]; - - // Check if we've pulled below past the lowest position - if (offset.y < minY) { - - // Figure out how much we've pulled down - CGFloat deltaY = fabsf(offset.y - minY); - - for (UICollectionViewLayoutAttributes *attrs in attributes) { - - // Locate the header attributes - NSString *kind = [attrs representedElementKind]; - if (kind == UICollectionElementKindSectionHeader) { - - // Adjust the header's height and y based on how much the user - // has pulled down. - CGSize headerSize = [self headerReferenceSize]; - CGRect headerRect = [attrs frame]; - headerRect.size.height = MAX(minY, headerSize.height + deltaY); - headerRect.origin.y = headerRect.origin.y - deltaY; - [attrs setFrame:headerRect]; - break; - } - } - } - return attributes; - } - - @end - - -这个自定义的布局会检查我们是否下拉到最低的偏移量,这意味着我们将开始拉伸头部视图。如果真的是,我们找到这个头部元素,根据拉伸的值来增加它的高度和y轴上的偏移量。 - -接下来,在你配置你的集合视图的地方,添加下面的代码: - - // Create a new instance of our stretchy layout and set the - // default size for our header (for when it's not stretched) - StretchyHeaderCollectionViewLayout *stretchyLayout; - stretchyLayout = [[StretchyHeaderCollectionViewLayout alloc] init]; - [stretchyLayout setHeaderReferenceSize:CGSizeMake(320.0, 160.0)]; - - // Set our custom layout - [collectionView setCollectionViewLayout:stretchyLayout]; - // and tell our collection view to always bounce. - [collectionView setAlwaysBounceVertical:YES]; - - // Then register a class to use for the header. - [collectionView registerClass:[UICollectionReusableView class] - forSupplementaryViewOfKind:UICollectionElementKindSectionHeader - withReuseIdentifier:@"ident"]; - -最后一件需要做的事情就是创建我们的头部视图,而且应该是[UICollectionReusableView][5]的子类。我们可以添加一个[UIImageView][6]作为它的子视图,然后使用autoresizing来让它大小合适: - - - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView - viewForSupplementaryElementOfKind:(NSString *)kind - atIndexPath:(NSIndexPath *)indexPath { - // You can make header an ivar so we only ever create one - if (!header) { - - header = [collectionView dequeueReusableSupplementaryViewOfKind:kind - withReuseIdentifier:@"ident" - forIndexPath:indexPath]; - CGRect bounds; - bounds = [header bounds]; - - UIImageView *imageView; - imageView = [[UIImageView alloc] initWithFrame:bounds]; - [imageView setImage:[UIImage imageNamed:@"header-background"]]; - - // Make sure the contentMode is set to scale proportionally - [imageView setContentMode:UIViewContentModeScaleAspectFill]; - // Clip the parts of the image that are not in frame - [imageView setClipsToBounds:YES]; - // Set the autoresizingMask to always be the same height as the header - [imageView setAutoresizingMask:UIViewAutoresizingFlexibleHeight]; - // Add the image to our header - [header addSubview:imageView]; - } - return header; - } - -这个autoresizingMask 会保持图片的高度与头部视图的一致。这个contentMode和clipsToBounds属性将居中图片以及裁切多余的内容。 - -你可以在[Github][7]上找到完整的代码。 - - [1]: https://nrj.io/stretchy-uicollectionview-headers - [2]:http://images.cnitblog.com/blog2015/406864/201503/161716591889553.gif -[3]:https://developer.apple.com/library/ios/documentation/uikit/reference/UICollectionViewFlowLayout_class/Reference/Reference.html - [3]: http://twitter.com/ - [4]: http://airbnb.com/ -[5]:https://developer.apple.com/library/ios/documentation/uikit/reference/UICollectionReusableView_class/Reference/Reference.html -[6]:https://developer.apple.com/library/ios/documentation/uikit/reference/UIImageView_Class/Reference/Reference.html -[7]:https://github.com/nrj/StretchyHeaderCollectionViewLayout \ No newline at end of file diff --git "a/_posts/2014-12-16-UICollectionView\345\256\236\347\216\260\344\270\223\350\276\221\345\260\201\351\235\242\350\247\206\345\267\256\346\273\232\345\212\250.md" "b/_posts/2014-12-16-UICollectionView\345\256\236\347\216\260\344\270\223\350\276\221\345\260\201\351\235\242\350\247\206\345\267\256\346\273\232\345\212\250.md" deleted file mode 100644 index 2c1f349648..0000000000 --- "a/_posts/2014-12-16-UICollectionView\345\256\236\347\216\260\344\270\223\350\276\221\345\260\201\351\235\242\350\247\206\345\267\256\346\273\232\345\212\250.md" +++ /dev/null @@ -1,118 +0,0 @@ ---- -layout: post -title: UICollectionView 实现专辑封面视差滚动 -date: 2014-12-16 -categories: blog -tags: [iOS] -description: 写不是义务,写本身就是写的报酬。 - ---- - -> 本文翻译自:[Parallax scrolling album covers with UICollectionView][1] - -视差效果现在风靡一时,iOS7上更是使用了很多。在新的音乐App中,在iTunes Radio中,都有一种我非常喜欢的特别的视差效果。滚动的专辑封面栈。实现这个效果似乎是一个非常有趣的挑战,今天我将向你展示如何创建这个效果。当然,我们使用的是[UICollectionView][2]。下面是最终的效果。 - -![][3] - -在我开始写代码之前,我想先解释一下我的方法。我们将创建一个[UICollectionViewCell][4],然后放置一个[UIImageView][5]在它的中间位置。这张照片是固定和等量的填充。然后我们将创建几个图像视图,每一个视图都稍微从原来的位置插入,并放在固定的图片视图后面。这些图片将根据当前单元格的滚动位置来流动,就是向左和向右移动,占据空余的空间。 - -这里你可以看到我描述的单元格的轮廓。蓝色的轮廓是我们固定的图片,绿色的轮廓是我们单元格的界限,而我们流动的图片用灰色轮廓标识。 - -接下来让我们决定这个流动图片怎么移动。我们需要知道每一个单元格相对于集合视图界限的位置。让我们创建一个通用的压缩系数来表示这个信息: - -**-1** 代表单元格滚动到视图的右边。 - -**0** 代表单元格完美地位于视图的中间。 - -**1** 代表单元格滚动到视图的左边。 - -这称为标准化([normalization][6]),我们可以做一个简单的线性方程: - - - (CGFloat)parallaxPositionForCell:(UICollectionViewCell *)cell { - - CGRect frame = [cell frame]; - CGPoint point = [[cell superview] convertPoint:frame.origin toView:collectionView]; - - const CGFloat minX = CGRectGetMinX([collectionView bounds]) - frame.size.width; - const CGFloat maxX = CGRectGetMaxX([collectionView bounds]); - - const CGFloat minPos = -1.0f; - const CGFloat maxPos = 1.0f; - - return (maxPos - minPos) / (maxX - minX) * (point.x - minX) + minPos; - } - - -现在我们需要一个方法来在collectionView 滚动时发送这些信息给每一个单元格: - - - (void)scrollViewDidScroll:(UIScrollView *)scrollView { - - for (id cell in [collectionView visibleCells]) { - CGFloat position = [self parallaxPositionForCell:cell]; - [cell setParallaxPosition:position]; // We will implement this next. - } - } - - -接下来我们需要在我们自定义的单元格类中实现*setParallaxPosition:*方法。这个方法将负责根据位置来移动我们的流动图片。因为我们使用通用的压缩系数,我们应该为单元格定义这些值代表什么。 - -**-1** 代表单元格滚动到视图的右边 - -![][7] - -**0** 代表单元格完美地位于视图的中间 - -![][7] - -**1** 代表单元格滚动到视图的左边 - -![][8] - -现在在我们自定义的单元格类中添加如下实现: - - - (void)setParallaxPosition:(CGFloat)position { - - CGRect bounds = [self bounds]; - - // We only use the height dimension for our image view. So the padding - // on either side will be the difference in width divided by 2. - const CGFloat padding = (bounds.size.width - bounds.size.height) / 2.0; - - const CGFloat minOffsetX = -padding; - const CGFloat maxOffsetX = padding; - - const CGFloat minPosition = 1.0; - const CGFloat maxPosition = -1.0; - - // Compute the total offset using a linear equation - CGFloat offsetX = (maxOffsetX - minOffsetX) / (maxPosition - minPosition) * (position - minPosition) + minOffsetX; - - // Divide the total offset among the images that will be moved - offsetX /= ([imageViews count] - 1); - - // Apply the offsetX to each image relative to the first one - CGRect fixedRect = [[imageViews objectAtIndex:0] frame]; - for (NSInteger i = 1; i < [imageViews count]; i++) { - - UIImageView *imageView = [imageViews objectAtIndex:i]; - CGRect imageRect = [imageView frame]; - CGFloat imageWidth = imageRect.size.width; - imageRect.origin.x = CGRectGetMidX(fixedRect) - 0.5 * imageWidth + (offsetX * i); - [imageView setFrame:imageRect]; - } - } - - -我们在这里所做的跟我们之前的非常相似。我们把位置转为-1 到 1范围内,然后转换为一个offsetX值。然后我们遍历这个流动图片视图,应用这个偏移量到每个相对于固定图片的frame上。 - -你可以在 [Github][9] 上查看完整的代码 - - [1]: https://nrj.io/parallax-scrolling-album-covers-with-uicollectionview - [2]: https://developer.apple.com/library/ios/documentation/UIKit/Reference/UICollectionView_class/Reference/Reference.html - [3]: http://images.cnitblog.com/blog2015/406864/201503/161613066733932.gif - [4]: https://developer.apple.com/library/ios/documentation/uikit/reference/UICollectionViewCell_class/Reference/Reference.html - [5]: https://developer.apple.com/library/ios/documentation/uikit/reference/UIImageView_Class/Reference/Reference.html - [6]: http://en.wikipedia.org/wiki/Normalization_(statistics) - [7]: http://images.cnitblog.com/blog2015/406864/201503/161614211428515.png - [8]: http://images.cnitblog.com/blog2015/406864/201503/161615015487460.png - [9]: https://github.com/nrj/ParallaxAlbumCovers \ No newline at end of file diff --git "a/_posts/2014-12-20-iOS7\347\212\266\346\200\201\346\240\217\344\270\212\346\234\211\350\266\243\347\232\204\346\270\220\345\217\230\351\201\256\347\275\251.md" "b/_posts/2014-12-20-iOS7\347\212\266\346\200\201\346\240\217\344\270\212\346\234\211\350\266\243\347\232\204\346\270\220\345\217\230\351\201\256\347\275\251.md" deleted file mode 100644 index 0b687eb9f3..0000000000 --- "a/_posts/2014-12-20-iOS7\347\212\266\346\200\201\346\240\217\344\270\212\346\234\211\350\266\243\347\232\204\346\270\220\345\217\230\351\201\256\347\275\251.md" +++ /dev/null @@ -1,98 +0,0 @@ ---- -layout: post -title: iOS7状态栏上有趣的渐变遮罩 -date: 2014-12-20 -categories: blog -tags: [iOS] -description: 写不是义务,写本身就是写的报酬。 - ---- - -> 本文翻译自:[Fun with gradient masks and the iOS 7 status bar][1] - -在iOS 7以前,设计师和开发者实际上并没有太多考虑到状态栏。它是存在于我们应用的框架frame之外的,而且仅仅占用了22像素高的空间。现在,一切都改变了。这个新的状态栏是在我们应用内的,我们作为应用制作者必须想出一种新的,有趣的方式来处理它。如果你曾经制作过一个有滚动内容的app,那么在某些滚动位置,你可能需要处理下面的情况: - -![][2] - -这好像不行,状态栏文本扰乱了我们的内容。有很多种方式来处理这种情况。我将展示给你一种非常酷的方式来处理这种情况,就是使用透明渐变遮罩和[ CAGradientLayer][3]。下面是结果: - -![][4] - -现在有些东西可以用Photoshop来完成。你可以把上面的背景独立创建一个图片,羽化下面和覆盖在上面。但是我将用代码来向你展示如何实现它。我假定你已经知道怎么设置一个[UICollectionView][5]了,如果没有,这里有一篇[文章][6]覆盖了全部的相关资源。 - -让我们开始吧。创建一个新的集合视图以及设置它的frame来充满整个屏幕。 让后用你的背景图片设置一个新的[UIImageView][7]并设为这个集合视图的backgroundView。 - - collectionView = [[UICollectionView alloc] initWithFrame:[[self view] bounds]]; - // ... set delegate, datasource, etc. - [[self view] addSubview:collectionView]; - [collectionView release]; - - // Set our "space" background image as the backgroundView. - UIImage *background = [UIImage imageNamed:@"space-background"]; - UIImageView *backgroundView = [[UIImageView alloc] initWithImage:background]; - [collectionView setBackgroundView:backgroundView]; - [backgroundView release]; - - -因为我们是通过代码来实现的,所以这里我们只需要这么一张图片即可。剩下的将用代码来实现。 - -下一步本质上是截背景的图,但仅仅是顶部的部分。我们可以使用*[CGImageCreateWithImageInRect][8]*来实现局部截图。这个函数将会从一张图片中意特定的矩形创建另一张图片。 - - // First get a reference to our background image - UIImage *background = [(id)[collectionView backgroundView] image]; - - // This is the rectangle of the image that the status bar is covering - // we also need to adjust it for scale. - CGRect barRect = CGRectMake(0.0f, 0.0f, 320.0f, 28.0f); - barRect.size.width *= [background scale]; - barRect.size.height *= [background scale]; - - // Create an image from the barRect area and convert it to a UIImage - CGImageRef imageRef = CGImageCreateWithImageInRect([background CGImage], barRect); - UIImage *topImage = [UIImage imageWithCGImage:imageRef - scale:[background scale] - orientation:UIImageOrientationUp]; - CGImageRelease(imageRef); - - -如果你在 *topImage*那一行放置一个断点,你可以把鼠标放到上面来查看当前内存中的图片。 - -![][9] - -最后,我们需要用这张图片创建一个视图来放到集合视图上面,然后应用一个透明渐变遮罩给它。我们可以使用 [CAGradientLayer][3],传递一个透明,一个不透明的颜色给它,而且通过*startPoint* *endPoint*来指定覆盖底部的一半。 - - // Create a gradient layer that goes transparent -> opaque - CAGradientLayer *alphaGradientLayer = [CAGradientLayer layer]; - NSArray *colors = [NSArray arrayWithObjects: - (id)[[UIColor colorWithWhite:0 alpha:0] CGColor], - (id)[[UIColor colorWithWhite:0 alpha:1] CGColor], - nil]; - [alphaGradientLayer setColors:colors]; - - // Start the gradient at the bottom and go almost half way up. - [alphaGradientLayer setStartPoint:CGPointMake(0.0f, 1.0f)]; - [alphaGradientLayer setEndPoint:CGPointMake(0.0f, 0.6f)]; - - // Create a image view for the topImage we created above and apply the mask - statusBarView = [[UIImageView alloc] initWithImage:topImage]; - [alphaGradientLayer setFrame:[statusBarView bounds]]; - [[statusBarView layer] setMask:alphaGradientLayer]; - - // Finally, add the masked image view on top of our collection view - [[self view] addSubview:statusBarView]; - [statusBarView release]; - - -这样就可以了。现在你运行你的应用,你将看到你的内容在上方绘模糊一些,你可以很方便地把这些代码应用到其它的应用中。 - -你可以在[Github][6]上找到完整的代码。 - - [1]: https://nrj.io/fun-with-gradient-masks-and-the-ios-7-status-bar - [2]: http://images.cnitblog.com/blog2015/406864/201503/161900096269601.png - [3]: https://developer.apple.com/library/Mac/DOCUMENTATION/GraphicsImaging/Reference/CAGradientLayer_class/Reference/Reference.html - [4]: http://images.cnitblog.com/blog2015/406864/201503/161902247351644.png - [5]: https://developer.apple.com/library/ios/documentation/UIKit/Reference/UICollectionView_class/Reference/Reference.html - [6]: https://github.com/nrj/AlphaGradientStatusBar - [7]: https://developer.apple.com/library/ios/documentation/uikit/reference/UIImageView_Class/Reference/Reference.html - [8]: https://developer.apple.com/library/mac/documentation/graphicsimaging/reference/CGImage/Reference/reference.html#jumpTo_6 - [9]: http://images.cnitblog.com/blog2015/406864/201503/161915251422996.png \ No newline at end of file diff --git "a/_posts/2015-01-05-\347\224\250Swift\345\210\233\345\273\272\344\270\200\344\270\252\350\207\252\345\256\232\344\271\211-\345\217\257\350\260\203\346\225\264\347\232\204\346\216\247\344\273\266.md" "b/_posts/2015-01-05-\347\224\250Swift\345\210\233\345\273\272\344\270\200\344\270\252\350\207\252\345\256\232\344\271\211-\345\217\257\350\260\203\346\225\264\347\232\204\346\216\247\344\273\266.md" deleted file mode 100644 index bd2ffe8a32..0000000000 --- "a/_posts/2015-01-05-\347\224\250Swift\345\210\233\345\273\272\344\270\200\344\270\252\350\207\252\345\256\232\344\271\211-\345\217\257\350\260\203\346\225\264\347\232\204\346\216\247\344\273\266.md" +++ /dev/null @@ -1,480 +0,0 @@ ---- -layout: post -title: 用Swift创建一个自定义-可调整的控件 -date: 2015-01-05 -categories: blog -tags: [iOS] -description: 写不是义务,写本身就是写的报酬。 - ---- - -> 本文翻译自:[HOW TO BUILD A CUSTOM (AND “DESIGNABLE”) CONTROL IN SWIFT][1] - -大约两年前我写了一篇关于如何在iOS里创建自定义控件的教程。那篇教程在开发者社区中非常受欢迎,所以我决定用Swift语言来更新它,同时添加 designale/inspectable 属性的支持,以便直接通过Interface Builder来设计这个控件。 - -在我们开始教程之前,让我们看一下最终的效果: - -![][2] - -### 开始吧! - -* * * - -不管是你自己设计用户界面还是有设计师帮你,UIKit提供的标准控件都无法完全符合你的需求。 - -例如,如果你想创建一个控件让用户选择0到360度的角度呢? - -一个解决方案是创建一个圆形滑块,让用户拖动旋钮选择角度值。这可能是你在很多其它的界面上看到的一种情况,但却不同于UIKit提供的。 - -这就是为什么这是一个完美的例子,让我们使用UIKit预留的功能,创建一些特别的东西。 - -### 子类化UIControl - -* * * - -UIControl 是UIView 的子类,同时也是所有UIKit 控件的父类(例如UIButton,UISlider,UISwitch等等)。 UIControl实例的主要规则就是创建一个逻辑来分发动作到它们的目标上。主要是根据它的状态来绘制特定的用户界面(90%的时间都是在做这个事,例如高亮,选择,禁用)。 - -通过UIControl我们管理三个重要的任务: - -* 绘制用户界面 - -* 跟踪用户交互 - -* 管理Target-Action模式 - -在圆形滑块中,我们将创建一个用户界面(滑块本身),用户可以与之交互(移动旋钮)。用户的决定将会被转换成动作传给控件的目标(控件转换这个旋钮的位置到一个0到360度之间的值,同时应用target/action模式)。 - -好了,我们准备打开Xcode。我提议你在文章的最后下载完整的项目工程,然后跟着这个教程来阅读我的代码。 - -我们将按照上面说的三步来实现。 - -这些步骤完全是模块化的,意味着如果你对我绘制这个组件的方法不感兴趣,你可以直接跳到步骤2和步骤3。 - -打开 BWCircluarSlider.swift文件,然后跟着下一章。 - -### 1) 绘制用户界面 - -* * * - -我喜欢Core Graphics,我想创建一些你可以继续自定义的东西。我唯一想用UIKit绘制的部分就是那个用来展示滑动值的文本框。 - -注意:Core Graphics的一些知识还是必须的,但是你应该还是可以阅读这个代码,我将尽我可能地从头解释。 - -让我们分析一下这个控件不同的部分,以便更好地查看它是如何绘制的。 - -首先,一个黑色圆环定义了滑块的背景。 - -![][3] - -活动的区域会用蓝色到紫色的渐变来填充。 - -![][4] - -那个旋钮是让用户拖动来选择值的 - -![][5] - -最后,一个文本框来表明当前选择的角度。下一节中它将会接受用户的从键盘输入的值。 - -![][6] - -为了绘制这个界面,我门主要使用 drawRect 函数,函数里面的第一步就是获取当前的图形上下文。 - - let ctx = UIGraphicsGetCurrentContext() - - -#### 绘制背景 - -背景是由一个360度的圆弧定义的。这可以通过CGContextAddArc 添加右路径到上下文中,然后添加笔画来实现。 - -下面的代码是用来实现这个简单的任务的: - - //Build the circle - CGContextAddArc(ctx, - CGFloat(self.frame.size.width / 2.0), - CGFloat(self.frame.size.height / 2.0), - radius, - 0, - CGFloat(M_PI * 2), - 0) - - // Set fill/stroke color - UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0).set() - - // Set line info - CGContextSetLineWidth(ctx, 72) - CGContextSetLineCap(ctx, kCGLineCapButt) - - // Draw it! - CGContextDrawPath(ctx, kCGPathStroke) - - -函数CGContextArc 以圆弧中心坐标和半径开始,还需要用弧度表示的开始和结束的角度(你可以在文件BWCircularSlider.swift开头的地方找到一些数学帮助方法)以及最后一个参数来表明绘制方向,0表示逆时针。 - -其它的代码行仅仅是设置,像颜色,行宽。最后我们使用CGContextDrawPath函数绘制这个路径。 - -#### 绘制活动区域 - -这部分有些技巧。我们绘制一个线性渐变遮罩的图片。让我们看看是如何做的。 - -![][7] - -这个遮罩图片像有一个洞穿过去了,所以我们只能看到原始渐变矩形的一部分。 - -一个有趣的方面是,这次这个圆弧绘制了一个阴影,创造了一种模糊效果的遮罩。 - -#### 创建这个遮罩图片: - - UIGraphicsBeginImageContext(CGSizeMake(self.bounds.size.width,self.bounds.size.height)); - let imageCtx = UIGraphicsGetCurrentContext() - CGContextAddArc(imageCtx, - CGFloat(self.frame.size.width/2) , - CGFloat(self.frame.size.height/2), - radius, - 0, - CGFloat(DegreesToRadians(Double(angle))) , - 0); - UIColor.redColor().set() - - //Use shadow to create the Blur effect - CGContextSetShadowWithColor(imageCtx, CGSizeMake(0, 0), CGFloat(self.angle/15), UIColor.blackColor().CGColor); - - //define the path - CGContextSetLineWidth(imageCtx, Config.TB_LINE_WIDTH) - CGContextDrawPath(imageCtx, kCGPathStroke) - - //save the context content into the image mask - var mask:CGImageRef = CGBitmapContextCreateImage(UIGraphicsGetCurrentContext()); - UIGraphicsEndImageContext(); - - -首先我们创建一个图片上下文,然后激活阴影。函数CGContextSetShadowWithColor帮我们选择了: - -* 图形上下文 - -* 偏移值(我们不需要) - -* 模糊值(我们会参数化这个值,在用户进行交互过程中使用当前角度除以15获得一个在模糊区域上的简单动画效果) - -* 颜色 - -我们还是绘制一个圆弧,这次是根据当前的角度。 - -例如,如果实例的角度变量等于360度,则绘制一整个圆,如果是90度,我们只是绘制一部分。最后我们使用CGBitmapContextCreateImage函数来从当前的绘制获取一个图片资源。这个图片将是我们的遮罩。 - -#### 剪切上下文: - -现在我们有了这个遮罩来定义这个可以看到渐变的“洞”。 - -我们使用CGContextClipToMask 函数来剪切上下文,然后传递我们刚刚创建的遮罩。 - - CGContextClipToMask(ctx, self.bounds, mask); - - -最后,我们绘制这个渐变: - - // Split colors in components (rgba) - let startColorComps:UnsafePointer = CGColorGetComponents(startColor.CGColor); - let endColorComps:UnsafePointer = CGColorGetComponents(endColor.CGColor); - - let components : [CGFloat] = [ - startColorComps[0], startColorComps[1], startColorComps[2], 1.0, // Start color - endColorComps[0], endColorComps[1], endColorComps[2], 1.0 // End color - ] - - // Setup the gradient - let baseSpace = CGColorSpaceCreateDeviceRGB() - let gradient = CGGradientCreateWithColorComponents(baseSpace, components, nil, 2) - - // Gradient direction - let startPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect)) - let endPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect)) - - // Draw the gradient - CGContextDrawLinearGradient(ctx, gradient, startPoint, endPoint, 0); - CGContextRestoreGState(ctx); - - -绘制这个渐变需要很多的操作,但基本上分为4个部分(用注释分开了): - -* 定义颜色 - -* 定义渐变方向 - -* 选择一种颜色空间 - -* 创建和绘制渐变 - -感谢这个遮罩,仅仅这个矩形的一部分会可见。 - -#### 绘制旋钮 - -现在我们想在当前的角度下的右边绘制这个旋钮。这一步非常简单(我们只是绘制一个白色圆圈),但是需要一些计算来获得这个旋钮的位置。 - -我们必须使用三角函数来把一个标量值转换成一个CGPoint。不用害怕,这只是使用了正弦和余弦函数。 - - func pointFromAngle(angleInt:Int)->CGPoint{ - - //Circle center - let centerPoint = CGPointMake(self.frame.size.width/2.0 - Config.TB_LINE_WIDTH/2.0, self.frame.size.height/2.0 - Config.TB_LINE_WIDTH/2.0); - - //The point position on the circumference - var result:CGPoint = CGPointZero - let y = round(Double(radius) * sin(DegreesToRadians(Double(-angleInt)))) + Double(centerPoint.y) - let x = round(Double(radius) * cos(DegreesToRadians(Double(-angleInt)))) + Double(centerPoint.x) - result.y = CGFloat(y) - result.x = CGFloat(x) - - return result; - } - - -给定一个角度,找到圆周上的点,我们同时需要圆心和它的半斤。 - -使用正弦函数sin可以获取Y轴上的坐标,使用余弦函数可以获取X轴上的坐标。 - -记住,正弦函数和余弦函数返回的值都是假定半径为1的。我们只需要乘以我们的半径值然后移到相对圆心的位置即可。 - -我希望下面的函数会帮助你更好地理解: - - point.y = center.y + (radius * sin(angle)); - point.x = center.x + (radius * cos(angle)); - - -现在我们知道如何获取旋钮的位置了,可以用下面的函数绘制: - - func drawTheHandle(ctx:CGContextRef){ - - CGContextSaveGState(ctx); - - //I Love shadows - CGContextSetShadowWithColor(ctx, CGSizeMake(0, 0), 3, UIColor.blackColor().CGColor); - - //Get the handle position - var handleCenter = pointFromAngle(angle) - - //Draw It! - UIColor(white:1.0, alpha:0.7).set(); - CGContextFillEllipseInRect(ctx, CGRectMake(handleCenter.x, handleCenter.y, Config.TB_LINE_WIDTH, Config.TB_LINE_WIDTH)); - - CGContextRestoreGState(ctx); - } - - -上面的步骤是: - -* 保存当前的上下文(当你在一个分开的函数中进行绘制时,保存上下文是一种很好的实践)。 - -* 为这个旋钮设置一些阴影。 - -* 定义旋钮的颜色,使用函数CGContextFillEllipseInRect绘制。 - -我们可以在函数 drawRect函数的最后调用这个函数: - - drawTheHandle(ctx) - - -我们已经完成了绘制的部分了。 - -### 2) 跟踪用户的交互 - -子类化UIControl,我们可以重写3个特别的方法来提供自定义的跟踪行为。 - -#### 开始跟踪 - -当在一个控件的范围内发生了一个触摸事件时,消息 beginTrackingWithTouch 会首先发送到这个控件。 - -让我们看看如何重写它吧: - - override func beginTrackingWithTouch(touch: UITouch, withEvent event: UIEvent) -> Bool { - super.beginTrackingWithTouch(touch, withEvent: event) - - return true - } - - -它返回一个Bool 值来决定这个控件是否需要在这个触摸移动时响应。对于我们的情况,需要跟踪触摸的移动,所以我们返回true。这个方法有两个参数,一个是触摸对象,一个是事件。 - -#### 继续跟踪 - -在上一个方法中我们已经指定了我们想跟踪一个持续的事件,所以一个特定的方法: continueTrackingWithTouch 将会在用户执行移动时调用: - - func continueTrackingWithTouch(touch: UITouch, withEvent event: UIEvent) -> Bool - - -这个方法返回一个Bool值来指定一个触摸跟踪是否应该持续。 - -我们可以使用这个函数根据触摸的位置来过滤用户的动作。例如,我们可以选择让触摸的位置在圆圈内时才激活这个控件。但在这里我们的情况是希望处理任何的触摸位置。 - -在这个教程里,这个方法负责改变旋钮的位置(我们将会在下面看到在这个方法里发送动作到目标)。 - -我们以下面的代码重写: - - override func continueTrackingWithTouch(touch: UITouch, withEvent event: UIEvent) -> Bool { - super.continueTrackingWithTouch(touch, withEvent: event) - - let lastPoint = touch.locationInView(self) - - self.moveHandle(lastPoint) - - self.sendActionsForControlEvents(UIControlEvents.ValueChanged) - - return true - } - - -首先我们使用locationInView 来获取触摸的位置。然后我们把它传递给moveHandle函数,这个函数会把这个位置转换成一个有效的旋钮的位置。 - -什么事有效的位置呢? - -因为这个旋钮应该仅仅在圆圈的区域内移动。但是我们不希望强制用户必须在圆圈内移动手指,因为位置太小了,不好操作,体验会很不好。所以我们将接受任何一个触摸位置,然后转换它到滑块圆圈内。 - -moveHandle函数做了这个工作,另外,在这个函数里,我们还把位置转换为角度值。 - - func moveHandle(lastPoint:CGPoint){ - - //Get the center - let centerPoint:CGPoint = CGPointMake(self.frame.size.width/2, self.frame.size.height/2); - //Calculate the direction from a center point and a arbitrary position. - let currentAngle:Double = AngleFromNorth(centerPoint, p2: lastPoint, flipped: false); - let angleInt = Int(floor(currentAngle)) - - //Store the new angle - angle = Int(360 - angleInt) - - //Update the textfield - textField!.text = "\(angle)" - - //Redraw - setNeedsDisplay() - } - - -大部分的工作都是由AngleFromNorth来完成的。给定两个点,它会返回角度值: - - func AngleFromNorth(p1:CGPoint , p2:CGPoint , flipped:Bool) -> Double { - var v:CGPoint = CGPointMake(p2.x - p1.x, p2.y - p1.y) - let vmag:CGFloat = Square(Square(v.x) + Square(v.y)) - var result:Double = 0.0 - v.x /= vmag; - v.y /= vmag; - let radians = Double(atan2(v.y,v.x)) - result = RadiansToDegrees(radians) - return (result >= 0 ? result : result + 360.0); - } - - -注意,不是我写的这个angleFromNorth方法,我是直接从Apple提供的一个clockControl 例子中拿来的。 - -现在我们有了一个表示角度的值,我们保存到angle 属性中,然后更新文本框的值。 - -setNeedDisplay 函数确保 drawRect 方法尽快被调用。 - -#### 结束跟踪 - -下面的方法是当跟踪结束时会触发: - - override func endTrackingWithTouch(touch: UITouch, withEvent event: UIEvent) { - super.endTrackingWithTouch(touch, withEvent: event) - } - - -在这个例子中,我们不需要重写这个方法,但是如果我们想在用户结束与这个控件交互时做些操作,那么这个方法将会很有用。 - -### 3) Target-Action 模式 - -* * * - -到这里,这个圆形滑块就可以工作了。你可以拖动这个旋钮,会看到文本框中的值变化。现在 - -#### 给控件事件发送动作 - -如果我们想与UIControl的行为一致,我们必须在控件的值发生变化时收到通知。为了实现这个,我们可以使用sendActionsForControlEvents函数来指定事件的类型,这里是UIControlEventValueChanged。 - -关于事件的类型还有很多可能的值(在Xcode中,cmd + 鼠标点击UIControlEventValueChanged会看到这个列表值)。例如,如果你的控件是一个UITextField的子类,你可能对UIControlEventEdigitingDidBegin事件感兴趣,或者,如果你想在触摸抬起时收到通知,可以使用UIControlTouchUpInside。 - -如果你看回第二小节,你将看到我们在函数continueTrackingWithTouch返回前调用了sendActionsForControlEvents。 - - self.sendActionsForControlEvents(UIControlEvents.ValueChanged) - - -感谢这个方法,当用户移动旋钮时,每一个注册的对象都会收到关于这个变化的通知。 - -### 如何使用这个控件 - -* * * - -现在我们可以在我们的应用中使用这个自定义的控件了。 - -因为这个控件是UIControl的子类,它不能直接在 Interface Builder 使用。但是别担心,我们可以使用一个UIView 作为桥梁,然后以这个视图的子视图来访问它。 - -打开 BWCircularSliderView.swift 文件来跟着我一起做。查看如下的awakeFromNib 方法: - - override func awakeFromNib() { - - super.awakeFromNib() - - // Build the slider - let slider:BWCircularSlider = BWCircularSlider(startColor:self.startColor, endColor:self.endColor, frame: self.bounds) - - // Attach an Action and a Target to the slider - slider.addTarget(self, action: "valueChanged:", forControlEvents: UIControlEvents.ValueChanged) - - // Add the slider as subview of this view - self.addSubview(slider) - - } - - -我们使用 init:startColor:endColor:frame 方法来实例化一个圆形滑块,顺便指定了颜色和大小。 - -这是一个自定义的初始化方法,用来保存渐变颜色和一个与桥梁视图大小一样的frame。意味着这个控件将继承这个视图的大小(你同样可以用自动布局来实现)。 - -现在我们使用 addTarget:action:forControlEvent: 来定义我们想如何与这个控件进行交互。 - - slider.addTarget(self, action: "valueChanged:", forControlEvents: UIControlEvents.ValueChanged) - - -然后我们可以实现valueChanged方法来在控件值发生变化时做点什么: - - func valueChanged(slider:BWCircularSlider){ - // Do something with the new value... - println("Value changed \(slider.angle)") - } - - -现在检查 重写的方法 willMoveToSuperview - - #if TARGET_INTERFACE_BUILDER - override func willMoveToSuperview(newSuperview: UIView?) { - let slider:BWCircularSlider = BWCircularSlider(startColor:self.startColor, endColor:self.endColor, frame: self.bounds) - self.addSubview(slider) - } - #else - - -这段代码,加上@IBInspectable 和 @IBDesignable 关键字可以实现在 Interface Builder中直接预览视图效果(可以查看这个[文章][8]来了解更多关于 IBDesignable的知识)。 - -(这个 TARGET_INTERFACE_BUILDER 表示我们想要在InterfaceBuilder 中才执行这段代码,而当应用运行时不会执行)。 - -现在我们只需要在Storyboard中添加一个BWCircularSliderView实例,直接在Interface Builder中改变 startColor 和 endColor属性值,然后这个控件的预览效果会立刻显示在屏幕上。 - -### 总结 - -* * * - -通过我在这篇教程一开始说的步骤,你可以创建任何你想要的控件。 - -当然可能还有很多其它的方式来创建类似的效果,但我还是按照苹果的建议,100%展示给你官方文档中建议的方法。 - -### [下载代码][9] - -[1]: http://www.thinkandbuild.it/building-a-custom-and-designabl-control-in-swift/ -[2]: http://images.cnitblog.com/blog2015/406864/201503/201038018131197.gif -[3]: http://images.cnitblog.com/blog2015/406864/201503/201156261738138.png -[4]: http://images.cnitblog.com/blog2015/406864/201503/201158237824168.png -[5]: http://images.cnitblog.com/blog2015/406864/201503/201159230791884.png -[6]: http://images.cnitblog.com/blog2015/406864/201503/201200583761499.png -[7]: http://images.cnitblog.com/blog2015/406864/201503/201329141883565.png -[8]: http://www.thinkandbuild.it/building-custom-ui-element-with-ibdesignable/ -[9]:https://github.com/ariok/BWCircularSlider \ No newline at end of file diff --git "a/_posts/2015-01-24-\350\256\276\350\256\241\346\250\241\345\274\217-\347\255\226\347\225\245\346\250\241\345\274\217.md" "b/_posts/2015-01-24-\350\256\276\350\256\241\346\250\241\345\274\217-\347\255\226\347\225\245\346\250\241\345\274\217.md" deleted file mode 100644 index bd89c24fd2..0000000000 --- "a/_posts/2015-01-24-\350\256\276\350\256\241\346\250\241\345\274\217-\347\255\226\347\225\245\346\250\241\345\274\217.md" +++ /dev/null @@ -1,245 +0,0 @@ ---- -layout: post -title: 设计模式:策略模式 -date: 2015-01-24 -categories: blog -tags: [设计模式] -description: 写不是义务,写本身就是写的报酬。 - ---- - -> 本文翻译自[Design Patterns: The Strategy Pattern][1] - -目前为止我们已经在这个系列中接触了三个设计模式。我们定义了4种类型的设计模式。在这篇文章中,我将讲解 策略模式,这是属于行为类别的设计模式的。 - -你可能会有一个疑问:我们什么时候该使用这个模式呢?当我们有不同的方式(算法)来执行同样的操作,而我们希望应用可以根据传入的参数来选择合适的方式去执行。 - -一个非常简单的例子就是排序。例如,我们有不同的算法来排序数组元素,但是需要根据数组中元素的个数来选择性能最好的算法。 - -## 问题 - -我将拿一个电子商务网站来作为例子。这个网站有多种支付通道,但是这些支付请求不会在前端显示出来,而是会根据用户购物车里面的商品价值来选择合适的支付通道。 - -一个实际点的例子就是,如果购物车里的商品价值少于$500,应该选择标准的PayPal支付通道,但是如果大于或等于$500,那么应该使用信用卡支付通道(假设已经收集了用户的信用卡信息)。 - -如果没有实现一个合适的策略,我们的代码将会像下面这样: - -首先,我们有一个主类,包含了使用Paypal和信用卡支付的方法: - - // Class to pay using Credit Card - class payByCC { - - private $ccNum = ''; - private $ccType = ''; - private $cvvNum = ''; - private $ccExpMonth = ''; - private $ccExpYear = ''; - - public function pay($amount = 0) { - echo "Paying ". $amount. " using Credit Card"; - } - - } - - // Class to pay using PayPal - class payByPayPal { - - private $payPalEmail = ''; - - public function pay($amount = 0) { - echo "Paying ". $amount. " using PayPal"; - } - - } - - // This code needs to be repeated every place where ever needed. - $amount = 5000; - if($amount >= 500) { - $pay = new payByCC(); - $pay->pay($amount); - } else { - $pay = new payByPayPal(); - $pay->pay($amount); - } - - -想象一下,上面中最后的一段代码将会出现在程序的各个地方,如果有一个新的逻辑需要添加或者需要改变旧的逻辑,你需要在每个出现这段代码的地方修补,这是很容易导致bug的。 - -## 解决方法 - -我们将使用策略模式实现同样的需求,这会让我们的代码非常整洁,易懂,可扩展。 - -### 接口 - -首先,我们定义一个接口,让所有不同的支付类实现这个接口: - - interface payStrategy { - public function pay($amount); - } - - class payByCC implements payStrategy { - - - private $ccNum = ''; - private $ccType = ''; - private $cvvNum = ''; - private $ccExpMonth = ''; - private $ccExpYear = ''; - - public function pay($amount = 0) { - echo "Paying ". $amount. " using Credit Card"; - } - - } - - class payByPayPal implements payStrategy { - - private $payPalEmail = ''; - - public function pay($amount = 0) { - echo "Paying ". $amount. " using PayPal"; - } - - } - - -接下来,我们将创建主类,可以使用我们已经创建的不同的策略。 - - class shoppingCart { - - public $amount = 0; - - public function __construct($amount = 0) { - $this->amount = $amount; - } - - public function getAmount() { - return $this->amount; - } - - public function setAmount($amount = 0) { - $this->amount = $amount; - } - - public function payAmount() { - if($this->amount >= 500) { - $payment = new payByCC(); - } else { - $payment = new payByPayPal(); - } - - $payment->pay($this->amount); - - } - } - - -这里你可以看到,我们的条件加载不同支付方法放在了*payAmount* 方法中。让我们把所有代码组合起来,看看我们如何使用这个: - - interface payStrategy { - public function pay($amount); - } - - class payByCC implements payStrategy { - - private $ccNum = ''; - private $ccType = ''; - private $cvvNum = ''; - private $ccExpMonth = ''; - private $ccExpYear = ''; - - public function pay($amount = 0) { - echo "Paying ". $amount. " using Credit Card"; - } - - } - - class payByPayPal implements payStrategy { - - private $payPalEmail = ''; - - public function pay($amount = 0) { - echo "Paying ". $amount. " using PayPal"; - } - - } - - class shoppingCart { - - public $amount = 0; - - public function __construct($amount = 0) { - $this->amount = $amount; - } - - public function getAmount() { - return $this->amount; - } - - public function setAmount($amount = 0) { - $this->amount = $amount; - } - - public function payAmount() { - if($this->amount >= 500) { - $payment = new payByCC(); - } else { - $payment = new payByPayPal(); - } - - $payment->pay($this->amount); - } - } - - $cart = new shoppingCart(499); - $cart->payAmount(); - - // Output - Paying 499 using PayPal - - $cart = new shoppingCart(501); - $cart->payAmount(); - - //Output - Paying 501 using Credit Card - -你可以看到,支付通道的选择对于应用来说是不透明的。根据传入的参数,它可以选择可用的,合适的支付通道。 - -## 添加一个新的策略 - -如果过了不久,用户需要添加一个新的策略(新的支付通道),用不同的逻辑,这种情况将会非常简单。假如我们需要添加一个新的支付通道 moneybooker, 当购物车商品价值多于$500,小于$1000时,使用这种通道。 - -我们只需要创建一个新的策略类,实现我们定义的接口: - - class payByMB implements payStrategy { - - private $mbEmail = ''; - - public function pay($amount = 0) { - echo "Paying ". $amount. " using Money Booker"; - } - - } - -有了新的策略类后,我们需要在主方法 *payAmount* 中进行相应的修改: - - public function payAmount() { - - if($this->amount > 500 && $this->amount < 1000) { - $payment = new payByMB(); - } else if($this->amount >= 500) { - $payment = new payByCC(); - } else { - $payment = new payByPayPal(); - } - - $payment->pay($this->amount); - } - -这样就可以了,只需要修改 *payAmount* 方法。 - -## 总结 - -当我们有不同的方式去执行同样的任务时(在软件编程语言中就是有不同的算法去执行同样的操作),我们就应该考虑使用策略模式。 - - [1]: http://code.tutsplus.com/tutorials/design-patterns-the-strategy-pattern--cms-22796 \ No newline at end of file diff --git "a/_posts/2015-02-10-\345\234\250 OSX\344\270\212\346\236\204\345\273\272\345\222\214\350\277\220\350\241\214Net\347\232\204CoreCLR.md" "b/_posts/2015-02-10-\345\234\250 OSX\344\270\212\346\236\204\345\273\272\345\222\214\350\277\220\350\241\214Net\347\232\204CoreCLR.md" deleted file mode 100644 index bae9d23da1..0000000000 --- "a/_posts/2015-02-10-\345\234\250 OSX\344\270\212\346\236\204\345\273\272\345\222\214\350\277\220\350\241\214Net\347\232\204CoreCLR.md" +++ /dev/null @@ -1,253 +0,0 @@ ---- -layout: post -title: 在 OS X 上构建和运行 .Net 的 CoreCLR -date: 2015-02-10 -categories: blog -tags: [iOS] -description: 写不是义务,写本身就是写的报酬。 - ---- - -> 本文翻译自:[Building and Running .NET’s CoreCLR on OS X][1] - -没错,你没听错,[微软已经开源了.Net 核心的完整运行时实现 CLR][2],而它不仅仅可以运行在Windows 上。他们不是随便地转储一堆ZIP文件到一个FTP服务器上,而是给我们提供了一个功能齐全,易于编译,容易托管在所有人喜欢的文件共享介质上的东西。微软甚至走得更远,在GitHub上设置了双向镜像,这样他们的内部系统会与我们看到的保持同步。这种行为给我留下了深刻的印象。 - -他们最初的版本适用于Windows和Ubuntu。这非常好- 的确是一个很好的开始 - 但是我使用的是Mac系统啊。微软说他们打算在未来支持OS X平台,但只是还没有得到解决。我想当他们有机会时,我大概会在几个月后看看CLR。 - -当然,一旦把东西开源了,他们往往会失控。 - -我们看看非凡的系统程序员[Geoff Norton][3]。他了解CLR的一切。他曾经为 [Mono][4] 工作了几年 - 就是原始开源的.Net ,而且他对于把它带到OS X 有自己的切身体会。 - -好吧,如果你使用了一次伎俩,那么你就可以再使用一次,对吧? - -> 最初的OSX 支持已经刚刚集成进CoreCLR [@DotNet][5] -> -> - Geoff Norton (@geoffnorton) -> -> [February 7, 2015][6] -> -> 我相信我是第一个把所有的开源CLRs带到 64位OS X 上的人。非常开心。 -> -> - Geoff Norton (@geoffnorton) -> -> [February 7, 2015][7] - -一切都是从Geoff的“[有了一些时间让它可以运行在Mac上][8]”的Pull Request开始的。一些时间...我们查看他的Pull Request,可以知道过程是这样的: - -* 修补构建系统,让它知道Linux并不是唯一的Unix分支 - -* 修补了很多汇编指令,因为Mac的指令比Ubuntu的更加严格 - -* [以字节][9](不是操作码)来重写高性能的比特组件,要不Mac不会产生期望的指令。 - -* 大量的unicode变化,为了适应神灵LLVM。 - -总之,它看起来像一个很大的高精度和繁琐的工作,我要感谢Geoff做了。 - -这还不是完美的支持。我与Geoff 要花费一个小时来实现打印一个Hello World。当我写这篇文章时,仍然有很多bugs。但是开源终将以胜利告终。我睡觉前有一个bug,睡醒后就已经被解决了。 - -但足以了!让我们开始吧:让我们构建.NET,写一些代码,然后运行它。 - -## 1\. 获取源码 - -这是很容易的一部分,这个[CoreCLR已经是托管在github上了][10]。你可以fork这个项目,或者直接获取: - - git clone git@github.com:dotnet/coreclr.git - - -你现在有超过200万行的复杂设计的代码(C#,C++和Java),来[使编程更加容易的平台][11]。我推荐一瓶酒和一个不错的grep工具。但在此之前... - -## 2\.获取构建工具 - -[CMake][12]是用来构建Core CLR的工具。我建议使用[Homebrew][13]来安装它。 - - brew install cmake - - -很快,你将有一个很热门的小型构建工具。 - -## 3\.执行构建! - -耶,认真的,就是这么简单: - - ./build.sh amd64 debug - - -仅仅支持 amd64(这已经足够了,苹果已经把32位像一个烫手的山芋一样扔掉了),我们选择做一个调试版本,因为这个项目还处于初期阶段。 - -你将在命令行下看到 **Detected OSX x86_64** ,然后是很多颜色出现。 - -只需要看着这些漂亮的颜色几分钟。不需要担心那些红线;不知道为什么,CMake决定用红色来表示成功。 - -如果你有一瓶葡萄酒,现在是一个好时机,去拿一些饼干或者蔬菜拼盘... - -## 4\.佩服它吧 - -当CMake认为你已经准备好了,就是以下面无害的信息向你打招呼了: - - Repo successfully built. - Product binaries are available at /Users/fak/Projects/coreclr/binaries/Product/amd64/debug - - -我们已经等了将近15年来让这个Repo 成功构建,而现在它成功了。我要向微软的CLR团队花时间来把这个构建打包得这么好脱帽致敬。(这比我下载单个C文件来编译容易多了) - -不管怎样,它已经把自己放到了一个目录下。让我们一起看看我们得到了什么。 - - $ cd binaries/Product/amd64/debug - $ ls -al - total 46032 - drwxr-xr-x 5 fak staff 170 Feb 7 11:47 . - drwxr-xr-x 4 fak staff 136 Feb 7 12:25 .. - -rwxr-xr-x 1 fak staff 49836 Feb 7 11:47 corerun - -rwxr-xr-x 1 fak staff 23503712 Feb 7 11:47 libcoreclr.dylib - -rwxr-xr-x 1 fak staff 4176 Feb 7 11:47 libmscordaccore.dylib - - -那些神奇的颜色创建了3个文件: - -### corerun - -corerun是一个小的驱动程序,接受一个托管程序集,初始化CLR,然后执行该程序集。它与 mono 命令等效,只是有一个糟糕的名字而已。这个每一个你想运行在Mac上的应用的入口点。 - -我们将很快看到它的用处。 - -### libmscordaccore.dylib - -一个代码库没有附件会怎样呢?有些是不属于它的,但是某些人认为包含这些会很有用。 - -libmscordaccore.dylib就是这样。我听说它可以“帮助调试”,但是只有4kb,我很怀疑。不,这只是一个丑陋的软件,否则会完整打包进程序中,我们最好忽略它。 - -### libcoreclr.dylib - -这个就是它的相亲,老爹,重量级人物,超级巨星。 - -libcoreclr 包含三样东西:一个真他妈好的垃圾回收器;一个JIT,是一个类型和元数据系统还有一个平台抽象的容器。好了,非常多的东西,都打包进了一个超级性感的动态库里。 - -还记得那些100MB大的.Net 文件吗? 还记得它们需要多长时间才能安装完吗?还记得一旦你在机器上更新了.Net,所有的应用是怎么使用的吗? - -再也没有这些烦恼了。不再有安装,不再有该死的DLL文件,不再有该死的部署。你想要.Net吗? 下面是一个单一的文件(及其附件),可以通过rsync部署。这就是.NET在一个库中的所有重要组成部分,仅仅24MB。不是由于Silverlight已经在CLR被包装得这么好(和AFAIK,这是一个比Silverlight更复杂的CLR)。 - -喝一小口酒。 - -你现在可以把 “The Book of the Runtime” 这篇文章放在你喜欢的稍后阅读服务上。当你从这些经验中醒来时,这篇文章会等着你,绝望地向你那因为太忙不能学习运行时的大脑灌输它十几年的知识。 - -## 5\.你将需要一个字符类... - -你可能注意到上面的那些文件中缺少了一样东西:mscorlib.dll。就像在说“我的字符类在哪里了?” - -你可以看到,虽然libcoreclr 知道所有的对象和类型,以及怎么管理它们,让它们开心,但是它实际上并不包含所有东西。 - -这个CoreCLR库包含了所有mscorelib的代码,全部都是用C#写的,而这个CoreCLR并不与C#编译器一起!因为没有编译器,这个构建系统不同编译标准的库。这很有趣,但也不是这种方式的有趣。 - -.Net 团队[推荐使用一个Windows机器][14]来通过命令在构建mscorlib: - - >build.cmd unixmscorlib - - -但是,嗯,是的,这个骗你的。 - -这一次又是 Geoff Norton。他已经构建了那个代码并发布了它。[从Geoff 的Dropbox下载这个文件即可][15]。 - -然后把它放到那三个文件所在的目录下: - - $ ls -al - total 51776 - drwxr-xr-x 6 fak staff 204 Feb 7 12:34 . - drwxr-xr-x 4 fak staff 136 Feb 7 12:25 .. - -rwxr-xr-x 1 fak staff 49836 Feb 7 11:47 corerun - -rwxr-xr-x 1 fak staff 23503712 Feb 7 11:47 libcoreclr.dylib - -rwxr-xr-x 1 fak staff 4176 Feb 7 11:47 libmscordaccore.dylib - -rw-r--r--@ 1 fak staff 2937856 Feb 7 12:34 mscorlib.dll - - -恭喜你,这个四个文件组成了一个OS X上完整功能的.Net 实现。 - -## 6\. 写一些代码 - -现在我们有了一个完整功能的.Net,我们需要给它一些东西来运行。如果你身边有些组建,它们会工作得非常好,不过我们还是在OS X上测试一下编译代码吧。我们可以使用Windows上的微软编译器,然后复制一些程序集到OS X上,但这是骗你的。 - -我们将使用[Mono的免费C#编译器][16]。简单[安装Mono][17]后,它就会在你的系统参数PATH路径里了。 - -好的,你知道这里要怎么做了....打开你最喜欢的编辑器,创建一个HelloWorld.cs文件: - - using System; - - public class HelloWorld - { - public static int Main (string[] args) - { - Console.WriteLine ("Hello, world! From CoreCLR."); - return 0; - } - } - - -现在编译它: - - $ dmcs -nostdlib -r:mscorlib.dll HelloWorld.cs - - -这个-nostdlib 选项用来告诉编译器不要自动引用它知道的任何标准库(不仅仅是mscorlib,还包括其它的)。这种情况下,我们只是通过手动明确地指定引用的组件。 - -然后我们通过 -r:mscorlib.dll 来引用我们的 mscorlib。这是必须的前面的选项。 - -事实上,我们可以看到编译器做的工作,我们也已经有了一个准备好执行的程序集: - - $ ls -al *.exe - -rwxr-xr-x 1 fak staff 3072 Feb 7 12:48 HelloWorld.exe - - -## 7\.运行那个小狗 - - $ ./corerun -c . HelloWorld.exe - - Hello, world! From CoreCLR. - - -太棒了。 - -喝一口酒。 - -我们使用 corerun 来开始运行.Net 代码。这个 -c 参数用来指定要运行时的所在的目录(那两个dylibs 和 mscorlib)。因为我所有的工作都在那个目录下的,所以我只是指定 . 即可。 - -这就完成了!我们现在有一个完全功能的工具链来在美美的OS X上编译和运行.Net 代码。 - -## 8\.回馈 - -现在我要提醒你一下:在OS X上的.Net仍然有已知的bugs。你不能使用它来发布你的应用。 - -如果你是一个系统工程师,或者想了解如何才能成为一个 , 那么深入该代码。试试运行你的应用程序,注意崩溃,然后加载LLDB。有可能到头来你会被丢失,但你会迷失在一个充满神秘和可能性的美好地方。 - -然后上来 ,我们将会讨论一切关于coreclr的东西,特别是如何让一些愚蠢的事情停止奔溃。;-) - -## 展望未来 - -这个 CoreCLR 在我脑海里是一个怪胎 - 我不知道怎么拿走它。 - -我们已经对.NET中高质量的开源实现移植到OS X有多年了。我甚至用它来[创建][18]了我赖以生活的[应用程序][19]。我花了近5年来学习它:它的怪癖和权力。除了技术,还有那已经长大了,超过10年的周围成熟的一个惊人的开发者社区。 - -微软不能仅仅丢一堆代码到Github上,然后希望一个社区围绕着它建立起来。事实上,我不知道她们是否想要一个。管理一个社区远比管理 pull request难多了。微软更高的管理层好像太关心Azure以至于没有关心其它非500强企业正在用他们的产品做什么。不过老实说,这非常好。 - -短期内,微软仍会继续利用他们难以置信的编程和测试能力贡献给CLR。我们也会惊叹他们对这些复杂的C++代码做出的精细的变化。当他们在我们宝贵的平台上打破了某些东西时,开源社区仍然会站出来,或者我们决定是时候用这些小宝石做出一些新的,令人难以置信的东西出来。 - -也就是说,目前这仍然是微软的CLR。我很好奇它是否会变成我们的。 - - [1]: http://praeclarum.org/post/110552954728/building-and-running-nets-coreclr-on-os-x - [2]: http://blogs.msdn.com/b/dotnet/archive/2015/02/03/coreclr-is-now-open-source.aspx - [3]: https://twitter.com/geoffnorton - [4]: http://www.mono-project.com/ - [5]: https://twitter.com/DotNet - [6]: https://twitter.com/geoffnorton/status/563908409380962304 - [7]: https://twitter.com/geoffnorton/status/563911442261213185 - [8]: https://github.com/dotnet/coreclr/pull/105 - [9]: https://github.com/dotnet/coreclr/pull/117#issuecomment-73343848 - [10]: https://github.com/dotnet/coreclr - [11]: https://github.com/dotnet/coreclr/blob/master/Documentation/intro-to-clr.md - [12]: http://www.cmake.org/ - [13]: http://brew.sh/ - [14]: https://github.com/dotnet/coreclr/wiki/Building-and-Running-CoreCLR-on-Linux - [15]: https://www.dropbox.com/s/zvl5tsj6peh12km/mscorlib.dll?dl=0 - [16]: https://github.com/mono/mono/tree/master/mcs/mcs - [17]: http://www.mono-project.com/download/ - [18]: http://icircuitapp.com - [19]: http://calca.io \ No newline at end of file diff --git a/_posts/2015-03-02-how-to-write.md b/_posts/2015-03-02-how-to-write.md deleted file mode 100755 index 41ac8ee567..0000000000 --- a/_posts/2015-03-02-how-to-write.md +++ /dev/null @@ -1,276 +0,0 @@ ---- -layout: post -title: 如何正确地练习写作 -date: 2015-3-02 -categories: blog -tags: [总结,知识管理] -description: 通告一下,我已不再每天写千字文,准备采用以下的方法进行练习,由于文章篇幅较长,链接较多,建议到简书或博客进行阅读。 ---- - -##初心 - -2014年2月10日,我开始每天写千字文,到现在(2015年3月3日)已超一年,在这期间,我写了347篇文章,全部放在我的博客(cnfeat.com)和公众号(cnfeat)上。 - -起初我练习千字文的目的很简单:锻炼思考、表达、总结和分享的能力,通过写来提升自己。 - -写的作用,《重来》这本书说: - ->如果准备在一堆人中挑出一个人来做某份工作,那就挑写作能力最好的那个。至于他有没有做过市场、销售、设计、编程或其他什么,倒并不那么重。他们的写作能力迟早会带来好处的。 -> ->这是因为,一个会写作的人,他厉害之处可不仅仅是会写作而已。文法清晰代表思路明晰。那些会写作的人懂得如何与他人进行沟通。他们使得事情变得更好理解了。他们擅于换位思考。他们懂得抓住重点。这些都是你想在一个应聘者身上看到的特质。 - -一年前,我没有写作的习惯,既然要写,就先养成写作的习惯,当时恰好看到王佩的[《每天写一篇千字文靠谱吗?》](http://www.baibanbao.net/2014/02/02/is-it-possible-to-write-1000-words-per-day.html),和简叔的[《如何坚持每天写一千字》](http://www.jianshu.com/p/53eea6022d58),我当时就想,既然他们都说好,那我就开练吧。 - -我当时练习千字文还有两个目的: - -一是想锻炼自己持续做一件事的能力 - -二是想通过写来逼自己更多地接触外界,接收更多的信息(阅读、观影、外出、社交……)通过持续的信息输出来强制自己进行信息输入。 - -这样做带来效果还不错,起码在此期间,千字文的压力迫使我去阅读书籍,写读书笔记,参加社会活动,组建写作社群,认识了各行业的人,这对当时的我都是非常有益的。 - -这期间,我自己摸索了一点写作的经验,大家可以去博客看看[「千字文与写作」](http://cnfeat.com/categories/%E5%8D%83%E5%AD%97%E6%96%87%E4%B8%8E%E5%86%99%E4%BD%9C/)的分类,虽然比较稚嫩,但是毕竟也是真实的记录,相当有比较意义。 - -##问题 - -在写千字文的过程中,我渐渐地发现了以下问题: - -###一、形成舒适区 - -各种被千字文牵引而成的信息输入行为模式(例如阅读、写笔记、总结等)已经养成,写作也已变成一种日常,也就是说,初始目的已经达成,我现在已经进入了每天写千字文的「舒适区」,每天写1000字这件事情对我而言不再是一件难事,我需要进去另一个「学习区」。 - -###二、思考太浅 - -每天写千字文,强制信息输出,好容易会将接到是每天写容易将浅度思考的内容分享出去,就好像拿着一个盘去接水,每次接到一点点就泼出去。比如说我最近写的关于实践卡写作法的文章,我在刚接触这个知识点之后就要匆匆忙忙地要将自己的认识和看法写出去,这种短时间得出东西毕竟十分粗浅薄寡。 - -虽然说这篇写出来的文章可以后续修改,但事实往往残酷:1.0版的想法放出去后,为了赶每天的千字文,并没有再多的时间和动力去做2.0,3.0版的修改(用大脑认知来讲,文章公开发表之后大脑误以为已经完成了指令,不会再驱动人去完善后续版本)。 - -###三、产出质量 - -以前的想法是:既然无法每篇都能写出高质量的文章,那么通过数量的堆积,经历试错,我可能可以捉摸到读者的口味,然后写出高质量的文章。但现在的事实是,我已深知自己的浅薄,需要读更多的书,接触更多源头的知识,才能写出更好的东西。 - -##改变 - -察觉问题不久后,我进入了阳志平发起的开智青年故事会([「开智微信群」入群指南](http://t.cn/RwYcbCP)),阳志平老师指出每天写千字文的不妥之处。 - ->隐私的文字、技术类、笔记类的文章可以每天写,但公开的每天进行持续地文字输出,会给创造力进行损伤,文气会被稀释。天天坚持对外输出这种事情,容易将酷的底线变低,同时你的大脑会习惯这种线性操作,日益写不出更好的文章。 - -虽然自己已经隐隐意识到以上弊端,但当局者迷,或者复盘的意识和能力还不够,或者人就是愿意停留在「舒适区」,还不愿意正视事实。 - -听到这个指正之后,自己猛地惊出一身冷汗,若不是有前辈指点,我岂不是还要原地踏步? - -接下来,阳志平老师还给出写作建议: - -###与其持续写低质量的东西,不如储力写高质量的文章 - -厚积薄发,在适当的时候爆发出来,这样才算是酷的事情,做酷的事情比每天一些小事情所带来的效应会强的得多。 - -所谓生命,是要写出最伟大的作品;所谓时间,是要浪费在最凝聚心力的伟大作品上。 - -###争取高水准人群的认同 - -社交媒体,因为庸众太多,太容易获取认可。而这个廉价的认可,会错失一些好机会。一旦年轻时,在社交媒体上获取点赞太多,比较对象就不是历史上的牛人、大家了,就是那群给你点赞的人了,一定要得到高水准的少数人认可自己,才不会陷入妄人境界,成为民科或者妄人。 - -是先有了高水准的少数人认可你,才接着是更多少数人,最后才是大众。每一步,遵从7%原理。而不是反之,这样根基最踏实,否则成名也快,衰落也快。 - - -##方法 - - -###写作可以练习 - -[《文心书话》](http://www.yangzhiping.com/psy/wenxin.html)说到 - ->中国文论讲究不要轻易点出文章「机心」,任读者自行领悟,使得中国文学中充斥着大量不可言说的「道」。一旦探讨写作的具体技巧与指导性强的术,往往被讥笑。所以写作的道始终掌握在少数人手中,站在一个云端来俯视与鄙视那些在金线之下挣扎的百姓们。 - -由此可见,写作自有方法可循,只不过那些「高人们」遮遮掩掩,故弄玄虚,对别人说「功夫在诗外」「文章本天成」,就像考试时得到一本黄冈秘题,练功时得到一本《葵花宝典》,自己生怕别人学到自己引以为傲的手艺。 - -题外话:我有一朋友就深受这种思想的毒害,坚信好文章是靠灵感爆发,随心而成的,结果我等了他好久,他也没有随心出一篇,就算好久随心出一篇,在我看来,那也是无关痛痒的QQ空间式的文章,白白浪费好苗子。 - ->写作要靠灵感,那么,灵感是否可以习得?在七八年前,刘未鹏就研究过灵感的形成,人一生中占据一个显著比例的[「暗时间」](http://mindhacks.cn/2009/12/20/dark-time/),这段「暗时间」存在于你走路、买菜、洗脸洗手、坐公车、逛街、出游、吃饭、睡觉的过程之中,你可以充分利用这些时间进行思考,反刍和消化平时看和读的东西,让你的认识能够脱离照本宣科的层面。这段时间看起来微不足道,但日积月累将会产生庞大的效应。[你利用到的「暗时间」越多,灵感的产出率就越高。](http://blog.csdn.net/pongba/article/details/2270171) - - - -###刻意练习 - -刻意练习不是傻乎乎地,天天坚持做一件事情,而是主动地,有计划地、艰苦地,有创造性去做。 - -关于刻意练习,阳志平老师的这篇文章[《心智工具箱(12):刻意练习》](http://www.yangzhiping.com/psy/Deliberate-Practice.html)提到 - ->刻意练习的任务难度要适中、能收到反馈、有足够的次数重复练习、学习者能够纠正自己的错误。如此如此,必成大器。 其中,多数不靠谱的成功学在于选择了错误的练习方式,虽说喊的口号是刻意练习但实质不是刻意练习,因为没有激活长时工作记忆能力。 - -同人于野在[《练习一万小时成天才》](http://www.geekonomics10000.com/519)中提到 - ->刻意练习不好玩,它要求练习调动大量的身体和精神资源,全力投入。如果你觉得在享受练习的过程,那就不是刻意练习。 找一本小说一边喝咖啡一边看,在一个空闲的下午打场球,这样的活动都非常令人愉快,但是做再多也不会提高技艺。 - -正确的练习方式应该是这样: - -1. 只在「学习区」练习; -2. 把要训练的内容分成有针对性的小块,对每个小块进行重复练习; -3. 在练习的过程中,随时能获得有效的反馈; -4. 练习时注意力必须高度集中。 - -对以上四点做一个简短的补充: - -####学习区 - -练习写作要界定自己的舒适区和学习区,随着知识和技能的掌握程度,舒适区和学习区都会发生转变,要及时调整学习计划和练习方法。 - ->有效的练习任务必须在学习区内进行,它具有高度的针对性。训练者必须随时了解自己最需要改进的地方。一旦已经学会了某个东西,就不应该在上面花时间,应该立即转入下一个困难点。 - ->在舒适区做事,叫生活;在学习区做事,才叫练习。真正的练习不是为了完成一定的量,练习的精髓是要持续地做自己做不好的事情。 - - -####分块练习 - -将要刻意练习的领域分割成各个小块,然后进行有针对的训练。例如哥伦比亚大学的写作课就要包含有描述声音的课程,要说出:那个声音是什么颜色,什么形状,什么质感,给人什么联想, - -####即时反馈 - -为什么那多健过身的人都推荐新手去请私教,因为私教可以即时纠正新手的错误,得到最及时有效的反馈,正反馈越多,进步就越大。 - -关于即时反馈,大家可以看看哥伦比亚大学写作课程的反馈安排: - ->像我这样的曾经的文学青年,如果有鲁迅或王安忆当面批改作品,有舒尔茨每天都在一旁拎着耳朵不断追问「你听到了什么」,最起码我们的文学梦可以做得更长久一些吧。严锋[《作家是怎样炼成的》](http://news.sina.com.cn/c/2009-12-09/112519222865.shtml) - -如果要了解写作的「分块练习」和「即时反馈」,还可以看看这篇[《大学写作是怎样炼成的——哥大核心课程之写作课》](http://www.dfdaily.com/html/150/2013/10/9/1077236.shtml) - -其中,即时反馈效果最好的环境就是群体学习,阳志平老师的[《心智工具箱(12):刻意练习》](http://www.yangzhiping.com/psy/Deliberate-Practice.html)提到: - - ->- 找到学习共同体:因为大量知识存在于学习共同体的实践中,不是书本中,所以有效的学习不是关门苦练,而是找到属于自己的学习小团体。如程序员在类似于github这样的网站练习编程。 ->- 隐性知识显性化:隐性知识是使人们有能力利用概念、事实以及程序来解决现实问题的知识。 ->- 模仿榜样:榜样可以是现实生活中的导师,也可以是网上的导师; ->- 培养多样性:在多种情境中实践,以此强调学习广阔的应用范围。 - -广告时间:暂时进不了哥伦比亚大学的,可以进开智微信群([「开智微信群」入群指南](http://t.cn/RwYcbCP))。 - -####高度集中的注意力 - -当你做一件事,这件事的难度与你的能力、兴趣以及技术相符(刻意练习就要找这种任务来做),你就会全神贯注,行为和意识高度统一,随之进入一种被称之为「心流」(Flow)的状态。在这种状态中,你会忘我,忘记时间、忘记其他所有不相关的东西,完全沉静在某项事物或情境中,从而找到极致的乐趣, - -虽然说刻意练习不好玩,但当练习达到心流状态,好玩的东西就随之而来,这种状态就像好吃的东西一样,你会念念不忘,想方设法再去进入心流,到这个时候,就无须再使用意志力去学习一种东西,而是靠内驱动力去学习一样东西,这样学习才会持久。 - - -###写作频率和练习文体 - -阳志平老师建议写作的频率为: - -1. 每周原创一到两篇 -1. 每周翻译一篇 -1. 每周读书笔记一篇 -1. 每月精华一篇(三千到六千字) - -####1、原创 - -原创就是独立完成的创作,写出自己独有的东西。 - -####2、翻译 - -翻译是提高写作最快的捷径。 - -找名家翻译过名篇,不看翻译,自己先试着翻译,然后对照自己的翻译与名家的翻译,找出区别,最后再翻译一次。 - -推荐用书:[《玩具屋九讲》](http://book.douban.com/subject/20453368/),这本书可以用来精磨慢读。 - ->关于慢读,同人于野在[《用强力研读书》](http://www.geekonomics10000.com/376)中提到,读书人的一个秘密就是,读得慢,吸收知识和增长内力的效率会更高。 -> ->好书应该至少读两遍,第一遍用来理解,第二遍用来强力研读(怎么强力研读?在后面在做读书笔记部分会有介绍)。 - -阳志平老师提到的翻译名家有:王佐良、余光中、钱歌川 、思果 、刘宓庆、郭岱宗。 - -关于翻译的书单:[译道技法](http://www.douban.com/doulist/13967508/) - ->在学习写作和翻译的过程中,也可以运用恶魔奶爸的透析法来学习英语。毕竟掌握了英语,你才能接触到信息源头。 - - -####3、读书笔记 - -写一篇好的读书笔记是一件挺费心力的事,同人于野在[《用强力研读书》](http://www.geekonomics10000.com/376)提到的强力研读法就是一个不错的方法。 - -先选对书,首先,只有为了理解某个我们原来不懂的东西而读书,才值得认真对待。其次,读书应该以自我为主,而不是以书为主。 - -强力研读要求读书笔记必须包括四方面的内容 - -1. 清晰地表现每一章的逻辑脉络; -1. 带走书中所有的亮点; -1. 有大量的自己的看法和心得; -1. 发现这本书和以前读书读过的其他书或文章的联系; - -####4、精华(三千到六千字) - -这篇文章应该凝聚练习者在一个领域内学到的心得、经验和总结等,建议按照写维基的形式来写,文章的架构可以参考一本书的目录,通过树形机构逼自己系统地思考问题。将文章结构搭建完成之后,再完成细枝末节。 - -一个月的时间,足以在一个陌生的领域打开一个口子,大可以将此文当作是自己钻研学习的小论文,放出来让市场接受检验,写长文尤其锻炼人,要写就要写出思维密集高的文章,既然花费而来一个月的时间来写这篇文章,就要写得好一点,让读文章人多多受益,从而吸引更多的人来看文章,这样才算赢了。 - -###开始基础训练 - -大家以前都上过语文课,写过作文,对写进教材的文章十分尊敬,总觉他们这样写是一定有道理的,只不过是自己有问题没搞懂,但事实上,被编进教材的文章其实也有很多问题,例如朱自清的《荷塘月色》就[滥用了比喻](http://www.yangzhiping.com/psy/wenxin06.html),那时候做学生,老师讲什么就听什么,一味地接收,根本不懂得学完之后跳出来返观教材,往往是接收了一点之后就马上去考试,考试一过就忘记了。 - -所以,要重新开始写作,就必须需要重新开始学习语文基础知识,同时向大师们学习。 - -那么,怎么学习语文? - ->中国写作传统不同于西方,两者美学趣味大相径庭。如果说西方是史诗与叙事诗传统,中国则是抒情诗传统。学习写作,还得在西方与古典中寻。 去古典中体悟最美的抒情;往西方练习简约的叙事之道。其中不二法门就是,看最优秀作家的文章,将自己沉浸在美文体验中。 - -按照阳志平老师的观点,学习习作可以向这些人学习。 - -西方是蒙田祖师爷=》休谟=》毛姆=》怀特=》奥威尔 =》平克; - -中国则是韩愈=》辛弃疾=》钱钟书=》余光中或王鼎钧。 - -###文章推荐 - -阳志平[《文心书话》](http://www.yangzhiping.com/psy/wenxin.html),[PDF地址](http://www.yangzhiping.com/files/pubs/wenxin_01.pdf)。 - -王烁:[《有效写作十三篇》](http://wangshuo.blog.caixin.com/archives/821) - - -###书单推荐: - -[《文心书话》第一章推荐阅读书目:](http://www.douban.com/doulist/37792154/) - -http://www.douban.com/doulist/37792154/ - -[阳志平:开始写作吧](http://www.douban.com/doulist/1269878/) - -http://www.douban.com/doulist/1269878/ - -[萧秋水:《作文三书》切实帮助提升写作水平的书 ](http://www.xiaoqiushui.com/archives/8900.html) - -http://www.douban.com/doulist/37792472/ - -备注:早期叫《作文三书》,再版后增加一书,改为《作为四书》。 - ---- - -###**【一期一会】** - -二十一世纪的五大元学科为:1)网络科学;2)认知&神经&心理科学;3)编程;4)数学;5)文艺创作。学习五大元学科,再进入任意一个新领域,容易事倍功倍;任意元学科的交集,容易创新。 - -————阳志平 - ----- - -![](http://cnfeat.qiniudn.com/signitrue-2015-03-05.png) - - -(题图:write your page by awby) - -回复关键词「开智」获得 - -###「开智微信群」入群指南 - -http://t.cn/RwYcbCP - - - - - - - - - - - - diff --git "a/_posts/2015-03-05-iOS8\344\270\213\347\256\200\345\215\225-\345\217\257\344\272\244\344\272\222\345\274\217\347\232\204\351\200\232\347\237\245.md" "b/_posts/2015-03-05-iOS8\344\270\213\347\256\200\345\215\225-\345\217\257\344\272\244\344\272\222\345\274\217\347\232\204\351\200\232\347\237\245.md" deleted file mode 100644 index c9ebf73358..0000000000 --- "a/_posts/2015-03-05-iOS8\344\270\213\347\256\200\345\215\225-\345\217\257\344\272\244\344\272\222\345\274\217\347\232\204\351\200\232\347\237\245.md" +++ /dev/null @@ -1,113 +0,0 @@ ---- -layout: post -title: iOS 8下简单,可交互式的通知 -date: 2015-03-05 -categories: blog -tags: [iOS] -description: 写不是义务,写本身就是写的报酬。 - ---- - -> 本文翻译自:[Simple, interactive notifications in iOS 8][1] - -在iOS 8 中新添加了一个API用来创建可交互式的通知。这些API允许你向在应用外的用户提供额外的功能。我发现网络上缺少清晰的例子,所以我认为我应该写一篇文章来向你展示如何简单地实现这个功能。下面是一个例子。 - -![][2] - -让我们开始吧,在iOS 8中有三个新的类是必须的: [UIUserNotificationSettings][4], [UIUserNotificationCategory][5], [UIUserNotificationAction][6] 以及它们的可变副本。 - -除了简单地注册通知类型(声音,横幅通知,警告),你还可以注册自定义的通知类别和动作。类别描述了一个你应用发送的自定义的通知类型和包含的用户可以执行响应的动作。例如你收到了别人在社交网络上关注你的通知。为了响应,你可能想也关注它们或者忽略。 - -下面是一个用Objectice-C写的简单例子,描述了如何注册一个有两个动作的通知。 - - NSString * const NotificationCategoryIdent = @"ACTIONABLE"; - NSString * const NotificationActionOneIdent = @"ACTION_ONE"; - NSString * const NotificationActionTwoIdent = @"ACTION_TWO"; - - - (void)registerForNotification { - - UIMutableUserNotificationAction *action1; - action1 = [[UIMutableUserNotificationAction alloc] init]; - [action1 setActivationMode:UIUserNotificationActivationModeBackground]; - [action1 setTitle:@"Action 1"]; - [action1 setIdentifier:NotificationActionOneIdent]; - [action1 setDestructive:NO]; - [action1 setAuthenticationRequired:NO]; - - UIMutableUserNotificationAction *action2; - action2 = [[UIMutableUserNotificationAction alloc] init]; - [action2 setActivationMode:UIUserNotificationActivationModeBackground]; - [action2 setTitle:@"Action 2"]; - [action2 setIdentifier:NotificationActionTwoIdent]; - [action2 setDestructive:NO]; - [action2 setAuthenticationRequired:NO]; - - UIMutableUserNotificationCategory *actionCategory; - actionCategory = [[UIMutableUserNotificationCategory alloc] init]; - [actionCategory setIdentifier:NotificationCategoryIdent]; - [actionCategory setActions:@[action1, action2] - forContext:UIUserNotificationActionContextDefault]; - - NSSet *categories = [NSSet setWithObject:actionCategory]; - UIUserNotificationType types = (UIUserNotificationTypeAlert| - UIUserNotificationTypeSound| - UIUserNotificationTypeBadge); - - UIUserNotificationSettings *settings; - settings = [UIUserNotificationSettings settingsForTypes:types - categories:categories]; - - [[UIApplication sharedApplication] registerUserNotificationSettings:settings]; - } - - -为了发送这个类型的通知,可以简单地添加这个类别到 payload。 - - "aps" : { - "alert" : "Pull down to interact.", - "category" : "ACTIONABLE" - } - - -现在可以去处理用户选择的动作了,在[UIApplicationDelegate][3]协议中有两个新方法: - - application:handleActionWithIdentifier:forLocalNotification:completionHandler: - application:handleActionWithIdentifier:forRemoteNotification:completionHandler: - - -当用户从你推送的通知上选择了一个动作后,这些方法会在后台被调用。 - - - (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo completionHandler:(void (^)())completionHandler { - - if ([identifier isEqualToString:NotificationActionOneIdent]) { - - NSLog(@"You chose action 1."); - } - else if ([identifier isEqualToString:NotificationActionTwoIdent]) { - - NSLog(@"You chose action 2."); - } - if (completionHandler) { - - completionHandler(); - } - } - - -根据标识符来判断用户选择了哪个动作,最后按照文档,确保调用那个*completionHandler*。就这样了。这是一个非常简单的例子,只是简单地从表层接触了iOS 8通知可以干什么。在将来的文章中,我会根据深入。Enjoy。 - - [1]: https://nrj.io/simple-interactive-notifications-in-ios-8 - [2]: http://images.cnitblog.com/blog2015/406864/201503/161519161425486.gif - [3]: https://developer.apple.com/Library/ios/documentation/UIKit/Reference/UIApplicationDelegate_Protocol/index.html -[4]:https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIUserNotificationSettings_class/index.html -[5]:https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIUserNotificationCategory_class/index.html -[6]:https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIUserNotificationAction_class/index.html - - - - - - - - - diff --git "a/_posts/2015-05-10-\345\234\250iOS8\344\270\213\347\224\250Swift\345\210\233\345\273\272\350\207\252\345\256\232\344\271\211\347\232\204\351\224\256\347\233\230.md" "b/_posts/2015-05-10-\345\234\250iOS8\344\270\213\347\224\250Swift\345\210\233\345\273\272\350\207\252\345\256\232\344\271\211\347\232\204\351\224\256\347\233\230.md" deleted file mode 100644 index 79d9954ca0..0000000000 --- "a/_posts/2015-05-10-\345\234\250iOS8\344\270\213\347\224\250Swift\345\210\233\345\273\272\350\207\252\345\256\232\344\271\211\347\232\204\351\224\256\347\233\230.md" +++ /dev/null @@ -1,423 +0,0 @@ ---- -layout: post -title: 在iOS8 下用Swift 创建自定义的键盘 -date: 2015-05-10 -categories: blog -tags: [iOS] -description: 写不是义务,写本身就是写的报酬。 - ---- - -> 本文翻译自[How to make a custom keyboard in iOS 8 using Swift][1] - -我将讲解一些关于键盘扩展的基本知识,然后使用iOS 8 提供的新应用扩展API来创建一个莫斯码键盘。大概需要你花20多分钟来走完所有的步骤。 [完整代码][2] - -## 综述 - -一个自定义的键盘会替换系统的键盘,来提供给用户一个新的文本输入方法,或者输入哪些iOS系统还不支持的语言。一个自定义键盘的基本功能很简单:响应点击,手势或者其它输入事件以及在当前的文本输入对象的文本插入点上提供非属性化的NSString对象的文本。 - -当用户选择了一个键盘,那么当用户打开一个app时,这个键盘会作为默认的键盘显示。因此这个键盘必须允许用户切换到另一个键盘。 - -> 对于每个自定义键盘,有两个开发要素: -> -> *信任* : 你的自定义键盘可以访问用户输入的每个字符,所以你和你用户之间的信任非常重要。 -> -> *下一个键盘按键* : 能够让用户切换另一个键盘这种可见性的功能应该是一个键盘用户界面的一部分;你必须提供这个切换功能。 - -注意:如果你只需要添加几个按钮到系统的键盘,你应该查看 [Custom Views for Data Input][3] - -## 一个自定义键盘不能够做什么 - -有一些特定的输入对象是你的自定义键盘没资格输入的:安全领域(例如密码输入框), 电话键盘对象(如在通讯录中的电话号码输入框)。 - -你的自定义键盘不能访问输入视图的层级结构,不能控制光标和选择文本。 - -另外,自定义键盘无法在顶行以上显示任何东西(如系统键盘,当你在顶行长按一个按键时)。 - -## 沙盒 - -默认情况下,一个键盘是没有网络访问权限的,而且也无法与键盘的容器app分享文件。为了获得这些权限,可以在Info.plist 文件中设置 `RequestsOpenAccess` 这个布尔类型的键的值为 YES。 做这些会扩展键盘的沙盒,如 [Establishing and Maintaining User Trust.][4]中描述的。 - -如何你这么做来申请开放权限,你的键盘会获得一下功能,每一个都伴随着责任: - -* 访问位置服务和 Address BOOK 数据库,在第一次访问时会要求申请用户权限。 - -* 可以与包含键盘的app共享一个容器,例如这样可以允许在包含键盘的app里面提供一个自定义的词库管理界面。 - -* 能够发送键盘的点击和其它输入事件到服务端去处理。 - -* 访问iCloud,例如确保同一个用户的键盘的设置和你的自动更正词库在所有设备上同步。 - -* 通过包含键盘的app访问Game Center 和 应用内购买。 - -* 如果你设计你的键盘支持手机设备管理(MDM),那么还可以允许与管理的app一起工作。 - -确保你阅读了[ Designing for User Trust][4],它描述了在你申请开放权限的情况下,你尊重和保护用户数据的责任。 - -## 高层视图 - -下面的图片显示了在一个运行的键盘中一些重要的对象,并且显示了在一个典型的开发流程中这些对象来源于哪里。在一个最基本的形式中,我们有一个app包含了键盘扩展和一个控制这个键盘和响应用户事件的`UIInputViewController`对象。 - -![][5] - -这个自定义的键盘模版包含一个 `UIInputViewController`的子类,这是你的键盘的主视图控制器。让我们看看它的接口是怎么定义的: - - class UIInputViewController : UIViewController, UITextInputDelegate, NSObjectProtocol { - - var inputView: UIInputView! - - var textDocumentProxy: NSObject! { get } - - func dismissKeyboard() - func advanceToNextInputMode() - - // This will not provide a complete repository of a language's vocabulary. - // It is solely intended to supplement existing lexicons. - func requestSupplementaryLexiconWithCompletion(completionHandler: ((UILexicon!) -> Void)!) - } - - -* `inputView` 是这个键盘的视图,与`view`属性一样 - -* `dismissKeyboard`方法可以被调用来关闭键盘视图 - -* `advanceToNextInputMode` 是用来切换键盘的 - -* `textDocumentProxy` 是你将用来与当前的文本输入进行交互的对象。 - -例如: - - self.textDocumentProxy.insertText("We ❤ Swift") // inserts the string "We ❤ Swift" at the insertion point - - self.textDocumentProxy.deleteBackward() // Deletes the character to the left of the insertion point - - -* `UIInputViewController` 实现了`UITextInputDelegate`协议,当文本或者选择的文本发生变化时,会使用`selectionWillChange` , `selectionDidChange`, `textWillChange` 和 `textDidChange` 消息来通知你。 - -## 创建一个莫斯码键盘 - -我们将创建一个简单的键盘,可以输入点和破折号,切换键盘,删除一个字符以及关闭键盘。这个例子只通过代码来创建用户界面。我们也可以使用Nib 文件来创建界面-这个会在教程末尾涉及到。 加载Nibs 文件可能会对性能产生负面影响。 - -### 创建一个新的工程 - -打开Xcode 6, 创建一个新的“Single Page Application” 项目,选择 Swift作为开发语言。 - -### 添加一个text field 文本框 - -打开 `Main.storyboard` ,然后从 Component Library 中拖动一个文本框。我们将在后面使用这个来测试我们的键盘。 - -把这个文本框居中,添加必要的约束。 - -![][6] - -暗示: 如果你在 `viewDidLoad` 中调用 `textField.becomeFirstResponder()` 那么当你打开这个app时键盘就会打开。 - -### 添加键盘扩展 - -在navigator中选择项目文件,点击 + 号添加一个新target。 - -![][7] - -选择 `Application Extension` ,使用 `Custom Keyboard` 模版, 命名为`MorseCodeKeyboard`。 - -![][8] - -这样就会创建一个新的组,名叫 `MorseCodeKeyboard`,里面包含了两个文件 `KeyboardViewController.swift` 和 `Info.plist`。 - -### 清理 - -打开 `KeyboardViewController.swift` 文件。这个模版键盘有一个已经创建好的按钮,用来进行切换键盘的。把这些代码从 `viewDidLoad` 中移到一个新的方法 `addNextKeyboardButton` 中。 - - func addNextKeyboardButton() { - self.nextKeyboardButton = UIButton.buttonWithType(.System) as UIButton - - ... - - var nextKeyboardButtonBottomConstraint = NSLayoutConstraint(item: self.nextKeyboardButton, attribute: .Bottom, relatedBy: .Equal, toItem: self.view, attribute: .Bottom, multiplier: 1.0, constant: -10.0) - self.view.addConstraints([nextKeyboardButtonLeftSideConstraint, nextKeyboardButtonBottomConstraint]) - } - - -创建一个 `addKeyboardButtons` 方法,然后在 `viewDidLoad` 中调用它。这样会有助于组织代码。现在我们只是有了几个按钮,但是在实际的项目中会有更多的按钮。 在 `addKeyboardButtons` 中调用 `addNextKeyboardButton` 。 - - class KeyboardViewController: UIInputViewController { - - ... - - override func viewDidLoad() { - super.viewDidLoad() - - addKeyboardButtons() - } - - func addKeyboardButtons() { - addNextKeyboardButton() - } - - ... - - } - - -### 点 - -现在添加点按钮。 创建一个类型为 `UIButton!` 为的 `dotButton` 属性。 - - class KeyboardViewController: UIInputViewController { - - var nextKeyboardButton: UIButton! - var dotButton: UIButton! - - ... - } - - -添加一个 `addDot` 方法。 以一个系统类型的按钮来初始化这个 `dotButton` 属性。给 `TouchUpInside` 事件添加一个回调。 设置一个更大的字体和添加一个圆角。 添加约束来把这个按钮放在离水平中心位置左边 50个点,垂直居中的位置。 代码与 `nextKeyboardButton` 的类似。 - - func addDot() { - // initialize the button - dotButton = UIButton.buttonWithType(.System) as UIButton - dotButton.setTitle(".", forState: .Normal) - dotButton.sizeToFit() - dotButton.setTranslatesAutoresizingMaskIntoConstraints(false) - - // adding a callback - dotButton.addTarget(self, action: "didTapDot", forControlEvents: .TouchUpInside) - - // make the font bigger - dotButton.titleLabel.font = UIFont.systemFontOfSize(32) - - // add rounded corners - dotButton.backgroundColor = UIColor(white: 0.9, alpha: 1) - dotButton.layer.cornerRadius = 5 - - view.addSubview(dotButton) - - // makes the vertical centers equa; - var dotCenterYConstraint = NSLayoutConstraint(item: dotButton, attribute: .CenterY, relatedBy: .Equal, toItem: view, attribute: .CenterY, multiplier: 1.0, constant: 0) - - // set the button 50 points to the left (-) of the horizontal center - var dotCenterXConstraint = NSLayoutConstraint(item: dotButton, attribute: .CenterX, relatedBy: .Equal, toItem: view, attribute: .CenterX, multiplier: 1.0, constant: -50) - - view.addConstraints([dotCenterXConstraint, dotCenterYConstraint]) - } - - -使用 `textDocumentProxy`实现 `dotButton` 的回调。 - - func didTapDot() { - var proxy = textDocumentProxy as UITextDocumentProxy - - proxy.insertText(".") - } - - -在 `addKeyboardButtons` 中调用 `addDot`。 - - func addKeyboardButtons() { - addDot() - - addNextKeyboardButton() - } - - -对于 `dash`,`delete`, `hideKeyboard` 按钮,过程类似。 - -### 破折号 - -代码类似于 `dotButton`,为了把它对称地放在水平中心位置,只需要改变水平约束的常量即可: - - func addDash() { - ... - - // set the button 50 points to the left (-) of the horizontal center - var dotCenterXConstraint = NSLayoutConstraint(item: dotButton, attribute: .CenterX, relatedBy: .Equal, toItem: view, attribute: .CenterX, multiplier: 1.0, constant: -50) - - view.addConstraints([dashCenterXConstraint, dashCenterYConstraint]) - } - - func didTapDash() { - var proxy = textDocumentProxy as UITextDocumentProxy - - proxy.insertText("_") - } - - -### 删除按钮 - -删除按钮会使用 `deleteBackward` 方法从 `textDocumentProxy` 中删除一个字符。 这个布局约束与 `nextKeyboardButton` 对称( .Left -> .Right, .Bottom-> .Top)。 - - func addDelete() { - deleteButton = UIButton.buttonWithType(.System) as UIButton - deleteButton.setTitle(" Delete ", forState: .Normal) - deleteButton.sizeToFit() - deleteButton.setTranslatesAutoresizingMaskIntoConstraints(false) - deleteButton.addTarget(self, action: "didTapDelete", forControlEvents: .TouchUpInside) - - deleteButton.backgroundColor = UIColor(white: 0.9, alpha: 1) - deleteButton.layer.cornerRadius = 5 - - view.addSubview(deleteButton) - - var rightSideConstraint = NSLayoutConstraint(item: deleteButton, attribute: .Right, relatedBy: .Equal, toItem: view, attribute: .Right, multiplier: 1.0, constant: -10.0) - - var topConstraint = NSLayoutConstraint(item: deleteButton, attribute: .Top, relatedBy: .Equal, toItem: view, attribute: .Top, multiplier: 1.0, constant: +10.0) - - view.addConstraints([rightSideConstraint, topConstraint]) - } - - func didTapDelete() { - var proxy = textDocumentProxy as UITextDocumentProxy - - proxy.deleteBackward() - } - - -### 隐藏键盘 - -`hideKeyboardButton` 会在点击时,调用 `dismissKeyboard` 来隐藏键盘: - - func addHideKeyboardButton() { - hideKeyboardButton = UIButton.buttonWithType(.System) as UIButton - - hideKeyboardButton.setTitle("Hide Keyboard", forState: .Normal) - hideKeyboardButton.sizeToFit() - hideKeyboardButton.setTranslatesAutoresizingMaskIntoConstraints(false) - - hideKeyboardButton.addTarget(self, action: "dismissKeyboard", forControlEvents: .TouchUpInside) - - view.addSubview(hideKeyboardButton) - - var rightSideConstraint = NSLayoutConstraint(item: hideKeyboardButton, attribute: .Right, relatedBy: .Equal, toItem: view, attribute: .Right, multiplier: 1.0, constant: -10.0) - - var bottomConstraint = NSLayoutConstraint(item: hideKeyboardButton, attribute: .Bottom, relatedBy: .Equal, toItem: view, attribute: .Bottom, multiplier: 1.0, constant: -10.0) - - view.addConstraints([rightSideConstraint, bottomConstraint]) - } - - -## 使用 Nib 文件 - -为了不用手写这些布局约束,你可以创建一个界面文件,然后直接在上面添加约束。 - -### 创建一个界面文件 - -右击 `MorseCodeKeyboard`组,然后选择 New File. - -![][9] - -选择 User Interface 和 View 模版。 命名为 `CustomKeyboardInterface` - -![][10] - -选择 File's Owner ,改变类名为 `KeyboardViewController` - -![][11] - -在视图中添加一个按钮,设置标题为 `We ❤ Swift` 。 界面类似下面这样: - -![][12] - -### 加载界面 - -在 `init(nibName, bundle)` 构造器中加载 `CustomKeyboard` nib 文件 - - class KeyboardViewController: UIInputViewController { - - ... - - var customInterface: UIView! - - init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) { - super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) - - var nib = UINib(nibName: "CustomKeyBoardInterface", bundle: nil) - let objects = nib.instantiateWithOwner(self, options: nil) - customInterface = objects[0] as UIView - } - - ... - - } - - -### 添加到 inputView - -在 `viewDidLoad` 方法中,添加自定义的界面到inputView中。 - - class KeyboardViewController: UIInputViewController { - - ... - - override func viewDidLoad() { - super.viewDidLoad() - - view.addSubview(customInterface) - - ... - } - - ... - } - - -### 给按钮添加一个回调 - - class KeyboardViewController: UIInputViewController { - - ... - - @IBAction func didTapWeheartSwift() { - var proxy = textDocumentProxy as UITextDocumentProxy - - proxy.insertText("We ❤ Swift") - } - - ... - } - - -### 连接按钮的事件到这个回调上 - -右键这个按钮,然后点击 `touchUpInside` 并拖动到 `didTapWeHeartSwift` 这个 IBAction中 - -![][13] - -最后,代码应该是[这样的][14]。 - -## 在你的设备上安装这个容器app - -在你的设备上运行这个app后,如下来添加你的自定义键盘: - -![][15] - -选择键盘。 - -![][16] - -选择添加一个新键盘。找到我们的 `MorseCode`键盘: - -![][17] - -现在重新运行我们的应用,尽情享受我们的新键盘吧。 - -![][18] - - [1]: https://www.weheartswift.com/make-custom-keyboard-ios-8-using-swift/ - [2]: https://github.com/WeHeartSwift/MorseCode - [3]: https://developer.apple.com/library/prerelease/ios/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/InputViews/InputViews.html#//apple_ref/doc/uid/TP40009542-CH12 - [4]: https://developer.apple.com/library/prerelease/ios/documentation/General/Conceptual/ExtensibilityPG/Keyboard.html#//apple_ref/doc/uid/TP40014214-CH16-SW3 - [5]: http://images.cnitblog.com/blog2015/406864/201505/101852133765446.png - [6]: http://images.cnitblog.com/blog2015/406864/201505/101908095326335.png - [7]: http://images.cnitblog.com/blog2015/406864/201505/101911310798628.png - [8]: http://images.cnitblog.com/blog2015/406864/201505/101913091261741.png - [9]: http://images.cnitblog.com/blog2015/406864/201505/101934252986763.png - [10]: http://images.cnitblog.com/blog2015/406864/201505/101935297049686.png - [11]: http://images.cnitblog.com/blog2015/406864/201505/101936385171863.png - [12]: http://images.cnitblog.com/blog2015/406864/201505/101938074234270.png - [13]: http://images.cnitblog.com/blog2015/406864/201505/101942401887337.png - [14]: https://github.com/WeHeartSwift/MorseCode/blob/master/MorseCodeKeyboard/KeyboardViewController.swift - [15]: http://images.cnitblog.com/blog2015/406864/201505/101944546889180.png - [16]: http://images.cnitblog.com/blog2015/406864/201505/101945389071305.png - [17]: http://images.cnitblog.com/blog2015/406864/201505/101946378294404.png - [18]: http://images.cnitblog.com/blog2015/406864/201505/101947391102115.png \ No newline at end of file diff --git "a/_posts/2015-06-30-Xcode-\344\270\200\344\270\252\346\224\271\345\217\230\344\275\240\347\224\237\346\264\273\347\232\204\345\245\207\346\200\252\350\260\203\350\257\225\346\212\200\345\267\247.md" "b/_posts/2015-06-30-Xcode-\344\270\200\344\270\252\346\224\271\345\217\230\344\275\240\347\224\237\346\264\273\347\232\204\345\245\207\346\200\252\350\260\203\350\257\225\346\212\200\345\267\247.md" deleted file mode 100644 index 063999860e..0000000000 --- "a/_posts/2015-06-30-Xcode-\344\270\200\344\270\252\346\224\271\345\217\230\344\275\240\347\224\237\346\264\273\347\232\204\345\245\207\346\200\252\350\260\203\350\257\225\346\212\200\345\267\247.md" +++ /dev/null @@ -1,68 +0,0 @@ ---- -layout: post -title: Xcode:一个改变你生活的奇怪调试技巧 -date: 2015-06-30 -categories: blog -tags: [iOS] -description: 写不是义务,写本身就是写的报酬。 - ---- - -> 本文翻译自[Xcode: One Weird Debugging Trick That Will Save Your Life][1] - -> 参考[Advanced Debugging in Xcode and Swift][2] - -希望你可以从题目看出我对于这个题目有多开心。不管怎样,让我们回到定期计划编程中... - -在过去的几天里,我一直在早餐时看这个[高级调试和地址消毒剂][3]。里面有个超级酷的调试技巧我希望能够写下来并记住它,从而能够使用它。 - -## 奔溃 - -写这篇博文的一个有趣的地方是,我随机打开一个已有的测试工程,并让它在下面一个我们都死掉的恐怖地方奔溃: - -![][4] - -## 技巧 - -为了看到真实的情况,在你项目的线程中点击 `objc_exception_throw` 来获取下面的信息: - -![][5] - -是的,我发现这些信息都很难懂,但恰恰这就是有趣的地方。在下方的控制台区域输入 `po $arg1` 命令,来获取人类可读的错误信息版本(这里的情况是 这个 nib 不正确) - -![][6] - -好的,这个错误很容易修复 - -## 一个更高级的技巧 - -最后,你可以在项目的 异常断点上添加这个 `po $arg1` 命令,来这个输出自动话: - -![][7] - -祝调试快乐!! - -## 补充 - -如果你想在异常断点发生时还输出程序的调用堆栈,可以再添加一个命令: - -![][8] - -那么输出将是如下: - -![][9] - -如果你想在本地所有工程中使用上面的这些技巧,可以右键这个异常断点,并选择 `Move breakpoint to => user` 就可以了: - -![][10] - - [1]: http://natashatherobot.com/xcode-debugging-trick - [2]: http://www.funky-monkey.nl/blog/2015/07/advanced-debugging-in-xcode-and-swift/ - [3]: https://developer.apple.com/videos/wwdc/2015/?id=413 - [4]: http://images0.cnblogs.com/blog2015/406864/201507/121048195807395.png - [5]: http://images0.cnblogs.com/blog2015/406864/201507/121050255491945.png - [6]: http://images0.cnblogs.com/blog2015/406864/201507/121054359867600.png - [7]: http://images0.cnblogs.com/blog2015/406864/201507/121056503468187.png - [8]: http://images0.cnblogs.com/blog2015/406864/201507/121059070024171.png - [9]: http://images0.cnblogs.com/blog2015/406864/201507/121102443464239.png - [10]: http://images0.cnblogs.com/blog2015/406864/201507/121101256276054.png \ No newline at end of file diff --git a/_posts/2015-07-08-Swift-Xcode-7-Beta3-new-stuff.md b/_posts/2015-07-08-Swift-Xcode-7-Beta3-new-stuff.md deleted file mode 100644 index c54a05111c..0000000000 --- a/_posts/2015-07-08-Swift-Xcode-7-Beta3-new-stuff.md +++ /dev/null @@ -1,50 +0,0 @@ ---- -layout: post -title: Swift: Xcode 7 Beta3 中新增的东西 -date: 2015-07-08 -categories: blog -tags: [iOS] -description: 写不是义务,写本身就是写的报酬。 - ---- - -> 本文翻译自[Swift: New stuff in Xcode 7 Beta 3][1] - -在今天苹果推送的最新beta 版中,下面新增的内容是最让我兴奋的。 - -## 默认枚举命名 - -在最新的 beta 3 版本中,String 类型的枚举如果没有显示赋值,则默认值为该枚举对应的名字。我非常喜欢这个巨大的改进,也是beta 3 中非常大的一个功能。让我们来看一下吧: - -![][2] - -## Explicit Label Exclusion. - -你是否曾经搞混函数中的参数和元组?现在再也不用担心这个问题了。未命名的参数现在要求显示加上 `_` 符号来区分函数`f((x: Double, y: Double))` 和 `f(x:Double, y:Double)` 。现在是这样使用 `f(_ point: (Double, Double))` - -## Arruples - -你现在可以添加元组类型的元素到数组中了。下面的代码是可以正常工作的,尽管在beta 3 中报错。 - -![][3] - -## OBJC 范型 - -Objective-C现在支持范型子类了。我还没有时间去试用这个新功能。我也尝试了 NS_REFINED_FOR_SWIFT 这个宏,让你来创建针对Swift 的增强实现。 - -## 点命令 - -点命令现在可以无限扩展多行了。如下面的例子,点用来扩展前面一行,来解析连体方法和属性: - - let values = split("Lorem ispum eejit".characters, - isSeparator:{$0 == Character(" ")}) - .map({String($0)}) - .map({"item \($0)"}) - .count - - -这种改变的副作用可能会随着发行版文档而改变。现在你不可以在一行的开头使用推断式静态成员变量了。 所以 `.staticVar = value` 已经不起作用了。因为我记不起来我有使用过这种形式了,所以我真的不关心它带来的副作用。 - - [1]: http://ericasadun.com/2015/07/08/swift-new-stuff-in-xcode-7-beta-3/ - [2]: http://images0.cnblogs.com/blog2015/406864/201507/111424203934315.png - [3]: http://images0.cnblogs.com/blog2015/406864/201507/111433235964296.png \ No newline at end of file diff --git a/_posts/2015-07-13-Failed-to-install-WatchKit-App-error-Application-Verification-Failed.md b/_posts/2015-07-13-Failed-to-install-WatchKit-App-error-Application-Verification-Failed.md deleted file mode 100644 index deb9fa2f98..0000000000 --- a/_posts/2015-07-13-Failed-to-install-WatchKit-App-error-Application-Verification-Failed.md +++ /dev/null @@ -1,88 +0,0 @@ ---- -layout: post -title: Failed to install WatchKit App, error: Application Verification Failed -date: 2015-07-13 -categories: blog -tags: [iOS] -description: 写不是义务,写本身就是写的报酬。 - ---- - -> 本文翻译自[Failed to install WatchKit App, error: Application Verification Failed][1] - -![](http://images2015.cnblogs.com/blog/406864/201509/406864-20150913110408387-1655507888.png) - -WatchKit 刚发布没有多久,它的文档还非常少。这样有好也有坏,并因祸得福得使我对这方面的知识挖得比平时更深,学到了也许我不会学到的知识。 - -我之前遇到一个问题,就是用xcrun 命令而不是使用Xcode的 Archive和导出功能来打包一个iOS 和 WatchKit 包到一个 .ipa格式的安装文件中。有可能到头来你也会遇到这种情况,那就是在你的Apple Watch 设备上安装你的WatchKit app 时,会弹出以下的错误信息: - - 'Failed to install WatchKit App, error: - Application Verification Failed'. - -你获取不到任何的堆栈信息,没有控制台输出.... 这种情况就变得非常特殊了。 - -我的情形是在这个特定的工程中,使用一些花哨的构建脚本来自动进行Archived,签名和导出app。这在团队中似乎是一个共同的过程,让整个团队能够更深入地控制他们的持续集成。 - -### xcode-build - -这个xcode-build 命令是用来编译Xcode 项目并生出一个 .app 后缀的文件。你不能把这个 .app 的文件直接分发,因为这个文件没有包含任何的 provision profiles 文件或者开发者的证书。 - -### xrun - -当Xcode 项目被编译到一个 .app 文件后,xcrun 命令就是用来把这个 .app文件打包进一个 .ipa 文件中。这个 .ipa文件包括了前面的那个 .app文件以及 provision profiles 文件和开发者证书,这个 .ipa文件就可以安装到用户 的iOS 设备上了。 - -## WatchKitSupport/ WK Required - -直到现在我才意识到,一个 .ipa 包在任何时候都必须有一个固定的内存结构: - - /Payload/ - /Payload/Application.app - /WatchKitSupport/WK - - -### /Payload/ - -这个目录包含了你的 .app文件,这个 .app文件本身包含了所有你的iOS 应用的资源, .xibs, .plist 。 - -### /Payload/MyApp.app/Plugins - -这个目录下包含了一个 `MyApp_WatchKitExtension.appex` 文件,它本身包含了所有你的WatchKit extension 扩展的资源。 - -### /Payload/MyApp.app/Plugins/MyApp_WatchKitExtension.appex/ - -这个目录下包含了一个 `MyApp_WatchKitApp.app` 文件,它本身包含了所有你的WatchKit App(不是extension 扩展)相关的文件,例如Storyboards,Assets和以及一切在你的Watch 设备上存活的资源。 它同时也包含了 WatchKit Extension 的可执行文件。 - - -### /WatchKitSupport/ - -无文档。 应用如果提供一个 WatchKit 应用,那么就需要这个WatchKitSupport目录以及这个目录里面的一个WK 二进制文件 - -## xcrun vs Xcode - -知道这些后,我对比了xcrun 命令和 Xcode自带的Archive和导出功能所生成的文件目录。 - -### Xcode - - /Payload/ - /Payload/Application.app - /WatchKitSupport/WK - -### xcrun - - /Payload/ - /Payload/Application.app - -然后我发现了一个苹果工程师在 [开发者论坛][2] 上发表的一篇帖子,无意中描述了xcrun 无法支持打包一个提供 WatchKit 应用的 .ipa 包。他和[Kassem Wridan][3]的解决方法似乎是唯一的解决方法了。 - -这肯定不是一个永久性的解决问题的方法,对我来说,这是一个[提交bug报告][4]的绝佳机会。如果你读到这篇文章,你也应该这么做,希望在个问题有一天会修复。 - - -## 更新 - -在最新的 Xcode 7 beta版中,已经修复了这个问题。太好了。 - - -[1]: http://phillfarrugia.com/2015/06/01/xcrun-watchkit/ -[2]:https://devforums.apple.com/message/1119973#1119973 -[3]:http://www.matrixprojects.net/p/watchkit-command-line-builds -[4]:https://openradar.appspot.com/radar?id=5021668984487936 \ No newline at end of file diff --git a/_posts/2015-09-14-what-i-give-is-what-i-get.md b/_posts/2015-09-14-what-i-give-is-what-i-get.md deleted file mode 100644 index e6dfc722c5..0000000000 --- a/_posts/2015-09-14-what-i-give-is-what-i-get.md +++ /dev/null @@ -1,351 +0,0 @@ ---- -layout: post -title: 写就是写本身的报酬 -date: 2015-09-15 -categories: blog -tags: [写作,千字文] -description: 今天下午临时定下要出差。已抵浙江,刚刚安顿。明天一早出发,不便多写。但会在工作之余尽量写。写不是义务,写本身就是写的报酬。 - ---- - -注:此文是2015年6月15日应开智社群邀请而写的讲座稿。 - -Ps - -感谢开智的邀请。很荣幸今天有机会回顾我的学习写作之旅,和大家交流如何写作。我今天分享的主题是「写本身就是写的报酬」,将会分成8个部分。 - - -* 1、为什么写? -* 2、为什么要写千字文? -* 3、我怎么写开始千字文? -* 4、我与简书的故事 -* 5、我与开智的故事 -* 6、写作的进步及变化 -* 7、开智写作班感受分享 -* 8、为什么写? - - -##1、为什么要写? - -「写本身就是写的报酬」 - -在看完2012年柴静《看见》发布会实录之后,我在博客上写下这句话,当时的我还不明晰写对我的意义,我就拿这句话作为了一篇文章的标题来给自己打鸡血。事实上,「写本身就是写的报酬」这句话并不是柴静在发布会上说的,而是2006年在她的博客上说的,原文标题就是《写本身就是写的报酬》。 - -这篇文章的全文如下: - ->今天下午临时定下要出差。已抵浙江,刚刚安顿。明天一早出发,不便多写。但会在工作之余尽量写。写不是义务,写本身就是写的报酬。 - -很多人总喜欢功利性的评价写作这个行为,如果有人问我你现在写这样东西有什么用?我的确很难解释,但柴静2006年忙中抽空写下这句话的时候,她应该没有想到6年之后会出一本书销量过百万的书吧? - -我总喜欢看博客,因为我觉得总能够人日常行动的细节中发现一点什么,我常以为是日常的一点一滴的积累造就了一个人,而不是在颁奖典礼上华丽的讲话。当然,我写东西更源于个人的习惯。以前喜欢乱想东西,但每次都会想完的东西没有记录下来,当下一次再想,又要重新来过,这样感觉做了很多无用功。 - -就像你在写一份文档,每次写都没有保存,每次都要重新来过,一次做很多件事不觉得烦,但一件事重复做很多次就会觉得很烦,于是我就想着,以后干脆想到什么就写什么,虽然这样更麻烦一点,但也只能这样了。 - -![image-2015-06-17-10-51.png](http://upload-images.jianshu.io/upload_images/32598-b9ab149f1f314228.png) - -写对于我就相当在每个人生阶段保存一下,再继续的时候无须重头再来,而且,当每次保存后,我都有一种重生的感觉。写让我感觉到以前走的路子并没有白走,即使是错的,也有错的价值,写下东西,像是节点记录,也像是错题集,总提醒我不要原地踏步。 - -所以说,写作对于我,更多是一种提升自我的方法。 - - -##2、为什么要写千字文? - -真正开始写狠下心来写是在是在2014年新年,那时我刚从一家国企辞职,偶然地在家看到王佩的一篇文章[《每天写一篇千字文靠谱吗?》](http://www.baibanbao.net/2014/is-it-possible-to-write-1000-words-per-day.html) - -这篇文章主要说了三点: - -* 一、千字文的起点可以很低; -* 二、练习有难度; -* 三、可以带来职业的发展; -* 四、塑造性格,磨炼技艺; - -具体是这样说的 - ->**千字文的起点可以很低** - ->如果你想成为一个优秀的Blogger,可以做一个每天写一千字的训练。找不到话题,没关系,想到哪儿,可以写到哪儿,你甚至可以关掉显示器,通过盲打来实现思绪的自由流动。 - ->**练习有难度** - ->在名利的刺激下,每天写千字文是有可能的,因为专栏压力,每天要保持一定高强度的知识输入,才能保证有一定质量的输入,否则就要苦思冥想地搜集素材。王佩举了两个例子:他在2013年写专栏的故事和董桥写散文的经历。 - ->**可以带来职业的发展** - ->王佩在文章举了他朋友月小刀的故事,月小刀每天写千字文为他带来职业的机会。当时招月小刀的经理是这样说的:你写的零售业的那些文章,有的也不怎么样,但是一个年轻人,能够一年时间利用业余时间坚持写一篇千字文,这种毅力让人佩服,用这样的人,我放心。 - ->**塑造性格,磨炼技艺** - ->我只知道,写作是一门手艺,需要大量的、看似重复实则螺旋式上升的练习。每天写一千字,会磨练一个人的技艺,同时塑造一个人的性格。有一门精湛的手艺,再加上不屈不挠的性格,在江湖上混,至少不需要看人脸色、仰人鼻息吧。 - -看完这篇文章后,我忽然觉得自己还没有长时间做过一件事,用现在话说,就是没有growth hacking的态度,既然写千字文有以上的几样好处,自己刚辞职也有空,写上一段时间当挑战自己,或当作自己的简历也可以,于是就开始写上了。 - - - -## 2、我怎么写开始千字文? - -决定写千字文首先要解决三个问题:一是态度,是否相信自己有能力写千字文;二是持续,能否保证自己输出的频率;三是内容,是否有足够的内容输出。 - -### 态度 - -我是知乎的第一批用户,知乎随机给了我一个[专栏](http://zhuanlan.zhihu.com/cnfeat -)名额,当时我不知道写什么就一直空着。看完王佩写的文章之后,我就觉得要有一个新的起点,于是知乎专栏就成为我第一个练习千字文的地方。 - - -这是我第一篇千字文:[《千字文的开始》](http://cnfeat.com/blog/2014/02/10/2014-02-10-qianziwen-start/),原文在知乎专栏(知乎专栏翻页太不人性化,不贴原链接了)。当然,这篇文章现在看来相当励志,每当有人不知底细地过来问,我底子那么差,我能练习写作吗?我都会甩这篇文章给他们看……你看,当初我也就这样(其实现在也不怎么样),你就先写下去再说。 - -再引用一段李笑来在《人人都能用英语》的话:当有人问李笑来,是不是人人都能用英语? - ->这个问题的答案是「能」。而这仅有一个字的答案非常重要。尽管不是每个人都能做到,但,「能」或起码「有可能」这个事实,会改变一切的,至少可以改变一些人的一切。 -> ->这就好像总是有一小部分人会真正明白:1和99之间的差别再大,也不如0和1之间的差别大。知道这一个字的答案,很可能就是一些人重生的起点。 - - -### 持续 - -话说,刚开始写千字文还真是难,难到我第一个月写的东西几乎都是扯淡,每天最难的就是哎呀今天要写千字文了题目是什么要怎么才能写够一千字呢?但难归难,事情还是要去做,办法还得要去想。千里之行始于足下,既然要练习千字文,那就先从写好一条朋友圈开始。 - -朋友圈是练习写作的好场所,培养的是自己每天写作的习惯,坚持每日写一点点,有主题,有目的,有内容地写,这样持续一周,每天有时间的时候思考一日之所得,积累写作的素材。 - -蔡志浩在《[写作,从一百字开始》](http://taiwan.chtsai.org/2014/02/22/xiezuo_cong_yibai_zi_kaishi/):中说到 - ->很多人觉得写作很困难。困难的关键不在文笔,而在观点。如果没有观点,文笔再好,文章也不会好。如果你想练习用文字精准表达想法却不知怎么做,我建议从一百字开始。不要小看这上限一百字的文本,它的力量是很大的。 - ->刚开始练习的时候,你一定会经常觉得一百字怎么这么快就用完了。这时要仔细检视,删掉赘字赘词赘词句,找出更浓缩的表达方式。这是一个反复压缩的过程。相信我,当你觉得压缩到不能再压缩的时候,还是可以再压缩的。 - ->如果字数压缩到了极限,想法还是没有全塞进一百字中,这时就要评估沟通效果,压缩想法,忍痛删掉不会影响读者整体理解的内容。有点像电影后制的剪辑,不是绝对必要的内容就不要留着。我知道你会舍不得,但还是要删。 - -我写朋友圈的格式,供大家参考一下:【主题】+内容+图片。用简短的文字来描述自己的状态,思考等。同时我也规定自己每天发送不能超过三条,限制发送的条数是让自己保证文字的质量,避免自己的朋友圈变成自己的心情絮语。 - -生活琐碎,但仍有值得记录的地方,小事多多,但也值得改进思考。这样,在朋友圈上建立起一条时间线之后,这样当你回望的时候,就可以看到自己的进步。每天一朋友圈看似容易,其实操作起来还有一定难度,需要找人监督,我就邀请一朋友作为监督人,请朋友来见证改变,如果中断一天则做出惩罚。 - -当然,监督人也要负责为你提供建议,如果你当天晚上十点仍未发出朋友圈,无内容可写,可向监督人求助,监督人可提供帮助,并可要求相应的物质交换。监督的方法因人制宜,合适就好。我当时找的监督人当然就是我的女朋友,她看到我每天写,也愿意给予我支持,我每天写好之后,就发给她,一是检查错别字,二是检查句子文法。#论有女友朋友的好处# - -毕竟写作者最不喜欢检查的就是自己的东西,一开始还真发现了不少错误,接着,我也努力地写好一点,力图不让她检查出错误。说个题外话,写作对找女朋友还是有帮助的,当初还没在一起的时候,我就说要给她写七封信,结果写到第三封的时候就在一起了,到现在还有两封还没写完…… - -以下是我总结的一些内容,可供参考: - -* (1)自我感悟或思考; -* (2)图文故事; -* (3)知识分享; -* (4)好文章总结; -* (5)书籍和电影推荐,注明推荐理由; - -当习惯有主题,有目的,有内容地写作之后,就会发觉,朋友圈简短的语句(其实朋友圈可以发长文,但阅读体验不好)无法表达你的所思所想,这样你就可以写一些短文。坚持写有价值的内容,别人才会看你写的东西,你才会进步。 - -### 内容 - - -每日写千字文最终严重的问题就是写着写着会发觉没有东西可写。就像李笑来在《人人都能用英语》中说,很多人渴求跟外国人聊天,但当他们真有机会和外国人呆在一起的时候,却发现没有什么可以说的,更或者是自己要说的却不知道怎么说出来。 - -这跟我当时的情景实在太相似了,硬要写却没有东西写,有东西写却不知道怎么用文字表达出来,有时候憋了半天只能吱吱呀呀地说上几百字。 - -开始的时候,我把能写的题目都列出来,例如 - -* 1、回顾一下自己写作的经历 -* 2、写你最看的电影、书籍和音乐 -* 3、写一写你最好的朋友 -* 4、在知乎找一个你感兴趣的话题聊一聊 -* 5、写一写你不同时期的自己 -* 6、描述你最近一件让你开心的小事 -* 7、写一写你最近在学的技能 -* 8、…… - -但这终究不能解决问题,瞎摸一个月后,我渐渐地找到了一个靠谱的方法,这个方法其实也很普通,其实就是《钢之炼金术师》的奥义演化:人没有牺牲的话就什么都得不到,为了得到什么东西,就必须付出同等的代价。输出和输入必须对等才能流通,要想保持稳定的输出,就必须想方设法地保持自己有稳定的信息输入渠道,无论是文字还是亲身经历。 - - -接下来是分享的第四部分:我和简书的故事 - -## 4、我和简书的故事 - -千字文大概练了一个礼拜之后,我重头翻看王佩的那篇文章,发现三个重要的细节。一是找到千字文的源头;二是参考别人的写作经验;三是找到合适的写作工具 - -先说源头,千字文的源头在Srinivas Rao的[《How Writing 1000 Words a Day Changed My Life》](https://medium.com/@skooloflife/how-writing-1000-words-a-day-changed-my-life-3895f4d045d2),核心观点是behavior matters more than outcomes,里面提到他在写千字文的六个月期间完成了以下这些事情: - -- I had to produce one piece a week for a freelance writing client. -- I had to produce blog posts for a startup that I held an equity stake in. -- I had to write a weekly newsletter for The Unmistakable Creative Podcast, the podcast that I’m the host and founder of. -- I had to keep writing for my own blog, and the books I wanted to write. - -最后他的结论就是Writing 1000 words a day changed my life. I don’t know how it will change yours. But I’d recommend doing it. - - - -还有另一篇就是[《How to Consistently Write 1000 Words a Day》](http://www.searchenginejournal.com/how-to-consistently-write-1000-words-a-day/),, - -简叔将这篇文章翻译了出来,标题为[《如何坚持每天写一千字》](http://www.jianshu.com/p/53eea6022d58),里面介绍到高产出的方法: - -* 1 无干扰的写作工具 -* 2 障碍最小化 -* 3 即兴写作 -* 4 盲写 -* 5 写作灵感 -* 6 写大纲 -* 7 播种想法 - -第二是简叔已经在实践每天写一千字,并且写出了[《千字练习的正确方式》](http://www.jianshu.com/p/0280096fd1e4),千字文练习更重要的价值并不在于写完。而在于你通过这样的练习明白你要写什么,培养了什么样的写作习惯。而且简书也聚集了一群写作者,上面有一个叫[《谈写作》](http://www.jianshu.com/collection/Df7njb)专题专门记录关于文学写作及其他写作的思考。 - - -第三是写作者们喜欢用Markdown写作。 - -- 纯文本,所以兼容性极强,可以用所有文本编辑器打开。 -- 让你专注于文字而不是排版。 -- 格式转换方便,Markdown 的文本你可以轻松转换为 html、电子书等。 -- Markdown 的标记语法有极好的可读性。 - - -随后,我开始学习Markdown语法,同时在简书写作。不得不说,在简书写作得到激励比知乎专栏多得多,因为当时简书集合了一群爱好写作的人,界面友好,书写简单,还能教会一些最基础的写作知识,我也很喜欢当时简书的slogan:找回文字的力量。 - - -过了两三个月,我发现一个人在练千字文实在太寂寞了,于是在改变自己的公众号上发起一起练习千字文的召集,并建立了两个微信群,和响应召集的人一起练习写作,相互激励,相互促进。 - -在这个时候,我发现,大多数进群的人其实只停留在愿望阶段而已,相当缺乏行动的动力,所以,我试着将我写作千字文的的经验总结了出来,写成了一篇文章[《经验之谈:我是如何做到每天写一千字的》](http://www.jianshu.com/p/9cc021ab4f6d)。 - -我在文章中是这样说的: - ->「持续的日记该如何写?当天的主要行为,加上一点评论和反思。日记不太适合作为抒情的载体,更合适的功能是真实记录生活的痕迹,用以分析、反思,然后自己才有可能提高。如果按照持续日记的方法去写,生活中的小事都可以提炼出一个主题。我写千字文的目的是想让我更加关注生活中的小事情,不再发泄情绪或者抒情。 -> ->每天留心身边发生的事情,观察事情的结果。生活、工作和亲密关系,以上这些都给自己带来各种各样的感受,将这些东西写下来。这是记录,是总结,也是反省。 -> ->将围绕在你身边的东西变成你可信手沾来的素材,积累的东西越多,可以写的东西就越多。 -> ->建议在手机安装一个印象笔记的app,这样你就可以随时随地记录你心中的想法。我现在这篇文章原本在我手机中只是一句话「如何坚持每天写千字文」,现在就变成了好几篇的千字文。 -> ->除了记录你的新想法之外,你也可以记录下你一直想写的题目,积攒起来。这样做的好处就是,虽然你不主动在思考,但是你已经在脑中开了一个头,这些想法会在在你脑中后台运行,就像是沙漏的一个口,点滴的构想会满满汇聚,时间一长,灵感突然就会来到,很多以前没有想过的东西就会蜂拥而至。 -> ->每天一千字是有要求地数量的写作,初期的目标是写,持续地写,有数量持续地写,培养出写作的习惯,然后才是有质量有数量地写。 - - -在和群友们进行沟通之后,我发现他们的写作平台很不一样,有的用lofter,有的用新浪博客,有的在用印象笔记,十分不方便写作的交流,于是便在简书上建了一个专题[《每天写1000字》](http://www.jianshu.com/collection/723de9bac3cd),接收写作练习者们的投稿。练习者们可以在此专题内找到同好,相互促进, - - -其实「每天写1000字」专题的还有一些故事,感兴趣的可以点击这个链接[《「每天写1000字」专题的故事》](http://www.jianshu.com/p/bb2528dbff86),由于这个专题符合当时简书的定位,此专题吸引了不少订阅者,作为专题建立者,我十分意外,觉得我只是做了一件满足写作者需求事情而已,现在这个专题的关注人数已经达到了12.4K,收录的文章有4686篇,有可能是收录文章最多的专题,但这个数据其实没有什么值得骄傲的,因为门槛实在太低,来者不拒。 - - -也是这个时候,我花了一周时间去琢磨如何建立博客,一步一步按照教程,搭建了自己的独立博客:cnfeat.com,将搭建博客的教程文章汇总整理成一篇[《如何搭建一个独立博客——简明Github Pages与Hexo教程》](http://cnfeat.com/2014/05/10/2014-05-11-how-to-build-a-blog/),虽然是摘录,但这篇东西现在还有人在用,虽不是原创,但整理出知识,还是相当有用的。 - - -在成立微信群,总结写作经验、建立写作专题和建立博客的过程中,我发现自己仍有太多东西要学,于是不停看书,写笔记,发觉自己水平有限,实在不能带动一百多人的些写作学习,狠下心把微信群给解散了,重新成立一个只有二十人的写作微信群。 - -我相信,只有二十人的话,彼此紧密带动,还是能够相互促进的,当时建立了共享印象笔记来分享写作资料,现在还在更新中。不过很快地,随着我进入了开智社群,我也给成员们安利了开智,她们进入了开智,我们也就一起被阳老开光了。 - - - -## 5、我和开智的故事 - -进入开智也算是机缘巧合。 - -某天逛豆瓣发现了[开智青年故事会的书单](http://www.douban.com/doulist/36847674/),看到书单下的入群申请格式,第一感觉是这个群貌似好高端,申请要发邮件,范例是博士……心生畏惧,第一次檫肩而过,过了几天,这个链接又出现我眼前,认真一看,是阳志平创办的,头脑中浮现了博客中头像的形象,话说,他的博客好像挺严肃的……过了几天,这个页面第三次出现在我面前,我觉得应该做些什么了,于是认真地写了一封邮件发了过去。为什么要认真?因为里面很多博士,我得跟很多博士站在同一起跑线上…… - -现在回想起来,很多人都挺容易被那个博士的申请格式示范吓住,曾经给同学们几次推荐,她们都说入群要求好像挺高的,待我回去修改下简历再发,后来就没有后来了。 - -进入开智后,明显感觉进入另一个加速世界,一个人进去太危险了,得赶紧把原来写作群的伙伴给安利过去,到时候一起抱团消化安全一点,随后购买了《追时间的人》,过年回家看开智以往的聊天记录,用了两个月的时间才稍微适应了开智的高压氛围。 - - -随后,阳老师对每日千字练习写作的方法提出了意见: - ->与其持续写低质量的东西,不如储力写高质量的文章,厚积薄发,在适当的时候爆发出来,这样才算是酷的事情,做酷的事情比每天一些小事情所带来的效应会强的得多。所谓生命,是要写出最伟大的作品;所谓时间,是要浪费在最凝聚心力的伟大作品上。 -> ->争取高水准人群的认同。社交媒体,因为庸众太多,太容易获取认可。而这个廉价的认可,会错失一些好机会。一旦年轻时,在社交媒体上获取点赞太多,比较对象就不是历史上的牛人、大家了,就是那群给你点赞的人了,一定要得到高水准的少数人认可自己,才不会陷入妄人境界,成为民科或者妄人。 -> ->是先有了高水准的少数人认可你,才接着是更多少数人,最后才是大众。每一步,遵从7%原理。而不是反之,这样根基最踏实,否则成名也快,衰落也快。 - - -* 建议写作频率和练习文体 -* 每周原创一到两篇 -* 每周翻译一篇 -* 每周读书笔记一篇 -* 每月精华一篇(三千到六千字) - -具体的细节在[《如何正确地练习写作》](http://www.jianshu.com/p/2621444b619d),感兴趣的可以翻看。 - -接下来是分享的第六部分:写作的变化 - -##6、写作的变化 - -自被阳老开光之后,我的写作频率开始进行调整,先着力写一两篇的精品,专注主题学习,再穿插一些学习见闻和古书。最近一段时间写出了比较满意的文章是 - -* [《世界并非如你所见——用可供性来发现更大的世界》](http://www.jianshu.com/p/6f1404e0240d) -* [《24款最值得推荐的中文字体》](http://www.jianshu.com/p/84f41d12d2a6) - - -第一篇是阅读《追时间的人》关于可供性篇章的读后感,第二篇是读阳老《字体情绪》后的总结。未来我会一直使用这种主题学习的方法进行写作,文章风格向阳老、大妈和oliver ding学习,尽量写出有主题,有质量,可延续,可迭代的文章。 - -关于写作方法,我再一个最近的例子。 - -大概一个月前,我在萧秋水的博客看到一篇文章[《我的工作方法:先完成,再反复修改》](http://www.xiaoqiushui.com/archives/9708.html),她在这篇文章中说到了写作方法: - ->写作,我会先从头到尾写完,有一个草稿,即使中间有些地方不算好,或者写不下去,也不管,略过那部分,总之先完成,然后返回去再做修改,反复修改,直到最终完稿。 -> ->我之所以那样做,是因为那样会有一个整体观,往往是写到后面,前面的也想通了,读完全书,不懂的也明白了。如果一味地被前面的细节绊住,可能进度就会受影响,甚至可能无法完成。 - -她的写作方法跟阳老此前示范过的快速写作法很相似,但阳老和萧秋水的不同在于:萧秋水说的是一种经验,经验是知其然而不知其所以然,而阳老教的是一种方法论,是知其然而知其所以然。阳老师的写作方法是这样:亲身演示玩快速写作,然后说明大脑的工作原理,接着介绍卡片写作法,讲述了他是利用认知和心理学来写作的,让学习者明白为何要如此写作。 - -高明的写作者(如博纳科夫)是将写作看做是一种大脑科学的游戏,让认知遵从大脑规律,得出最优的结果,这样就可以让后来者都能按照这种方法去实践。 - -## 开智写作班感受分享 - -最近加入了开智写作班,说说学习心得: - -### 老师和同学都很拼 - -阳老真的很拼,第一节课的课件是在上课前一天凌晨一点做到凌晨六点做的,整个课件有74页,上课的三个小时,中途无间断,毫无尿点,连个厕所都不敢去; - -同学们也很拼,平时不在群上说话的人其实私底下学习更凶猛,平时打开邮件都看完作业提交的速度和质量深感同侪压力巨大,跟这些人一起学习,晚上睡觉都觉得是浪费时间; - -### 课程需要用心修炼 - -课程知识相当密集,平时上课都是70%已知信息加上30%未知信息,阳老一上来就90%未知,需要课后查阅资料、组团解读和亲身练习才能体验到效果;单靠课程还不够,还得自个修炼,除了做好课程笔记,我也打算将在写作班的自个的学习过程也记录下来,![《文学的理由|写作训练日志(1)》](http://www.jianshu.com/p/6f31bbed484d), - - -## 为什么写? - - -最后,再回到「为什么写」的问题中,为什么写?人总是喜欢从结果评估行为的价值,但我发现,写作给我带来的好处在不停地写的过程中发现的,就像有时候学一切可以学的东西一样,我不知道这些东西现在会给我带来什么,但未来谁知道呢,做事儿不一定非要有「功利」刺激才去做,因为很多惊人的结果需要偶然的机会作为催化剂才可能发生。 - -就像现在,在我开始写之前,我是不会知道我会在这里和大家分享,如果我早知道的话,我可能会后悔我开始得太迟。 - -所以现在,我就当写是写的报酬。 - - - -—End— - -##迭代 - -- 2015-09-14 01:52:09 再改 -* 2015年6月19日 19:36:49 四稿 -* 2015年6月17日 19:51:24 三稿 -* 2015年6月16日 20:11:01 二稿 -* 2015年6月15日 19:09:17 初稿 - - - - ---- - -### **【学苟知本,六经皆我注脚】** - - -若其心正,其事善,虽不曾识字,亦自有读书之功。其心不正,其事不善,虽多读书,有何所用?用之不善,反增罪恶耳。 - -——陆九渊 - - ----- - - - - -![](http://7d9mjz.com1.z0.glb.clouddn.com/20150913-220033.jpg) - - -(题图:saurabh mohnot by Nik FC) - - - - - - - - - diff --git "a/_posts/2016-02-22-swift\345\260\217\351\241\271\347\233\256-\347\256\200\346\230\223\347\247\222\350\241\250simpleStopWatch.md" "b/_posts/2016-02-22-swift\345\260\217\351\241\271\347\233\256-\347\256\200\346\230\223\347\247\222\350\241\250simpleStopWatch.md" new file mode 100644 index 0000000000..671c59fc9d --- /dev/null +++ "b/_posts/2016-02-22-swift\345\260\217\351\241\271\347\233\256-\347\256\200\346\230\223\347\247\222\350\241\250simpleStopWatch.md" @@ -0,0 +1,20 @@ +--- +layout: post +title: swift小项目-简易秒表simpleStopWatch +date: 2016-02-22 +categories: blog +tags: [swift] +description: 一个简易的swift秒表 + +--- + +前天在github看到一个30天一天一个swift的项目。想想自己也可以试试,于是便有了今天的swift小项目。下面是几个总结 + +* 使用autolayout时,在storyborad里为各个组件添加约束的时候,记住不要先写死某些长度宽度,而是要用各种约束去把宽度长度定死,最后才加上一些自己需要的长宽约束。用这种思维会清晰很多。 + +* 在用ctrl+拖动的方式绑定到代码里,要注意命名最好一开始就想好,不然以后改名字的时候,这些绑定要重新删除再绑定,不然会报 "setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key name" 这样一种错误。 + +* 可以用『String(format: "%.1f", countNumber)』这种方式来将double类型格式化显示你要展示的String类型 + +代码可以在我的github看到 + diff --git "a/_posts/2016-02-27-swift\345\260\217\351\241\271\347\233\256-\347\256\200\345\215\225\350\256\241\347\256\227\345\231\250\345\255\246\344\271\240\345\260\217\347\273\223.md" "b/_posts/2016-02-27-swift\345\260\217\351\241\271\347\233\256-\347\256\200\345\215\225\350\256\241\347\256\227\345\231\250\345\255\246\344\271\240\345\260\217\347\273\223.md" new file mode 100644 index 0000000000..9685319623 --- /dev/null +++ "b/_posts/2016-02-27-swift\345\260\217\351\241\271\347\233\256-\347\256\200\345\215\225\350\256\241\347\256\227\345\231\250\345\255\246\344\271\240\345\260\217\347\273\223.md" @@ -0,0 +1,31 @@ +--- +layout: post +title: swift小项目-小计算器学习小结 +date: 2016-02-27 +categories: blog +tags: [swift] +description: 简单计算器小结 + +--- + +这是一个超简单的计算器app,学习过程中有几点觉得可以记录一下的,具体注意的点见下面代码片段的注释: + + @IBOutlet weak var textOne: UITextField! + @IBOutlet weak var textTwo: UITextField! + @IBOutlet weak var resultLabel: UILabel! + + //self.textOne.text.toInt()这是旧的写法,会报错 + var num1 = Int(self.textOne.text!) + + //下面的两种方法判断UITextField有没有值 + if(self.textOne.text!.isEmpty){ + num1 = 0 + } + if(!self.textTwo.hasText()){ + num2 = 0 + } + + +还有一个报错记录一下 "Scene is unreachable due to lack of entry points and does not have an identifier for runtime access via -instantiateViewControllerWithIdentifier:" + +原因是初始ViewController没有指定,可以在StoryBorder里在ViewController的属性那里把『Is Initial View Controller里勾上』 diff --git "a/_posts/2016-02-29-swift\345\260\217\351\241\271\347\233\256-\345\210\207\346\215\242\350\207\252\345\256\232\344\271\211\345\255\227\344\275\223\345\260\217\347\273\223.md" "b/_posts/2016-02-29-swift\345\260\217\351\241\271\347\233\256-\345\210\207\346\215\242\350\207\252\345\256\232\344\271\211\345\255\227\344\275\223\345\260\217\347\273\223.md" new file mode 100644 index 0000000000..5d94dce13e --- /dev/null +++ "b/_posts/2016-02-29-swift\345\260\217\351\241\271\347\233\256-\345\210\207\346\215\242\350\207\252\345\256\232\344\271\211\345\255\227\344\275\223\345\260\217\347\273\223.md" @@ -0,0 +1,34 @@ +--- +layout: post +title: swift小项目-自定义字体 +date: 2016-02-29 +categories: blog +tags: [swift] +description: IOS使用自定义字体 + +--- + +这是一个可以改变页面上显示字体的小app,其中用了UITableview。 +1.有一个很奇怪的报错: +Terminating app due to uncaught exception 'CALayerInvalid', reason: 'layer is a part of cycle in its layer tree' + +这个很奇怪的报错,通过网上查找发现,只要去到storybord那里,在Connections Inspector里把accessoryView 关联的outlet删掉就不会报错了! + +2.使用自定义字体的时候,把字体文件添加进入项目后,要去到Build Phases—Copy Bundle Resources,确保刚添加的字体文件在列表中,否则需要手动加到这里面。然后要去info.plist文件里添加Fonts provided by application这一项,然后把刚刚添加进去的字体文件的文件名加在这里,记住要加上文件格式的后缀,比如.ttf + +3.一种使数字循环的方法: + var fontName = ["Gaspar Regular","MFTongXin_Noncommercial-Regular", "MFJinHei_Noncommercial-Regular", "MFZhiHei_Noncommercial-Regular"] + + var fontIndex = 0 + + @IBOutlet weak var changeButton: UIButton! + @IBOutlet weak var tableFontView: UITableView! + + @IBAction func ToChangeFont(sender: AnyObject) { + + fontIndex = (fontIndex + 1) % 4 //用整除的方法可以使数字循环,达到每次点击button切换一种字体 + tableFontView.reloadData() + print(fontIndex) + } + + diff --git "a/_posts/2016-03-04-swift\345\260\217\351\241\271\347\233\256-\346\222\255\346\224\276\346\234\254\345\234\260\350\247\206\351\242\221\345\260\217\347\273\223.md" "b/_posts/2016-03-04-swift\345\260\217\351\241\271\347\233\256-\346\222\255\346\224\276\346\234\254\345\234\260\350\247\206\351\242\221\345\260\217\347\273\223.md" new file mode 100644 index 0000000000..94a3feba21 --- /dev/null +++ "b/_posts/2016-03-04-swift\345\260\217\351\241\271\347\233\256-\346\222\255\346\224\276\346\234\254\345\234\260\350\247\206\351\242\221\345\260\217\347\273\223.md" @@ -0,0 +1,42 @@ +--- +layout: post +title: swift小项目-播放本地视频小结 +date: 2016-03-04 +categories: blog +tags: [swift] +description: 播放本地视频小结 + +--- + +这是一个页面用了UITableview,然后播放本地视频的一个小app,这次实现起来有几点注意的: + +1.继承UIViewController, UITableViewDataSource, UITableViewDelegate的viewcontroller要注意实现必要的方法,以及 +在viewDidLoad()这个方法里要添加内容如下,不然的话运行起来界面看到的是空白。 + + override func viewDidLoad() { + super.viewDidLoad() + //注意这里一定要添加datasource和delegate + videoView.dataSource = self + videoView.delegate = self + } + +2.使用本地文件时,记得在项目里的Targets -> Build Phases -> Copy Bundle Resources里面导入相对应的资源文件,不然在使用NSBundle.mainBundle().pathForResource(name: String?, ofType: String?) +是会返回空的地址。 + +3.播放本地视频的简要方法: + + var playerViewController = AVPlayerViewController() + var playerView = AVPlayer() + + let videoPath = NSBundle.mainBundle().pathForResource("eat", ofType: "mp4") + + print("video path is "+videoPath!) + + playerView = AVPlayer(URL: NSURL(fileURLWithPath: videoPath!)) + + playerViewController.player = playerView + + self.presentViewController(playerViewController, animated: true){ + self.playerViewController.player?.play() + } + diff --git "a/_posts/2016-03-19-swift\345\260\217\351\241\271\347\233\256-\345\233\276\347\211\207\346\250\252\346\216\222\345\261\225\347\244\272\345\260\217\347\273\223.md" "b/_posts/2016-03-19-swift\345\260\217\351\241\271\347\233\256-\345\233\276\347\211\207\346\250\252\346\216\222\345\261\225\347\244\272\345\260\217\347\273\223.md" new file mode 100644 index 0000000000..d1b388d368 --- /dev/null +++ "b/_posts/2016-03-19-swift\345\260\217\351\241\271\347\233\256-\345\233\276\347\211\207\346\250\252\346\216\222\345\261\225\347\244\272\345\260\217\347\273\223.md" @@ -0,0 +1,59 @@ +--- +layout: post +title: swift小项目-图片横排展示小结 +date: 2016-03-19 +categories: blog +tags: [swift] +description: 图片横排展示小结 + +--- + +这是一个页面用了UICollectionView,然后配合UIVisualEffectView展示出毛玻璃效果的一个小demo。下面是几点注意的: + +1.注意在StoryBoard里把dataSource和delegate通过referencing Outlets关联,然后在代码里使用extension方法实现datasoruce方法 + + class CollectionViewController: UIViewController { + + @IBOutlet weak var collectView: UICollectionView! + + private var contents = Content.createContents() + let CellIdentifier = "contentCell" + + override func viewDidLoad() { + super.viewDidLoad() + } + + override func preferredStatusBarStyle() -> UIStatusBarStyle { + return UIStatusBarStyle.LightContent //设置状态栏字符为白色 + } + + //用struct定义 + private struct Storyboard { + static let CellIdentifier = "contentCell" + } + + } + + extension CollectionViewController : UICollectionViewDataSource{ + + func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int { + return 1 + } + + func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return contents.count + } + + func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { + let collectionViewCell = collectionView.dequeueReusableCellWithReuseIdentifier(CellIdentifier /*Storyboard.CellIdentifier*/, forIndexPath: indexPath) as! CollectionViewCell + + collectionViewCell.content = contents[indexPath.item] + + return collectionViewCell + } + + } + + + +2.设置UICollectionViewCell之间的距离,可以在StoryBoard里找到Collection View Flow Layout这个选项,在Size Inspector里,找到Section Insets 的选项,里面的Left 和 Right 就是cell之间的距离 \ No newline at end of file diff --git "a/_posts/2016-03-23-swift\345\260\217\351\241\271\347\233\256-\345\237\272\344\272\216CoreLoaction\345\222\214MapKit\347\232\204\344\275\215\347\275\256app.md" "b/_posts/2016-03-23-swift\345\260\217\351\241\271\347\233\256-\345\237\272\344\272\216CoreLoaction\345\222\214MapKit\347\232\204\344\275\215\347\275\256app.md" new file mode 100644 index 0000000000..2d050b27b2 --- /dev/null +++ "b/_posts/2016-03-23-swift\345\260\217\351\241\271\347\233\256-\345\237\272\344\272\216CoreLoaction\345\222\214MapKit\347\232\204\344\275\215\347\275\256app.md" @@ -0,0 +1,108 @@ +--- +layout: post +title: swift小项目-基于CoreLoaction和MapKit的位置app +date: 2016-03-23 +categories: blog +tags: [swift] +description: 基于CoreLoaction和MapKit的位置app + +--- + +这是一个用了CoreLoaction和MapKit的一个小项目,界面是点击按钮找到当前位置并且在地图上标记出来。下面是几点注意的: + +1.使用CoreLocation时,requestAlwaysAuthorization(),因为要获取位置权限,所以在info.plist里要添加NSLocationAlwaysUsageDescription和NSLocationWhenInUseUsageDescription这两个授权提示的描述,不然在debug的时候是进不到delegate实现的方法的。 + + + +2.下面直接贴出部分代码 + + import UIKit + import CoreLocation + import MapKit + + class ViewController: UIViewController , CLLocationManagerDelegate{ + + @IBOutlet weak var locationLabel: UILabel! + @IBOutlet weak var mapView: MKMapView! + + var locationManager: CLLocationManager! + + override func viewDidLoad() { + super.viewDidLoad() + self.mapView.mapType = MKMapType.Standard + } + + override func didReceiveMemoryWarning() { + super.didReceiveMemoryWarning() + } + + @IBAction func FindButtonDidPressed(sender: AnyObject) { + + locationManager = CLLocationManager() + locationManager.delegate = self + locationManager.desiredAccuracy = kCLLocationAccuracyBest + locationManager.requestAlwaysAuthorization() + locationManager.startUpdatingLocation() + + //创建一个MKCoordinateSpan对象,设置地图的范围(越小越精确) + let latDelta = 0.05 + let longDelta = 0.05 + let currentLocationSpan:MKCoordinateSpan = MKCoordinateSpanMake(latDelta, longDelta) + + //使用当前位置 + var center:CLLocation = locationManager.location! + // //使用自定义位置 + // let center:CLLocation = CLLocation(latitude: 32.029171, longitude: 118.788231) + let currentRegion:MKCoordinateRegion = MKCoordinateRegion(center: center.coordinate, + span: currentLocationSpan) + + //设置显示区域 + self.mapView.setRegion(currentRegion, animated: true) + + //创建一个大头针对象 + let objectAnnotation = MKPointAnnotation() + objectAnnotation.coordinate = center.coordinate + objectAnnotation.title = center.description + objectAnnotation.subtitle = "" + self.mapView.addAnnotation(objectAnnotation) + + } + + func locationManager(manager: CLLocationManager, didFailWithError error: NSError) { + + self.locationLabel.text = "更新位置发生错误:" + error.description + } + + func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { + + CLGeocoder().reverseGeocodeLocation(manager.location!, completionHandler: {(placemarks, error)->Void in + + if (error != nil) { + self.locationLabel.text = "Reverse geocoder failed with error" + error!.localizedDescription + return + } + + if placemarks!.count > 0 { + let pm = placemarks![0] + self.displayLocationInfo(pm) + } else { + self.locationLabel.text = "Problem with the data received from geocoder" + } + }) + } + + func displayLocationInfo(placemark:CLPlacemark?){ + + if let containsPlacemark = placemark { + + locationManager.stopUpdatingLocation() + + let locality = (containsPlacemark.locality != nil) ? containsPlacemark.locality : "" + let postalCode = (containsPlacemark.postalCode != nil) ? containsPlacemark.postalCode : "" + let administrativeArea = (containsPlacemark.administrativeArea != nil) ? containsPlacemark.administrativeArea : "" + let country = (containsPlacemark.country != nil) ? containsPlacemark.country : "" + + self.locationLabel.text = locality! + "-" + postalCode! + "-" + administrativeArea! + "-" + country! + } + } + } diff --git a/_posts/2016-08-07-test.md b/_posts/2016-08-07-test.md new file mode 100644 index 0000000000..8814d17411 --- /dev/null +++ b/_posts/2016-08-07-test.md @@ -0,0 +1,17 @@ +--- +layout: post +title: test +date: 2016-08-07 +categories: blog +tags: [swift] +description: test + +--- + +这是一个测试页面 +![TEST](/assets/image/test.png =100) + +1.使用CoreLocation时,requestAlwaysAuthorization(),因为要获取位置权限,所以在info.plist里要添加NSLocationAlwaysUsageDescription和NSLocationWhenInUseUsageDescription这两个授权提示的描述,不然在debug的时候是进不到delegate实现的方法的。 + +替代文本 + diff --git a/about.md b/about.md index 71c1e95e8f..b80a3dadc7 100644 --- a/about.md +++ b/about.md @@ -1,51 +1,26 @@ --- layout: page title: "About" -description: "学苟知本,六经皆我注脚 " +description: "my name is Hanson Zhang.On my way to change" header-img: "img/green.jpg" --- -写作,是因为在奋斗和学习的过程中非常孤独和辛苦,借此来记录自己的成长过程,借此来发泄自己的想法,借此来督促自己不能放弃。 - -感觉编程的过程中,是幸福的;在学习的过程中,是孤独的! # 联系方式 -* 手机:15011959802 -* Email:zyuanming@outlook.com -* QQ/微信号:279856811/大雄 +* Email:yuanpingzhang123@gmail.com +* QQ/微信号:835749634/张远平 * * * # 个人信息 -* 张远铭/男/1990 +* 张远平/男/1993 * 本科/广东工业大学 -* 工作年限:2年 -* 技术博客: -* Github: -* 目前就职于:百度 +* 技术博客: +* Github: +* 目前就职于:ZTESoft * * * - -# 项目经历 - -### [魔坛 iOS 客户端][1] - -* 负责iOS客户端开发。 -* 负责Android和iOS自动打包的开发,我们也提供站长自己上传证书,自动打包后由站长上传到App Store(高顿部落 iPhone版,可在App Store搜索)的功能。 -* 关于证书和mobileprovision的认证,我写了一个[小脚本][3] -* 同时负责Android客户端的前期技术调研和初期开发。 -* 逆向工程了当时市面上的论坛 Android App,其中包括Discuz的官方Android app,并基本复原了所有的功能。[GitHub][4]. 做这个主要是当时技术选型时的调研:是插件开发还是无插件,网页抓取这种方式。 - -### [顺企汇 iOS 客户端][2] - -* 负责全部的iOS开发工作。 -* 已经上线App Store,可以下载试用 - -[1]: http://www.motnt.com -[2]: http://www.shunqihui.com/ -[3]:https://github.com/zyuanming/verifyP12AndMobileProvision -[4]:https://github.com/zyuanming/discuzforstudy \ No newline at end of file diff --git a/assets/image/test.png b/assets/image/test.png new file mode 100644 index 0000000000..3afc241018 Binary files /dev/null and b/assets/image/test.png differ diff --git a/img/cnfeat.jpg b/img/cnfeat.jpg deleted file mode 100644 index 5e958f0bbe..0000000000 Binary files a/img/cnfeat.jpg and /dev/null differ diff --git a/img/favicon.ico b/img/favicon.ico index d09d122fa6..415005acc7 100644 Binary files a/img/favicon.ico and b/img/favicon.ico differ diff --git a/img/favicon.png b/img/favicon.png index d09d122fa6..06778d9f77 100644 Binary files a/img/favicon.png and b/img/favicon.png differ diff --git a/index.html b/index.html index 8464bd7b9c..ccbf16880d 100755 --- a/index.html +++ b/index.html @@ -1,6 +1,6 @@ --- layout: page -description: "剑尖的技术博客" +description: "Hanson's World" --- {% for post in paginator.posts %} diff --git a/milestone.md b/milestone.md index f36712dfed..0c860501e2 100644 --- a/milestone.md +++ b/milestone.md @@ -1,19 +1,20 @@ --- layout: page title: "milestone" -description: "知之真切笃实处即是行,行之明觉精察处即是知 " +description: "Life is Miracle" header-img: "img/zhihu.jpg" --- -###转折: +###Path: -- 加入了魔坛,一起创业的时光 +- 加入了XOGroup旗下TheKnots.com广州研发中心——诺特软件开发有限公司 Android开发 -- 加入了百度,重新启航 +- 加入了ZTESoft 中兴软创 广州研发中心 + Java开发 -- 遇见的每一个你 +- 追寻更好的自己