RubyMotion 指南:API 驱动开发示例
翻译:@shiweifu
本文链接:http://segmentfault.com/blog/shiweifu
原文链接:http://rubymotion-tutorial.com/10-api-driven-example/
目标读者:["想了解RubyMotion开发模式", "想学习RubyMotion", "逗比"]
我们将创建一个使用Colr JSON API作为后端的应用。用户输入颜色的16进制值( #3B5998)他们会看见标签的颜色发生对应的变化。他们可以往里添加新的颜色。
初始化
Models
首先,让我们先看下模型。Colr API 的 Color JSON 结构如下:
{"timestamp": 1285886579,"hex": "ff00ff","id": 3976,"tags": [{"timestamp": 1108110851,"id": 2583,"name": "fuchsia"}]
}
我们的 Colors 需要timestamp
,hex
, id
, tags
这些属性,特别注意的是,tags
属性将包含多个Tag
对象
创建./app/models/color.rb
然后填写 Model 代码:
class ColorPROPERTIES = [:timestamp, :hex, :id, :tags]PROPERTIES.each { |prop|attr_accessor prop}def initialize(hash = )hash.each { |key, value|if PROPERTIES.member? key.to_symself.send((key.to_s + "=").to_s, value)end}end...
PROPERTIES
这块是个小trick,很容易就定义了属性。需要稍微说一下的是tags
这个属性,让它始终返回一个Tag
Model的数组。
...def tags@tags ||= []enddef tags=(tags)if tags.first.is_a? Hashtags = tags.collect |tag| Tag.new(tag) endtags.each { |tag|if not tag.is_a? Tagraise "Wrong class for attempted tag #tag.inspect"end}@tags = tagsend
end
我们覆盖了#tags
的getter和setter,所以当tags
没有值的时候,将返回一个空的数组。#tags=
保证解析和返回Tag
对象数组。我们接下来编看看Tag
Model里面都有啥。
创建并打开./app/models/tag.rb
,接口返回的数据如下所示:
{"timestamp": 1108110851,"id": 2583,"name": "fuchsia"
}
class TagPROPERTIES = [:timestamp, :id, :name]PROPERTIES.each { |prop|attr_accessor prop}def initialize(hash = )hash.each { |key, value|if PROPERTIES.member? key.to_symself.send((key.to_s + "=").to_s, value)end}end
end
Controllers
class SearchController < UIViewControllerdef viewDidLoadsuperself.title = "Search"end
end
class ColorController < UIViewControllerdef viewDidLoadsuperself.title = "Color"end
end
将我们的控制器带上UINavigationController
和UIWindow
,甩给AppDelegate
:
class AppDelegatedef application(application, didFinishLaunchingWithOptions:launchOptions)@window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)@search_controller = SearchController.alloc.initWithNibName(nil, bundle:nil)@navigation_controller = UINavigationController.alloc.initWithRootViewController(@search_controller)@window.rootViewController = @navigation_controller@window.makeKeyAndVisibletrueend
end
代码堆砌完了,是时候看看成果了,执行rake
命令,在屏幕中会出现:
一切都很好,该看看SearchController
里面都有啥了。
SearchController
(译者著:原文是系列文章,之前的部分从未出现过UITextField
,所以这里假设UITextField
从未出现过,不然接不上。)
以下是SearchController
的初始化时干活的代码:
def viewDidLoadsuperself.title = "Search"self.view.backgroundColor = UIColor.whiteColor@text_field = UITextField.alloc.initWithFrame [[0,0], [160, 26]]@text_field.placeholder = "#abcabc"@text_field.textAlignment = UITextAlignmentCenter@text_field.autocapitalizationType = UITextAutocapitalizationTypeNone@text_field.borderStyle = UITextBorderStyleRoundedRect@text_field.center = CGPointMake(self.view.frame.size.width / 2, self.view.frame.size.height / 2 - 100)self.view.addSubview @text_field@search = UIButton.buttonWithType(UIButtonTypeRoundedRect)@search.setTitle("Search", forState:UIControlStateNormal)@search.setTitle("Loading", forState:UIControlStateDisabled)@search.sizeToFit@search.center = CGPointMake(self.view.frame.size.width / 2, @text_field.center.y + 40)self.view.addSubview @searchend
(译者注:BubbleWrap是RubyMotion官方开发的一个库,里面封装了很多用Cocoa写起来很蛋疼的地方,使代码更加「Ruby」)
该处理事件了。还记得我之前提到过BubbleWrap
屌屌的么?使用它我们不用再像过去写傻傻的addTarget:action:forControlEvents
啥啥啥的来添加事件,代码清晰很多:
def viewDidLoad...self.view.addSubview @search@search.when(UIControlEventTouchUpInside) do@search.enabled = false@text_field.enabled = falsehex = @text_field.text# chop off any leading #shex = hex[1..-1] if hex[0] == "#"Color.find(hex) do |color|@search.enabled = true@text_field.enabled = trueendendend
when
方法在所有UIControl
的子类都可以用。使用UIControlEvent
开头的那些标识事件位作为参数。当请求发出后,我们临时禁用UI。
class Color...def self.find(hex, &block)BW::HTTP.get("http://www.colr.org/json/color/#hex") do |response|p response.body.to_str# for now, pass nil.block.call(nil)endend
end
(译者注:RubyMotion中的block。如果困惑或者想深入研究,可以去看看Ruby的lambda,还有RubyMotion的block传递)
再rake
一下,来个数据测试一下这个方法,如:3B5998
。你将在终端中看到:
(main)> "\"colors\": [{\"timestamp\": 1285886579, \"hex\": \"ff00ff\", \"id\": 3976, \"tags\": [{\"timestamp\": 1108110851, \"id\": 2583, \"name\": \"fuchsia\"}, {\"timestamp\": 1108110864, \"id\": 3810, \"name\": \"magenta\"}, {\"timestamp\": 1108110870, \"id\": 4166, \"name\": \"magic\"}, {\"timestamp\": 1108110851, \"id\": 2626, \"name\": \"pink\"}, {\"timestamp\": 1240447803, \"id\": 24479, \"name\": \"rgba8b24ff00ff\"}, {\"timestamp\": 1108110864, \"id\": 3810, \"name\": \"magenta\"}]], \"schemes\": [], \"schemes_history\": , \"success\": true, \"colors_history\": \"ff00ff\": [{\"d_count\": 0, \"id\": \"4166\", \"a_count\": 1, \"name\": \"magic\"}, {\"d_count\": 0, \"id\": \"2626\", \"a_count\": 1, \"name\": \"pink\"}, {\"d_count\": 0, \"id\": \"24479\", \"a_count\": 1, \"name\": \"rgba8b24ff00ff\"}, {\"d_count\": 0, \"id\": \"3810\", \"a_count\": 1, \"name\": \"magenta\"}], \"messages\": [], \"new_color\": \"ff00ff\"}\n"
WTF!!一坨JSON字符串啊,亲我不想要字符串啊,能不能给我Ruby的Hash?
在BubbleWrap里已经集成了解析JSON的方法:BW::JSON.parse,开箱即用:
def self.find(hex, &block)BW::HTTP.get("http://www.colr.org/json/color/#hex") do |response|result_data = BW::JSON.parse(response.body.to_str)color_data = result_data["colors"][0]# Colr will return a color with id == -1 if no color was foundcolor = Color.new(color_data)if color.id.to_i == -1block.call(nil)elseblock.call(color)endend
end
在我们的SearchController中,要做一些对无效输入的校验:
def viewDidLoad...Color.find(hex) do |color|if color.nil?@search.setTitle("None :(", forState: UIControlStateNormal)else@search.setTitle("Search", forState: UIControlStateNormal)self.open_color(color)end@search.enabled = true@text_field.enabled = trueendendenddef open_color(color)p "Opening #color"end
一切看起来很好。当遇到无效的JSON的时候界面上会给出明确的反馈:
现在改补上 open_color
方法的代码了。它push一个ColorController
,然后在其中显示颜色。
def open_color(color)self.navigationController.pushViewController(ColorController.alloc.initWithColor(color), animated:true)
end
ColorController
class ColorController < UIViewControllerattr_accessor :colordef initWithColor(color)initWithNibName(nil, bundle:nil)self.color = colorselfend...
当重载一个iOS SDK 构造函数的时候,你需要做两件事:调用它的父构造函数;在函数结尾的时候返回初始化过的它自己。在RubyMotion中,你不能像标准Ruby一样初始化。
def viewDidLoadsuperself.title = self.color.hex# You must comment out the following line if you are developing on iOS < 7.self.edgesForExtendedLayout = UIRectEdgeNone# A light grey background to separate the Tag table from the Color info@info_container = UIView.alloc.initWithFrame [[0, 0], [self.view.frame.size.width, 110]]@info_container.backgroundColor = UIColor.lightGrayColorself.view.addSubview @info_container# A visual preview of the actual color@color_view = UIView.alloc.initWithFrame [[10, 10], [90, 90]]# String#to_color is another handy BubbbleWrap addition!@color_view.backgroundColor = String.new(self.color.hex).to_colorself.view.addSubview @color_view# Displays the hex code of our color@color_label = UILabel.alloc.initWithFrame [[110, 30], [0, 0]]@color_label.text = self.color.hex@color_label.sizeToFitself.view.addSubview @color_label# Where we enter the new tag@text_field = UITextField.alloc.initWithFrame [[110, 60], [100, 26]]@text_field.placeholder = "tag"@text_field.textAlignment = UITextAlignmentCenter@text_field.autocapitalizationType = UITextAutocapitalizationTypeNone@text_field.borderStyle = UITextBorderStyleRoundedRectself.view.addSubview @text_field# Tapping this adds the tag.@add = UIButton.buttonWithType(UIButtonTypeRoundedRect)@add.setTitle("Add", forState:UIControlStateNormal)@add.setTitle("Adding...", forState:UIControlStateDisabled)@add.setTitleColor(UIColor.lightGrayColor, forState:UIControlStateDisabled)@add.sizeToFit@add.frame = [[@text_field.frame.origin.x + @text_field.frame.size.width + 10, @text_field.frame.origin.y],@add.frame.size]self.view.addSubview(@add)# The table for our color's tags.table_frame = [[0, @info_container.frame.size.height],[self.view.bounds.size.width, self.view.bounds.size.height - @info_container.frame.size.height - self.navigationController.navigationBar.frame.size.height]]@table_view = UITableView.alloc.initWithFrame(table_frame, style:UITableViewStylePlain)self.view.addSubview(@table_view)end
……好大一坨代码啊!不要慌,这些代码很容易理解,我们只是添加了几个子view。
def viewDidLoad...@table_view.dataSource = selfenddef tableView(tableView, numberOfRowsInSection:section)self.color.tags.countenddef tableView(tableView, cellForRowAtIndexPath:indexPath)@reuseIdentifier ||= "CELL_IDENTIFIER"cell = tableView.dequeueReusableCellWithIdentifier(@reuseIdentifier) || beginUITableViewCell.alloc.initWithStyle(UITableViewCellStyleDefault, reuseIdentifier:@reuseIdentifier)endcell.textLabel.text = self.color.tags[indexPath.row].namecellend
!()[http://rubymotion-tutorial.com/10-api-driven-example/images/4.png]
def add_tag(tag, &block)BW::HTTP.post("http://www.colr.org/js/color/#{self.hex}/addtag/", payload: {tags: tag}) do |response|block.callendend
最后那个参数是在请求执行结束后回调的。好的做法是分别处理成功和失败两种情况,这个例子为了简单,就先不考虑了。
现在给ColorController
的按钮添加事件处理代码。我们想在Tag被发送到服务器之后,根据当前服务器返回的数据刷新:
def viewDidLoad...self.view.addSubview(@add)@add.when(UIControlEventTouchUpInside) do@add.enabled = false@text_field.enabled = falseself.color.add_tag(@text_field.text) dorefreshendend...enddef refreshColor.find(self.color.hex) do |color|self.color = color@table_view.reloadData@add.enabled = true@text_field.enabled = trueendend
时候到溜
到底讲了点啥?
RubyMotion 指南:API 驱动开发示例相关推荐
- Jetty 开发指南:嵌入式开发示例
Jetty具有嵌入各种应用程序的丰富历史. 在本节中,我们将向您介绍我们的git存储库中的embedded-jetty-examples项目下的一些简单示例. 重要:生成此文档时,将直接从我们的git ...
- 树莓派底层IO驱动开发示例(一个简单io口驱动的开发)
一.驱动代码的开发 1.树莓派寄存器的介绍 点击查看:树莓派(bcm2835芯片手册) GPFSEL0 GPIO Function Select 0: 功能选择 输入/输出 GPSET0 GPIO P ...
- c语言windows驱动编程入门,Windows驱动开发技术详解 PDF扫描版[175MB]
Windows驱动开发技术详解由浅入深.循序渐进地介绍了windows驱动程序的开发方法与调试技巧.本书共分23章,内容涵盖了windows操作系统的基本原理.nt驱动程序与wdm驱动程序的构造.驱动 ...
- 转:Windows驱动开发(中间层)
Windows驱动开发(中间层) - 慧由心生 - 博客园Windows驱动开发一.前言依据<Windows内核安全与驱动开发>及MSDN等网络质料进行学习开发.二.初步环境1.下载安装W ...
- 【致敬未来的攻城狮计划】第2期定向赠书《RT-Thread设备驱动开发指南》+ 《GD32 MCU原理及固件库开发指南》
开启攻城狮的成长之旅!这是我参与的由 CSDN博客专家 架构师李肯(超链接:http://yyds.recan-li.cn)和 瑞萨MCU (超链接:瑞萨电子 (Renesas Electronics ...
- 【正点原子MP157连载】第四十章 Linux I2C驱动实验-摘自【正点原子】STM32MP1嵌入式Linux驱动开发指南V1.7
1)实验平台:正点原子STM32MP157开发板 2)购买链接:https://item.taobao.com/item.htm?&id=629270721801 3)全套实验源码+手册+视频 ...
- Exynos4412的Linux5.4.174时钟驱动开发(四)——clk API的调用方法
系列文章目录 Exynos4412的Linux时钟驱动开发(一)--Exynos4412的时钟管理单元CMU Exynos4412的Linux时钟驱动开发(二)--时钟驱动的初始化(CLK_OF_DE ...
- 【正点原子Linux连载】第四十五章 pinctrl和gpio子系统实验 -摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0
1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...
- 【正点原子MP157连载】第二十五章 pinctrl和gpio子系统实验-摘自【正点原子】STM32MP1嵌入式Linux驱动开发指南V1.7
1)实验平台:正点原子STM32MP157开发板 2)购买链接:https://item.taobao.com/item.htm?&id=629270721801 3)全套实验源码+手册+视频 ...
最新文章
- word里实现在方框中打勾和打叉
- python 大数据学习 遇到的问题,及解决方法。
- javascript包装对象
- delphi 安卓图片保存数据库_delphi 把图片存入数据库
- linux下qq怎么截图,ubuntu 12.04使用QQ截图安装教程
- React项目build之后资源文件路径不正确或打开空白页的问题及简易解决方法
- eclipse插件之——PropertiesEditor
- 破坏计算机系统信息罪司法解释,破坏计算机信息系统罪司法解释是怎样的的呢?...
- java时间往后一天_往后余生,不能再陪你了
- 运用R语言绘制火山图
- mybatis java 帅帅 2021年5月17日20:36:25
- .netcf 图片区域拷贝[图片切割]
- 【Unity脚本】游戏开发常用功能——以平台动作游戏为例解决“瞬移穿墙”问题
- VisionPro 工具
- 什么邮箱群发进箱率高,邮箱怎么群发邮件进箱率怎么样?
- 如何使用万能的钢笔抠图
- 记账本系统的功能结构图
- java实现电脑远程开机(网络唤醒)
- showDoc的基本使用方法
- 160个CrackMe-CrackMe-3
热门文章
- mysql 存在索引但不能使用索引的典型场景
- Docker、Kubernetes、Apache Mesos 之争 | 一个与传说不同的故事
- Linux Shell编程实战---以逆序形式打印行
- table点击一行显示下一行的特效
- 深入入门正则表达式(java) - 匹配原理 - 1 - 引擎分类与普适原则
- 十个非常有创意的验证码设计
- Android中文API(115)——AudioFormat
- qq邮箱电脑版登录入口_青骄第二课堂学生平台登录入口,青骄第二课堂登录入口(官网手机版入口:https://m.2class.com/)...
- 【转载】在Linux CentOS中安装VSCode用于C++学习,亲测可用
- oracle之4多行函数之分组函数