sugarcube 0.20.25 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (162) hide show
  1. data/Gemfile +3 -1
  2. data/Gemfile.lock +1 -1
  3. data/README.md +897 -1306
  4. data/Rakefile +3 -7
  5. data/app/uicontrol_controller.rb +2 -2
  6. data/lib/sugarcube-568.rb +1 -9
  7. data/lib/sugarcube-all.rb +18 -0
  8. data/lib/sugarcube-animations.rb +15 -0
  9. data/lib/{sugarcube → sugarcube-animations}/animation_chain.rb +0 -0
  10. data/lib/{sugarcube → sugarcube-animations}/uiview.rb +41 -111
  11. data/lib/sugarcube-anonymous.rb +1 -9
  12. data/lib/sugarcube-attributedstring.rb +1 -9
  13. data/lib/sugarcube-attributedstring/nsattributedstring.rb +2 -11
  14. data/lib/sugarcube-awesome.rb +15 -0
  15. data/lib/sugarcube-awesome/awesome_exts.rb +22 -0
  16. data/lib/sugarcube-classic.rb +26 -0
  17. data/lib/sugarcube-color.rb +15 -0
  18. data/lib/sugarcube-color/fixnum.rb +18 -0
  19. data/lib/sugarcube-color/nsarray.rb +11 -0
  20. data/lib/sugarcube-color/nsstring.rb +16 -0
  21. data/lib/{sugarcube/symbol/symbol_uicolor.rb → sugarcube-color/symbol.rb} +12 -4
  22. data/lib/{sugarcube → sugarcube-color}/uicolor.rb +16 -2
  23. data/lib/sugarcube-color/uiimage.rb +13 -0
  24. data/lib/sugarcube-common.rb +22 -0
  25. data/lib/sugarcube-constants.rb +15 -0
  26. data/lib/sugarcube-constants/symbol.rb +759 -0
  27. data/lib/sugarcube-coregraphics.rb +15 -0
  28. data/lib/{sugarcube → sugarcube-coregraphics}/core_graphics.rb +0 -0
  29. data/lib/sugarcube-corelocation.rb +17 -0
  30. data/lib/{sugarcube → sugarcube-corelocation}/core_location.rb +0 -0
  31. data/lib/sugarcube-events.rb +15 -0
  32. data/lib/{sugarcube → sugarcube-events}/uicontrol.rb +3 -3
  33. data/lib/{sugarcube → sugarcube-events}/uitextview.rb +0 -0
  34. data/lib/sugarcube-factories.rb +15 -0
  35. data/lib/{sugarcube → sugarcube-factories}/nserror.rb +0 -0
  36. data/lib/{sugarcube → sugarcube-factories}/uiactionsheet.rb +3 -1
  37. data/lib/sugarcube-factories/uiactivityindicator.rb +17 -0
  38. data/lib/{sugarcube → sugarcube-factories}/uialertview.rb +3 -3
  39. data/lib/{sugarcube → sugarcube-factories}/uibarbuttonitem.rb +19 -3
  40. data/lib/{sugarcube → sugarcube-factories}/uibutton.rb +10 -10
  41. data/lib/{sugarcube → sugarcube-factories}/uisegmentedcontrol.rb +4 -4
  42. data/lib/{sugarcube → sugarcube-factories}/uitableview.rb +0 -0
  43. data/lib/{sugarcube → sugarcube-factories}/uitableviewcell.rb +3 -7
  44. data/lib/sugarcube-files.rb +15 -0
  45. data/lib/{sugarcube/nsstring_files.rb → sugarcube-files/nsstring.rb} +0 -0
  46. data/lib/sugarcube-foundation.rb +15 -0
  47. data/lib/{sugarcube → sugarcube-foundation}/nsarray.rb +0 -18
  48. data/lib/sugarcube-foundation/nsindexpath.rb +13 -0
  49. data/lib/{sugarcube → sugarcube-foundation}/nsindexset.rb +0 -0
  50. data/lib/sugarcube-foundation/nsstring.rb +31 -0
  51. data/lib/{sugarcube → sugarcube-foundation}/nsurl.rb +0 -4
  52. data/lib/sugarcube-gestures.rb +1 -9
  53. data/lib/sugarcube-image.rb +15 -0
  54. data/lib/sugarcube-image/cicolor.rb +7 -0
  55. data/lib/sugarcube-image/cifilter.rb +772 -0
  56. data/lib/sugarcube-image/ciimage.rb +22 -0
  57. data/lib/sugarcube-image/uicolor.rb +7 -0
  58. data/lib/{sugarcube → sugarcube-image}/uiimage.rb +38 -41
  59. data/lib/sugarcube-indexpath.rb +15 -0
  60. data/lib/{sugarcube/nsindexpath.rb → sugarcube-indexpath/indexpath.rb} +12 -21
  61. data/lib/sugarcube-legacy.rb +15 -0
  62. data/lib/{sugarcube → sugarcube-legacy}/activesupport.rb +0 -0
  63. data/lib/sugarcube-legacy/log.rb +45 -0
  64. data/lib/sugarcube-legacy/symbol.rb +219 -0
  65. data/lib/sugarcube-localized.rb +15 -0
  66. data/lib/sugarcube-localized/nserror.rb +8 -0
  67. data/lib/sugarcube-localized/nsstring.rb +10 -0
  68. data/lib/sugarcube-modal.rb +15 -0
  69. data/lib/{sugarcube → sugarcube-modal}/modal.rb +0 -0
  70. data/lib/sugarcube-notifications.rb +15 -0
  71. data/lib/{sugarcube → sugarcube-notifications}/notifications.rb +0 -0
  72. data/lib/sugarcube-nscoder.rb +15 -0
  73. data/lib/{sugarcube → sugarcube-nscoder}/nscoder.rb +0 -0
  74. data/lib/sugarcube-nsdata.rb +15 -0
  75. data/lib/{sugarcube → sugarcube-nsdata}/nsdata.rb +0 -0
  76. data/lib/sugarcube-nsdata/nsstring.rb +9 -0
  77. data/lib/sugarcube-nsdata/nsurl.rb +7 -0
  78. data/lib/sugarcube-nsdata/uiimage.rb +8 -0
  79. data/lib/sugarcube-nsdate.rb +15 -0
  80. data/lib/{sugarcube → sugarcube-nsdate}/date_parser.rb +0 -0
  81. data/lib/sugarcube-nsdate/fixnum.rb +23 -0
  82. data/lib/{sugarcube → sugarcube-nsdate}/nsdate.rb +6 -1
  83. data/lib/{sugarcube → sugarcube-nsdate}/nsdate_delta.rb +0 -0
  84. data/lib/sugarcube-nsdate/nsstring.rb +16 -0
  85. data/lib/sugarcube-nsuserdefaults.rb +15 -0
  86. data/lib/{sugarcube → sugarcube-nsuserdefaults}/nsuserdefaults.rb +0 -0
  87. data/lib/sugarcube-numbers.rb +15 -0
  88. data/lib/sugarcube-numbers/fixnum.rb +25 -0
  89. data/lib/sugarcube-numbers/nsstring.rb +13 -0
  90. data/lib/{sugarcube → sugarcube-numbers}/numeric.rb +44 -4
  91. data/lib/sugarcube-osx/adjust.rb +373 -0
  92. data/lib/sugarcube-pipes.rb +15 -0
  93. data/lib/sugarcube-pipes/pipes.rb +78 -0
  94. data/lib/sugarcube-pointer.rb +15 -0
  95. data/lib/sugarcube-pointer/nsarray.rb +13 -0
  96. data/lib/sugarcube-repl.rb +1 -9
  97. data/lib/sugarcube-resources.rb +15 -0
  98. data/lib/sugarcube-timer.rb +15 -0
  99. data/lib/{sugarcube → sugarcube-timer}/timer.rb +0 -0
  100. data/lib/sugarcube-to_s.rb +15 -0
  101. data/lib/{sugarcube/to_s → sugarcube-to_s}/nserror.rb +1 -4
  102. data/lib/sugarcube-to_s/nsindexpath.rb +7 -0
  103. data/lib/{sugarcube/to_s → sugarcube-to_s}/nslayoutconstraint.rb +0 -0
  104. data/lib/{sugarcube/to_s → sugarcube-to_s}/nsnotification.rb +0 -0
  105. data/lib/{sugarcube/to_s → sugarcube-to_s}/nsset.rb +0 -0
  106. data/lib/{sugarcube/to_s → sugarcube-to_s}/nsurl.rb +0 -0
  107. data/lib/{sugarcube/to_s → sugarcube-to_s}/uicolor.rb +0 -0
  108. data/lib/{sugarcube/to_s → sugarcube-to_s}/uievent.rb +0 -0
  109. data/lib/{sugarcube/to_s → sugarcube-to_s}/uilabel.rb +0 -0
  110. data/lib/{sugarcube/to_s → sugarcube-to_s}/uitextfield.rb +0 -0
  111. data/lib/{sugarcube/to_s → sugarcube-to_s}/uitouch.rb +0 -0
  112. data/lib/{sugarcube/to_s → sugarcube-to_s}/uiview.rb +0 -0
  113. data/lib/{sugarcube/to_s → sugarcube-to_s}/uiviewcontroller.rb +0 -0
  114. data/lib/sugarcube-uicolor.rb +1 -0
  115. data/lib/sugarcube-uifont.rb +15 -0
  116. data/lib/sugarcube-uiimage.rb +1 -0
  117. data/lib/sugarcube-uikit.rb +15 -0
  118. data/lib/{sugarcube → sugarcube-uikit}/calayer.rb +0 -0
  119. data/lib/sugarcube-uikit/nsattributedstring.rb +12 -0
  120. data/lib/sugarcube-uikit/nsstring.rb +38 -0
  121. data/lib/sugarcube-uikit/symbol.rb +67 -0
  122. data/lib/{sugarcube → sugarcube-uikit}/uifont.rb +1 -4
  123. data/lib/sugarcube-uikit/uiimage.rb +9 -0
  124. data/lib/{sugarcube → sugarcube-uikit}/uilabel.rb +0 -0
  125. data/lib/{sugarcube → sugarcube-uikit}/uipickerview.rb +0 -0
  126. data/lib/sugarcube-uikit/uiview.rb +100 -0
  127. data/lib/{sugarcube → sugarcube-uikit}/uiviewcontroller.rb +0 -0
  128. data/lib/{sugarcube → sugarcube-uikit}/uiwebview.rb +0 -0
  129. data/lib/sugarcube-unholy.rb +1 -9
  130. data/lib/sugarcube.rb +4 -9
  131. data/lib/sugarcube/adjust.rb +1 -0
  132. data/lib/sugarcube/look_in.rb +22 -0
  133. data/lib/sugarcube/version.rb +1 -1
  134. data/runtests +1 -1
  135. data/spec/{uicolor_components_spec.rb → color_components_spec.rb} +0 -0
  136. data/spec/{uicolor_other_representations_spec.rb → color_other_representations_spec.rb} +0 -0
  137. data/spec/{uicolor_spec.rb → color_spec.rb} +0 -0
  138. data/spec/image_cifilters_spec.rb +549 -0
  139. data/spec/{uiimage_color_at_spec.rb → image_color_at_spec.rb} +0 -0
  140. data/spec/{uiimage_scale_spec.rb → image_scale_spec.rb} +0 -0
  141. data/spec/image_uiimage_filters_spec.rb +47 -0
  142. data/spec/nsdate_spec.rb +11 -0
  143. data/spec/nsurl_spec.rb +1 -1
  144. data/spec/numeric_spec.rb +10 -0
  145. data/spec/pipes_spec.rb +135 -0
  146. data/spec/sugarcube_legacy.rb +59 -0
  147. data/spec/symbol_constants_spec.rb +809 -0
  148. data/spec/symbol_uicolor_spec.rb +20 -4
  149. data/spec/symbol_uifont_spec.rb +30 -0
  150. data/spec/uiactionsheet_spec.rb +1 -1
  151. data/spec/uialertview_spec.rb +1 -1
  152. data/spec/uibarbuttonitem_spec.rb +46 -46
  153. data/sugarcube.gemspec +1 -1
  154. metadata +142 -74
  155. data/lib/sugarcube/exceptions.rb +0 -2
  156. data/lib/sugarcube/fixnum.rb +0 -59
  157. data/lib/sugarcube/nsstring.rb +0 -107
  158. data/lib/sugarcube/symbol.rb +0 -766
  159. data/lib/sugarcube/uiactivityindicator.rb +0 -17
  160. data/lib/sugarcube/uuid.rb +0 -10
  161. data/spec/activesupport_spec.rb +0 -96
  162. data/spec/symbol_spec.rb +0 -32
data/Gemfile CHANGED
@@ -1,5 +1,7 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- gem 'rake'
3
+ group :test, :development do
4
+ gem 'rake'
5
+ end
4
6
 
5
7
  gemspec
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- sugarcube (0.20.25)
4
+ sugarcube (1.0.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  SugarCube
2
2
  =========
3
3
 
4
- Some sugar for your cocoa, or your [tea][sweettea].
4
+ Some sugar for your [cocoa](http://rubymotion.com), or your [tea][sweettea].
5
5
 
6
6
  [![Build Status](https://travis-ci.org/rubymotion/sugarcube.png)](https://travis-ci.org/rubymotion/sugarcube)
7
7
 
@@ -9,15 +9,14 @@ About
9
9
  -----
10
10
 
11
11
  CocoaTouch/iOS is a *verbose* framework. These extensions hope to make
12
- development in rubymotion more enjoyable by tacking "UI" methods onto the base
13
- classes (String, Fixnum, Numeric). With SugarCube, you can create a color from an
14
- integer or symbol, or create a UIFont or UIImage from a string.
12
+ development in rubymotion more enjoyable. With SugarCube, you can create a color
13
+ from an integer or symbol, or create a UIFont or UIImage from a string.
15
14
 
16
- Some UI classes are opened up as well, like adding the '<<' operator to a UIView
17
- instance, instead of view.addSubview(subview), you can use the more idiomatic:
18
- view << subview.
15
+ Some core classes are opened up as well, like adding the '<<' operator to a
16
+ UIView instance, instead of view.addSubview(subview), you can use the more
17
+ idiomatic: view << subview.
19
18
 
20
- The basic idea of SugarCube is to turn some operations on their head. Insead of
19
+ The basic idea of SugarCube is to turn operations on their head. So instead of:
21
20
 
22
21
  UIApplication.sharedApplication.openURL(NSURL.URLWithString(url))
23
22
 
@@ -49,6 +48,7 @@ diligent about adding Yard documentation, which is available here:
49
48
 
50
49
  <http://rubydoc.info/gems/sugarcube/latest>
51
50
 
51
+ [documentation]: http://rubydoc.info/gems/sugarcube/latest
52
52
 
53
53
  Installation
54
54
  ============
@@ -59,554 +59,353 @@ Installation
59
59
  require 'sugarcube'
60
60
 
61
61
  # or in Gemfile
62
- gem 'sugarcube'
62
+ gem 'sugarcube', :require => 'sugarcube-classic'
63
+ # or for the bold:
64
+ # gem 'sugarcube', :require => 'sugarcube-all'
63
65
 
64
66
  # in terminal
65
67
  $ bundle install
66
68
 
67
- Examples
69
+ Packages
68
70
  ========
69
71
 
70
- Array
71
- -------
72
+ SugarCube has grown over time to be a pretty massive collection of helpers.
73
+ While some people choose to use the entire library, other people like to pick
74
+ and choose the extensions they want to use. With that in mind, SugarCube is
75
+ written so that it does *not* pollute any classes by default. So if all you do
76
+ is `require "sugarcube"`, you are NOT going to get much mileage!
77
+
78
+ In the installation code above, I show the example of using `:require => 'sugarcube-all'`
79
+ to include *all* of SugarCube's extensions. Usually you will require the
80
+ packages you need from your Rakefile:
72
81
 
73
82
  ```ruby
74
- [1, 3].nsindexpath # NSIndexPath.indexPathWithIndex(1).indexPathByAddingIndex(3)
75
- [160, 210, 242].uicolor # => UIColor.colorWithRed(0.6274, green:0.8235, blue:0.9490, alpha:1.0)
76
- [160, 210, 242].uicolor(0.5) # => UIColor.colorWithRed(0.6274, green:0.8235, blue:0.9490, alpha:0.5)
83
+ $:.unshift('/Library/RubyMotion/lib')
84
+ require 'motion/project/template/ios'
85
+ require 'bundler'
86
+ Bundler.require
87
+ require './lib/sugarcube-uikit'
88
+ require './lib/sugarcube-events'
89
+ require './lib/sugarcube-gestures'
90
+ require './lib/sugarcube-568'
91
+ require './lib/sugarcube-attributedstring'
92
+ # ...
77
93
  ```
78
94
 
79
- Hash => Object
80
- --------
95
+ You can require the packages in piecemeal like this, or you can require a group
96
+ of packages: `classic, common, or all`.
81
97
 
82
- ```ruby
83
- require 'sugarcube-attributedstring'
84
- ```
98
+ * `sugarcube-classic`: Excludes **568**, **attributedstring**, **gestures**, **repl**, **awesome**, **anonymous**, **unholy**, and **legacy**
99
+ * `sugarcube-common`: Excludes **awesome**, **anonymous**, **unholy**, and **legacy**
100
+ * `sugarcube-all`: Excludes **legacy**
85
101
 
86
- Convert `Hash`es into an "anonymous object". Existing keys will be able to be
87
- accessed using method names. Uses the `SugarCube::Anonymous` class to
88
- accomplish this, though the usual interface is via `Hash#to_object`.
102
+ So without further ado,
89
103
 
90
- ```ruby
91
- h = { foo: 'FOO', 'bar' => 'BAR' }.to_object
104
+ SugarCube
105
+ =========
92
106
 
93
- # You can use methods instead of keys.
94
- h.foo # => h[:foo]
95
- h.bar # => h['bar']
96
- h.foo = 'Foo' # => h[:foo] = 'Foo'
97
- h.bar = 'Bar' # => h['bar'] = 'Bar'
107
+ Packages are sorted more-or-less by their usefulness. The more esoteric ones
108
+ are at the end.
98
109
 
99
- # only existing keys are accessed this way
100
- h.baz # => NoMethodError
101
- h.baz = 'baz' # => NoMethodError
102
- ```
110
+ REPL ([wiki][REPL Wiki])
111
+ ----
103
112
 
104
- Fixnum
105
- --------
113
+ If you install SugarCube and *only* use the REPL package, you will benefit from
114
+ some of its greatest tools!
106
115
 
107
- ```ruby
108
- # create a UIColor from a hex value
109
- 0xffffff.uicolor # => UIColor.colorWithRed(1.0, green:1.0, blue:1.0, alpha:1.0)
110
- 0xffffff.uicolor(0.5) # => UIColor.colorWithRed(1.0, green:1.0, blue:1.0, alpha:0.5)
116
+ > `require 'sugarcube-repl'`
111
117
 
112
- # some number-to-string stuff
113
- 1.nth # => 'st'
114
- 2.nth # => 'nd'
115
- 3.nth # => 'rd'
116
- 4.nth # => 'th'
117
- 11.nth # => 'th'
118
- 13.nth # => 'th'
119
- 21.nth # => 'st'
120
- 23.nth # => 'rd'
118
+ This package is useful during development because it adds methods to the REPL
119
+ that make adjusting and introspecting views much easier. You'll get a lot more
120
+ done in the REPL with these additions.
121
121
 
122
- NSDate.new # => 2013-01-03 11:42:24 -0700
123
- 5.days.ago # => 2012-12-29 11:42:24 -0700
124
- 5.days.before(NSDate.new) # => 2012-12-29 11:42:24 -0700
125
- 5.days.hence # => 2013-01-08 11:42:24 -0700
126
- 5.days.after(NSDate.new) # => 2013-01-08 11:42:24 -0700
127
- # don't confuse 'after' and 'later'
128
- # after => NSDate
129
- # later => NSTimer
130
- ```
122
+ To keep this document lean-and-mean, I've put most of the REPL documentation [in
123
+ the wiki][REPL Wiki], but a
124
+ quick overview:
131
125
 
132
- Numeric
133
- ---------
126
+ * Use the `tree` commands to output your view hierarchy. It can accept a UIView,
127
+ `UIViewController`, or `CALayer` object as the root object, or it defaults to
128
+ your application's `UIWindow` object.
134
129
 
135
- ```ruby
136
- # create a percentage
137
- 100.0.percent # => 1.00
138
- 55.0.percent # => 0.55
130
+ ```
131
+ (main)> tree
132
+ 0: . UIWindow(#6e1f950: [[0.0, 0.0], [320.0, 480.0]])
133
+ 1: `-- UIView(#8b203b0: [[0.0, 20.0], [320.0, 460.0]])
134
+ 2: +-- UIButton(#d028de0: [[10.0, 10.0], [320.0, 463.400512695312]])
135
+ ```
136
+ * The number can be passed to the `adjust` method, aliased to `a`, and that will
137
+ become the view or object you are adjusting.
139
138
 
140
- # convert to radians. does this look weird?
141
- 180.degrees # => Math::PI
139
+ ```
140
+ (main)> a 2
141
+ => UIButton(#d028de0: [[10.0, 10.0], [320.0, 463.400512695312]])
142
+ ```
142
143
 
143
- # convert multiples of pi
144
- 2.pi # => 2 * Math::PI
145
- 0.5.pi # => 0.5 * Math::PI
144
+ * Now you can modify that view, either by accessing it via `a` (with no
145
+ arguments it returns the object being adjusted) or by using an adjust method:
146
146
 
147
- # if you thought conversion from degrees to radians looks weird, you'll hate
148
- # conversion from meters to miles:
149
- distance = 1500 # this is in meters. why? because all the methods that return
150
- # a "distance" return it in meters
151
- distance.miles # => 0.932056427001953
147
+ ```
148
+ > up 1
149
+ > wider 15
150
+ # these have shorthands, too
151
+ > u 1
152
+ > w 15
153
+ ```
152
154
 
153
- # use NSNumberFormatter to easily format a number in the current locale
154
- 10000.string_with_style # => "10,000"
155
- 10000.string_with_style(NSNumberFormatterCurrencyStyle) # => "$10,000.00"
156
- 10000.string_with_style(:currency) # => "$10,000.00"
157
- ```
155
+ Be sure to read more in the [REPL Adjustments][REPL Wiki] Wiki page.
158
156
 
159
- NSAttributedString
160
- ---------
157
+ [REPL Wiki]: https://github.com/rubymotion/sugarcube/wiki/REPL-Additions
161
158
 
162
- ```ruby
163
- require 'sugarcube-attributedstring'
164
- ```
159
+ UIKit ([wiki][UIKit Wiki])
160
+ -----
165
161
 
166
- These become pretty fun! Check out `nsattributedstring_spec.rb` for all the
167
- supported attributes (in theory they are all supported, but there's weird
168
- issues with missing constants).
162
+ A big package chock full of methods to make working in UIKit a joy.
169
163
 
170
- ```ruby
171
- 'test'.nsattributedstring({}) #=> NSAttributedString.alloc.initWithString('test', attributes:{})
172
- 'test'.attrd # => alias for `nsattributedstring`
173
- 'test'.bold # => NSAttributedString.alloc.initWithString('test', attributes:{NSFontAttributeName => :bold.uifont})
174
- 'test'.italic # => NSAttributedString.alloc.initWithString('test', attributes:{NSFontAttributeName => :italic.uifont})
175
- 'test'.underline # => NSAttributedString.alloc.initWithString('test', attributes:{NSUnderlineStyleAttributeName => NSUnderlineStyleSingle})
164
+ > `require 'sugarcube-uikit'`
176
165
 
177
- # you can chain 'em, too.
178
- 'test'.bold.underline
179
- # If you look up NSAttributedString Application Kit Additions, you can see all
180
- # the constants. Each of those has a method on NSAttributedString.
166
+ A few varieties of methods are in this package:
181
167
 
182
- # you can add 'em, but the FIRST one MUST be an NSAttributedString
183
- 'test'.attrd + '-ing'.italic
168
+ * Conversions: `'string-to'.uiimage`, `image.uiimageview`
169
+ * Helpers: shorthands for common operations, like `a_view << a_subview`
170
+ * Symbols: `:system.uifont(20)`, `:label.uifontsize`
184
171
 
185
- # And there's where it gets FUN:
186
- ('This'.italic + ' is going to be ' + 'FUN'.bold).underline
187
- ```
172
+ There are too many methods to define here. Instead: a complete list of methods
173
+ is available in the [documentation][], and the [wiki page][UIKit Wiki] is a
174
+ great source as well.
188
175
 
189
- And you can easily turn this into a label!
176
+ [UIKit Wiki]: https://github.com/rubymotion/sugarcube/wiki/UIKit
190
177
 
191
- ```ruby
192
- view << (("We just met\n".attrd +
193
- "and this is " + "CRAZY".italic + "\n"
194
- "But here's my " + "id_rsa.pub".monospace + " file,\n" +
195
- "so give me SSH access.").uilabel
196
- ```
178
+ Constants
179
+ -----
197
180
 
198
- NSCoder
199
- ---------
181
+ > `require 'sugarcube-constants'`
200
182
 
201
- Shorthands and hash-like access to the coder/decoder objects.
183
+ There are lots and lots of constants in UIKit, so many that I wanted a way to
184
+ write these as symbols instead of UILongConstantNames. This package adds
185
+ methods to `Symbol`s to convert them into a UIKit or Foundation constant.
202
186
 
203
187
  ```ruby
204
- # hash access is the handiest
205
- coder['key'] = self.value
206
- self.value = decoder['key']
207
-
208
- # but if you want to store booleans and such (in their C form,
209
- # which will take up less space I suppose):
210
- coder.set('sugarcube_is_neat', toBool:self.sugarcube_is_neat?)
211
- self.sugarcube_is_neat = decoder.bool('sugarcube_is_neat')
188
+ :center.uialignment # => UITextAlignmentCenter
189
+ :upside_down.uiorientation # => UIDeviceOrientationPortraitUpsideDown
190
+ :rounded.uibuttontype # => UIButtonTypeRoundedRect
191
+ :highlighted.uicontrolstate # => UIControlStateHighlighted
192
+ :touch.uicontrolevent # => UIControlEventTouchUpInside
193
+ :change.uicontrolevent # => UIControlEventValueChanged
194
+ :all.uicontrolevent # => UIControlEventAllEvents
212
195
 
213
- coder.set('number_of_things', toInt:self.number_of_things)
214
- self.number_of_things = decoder.int('number_of_things')
196
+ # these are really handy for custom buttons - touch_start means the finger is
197
+ # inside the button, touch_stop is outside the button or canceled
198
+ :touch_start # => UIControlEventTouchDown | UIControlEventTouchDragEnter
199
+ :touch_stop # => UIControlEventTouchUpInside | UIControlEventTouchCancel | UIControlEventTouchDragExit
215
200
 
216
- # the entire list:
217
- coder.set(key, toBool:value)
218
- coder.set(key, toDouble:value)
219
- coder.set(key, toFloat:value)
220
- coder.set(key, toInt:value)
221
- coder.set(key, toPoint:value)
222
- coder.set(key, toRect:value)
223
- coder.set(key, toSize:value)
201
+ :large.uiactivityindicatorstyle # :large, :white, :gray
202
+ :bar.uisegmentedstyle # :plain, :bordered, :bar, :bezeled
224
203
 
225
- decoder.bool(key)
226
- decoder.double(key)
227
- decoder.float(key)
228
- decoder.int(key)
229
- decoder.point(key)
230
- decoder.rect(key)
231
- decoder.size(key)
204
+ # UITableView and UITableViewCell have LOTS of associated constants... I'm
205
+ # adding them as I come across them.
206
+ :automatic.uitablerowanimation # or .uitableviewrowanimation
207
+ :default.uitablecellstyle # or .uitableviewcellstyle
208
+ :disclosure.uitablecellaccessory # or .uitableviewcellaccessorytype
209
+ :blue.uitablecellselectionstyle # or .uitableviewcellselectionstyle
232
210
  ```
233
211
 
234
- NSData
235
- ------
212
+ See the complete list by browsing the [documentation][], or open up [symbol.rb][].
236
213
 
237
- Going to and from `NSData` is really useful when doing HTTP posts.
214
+ [symbol.rb]: https://github.com/rubymotion/sugarcube/blob/master/lib/sugarcube-constants/symbol.rb
238
215
 
239
- ```ruby
240
- string_data = 'String'.nsdata # => default encoding is UTF8.
241
- image = 'an image'.uiimage
242
- image_data = image.nsdata # PNG data representation
216
+ Timer
217
+ -----
243
218
 
244
- string_data.nsstring # => 'String'
245
- image_data.nsimage # => whatever 'an image' was
246
- ```
219
+ > `require 'sugarcube-timer'`
247
220
 
248
- NSDate
249
- --------
250
-
251
- `NSDate` objects are converted to `Time` objects automatically by rubymotion.
252
- That's the good news.
253
- The bad news? That still doesn't help a lot with some of
254
- the everyday date & time crap we have to deal with. (I hate dates, especially
255
- recurring events)
256
- ##### NSDate
257
-
258
- 1. Adds the following methods to get date and time components: `date_array, time_array, datetime_array`.
259
- These methods return arrays. Comparing dates, times, or both become
260
- simple `date1.date_array == date2.date_array`.
261
- 2. While I would love to support `date + 1.month` and have that support "smart"
262
- calendar math (e.g. "2/13/2013" + 1.month => "3/13/2013"), I can't fudge with
263
- the return value of `1.month` (=> `Fixnum`), and I won't make the terrible
264
- assumption that "30 days of seconds is *about* one month". So instead, a new
265
- method that accepts date components as options is introduced: `date.delta(months:1)`
266
- 3. Checking whether two dates are the same, ignoring the time components, is often required
267
- `start_of_day` and `end_of_day` methods help
268
- you here. They are akin to `floor` and `ceil`; if you consider the time to
269
- be the "floating" component, and the date to be the nearest "integer".
270
- 4. Formatting is made easier with `NSDate#string_with_style(NSDateStyleConstant or Symbol for date, time)`
271
- and `NSDate#string_with_format(format_string)`. See
272
- <http://www.unicode.org/reports/tr35/tr35-25.html#Date_Format_Patterns> for
273
- the formatters, they take getting used to, coming from `strftime`, but they
274
- are much more powerful and locale-aware.
275
- 5. Miscellaneous other helpers. I'll go over these first.
276
-
277
- ###### Helpers
221
+ Methods get added to the Fixnum class, and are available as methods on
222
+ `NSTimer`, *and* can be called via the SugarCube::Timer module.
278
223
 
279
224
  ```ruby
280
- (main)> now = NSDate.new # Time.new is the same thing
281
- => 2012-09-13 09:19:06 -0600
282
-
283
- # NSDate##from_components
284
- (main)> feb_1_2013 = NSDate.from_components(year: 2013, month: 2, day:1)
285
- => 2013-02-01 00:00:00 -0700
286
- (main)> feb_1_2013_sometime_later = NSDate.from_components(year: 2013, month: 2, day:1, hour:13, minute: 59, second:30)
287
- => 2013-02-01 13:59:30 -0700
288
- (main)> feb_1_2012 = NSDate.from_components(year: 2012, month: 2, day:1)
289
- => 2012-02-01 00:00:00 -0700
290
-
291
-
292
- (main)> feb_1_2013.timezone.name
293
- => "America/Denver"
294
- (main)> feb_1_2013.era
295
- => 1 # no, I don't know what this is :-/
296
- (main)> feb_1_2013.today?
297
- => false # actually, at the time I am WRITING this, it IS true, but by the time
298
- # you read it, not so much ;-)
299
- (main)> NSDate.new.today?
300
- => true
301
- (main)> feb_1_2013.same_day?(NSDate.new)
302
- => false
303
- (main)> feb_1_2013.same_day?(feb_1_2013_sometime_later)
304
- # even though the time is different!?
305
- => true
306
- (main)> feb_1_2013.utc_offset
307
- => -25200
308
- (main)> feb_1_2013.leap_year?
309
- => false
310
- (main)> NSDate.from_components(year: 2012).leap_year?
311
- => true
312
- (main)> feb_1_2013.start_of_day
313
- => 2013-02-01 00:00:00 -0700
314
- (main)> feb_1_2013.end_of_day
315
- # NOTE! end_of_day is the NEXT DAY. this is not an accident, it makes comparisons cleaner. deal with it.
316
- => 2013-02-02 00:00:00 -0700
317
- (main)> feb_1_2013.start_of_week # in the USA, start of week is Sunday
318
- => 2013-01-27 00:00:00 -0700
319
- => 2013-01-28 00:00:00 -0700 # in most other countries you will get Monday
320
- (main)> feb_1_2013.start_of_week(:monday) # or you can specify it!
321
- => 2013-01-28 00:00:00 -0700
322
- (main)> feb_1_2013.end_of_week # Just like end_of_day, end_of_week returns midnight of the *next day*
323
- => 2013-02-03 00:00:00 -0700
324
- (main)> feb_1_2013.end_of_week(:monday)
325
- => 2013-02-04 00:00:00 -0700
326
- (main)> feb_1_2013.days_in_month
327
- => 28
328
- (main)> feb_1_2013.days_in_year
329
- => 365
330
- (main)> feb_1_2012.days_in_month
331
- => 29
332
- (main)> feb_1_2012.days_in_year
333
- => 366
334
-
335
- (main)> now.date_array
336
- => [2012, 9, 13]
337
- (main)> now.time_array
338
- => [9, 19, 6]
339
- (main)> now.datetime_array
340
- => [2012, 9, 13, 9, 19, 6]
341
- ```
342
-
343
- Use `NSDate#string_with_style` to generate date and/or time strings.
225
+ # once
226
+ 1.second.later do
227
+ @view.shake
228
+ end
229
+ # repeating
230
+ 1.second.every do
231
+ @view.shake
232
+ end
344
233
 
345
- ```ruby
346
- (main)> now.string_with_style
347
- => "January 29, 2013"
348
- (main)> now.string_with_style(NSDateFormatterShortStyle)
349
- => "1/29/13"
350
- (main)> now.string_with_style(:short)
351
- => "1/29/13"
352
- (main)> now.string_with_style(NSDateFormatterMediumStyle, NSDateFormatterShortStyle)
353
- => "Jan 29, 2013, 9:19 AM"
354
- (main)> now.string_with_style(:short, :medium)
355
- => "1/29/13, 9:19:06 AM"
356
- (main)> now.string_with_style(:none, :long)
357
- => "9:19:06 AM GMT+01:00"
358
- ```
234
+ # you can assign the return value (an NSTimer)
235
+ timer = 1.second.every do
236
+ @view.shake
237
+ end
238
+ # and invalidate it
239
+ timer.invalidate
359
240
 
360
- It is easy to add seconds to the date using the time-related methods added to
361
- `Numeric`, though the `NSDate#delta` method is MUCH more capable.
241
+ # the `every` method is available in the SugarCube::Timer module,
242
+ # which you might find more readable
243
+ include SugarCube::Timer
362
244
 
363
- ```ruby
364
- (main)> now + 5
365
- => 2012-09-13 09:19:11 -0600
366
- (main)> now - 5
367
- => 2012-09-13 09:19:01 -0600
368
- (main)> now + 5.minutes
369
- => 2012-09-13 09:24:06 -0600
370
- (main)> now + 5.days
371
- => 2012-09-18 09:19:06 -0600
372
- ```
245
+ every 1.minute do
246
+ puts "tick"
247
+ end
373
248
 
374
- Time zone objects are available, but the `Time#utc_offset` method is a little
375
- more useful. It returns the offset *in seconds*, so divide by `1.0.hour` to get
376
- the offset in hours. `utc_offset` is built into `Time`, not added by SugarCube,
377
- but it is added to the `NSDate` class in case you get one of those instead.
249
+ # might as well make an alias for 'later', too
250
+ after 1.minute do
251
+ puts "ding!"
252
+ end
378
253
 
379
- ```ruby
380
- (main)> now.timezone
381
- => #<__NSTimeZone:0x9384c70>
382
- (main)> now.timezone.name
383
- => "America/Denver"
384
- (main)> now.utc_offset
385
- => -21600
386
- (main)> now.utc_offset / 1.hour
387
- => -6
254
+ # other time-related methods
255
+ # for compatibility with Time methods, the mins/secs (and min/sec) aliases are provided. Personally,
256
+ # I like the more verbose minutes/seconds.
257
+ 1.millisecond || 2.milliseconds
258
+ 1.millisec || 2.millisecs
259
+ 1.second || 2.seconds
260
+ 1.sec || 2.secs # aliases
261
+ 1.minute || 2.minutes # 1.minute = 60 seconds
262
+ 1.min || 2.mins # aliases
263
+ 1.hour || 2.hours # 1.hour = 60 minutes
264
+ 1.day || 2.days # 1.day = 24 hours
265
+ 1.week || 2.weeks # 1.week = 7 days
266
+ # sensible values for 'month' and 'year', even though we all know you can't
267
+ # **really** define them this way (go back to python if you find your brain hemorrhaging):
268
+ 1.month || 2.months # 1.month = 30 days
269
+ 1.year || 2.years # 1.year = 365 days
388
270
  ```
389
271
 
390
- The `delta` method is smart. See the tests! It will do its best to compensate
391
- for daylight savings, leap years, different numbers of days in the month, and so
392
- on.
272
+ Events
273
+ -----
393
274
 
394
- ```ruby
395
- (main)> feb_28_2012 = NSDate.from_components(year:2012, month: 2, day: 28)
396
- => 2012-02-28 17:00:00 -0700
397
-
398
- # add an hour or two
399
- (main)> feb_28_2012.delta(hours:1)
400
- => 2012-02-28 18:00:00 -0700
401
- (main)> feb_28_2012.delta(hours:2)
402
- => 2012-02-28 19:00:00 -0700
403
-
404
- # add some days
405
- (main)> feb_28_2012.delta(days:1)
406
- => 2012-02-29 17:00:00 -0700
407
- (main)> feb_28_2012.delta(days:2)
408
- => 2012-03-01 17:00:00 -0700
409
-
410
- # how about a month?
411
- (main)> feb_28_2012.delta(months:1)
412
- => 2012-03-28 17:00:00 -0600 # look, the time didn't change, event though there was a DST change in this period!
413
-
414
- # cool, but if you want a more literal "24 hours", specify a time unit
415
- (main)> feb_28_2012.delta(months:1, hours:0)
416
- => 2012-03-28 18:00:00 -0600 # disable the DST fix by specifying hours, minutes, or seconds (a "precise" delta)
417
-
418
- # in one year, it will still be Feb 28th
419
- (main)> feb_28_2012.delta(years:1)
420
- => 2013-02-28 17:00:00 -0700
421
-
422
- # and we already know what adding a day looks like
423
- (main)> feb_28_2012.delta(days:1)
424
- => 2012-02-29 17:00:00 -0700
425
-
426
- # a year and a day is tricky, because do we add a day, then a year? or add a
427
- # year and then a day? well, i'll tell you, **I** add a day and then a year,
428
- # which is feb 29th, which is no good, and the algorithm rolls back days to the
429
- # last day of the month, so we get the 28th.
430
- (main)> feb_28_2012.delta(days:1, years:1)
431
- => 2013-02-28 17:00:00 -0700
432
-
433
- # adding 2 days puts us into March, which then "looks right", but it's both
434
- # right AND wrong, depending on how you look at it. Another example is below,
435
- # where we add a month to January 30th. Really, though, think of this: how
436
- # often do you need to add a year AND a day!? Adding a year is more common, and
437
- # this is showing that adding a year to Feb 29th will give you Feb 28th, which I
438
- # think is better than March 1st.
439
- (main)> feb_28_2012.delta(days:2, years:1)
440
- => 2013-03-01 17:00:00 -0700
441
-
442
- # Crazier: add a day (Feb 29th), then a month (March 29th), THEN a year.
443
- (main)> feb_28_2012.delta(days:1, years:1, months:1)
444
- => 2013-03-29 17:00:00 -0600
445
-
446
- # k, for the next examples, we need a new date, and this is a non-leap year.
447
- (main)> jan_29_2013 = feb_28_2012.delta(days:1, months:11)
448
- => 2013-01-29 17:00:00 -0700
449
-
450
- # what is 1/29/2013 plus two months? easy! march 29, 2013
451
- (main)> jan_29_2013.delta(months:2)
452
- => 2013-03-29 17:00:00 -0600
453
-
454
- # Yeah, smart guy? Well then what is 1/29/2013 plus ONE month. It's Feb 28th.
455
- # When someone says "see you in a month!" they mean "next month", not "in the
456
- # early part of two months in the future", which is where the math will take you
457
- # if you don't add a "day of month" correction.
458
- (main)> jan_29_2013.delta(months:1)
459
- => 2013-02-28 17:00:00 -0700
460
- # but last year was a leap year, so we should get Feb 29th, 2012:
461
- (main)> jan_29_2013.delta(months:1, years: -1)
462
- => 2012-02-29 17:00:00 -0700 # success!
463
-
464
- # do other deltas work in reverse? fuuuuuu...
465
- (main)> jan_29_2013.delta(months:-11)
466
- => 2012-02-29 17:00:00 -0700
467
- # ...ck yeah! :-)
468
-
469
- # daylight savings!? GEEZ dates are annoying
470
- (main)> mar_10_2013 = NSDate.from_components
471
-
472
- # unfortunately you will, in the edge cases, end up with stuff like this:
473
- (main)> feb_28_2012 == feb_28_2012.delta(days:1, months:12).delta(days: -1, months:-12)
474
- => 2012-02-29 00:00:00 -0700
475
- ```
275
+ > `require 'sugarcube-events'`
476
276
 
477
- NSError
478
- -------
277
+ Inspired by [BubbleWrap's][BubbleWrap] `when` method, but I prefer jQuery-style
278
+ verbs and SugarCube symbols. Adds methods to UIControl and UITextView.
479
279
 
480
- `NSError.new` was just a mess, so I made it a legal method.
280
+ UIControl
281
+ -----------
481
282
 
482
283
  ```ruby
483
- NSError.new('Error Message') # code: 0, domain: 'Error'
484
- # with options
485
- NSError.new('Error Message', code: 404)
486
-
487
- # error messages ('Error Message' in this example) are stored in a Hash with the
488
- # key 'NSLocalizedDescriptionKey'. If you pass a `userInfo` option, it will get
489
- # merged with this array. So you can ignore that ugly-looking key.
490
- NSError.new('Error Message', code: 404, userInfo: { warnings: ['blabla'] })
491
- ```
284
+ button = UIButton.alloc.initWithFrame([0, 0, 10, 10])
492
285
 
493
- NSURL
494
- -------
286
+ button.on(:touch) { my_code }
287
+ button.on(:touch_up_outside, :touch_cancel) { |event|
288
+ puts event.inspect
289
+ # my_code...
290
+ }
495
291
 
496
- ```ruby
497
- # see String for easy URL creation
498
- "https://github.com".nsurl.open # => UIApplication.sharedApplication.openURL(NSURL.URLWithString("https://github.com"))
292
+ # remove handlers
293
+ button.off(:touch, :touch_up_outside, :touch_cancel)
294
+ button.off(:all)
499
295
  ```
500
296
 
501
- NSString
502
- ----------
503
-
504
- ```ruby
505
- # UIImage from name
506
- "my_image".uiimage # => UIImage.imageNamed("my_image")
507
- "pattern".uicolor == "pattern".uiimage.uicolor # => UIColor.colorWithPatternImage(UIImage.imageNamed("pattern"))
508
-
509
- # UIFont from name
510
- "my_font".uifont # => UIFont.fontWithName("my_font", size:UIFont.systemFontSize)
511
- "my_font".uifont(20) # => UIFont.fontWithName("my_font", size:20)
297
+ You can only remove handlers by "type", not by the action. e.g. If you bind
298
+ three `:touch` events, calling `button.off(:touch)` will remove all three.
512
299
 
513
- # UIColor from image name or hex code
514
- "pattern".uicolor # => UIColor.colorWithPatternImage(UIImage.imageNamed('pattern'))
515
- "#ff00ff".uicolor == :fuchsia.uicolor == 0xff00ff.uicolor # => UIColor.colorWithRed(1.0, green:0.0, blue:1.0, alpha:1.0)
516
- "#f0f".uicolor(0.5) == :fuchsia.uicolor(0.5) == 0xff00ff.uicolor(0.5) # => UIColor.colorWithRed(1.0, green:1.0, blue:1.0, alpha:0.5)
517
- # note: 0xf0f.uicolor == 0x000f0f.uicolor. There's no way to tell the difference
518
- # at run time between those two Fixnum literals.
300
+ UITextView
301
+ ------------
519
302
 
520
- # NSLocalizedString from string
521
- "hello".localized # => NSBundle.mainBundle.localizedStringForKey("hello", value:nil, table:nil)
522
- "hello"._ # == "hello".localized
523
- "hello".localized('Hello!', 'hello_table') # => ...("hello", value:'Hello!', table:'hello_table')
303
+ You MUST call the `off` methods, because these methods use `NSNotification`s,
304
+ and you must turn off listeners.
524
305
 
525
- # file operations
526
- "my.plist".exists? # => NSFileManager.defaultManager.fileExistsAtPath("my.plist")
527
- "my.plist".document # => NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true)[0].stringByAppendingPathComponent("my.plist")
528
- "my.plist".cache # => NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, true)[0].stringByAppendingPathComponent("my.plist")
529
- "my.plist".remove! # => NSFileManager.defaultManager.removeItemAtPath("my.plist".document, error: error) (returns error, if any occurred)
306
+ There are two aliases for each event. I prefer the present tense (jQuery-style `on :change`),
307
+ but UIKit prefers past simple (`UITextViewTextDidBeginEditingNotification`).
530
308
 
531
- # get the resource path, useful if you include json files or images you manipulate in the app
532
- "my.plist".resource # => NSBundle.mainBundle.resourcePath.stringByAppendingPathComponent("my.plist")
533
- # same, but get a URL instead - often used to display a static HTML page that is stored in resources
534
- "index.html".resource_url # => NSBundle.mainBundle.URLForResource("index", withExtension:"html")
309
+ So these are all the same:
535
310
 
536
- # access data from Info.plist
537
- "CFBundleVersion".info_plist # => NSBundle.mainBundle.infoDictionary["CFBundleVersion"]
311
+ :editing_did_begin :begin
312
+ :editing_did_change :change
313
+ :editing_did_end :end
538
314
 
539
- # NSURL
540
- "https://github.com".nsurl # => NSURL.URLWithString("https://github.com")
315
+ ```ruby
316
+ text_view = UITextView.new
317
+ text_view.on :begin do
318
+ p 'wait for it...'
319
+ end
320
+ text_view.on :change do
321
+ p text_view.text
322
+ end
323
+ text_view.on :end do
324
+ p 'done!'
325
+ end
541
326
 
542
- # check if string is not a number
543
- "pi".nan? # => NSNumberFormatter.alloc.init.numberFromString("pi").nil?
327
+ # later... like in `viewWillDisappear`. I'll use the alternative aliases here
328
+ text_view.off :editing_did_change, :editing_did_end, :editing_did_begin
544
329
  ```
545
330
 
546
- NSIndexPath
547
- -------------
331
+ Gestures
332
+ -----
548
333
 
549
- Use the `IndexPath` class to match `NSIndexPath` objects, for instance in a
550
- `UITableViewDelegate`.
334
+ > `require 'sugarcube-gestures'`
335
+
336
+ SugarCube's gesture support is very similar to BubbleWrap's, and it's entirely
337
+ possible that the two will be merged into one thing. But SugarCube is all about
338
+ extending base classes, whereas BubbleWrap tends to add *new* classes to do the
339
+ heavy lifting. Plus the options you pass to SugarCube are very different, and
340
+ the prefix is "on" instead of "when" (e.g. "on_pan" instead of "when_panned")
551
341
 
552
342
  ```ruby
553
- index_path = [0, 2].nsindexpath
554
- case index_path
555
- when IndexPath[0]
556
- when IndexPath[1, 0..5]
557
- when IndexPath[1, 5..objects.length]
343
+ view.on_pan do |gesture|
344
+ location = gesture.view.locationInView(view)
558
345
  end
559
- [0, 2].nsindexpath.to_a == [0, 2] # => true
346
+
347
+ # other gesture methods, with common options:
348
+ view.on_tap # use system defaults
349
+ view.on_tap(1) # number of taps
350
+ view.on_tap(taps: 1, fingers: 1) # number of taps and number of fingers
351
+
352
+ view.on_pinch # no options
353
+ view.on_rotate # no options
354
+
355
+ view.on_swipe # use system defaults
356
+ view.on_swipe :left
357
+ view.on_swipe(direction: :left, fingers: 1)
358
+ view.on_swipe(direction: UISwipeGestureRecognizerDirectionLeft, fingers: 1)
359
+
360
+ view.on_pan # use system defaults
361
+ view.on_pan(2) # minimum and maximum fingers required
362
+ view.on_pan(fingers: 2)
363
+ view.on_pan(min_fingers: 2, max_fingers: 3)
364
+
365
+ view.on_press # use system defaults
366
+ view.on_press(1.5) # duration
367
+ view.on_press(duration: 1.5, taps: 1, fingers: 1)
560
368
  ```
561
369
 
562
- Symbol
563
- --------
370
+ Notifications
371
+ -----
372
+
373
+ > `require 'sugarcube-notifications'`
564
374
 
565
- This is the "big daddy". Lots of sugar here...
375
+ Makes it easy to post a notification to some or all objects.
566
376
 
567
377
  ```ruby
568
- :center.uialignment # => UITextAlignmentCenter
569
- :upside_down.uiorientation # => UIDeviceOrientationPortraitUpsideDown
570
- :rounded.uibuttontype # => UIButtonTypeRoundedRect
571
- :highlighted.uicontrolstate # => UIControlStateHighlighted
572
- :touch.uicontrolevent # => UIControlEventTouchUpInside
573
- :change.uicontrolevent # => UIControlEventValueChanged
574
- :all.uicontrolevent # => UIControlEventAllEvents
575
- :blue.uicolor # UIColor.blueColor
378
+ # this one is handy, I think:
379
+ MyNotification = "my notification"
380
+ MyNotification.post_notification # => NSNotificationCenter.defaultCenter.postNotificationName(MyNotification, object:nil)
381
+ MyNotification.post_notification(obj) # => NSNotificationCenter.defaultCenter.postNotificationName(MyNotification, object:obj)
382
+ MyNotification.post_notification(obj, user: 'dict') # => NSNotificationCenter.defaultCenter.postNotificationName(MyNotification, object:obj, userInfo:{user: 'dict'})
576
383
 
577
- # these are really handy for custom buttons - touch_start means the finger is inside the button, touch_stop is outside the button or canceled
578
- :touch_start # => UIControlEventTouchDown | UIControlEventTouchDragEnter
579
- :touch_stop # => UIControlEventTouchUpInside | UIControlEventTouchCancel | UIControlEventTouchDragExit
384
+ # you can access the userInfo dictionary directly from the notification
385
+ def notified(notification)
386
+ notification[:user] # => 'dict'
387
+ end
580
388
 
581
- # all CSS colors are supported, and alpha
582
- # (no "grey"s, only "gray"s, consistent with UIKit, which only provides "grayColor")
583
- :firebrick.uicolor(0.25) # => 0xb22222.uicolor(0.25)
584
- :bold.uifont # UIFont.boldSystemFontOfSize(UIFont.systemFontSize)
585
- :bold.uifont(10) # UIFont.boldSystemFontOfSize(10)
586
- :small.uifontsize # => UIFont.smallSystemFontSize
587
- :small.uifont # => UIFont.systemFontOfSize(:small.uifontsize)
588
- :bold.uifont(:small) # UIFont.boldSystemFontOfSize(:small.uifontsize)
589
- :large.uiactivityindicatorstyle # :large, :white, :gray
590
- :bar.uisegmentedstyle # :plain, :bordered, :bar, :bezeled
389
+ # very similar to add or remove an observer
390
+ MyNotification.add_observer(observer, :method_name)
391
+ MyNotification.add_observer(observer, :method_name, object)
591
392
 
592
- # UITableView and UITableViewCell have LOTS of associated constants... I'm
593
- # adding them as I come across them.
594
- :automatic.uitablerowanimation # or .uitableviewrowanimation
595
- :default.uitablecellstyle # or .uitableviewcellstyle
596
- :disclosure.uitablecellaccessory # or .uitableviewcellaccessorytype
597
- :blue.uitablecellselectionstyle # or .uitableviewcellselectionstyle
393
+ # remove the observer
394
+ MyNotification.remove_observer(observer)
395
+ MyNotification.remove_observer(observer, object)
598
396
  ```
599
397
 
600
398
  UIImage
601
- ---------
399
+ -----
602
400
 
603
- ```ruby
604
- image = "my_image".uiimage
605
- image.uicolor # => UIColor.colorWithPatternImage(image)
606
- ```
401
+ Image Manipulation - VERY handy! Includes some quick maniputions on UIImage,
402
+ and adds an interface to chain together CIFilters. Plus, you can refer to
403
+ `cifilter.rb` to find out what filters are supported in iOS (all supported
404
+ filters get a class method in this file).
607
405
 
608
- ###### Image Manipulation - VERY handy!
406
+ > `require 'sugarcube-image'`
609
407
 
408
+ ###### UIImage additions
610
409
  ```ruby
611
410
  image.scale_to [37, 37]
612
411
  image.rounded # default: 5 pt radius
@@ -651,24 +450,107 @@ image.masked(mask_image)
651
450
  image_ab = image_a << image_b
652
451
  ```
653
452
 
654
- #### 568
453
+ ###### CIFilter additions
454
+ ```ruby
455
+ # create a filter
456
+ gaussy = CIFilter.gaussian_blur(radius: 5)
457
+ gaussy = CIFilter.gaussian_blur(5) # this also works - you can find the arg order by looking in cifilter.rb
655
458
 
656
- If you `require 'sugarcube-568'` in your Rakefile, you can use
657
- `UIImage.imageNamed(name)` or `name.uiimage` to load images that are specific to
658
- the 4" iphone.
459
+ # apply a filter to a UIImage
460
+ new_image = image.apply_filter(gaussy).uiimage # apply_filter returns a CIImage, which is converted to UIImage
461
+
462
+ # apply a chain of filters using the `|` operator or `apply_filter`
463
+ darken = CIFilter.color_controls(saturation: 0, brightness: 0)
464
+ new_image = image.apply_filter(gaussy).apply_filter(darken).uiimage
465
+ ```
466
+
467
+ If you include `sugarcube-pipes` you can use the `|` operator to chain filters:
659
468
 
660
469
  ```ruby
661
- 'tall'.uiimage # => UIImage.imageNamed('tall')
662
- # => tall.png on iphone 3g
663
- # => tall@2x.png on iphone 4
664
- # => tall-568h@2x.png on iphone 5
470
+ # using the filters from above
471
+ new_image = image | gaussy | darken | UIImage
472
+ new_view = view | gaussy | darken | UIView
665
473
  ```
666
474
 
667
- This code is ported from <https://github.com/gaj/imageNamed568>, which I had
668
- some problems with on RubyMotion (it worked, but not *always*. Very strange).
475
+ There are 91 filters available in iOS 6, I won't list them here, but check out
476
+ [the Apple documentation][core-image-filters] to read about them, and study
477
+ [cifilter.rb][].
478
+
479
+ [core-image-filters]: http://developer.apple.com/library/mac/#documentation/GraphicsImaging/Reference/CoreImageFilterReference/Reference/reference.html
480
+ [cifilter.rb]: https://github.com/colinta/sugarcube/blob/1.0/lib/sugarcube-image/cifilter.rb
481
+ [cifilter.rb-post-merge]: https://github.com/rubymotion/sugarcube/blob/master/lib/sugarcube-image/cifilter.rb
482
+
483
+ UIColor
484
+ -----
485
+
486
+ > `require 'sugarcube-color'`
487
+
488
+ Methods to merge or manipulate a color, or to get information about a color.
489
+ Works best on RGB colors, but HSB will work well, too. `UIColor`s based on
490
+ image patterns can't easily be inverted or mixed.
491
+
492
+ Any classes that have a well-defined "color" representation are given a
493
+ `uicolor` method, so it's easy to create a color from hex codes, css names,
494
+ or images (as patterns).
495
+
496
+ ```ruby
497
+ :blue.uicolor # UIColor.blueColor
498
+ # uicolor() accepts an alpha value, too
499
+ :blue.uicolor(0.5)
500
+
501
+ # all CSS colors are supported (but no "grey" aliases, consistent with UIKit,
502
+ # which only provides "grayColor")
503
+ :firebrick.uicolor # => 0xb22222.uicolor
504
+
505
+ # RGB values, in the range 0..255.
506
+ [160, 210, 242].uicolor # => UIColor.colorWithRed(0.6274, green:0.8235, blue:0.9490, alpha:1.0)
507
+ [160, 210, 242].uicolor(0.5) # => UIColor.colorWithRed(0.6274, green:0.8235, blue:0.9490, alpha:0.5)
508
+
509
+ # create a UIColor from a hex value
510
+ 0xffffff.uicolor # => UIColor.colorWithRed(1.0, green:1.0, blue:1.0, alpha:1.0)
511
+ 0xffffff.uicolor(0.5) # => UIColor.colorWithRed(1.0, green:1.0, blue:1.0, alpha:0.5)
512
+
513
+ # works when using strings, too
514
+ "#fff".uicolor # => UIColor.whiteColor
515
+ "#ffffff".uicolor # => UIColor.whiteColor
516
+ "#ff00ff".uicolor == :fuchsia.uicolor == 0xff00ff.uicolor # => UIColor.colorWithRed(1.0, green:0.0, blue:1.0, alpha:1.0)
517
+ "#f0f".uicolor(0.5) == :fuchsia.uicolor(0.5) == 0xff00ff.uicolor(0.5) # => UIColor.colorWithRed(1.0, green:1.0, blue:1.0, alpha:0.5)
518
+
519
+ # note: 0xf0f.uicolor == 0x000f0f.uicolor. There's no way to tell the difference
520
+ # at run time between those two Fixnum literals.
521
+
522
+ # UIColor from image name, if the first character is not "#"
523
+ "pattern".uicolor == "pattern".uiimage.uicolor # => UIColor.colorWithPatternImage(UIImage.imageNamed("pattern"))
524
+ ```
525
+
526
+ These methods are added onto the UIColor class:
527
+
528
+ ```ruby
529
+ :red.uicolor.invert # => UIColor.cyanColor
530
+ :blue.uicolor.invert # => UIColor.yellowColor
531
+ :green.uicolor.invert # => UIColor.magentaColor
532
+ :red.uicolor + :blue.uicolor # => UIColor.purpleColor
533
+ :red.uicolor + :green.uicolor # => :olive.uicolor
534
+ # (I didn't know that until I tried it in the REPL, but it was pretty cool to
535
+ # see the UIColor#to_s method match that mixture to olive!)
536
+
537
+ # a more generic color mixing method (`+` delegates to this method):
538
+ :white.uicolor.mix_with(:black.uicolor, 0) # => :white
539
+ :white.uicolor.mix_with(:black.uicolor, 0.25) # => 0x404040.uicolor
540
+ :white.uicolor.mix_with(:black.uicolor, 0.5) # => :gray, same as :white.uicolor + :black.uicolor
541
+ :white.uicolor.mix_with(:black.uicolor, 0.75) # => 0xbfbfbf.uicolor
542
+ :white.uicolor.mix_with(:black.uicolor, 1) # => :black
543
+
544
+ # convert to CGColor
545
+ color.cgcolor
546
+ ```
547
+
548
+ Factories
549
+ -----
550
+
551
+ > `require 'sugarcube-factories'`
669
552
 
670
- UIAlertView
671
- --------
553
+ ###### UIAlertView
672
554
 
673
555
  Accepts multiple buttons and handlers. In its simplest form, you can pass just
674
556
  a title and block.
@@ -695,8 +577,7 @@ UIAlertView.alert "I mean, is this cool?", buttons: %w[No! Sure! Hmmmm],
695
577
  success: proc { |pressed| self.proceed if pressed == "Sure!" }
696
578
  ```
697
579
 
698
- UIActionSheet
699
- --------
580
+ ###### UIActionSheet
700
581
 
701
582
  This is very similar to `UIAlertView.alert`, but instead of `cancel` and
702
583
  `success` handlers, you can have `cancel, success, and destructive` handlers,
@@ -725,352 +606,220 @@ UIActionSheet.alert 'I mean, is this cool?', buttons: ['Nah', 'With fire!', 'Sur
725
606
  success: proc { |pressed| self.proceed if pressed == 'Sure' }
726
607
  ```
727
608
 
728
- UIColor
729
- ---------
730
-
731
- Methods to merge or manipulate a color, or to get information about a color.
732
- Works best on RGB colors, but HSB will work well, too. `UIColor`s based on
733
- image patterns can't easily be inverted or mixed.
734
-
735
- ```ruby
736
- :red.uicolor.invert # => UIColor.cyanColor
737
- :blue.uicolor.invert # => UIColor.yellowColor
738
- :green.uicolor.invert # => UIColor.magentaColor
739
- :red.uicolor + :blue.uicolor # => UIColor.purpleColor
740
- :red.uicolor + :green.uicolor # => :olive.uicolor
741
- # (I didn't know that until I tried it in the REPL, but it was pretty cool to
742
- # see the UIColor#to_s method match that mixture to olive!)
743
-
744
- # a more generic color mixing method (`+` delegates to this method):
745
- :white.uicolor.mix_with(:black.uicolor, 0) # => :white
746
- :white.uicolor.mix_with(:black.uicolor, 0.25) # => 0x404040.uicolor
747
- :white.uicolor.mix_with(:black.uicolor, 0.5) # => :gray, same as :white + :black
748
- :white.uicolor.mix_with(:black.uicolor, 0.75) # => 0xbfbfbf.uicolor
749
- :white.uicolor.mix_with(:black.uicolor, 1) # => :black
750
- ```
751
-
752
- UIView
753
- --------
754
-
755
- ```ruby
756
- UIView.first_responder # => returns the first responder, starting at UIApplication.sharedApplication.keyWindow
757
- my_view.first_responder # => also returns the first responder, but starts looking in my_view
758
- my_view.controller # => returns the UIViewController that this view belongs to
759
- self.view << subview # => self.view.addSubview(subview)
760
- self.view.show # => self.hidden = false
761
- self.view.hide # => self.hidden = true
762
-
763
- # convert to UIImage. retina-ready.
764
- my_view.uiimage
765
- # that will use the `bounds` property to size the image. but if you want a
766
- # screen shot of the contents of a scroll view, pass in `true` or `:all` to this
767
- # method.
768
- my_scroll_view.uiimage(:all)
769
- ```
770
-
771
- When defining a UIView subclass, you often have attributes that affect your
772
- `drawRect` method (99% of the time, ALL the attributes affect drawing, right?).
773
- So SugarCube adds a `attr_updates` method, which creates an attribute identical
774
- to `attr_accessor` but the setter calls setNeedsDisplay if the new value != the
775
- old value.
776
-
609
+ ###### UIButton
777
610
  ```ruby
778
- class NiftyView < UIView
779
- attr_updates :insets, :pattern
780
-
781
- def drawRect(rect)
782
- # ...
783
- end
784
- end
611
+ UIButton.buttonWithType(:custom.uibuttontype)
612
+ # =>
613
+ UIButton.custom
785
614
 
786
- nifty_view.pattern = 'my_pattern' # => setNeedsDisplay gets called
787
- nifty_view.pattern = 'my_pattern' # => setNeedsDisplay doesn't get called, because the value didn't change.
615
+ UIButton.custom => UIButton.buttonWithType(:custom.uibuttontype)
616
+ UIButton.rounded => UIButton.buttonWithType(:rounded.uibuttontype)
617
+ UIButton.rounded_rect => UIButton.buttonWithType(:rounded_rect.uibuttontype)
618
+ UIButton.detail => UIButton.buttonWithType(:detail.uibuttontype)
619
+ UIButton.detail_disclosure => UIButton.buttonWithType(:detail_disclosure.uibuttontype)
620
+ UIButton.info => UIButton.buttonWithType(:info.uibuttontype)
621
+ UIButton.info_light => UIButton.buttonWithType(:info_light.uibuttontype)
622
+ UIButton.info_dark => UIButton.buttonWithType(:info_dark.uibuttontype)
623
+ UIButton.contact => UIButton.buttonWithType(:contact.uibuttontype)
624
+ UIButton.contact_add => UIButton.buttonWithType(:contact_add.uibuttontype)
788
625
  ```
789
626
 
790
- ###### Animations
627
+ ###### UITableView
791
628
 
792
- jQuery-like animation methods. They accept a "completed" callback handler that
793
- accepts an optional 'completed' boolean (the
794
- `UIView.animateWithDuration(delay:options:animations:completion:)`) method
795
- provides it to all its completion handlers).
629
+ Default frame is `[[0, 0], [0, 0]]`, but most containers will resize it to be
630
+ the correct size. But heads up, it *was* `[[0, 0], [320, 480]]` (until
631
+ the iphone 5 / 4-inch retina came out).
796
632
 
797
633
  ```ruby
798
- # default timeout is 0.3
799
- view.fade_out
800
-
801
- # with a callback
802
- view.fade_out do
803
- view.removeFromSuperview
804
- end
805
-
806
- # and the completed argument
807
- view.fade_out do |completed|
808
- view.removeFromSuperview
809
- end
810
-
811
- # fade_out options
812
- view.fade_out(duration: 0.5,
813
- delay: 0,
814
- options: UIViewAnimationOptionCurveLinear,
815
- opacity: 0.5) do
816
- view.removeFromSuperview
817
- end
634
+ UITableView.alloc.initWithFrame([[0, 0], [0, 0]], style: :plain.uitableviewstyle)
635
+ UITableView.alloc.initWithFrame([[0, 0], [320, 480]], style: :plain.uitableviewstyle)
636
+ UITableView.alloc.initWithFrame([[0, 0], [320, 568]], style: :plain.uitableviewstyle)
637
+ # custom frame:
638
+ UITableView.alloc.initWithFrame([[0, 0], [320, 400]], style: :grouped.uitableviewstyle)
818
639
 
819
- view.move_to([0, 100]) # move to position 0, 100
820
- view.delta_to([0, 100]) # move over 0, down 100, from current position
821
-
822
- view.rotate_to Math::PI # rotate view upside down
823
- view.rotate 45.degrees # rotate *an additional* 45 degrees
824
- view.rotate_to(duration: 0.5, angle: 45.degrees) # using options
825
-
826
- view.slide :left # slides the entire view left, right, up, or down. The
827
- # default amount is the width of the view being moved, but
828
- # you can override
829
- view.slide :left, 320
830
-
831
- view.shake # shakes the view.
832
- # options w/ default values:
833
- shake offset: 8, # move 8 px left, and 8 px right
834
- repeat: 3, # three times
835
- duration: 0.3, # for a total of 0.3 seconds
836
- keypath: 'transform.translate.x'
837
-
838
- # vigorous nodding - modifying transform.translation.y:
839
- view.shake offset: 20, repeat: 10, duration: 5, keypath: 'transform.translation.y'
840
- # an adorable wiggle - modifying transform.rotation:
841
- view.shake offset: 0.1, repeat: 2, duration: 0.5, keypath: 'transform.rotation'
842
-
843
- # this was pulled off warrenm's AHAlertView project. I thought the effect was
844
- # awesome, and deserved more attention!
845
- # https://github.com/warrenm/AHAlertView
846
- view.tumble # the view will fall and rotate - a good 'cancel button effect'
640
+ # =>
641
+ UITableView.plain
642
+ UITableView.plain([[0, 0], [320, 480]])
643
+ UITableView.plain([[0, 0], [320, 568]])
644
+ # custom frame:
645
+ UITableView.grouped([[0, 0], [320, 400]])
847
646
  ```
848
647
 
849
- The default behavior on all the animation methods is to animate from "the
850
- current" position (`UIViewAnimationOptionBeginFromCurrentState`). To disable
851
- that, you can either assign `options:` to something else, or you can disable
648
+ ###### UITableViewCell
852
649
 
853
650
  ```ruby
854
- # *just* that option.
855
- view.slide :left, from_current: false
856
- ```
857
-
858
- Other options can be assigned this way, like the curve
651
+ # factory methods, named for the cell style. cell identifier is required.
652
+ UITableViewCell.default('cell_identifier')
653
+ UITableViewCell.value1('cell_identifier')
654
+ UITableViewCell.value2('cell_identifier')
655
+ UITableViewCell.subtitle('cell_identifier')
859
656
 
860
- ```ruby
861
- view.slide :left, from_current: false, curve: :ease_in # :ease_in_out, :ease_in, :ease_out, :linear
657
+ # you can options for the common settings
658
+ cell = UITableViewCell.default('cell_identifier',
659
+ accessory: :disclosure,
660
+ selection: :blue,
661
+ text: 'text',
662
+ image: 'icon', # coerced into a UIImage
663
+ )
862
664
  ```
863
665
 
864
- Allow/disallow user interaction
666
+ ###### UISegmentedControl
865
667
 
866
668
  ```ruby
867
- view.slide :left, allow_interaction: true
868
- ```
669
+ control = UISegmentedControl.alloc.initItems(["one", "ah-two-whoo", "thr-r-r-ree"])
670
+ control.segmentedControlStyle = :bar.uisegmentedstyle
869
671
 
870
- Not all options are configurable this way. Refer to `UIViewAnimationOptions` and
871
- assign them direcly to `options:` if there are options you need that are not
872
- listed here.
672
+ # =>
873
673
 
874
- ```ruby
875
- view.slide :left, options: UIViewAnimationOptionAllowAnimatedContent | UIViewAnimationOptionCurveEaseInOut
674
+ UISegmentedControl.bar(["one", "ah-two-whoo", "thr-r-r-ree"])
675
+ # plain, bordered, and bezeled are the other types
876
676
  ```
877
677
 
878
- Using the completed callback you can string animations together for a low-tech
879
- animation sequence.
678
+ ###### UIActivityViewIndicator
880
679
 
881
680
  ```ruby
882
- view.slide(:left, 20) do
883
- view.slide(:up, 20) do
884
- view.slide(:right, 20) do
885
- view.slide(:down, 20) do
886
- view.fade_out
887
- end
888
- end
889
- end
890
- end
891
- ```
681
+ UIActivityIndicatorView.alloc.initWithActivityIndicatorStyle(UIActivityIndicatorViewStyleWhite)
682
+ UIActivityIndicatorView.alloc.initWithActivityIndicatorStyle(UIActivityIndicatorViewStyleWhiteLarge)
683
+ UIActivityIndicatorView.alloc.initWithActivityIndicatorStyle(UIActivityIndicatorViewStyleGray)
892
684
 
893
- Those be some gnarly callbacks. You can write this as a chain instead!
685
+ # =>
894
686
 
895
- ```ruby
896
- UIView.animation_chain {
897
- view.slide(:left, 20)
898
- }.and_then {
899
- view.slide(:up, 20)
900
- }.and_then {
901
- view.slide(:right, 20)
902
- }.and_then {
903
- view.slide(:down, 20)
904
- }.and_then {
905
- view.fade_out
906
- }.start
687
+ UIActivityIndicatorView.white
688
+ UIActivityIndicatorView.large
689
+ UIActivityIndicatorView.gray
907
690
  ```
908
691
 
909
- Behind the scenes, any calls to a SugarCube animate method (`slide`, `fade`,
910
- `rotate`) will be setup to run *immediately* instead of in a
911
- `UIView#animateWithDuration(...)` block. You can also do multiple animations
912
- within that block, as long as no two animations affect the same property:
913
-
914
- ```ruby
915
- UIView.animation_chain {
916
- view.slide(:left, 20)
917
- view.rotate(90.degrees)
918
- }.and_then {
919
- view.slide(:up, 20)
920
- view.rotate(90.degrees)
921
- }.and_then {
922
- view.slide(:right, 20)
923
- view.rotate(90.degrees)
924
- }.and_then {
925
- view.slide(:down, 20)
926
- view.rotate(90.degrees)
927
- }.and_then {
928
- view.fade_out
929
- view.rotate_to(0.degrees)
930
- }.start
931
- ```
692
+ ###### UIBarButtonItem
932
693
 
933
- Chains can also be written like this:
694
+ These factory methods accept a block, which will get wired up as a
695
+ target/action.
934
696
 
935
697
  ```ruby
936
- chain = UIView.animation_chain
937
- chain << proc { view.slide(:left, 20) }
938
- chain << proc { view.slide(:up, 20) }
939
- chain << proc { view.slide(:right, 20) }
940
- chain << proc { view.slide(:down, 20) }
941
- chain << proc { view.fade_out }
942
- chain.start
943
- ```
944
-
945
- **AND** chains can be looped! Either number of times, or call `stop` on the
946
- chain.
698
+ # Get an instance containing the specified system item.
699
+ UIBarButtonItem.done do
700
+ self.dismissViewControllerAnimated true, completion:nil
701
+ end
702
+ # =>
703
+ UIBarButtonItem.alloc.initWithBarButtonSystemItem(:done.uibarbuttonitem, target:self, action:"action:")
704
+ # with 'action' defined as:
705
+ def action(sender)
706
+ self.dismissViewControllerAnimated true, completion:nil
707
+ end
947
708
 
948
- ```ruby
949
- chain = UIView.animation_chain {
950
- view.slide(:left, 20)
951
- }.and_then {
952
- view.slide(:right, 20)
953
- }.loop # loop forever
954
- 2.seconds.later { chain.stop } # the animation will complete, but not loop again
955
- chain.loop(10) # would loop 10 times
956
-
957
- # if you're impatient
958
- chain.abort
959
- # will stop the animation at the end of whatever block it is in, so it could be
960
- # in a strange position, depending on where in the chain it is. Better to call
961
- # `stop`
709
+ # the method names are 1::1 with the uibarbuttonitem constants in symbol.rb
710
+ UIBarButtonItem.cancel { ... } => UIBarButtonItem.alloc.initWithBarButtonSystemItem(:cancel.uibarbuttonitem, target:self, action:"action:")
711
+ UIBarButtonItem.edit { ... } => UIBarButtonItem.alloc.initWithBarButtonSystemItem(:edit.uibarbuttonitem, target:self, action:"action:")
712
+ UIBarButtonItem.save { ... } => UIBarButtonItem.alloc.initWithBarButtonSystemItem(:save.uibarbuttonitem, target:self, action:"action:")
713
+ .
714
+ .
715
+ .
716
+ UIBarButtonItem.page_curl { ... } => UIBarButtonItem.alloc.initWithBarButtonSystemItem(:page_curl.uibarbuttonitem, target:self, action:"action:")
962
717
  ```
963
718
 
964
- ##### View factories
965
-
966
- ###### UIButton
719
+ For custom `UIBarButtonItem`s, you can use the `titled` and `imaged` methods:
967
720
 
968
721
  ```ruby
969
- UIButton.buttonWithType(:custom.uibuttontype)
722
+ # Create a UIBarButtonItem, specifying the title
723
+ UIBarButtonItem.titled('Close') do
724
+ self.dismissViewControllerAnimated(true, completion:nil)
725
+ end
970
726
  # =>
971
- UIButton.custom
727
+ UIBarButtonItem.alloc.initWithTitle('Close', style: :bordered.uibarbuttonstyle, target:self, action:"action:")
728
+ def action(sender)
729
+ self.dismissViewControllerAnimated(true, completion:nil)
730
+ end
972
731
 
973
- UIButton.custom => UIButton.buttonWithType(:custom.uibuttontype)
974
- UIButton.rounded => UIButton.buttonWithType(:rounded.uibuttontype)
975
- UIButton.rounded_rect => UIButton.buttonWithType(:rounded_rect.uibuttontype)
976
- UIButton.detail => UIButton.buttonWithType(:detail.uibuttontype)
977
- UIButton.detail_disclosure => UIButton.buttonWithType(:detail_disclosure.uibuttontype)
978
- UIButton.info => UIButton.buttonWithType(:info.uibuttontype)
979
- UIButton.info_light => UIButton.buttonWithType(:info_light.uibuttontype)
980
- UIButton.info_dark => UIButton.buttonWithType(:info_dark.uibuttontype)
981
- UIButton.contact => UIButton.buttonWithType(:contact.uibuttontype)
982
- UIButton.contact_add => UIButton.buttonWithType(:contact_add.uibuttontype)
983
- ```
984
732
 
985
- ###### UITableView
733
+ # You can also specify the style.
734
+ UIBarButtonItem.titled('Close', :plain) do # :plain, :bordered, :done
735
+ # ...
736
+ end
986
737
 
987
- Default frame is `[[0, 0], [0, 0]]`, but most containers will resize it to be
988
- the correct size. But heads up, it *was* `[[0, 0], [320, 480]]` (until
989
- the iphone 5 / 4-inch retina came out).
738
+ # Or specify the image instead
739
+ UIBarButtonItem.imaged('close_icon') do # 'close_icon' will be coerced into a UIImage
740
+ # ...
741
+ end
742
+ # =>
743
+ UIBarButtonItem.alloc.initWithImage('Close'.uiimage, style: :bordered.uibarbuttonstyle, ...)
990
744
 
991
- ```ruby
992
- UITableView.alloc.initWithFrame([[0, 0], [0, 0]], style: :plain.uitableviewstyle)
993
- UITableView.alloc.initWithFrame([[0, 0], [320, 480]], style: :plain.uitableviewstyle)
994
- UITableView.alloc.initWithFrame([[0, 0], [320, 568]], style: :plain.uitableviewstyle)
995
- # custom frame:
996
- UITableView.alloc.initWithFrame([[0, 0], [320, 400]], style: :grouped.uitableviewstyle)
745
+ # And, like `titled`, specify the style
746
+ UIBarButtonItem.imaged('close'.uiimage, :done) do
747
+ # ...
748
+ end
997
749
 
998
- # =>
999
- UITableView.plain
1000
- UITableView.plain([[0, 0], [320, 480]])
1001
- UITableView.plain([[0, 0], [320, 568]])
1002
- # custom frame:
1003
- UITableView.grouped([[0, 0], [320, 400]])
750
+ # If you provide two images, they will be used as the portrait and landscape images
751
+ UIBarButtonItem.imaged(['portrait'.uiimage, 'landscape'.uiimage) do
752
+ # ...
753
+ end
754
+ # =>
755
+ UIBarButtonItem.alloc.initWithImage('portrait'.uiimage, landscapeImagePhone:'landscape'.uiimage, style: :bordered.uibarbuttonstyle, target:self, action:"action:")
1004
756
  ```
1005
757
 
1006
- ###### UITableViewCell
1007
-
758
+ Example Usage:
1008
759
  ```ruby
1009
- # factory methods, named for the cell style. cell identifier is required.
1010
- UITableViewCell.default('cell_identifier')
1011
- UITableViewCell.value1('cell_identifier')
1012
- UITableViewCell.value2('cell_identifier')
1013
- UITableViewCell.subtitle('cell_identifier')
1014
-
1015
- # you can options for the common settings
1016
- cell = UITableViewCell.default('cell_identifier',
1017
- accessory: :disclosure,
1018
- selection: :blue,
1019
- text: 'text',
1020
- image: 'icon', # coerced into a UIImage
1021
- )
760
+ toolbar = UIToolbar.new
761
+ toolbar.items = [
762
+ @image_picker_button = UIBarButtonItem.camera { presentImagePickerController(self) },
763
+ UIBarButtonItem.flexiblespace,
764
+ @saveButton = UIBarButtonItem.save { save_photo(self) }
765
+ ]
1022
766
  ```
1023
767
 
1024
- ###### UISegmentedControl
1025
-
768
+ ###### NSError
1026
769
  ```ruby
1027
- control = UISegmentedControl.alloc.initItems(["one", "ah-two-whoo", "thr-r-r-ree"])
1028
- control.segmentedControlStyle = :bar.uisegmentedstyle
1029
-
1030
- # =>
1031
-
1032
- UISegmentedControl.bar(["one", "ah-two-whoo", "thr-r-r-ree"])
770
+ # usually, NSError.new doesn't work, because the only initializer for NSError
771
+ # needs more arguments. This method passes some defaults in.
772
+ NSError.new('message')
773
+ # same as =>
774
+ NSError.new('message', domain: 'Error', code: 0, userInfo: {})
1033
775
  ```
1034
776
 
1035
- ###### UIActivityViewIndicator
777
+ Animations ([wiki][Animations Wiki])
778
+ -----
1036
779
 
1037
- ```ruby
1038
- UIActivityIndicatorView.alloc.initWithActivityIndicatorStyle(:white.uiactivityindicatorstyle)
1039
- UIActivityIndicatorView.alloc.initWithActivityIndicatorStyle(:large.uiactivityindicatorstyle)
1040
- UIActivityIndicatorView.alloc.initWithActivityIndicatorStyle(:gray.uiactivityindicatorstyle)
780
+ > `require 'sugarcube-animations'`
1041
781
 
1042
- # =>
782
+ Careful, once you start using these helpers, you'll never go back.
1043
783
 
1044
- UIActivityIndicatorView.white
1045
- UIActivityIndicatorView.large
1046
- UIActivityIndicatorView.gray
784
+ ```ruby
785
+ view.fade_out
786
+ view.slide :left, 100
787
+ view.rotate_to 180.degrees
788
+ view.shake # great for showing invalid form elements
789
+ view.tumble # great way to dismiss an alert-like-view
1047
790
  ```
1048
791
 
1049
- UIControl
1050
- -----------
1051
-
1052
- Inspired by [BubbleWrap's][BubbleWrap] `when` method, but I prefer jQuery-style
1053
- verbs and SugarCube symbols.
792
+ These helpers all delegate to the `UIView.animate` method, which accepts all the
793
+ options that `UIView.animateWithDuration(delay:options:animations:completion:)`
794
+ accepts, but they are optional, and they will play nicely inside an animation
795
+ chain.
1054
796
 
1055
797
  ```ruby
1056
- button = UIButton.alloc.initWithFrame([0, 0, 10, 10])
798
+ UIView.animate do
799
+ view.alpha = 0
800
+ end
801
+ ```
1057
802
 
1058
- button.on(:touch) { my_code }
1059
- button.on(:touch_up_outside, :touch_cancel) { |event|
1060
- puts event.inspect
1061
- # my_code...
1062
- }
803
+ The [wiki] page documents all the different animation methods, and documents
804
+ animation chaining, which looks like this:
1063
805
 
1064
- # remove handlers
1065
- button.off(:touch, :touch_up_outside, :touch_cancel)
1066
- button.off(:all)
806
+ ```ruby
807
+ # fade out and slide left, then fade back in while returning to original position
808
+ UIView.animation_chain do
809
+ view.fade_out
810
+ view.slide :left
811
+ end.and_then do
812
+ view.fade_in
813
+ view.slide :right
814
+ end.start
1067
815
  ```
1068
816
 
1069
- You can only remove handlers by "type", not by the action. e.g. If you bind
1070
- three `:touch` events, calling `button.off(:touch)` will remove all three.
817
+ [Animations Wiki]: https://github.com/rubymotion/sugarcube/wiki/Animations
818
+
819
+ Modal
820
+ -----
1071
821
 
1072
- UIViewController
1073
- ------------------
822
+ > `require 'sugarcube-modal'`
1074
823
 
1075
824
  It is nice that *any* `UIViewController` can present a modal, but if you have
1076
825
  tabs or navs or crap in the way, this is actually *NOT* what you want. You
@@ -1080,7 +829,8 @@ And since this is a property on `UIWindow`, which is more-or-less a constant, we
1080
829
  can make this the easiest to do!
1081
830
 
1082
831
  ```ruby
1083
- include SugarCube::Modal
832
+ include SugarCube::Modal # make these methods available globally
833
+
1084
834
  view_ctlr = EditSomethingViewController.new
1085
835
  present_modal(view_ctlr)
1086
836
  # ...later, when all is well...
@@ -1101,247 +851,331 @@ re-defined on `UIViewController` for this purpose:
1101
851
  controller.present_modal(other_controller) { puts "presented" }
1102
852
  ```
1103
853
 
1104
- UINavigationController
1105
- ------------------------
854
+ Numbers
855
+ -----
1106
856
 
1107
- `push`, `<<` and `pop` instead of `pushViewController` and `popViewController`.
857
+ > `require 'sugarcube-numbers'`
1108
858
 
1109
- animated is `true` for all these.
859
+ ### Converting input
1110
860
 
1111
- `pop` accepts an argument: either a view controller to pop to, or the symbol
1112
- `:root` which does what you might expect
861
+ Uses `NSNumberFormatter` to try and parse a human-readable number string.
1113
862
 
1114
863
  ```ruby
1115
- nav_ctlr << root_ctlr
1116
- # or nav_ctlr.push(root_ctlr)
1117
- nav_ctlr << new_ctlr
1118
- # ... imagine we push on a ton of controllers ...
1119
- nav_ctlr << another_ctlr
1120
- nav_ctlr << last_ctlr
1121
- nav_ctlr.pop # => pops to another_ctlr, because it's next on the stack
1122
- nav_ctlr.pop new_ctlr # => pops to new_ctlr
1123
- nav_ctlr.pop :root # => pops to root_ctlr, because it's on the bottom
864
+ if input.nan?
865
+ UIAlertView.alert('not a number!')
866
+ else
867
+ number = input.to_number
868
+ end
1124
869
  ```
1125
870
 
1126
- UITabBarController
1127
- ------------------------
871
+ ### Pretty print numbers
1128
872
 
1129
- I have mixed feelings about adding this extension, but **I** needed it, so maybe
1130
- you will, too... Usually a `UITabBarController` has a static number of tabs,
1131
- but in my case, I needed to be able to add one later, when a certain condition
1132
- was met.
873
+ Use NSNumberFormatter to easily format a number in the current locale
1133
874
 
1134
875
  ```ruby
1135
- controllers = tabbar_ctlr.viewControllers
1136
- controllers << new_ctlr
1137
- tabbar_ctlr.setViewControllers(controllers, animated: true)
876
+ 10000.string_with_style # => "10,000"
877
+ 10000.string_with_style(NSNumberFormatterCurrencyStyle) # => "$10,000.00"
878
+ # will convert symbol-constants using the sugarcube-constants package, if it is available
879
+ 10000.string_with_style(:currency) # => "$10,000.00"
880
+ ```
1138
881
 
1139
- # =>
882
+ ### Percent
1140
883
 
1141
- tabbar_ctlr << new_ctlr
884
+ ```ruby
885
+ 100.0.percent # => 1.00
886
+ 55.0.percent # => 0.55
1142
887
  ```
1143
888
 
1144
- UITextView
1145
- ------------
889
+ ### Ordinals
1146
890
 
1147
- Added some `UIControl`-like event binding. You MUST call the `off` methods,
1148
- because these methods use `NSNotification`s, and you must turn off listeners.
891
+ ```ruby
892
+ # some number-to-string stuff
893
+ 1.nth # => 'st'
894
+ 2.nth # => 'nd'
895
+ 3.nth # => 'rd'
896
+ 4.nth # => 'th'
897
+ 11.nth # => 'th'
898
+ 13.nth # => 'th'
899
+ 21.nth # => 'st'
900
+ 23.nth # => 'rd'
901
+ ```
1149
902
 
1150
- There are two aliases for each event. I prefer the present tense (jQuery-style `on :change`),
1151
- but UIKit prefers past simple (`UITextViewTextDidBeginEditingNotification`).
903
+ ### Angles
1152
904
 
1153
- So these are all the same:
905
+ Since you always want to work in radians, calling `10.degrees` returns 10°, *in
906
+ radians*. You can convert back to degrees using `to_degrees`. Lastly, you can
907
+ specify a multiple of π as a number:
1154
908
 
1155
- :editing_did_begin :begin
1156
- :editing_did_change :change
1157
- :editing_did_end :end
909
+ ```ruby
910
+ 10.degrees # => π / 18
911
+ 45.degrees # => π / 4
912
+ 3.14159.to_degrees # => approx 180
913
+ 2.pi # => 6.28318...
914
+ ```
915
+
916
+ ### Distances
917
+
918
+ If you thought conversion from degrees to radians looks weird, you'll hate
919
+ conversion from meters to miles:
1158
920
 
1159
921
  ```ruby
1160
- text_view = UITextView.new
1161
- text_view.on :editing_did_begin do
1162
- p 'wait for it...'
1163
- end
1164
- text_view.on :editing_did_change do
1165
- p text_view.text
1166
- end
1167
- text_view.on :editing_did_end do
1168
- p 'done!'
1169
- end
922
+ distance = 1500 # this is in meters. why? because all the methods that return
923
+ # a "distance" return it in meters
924
+ distance = 2.miles # => 3218.688, that's how many meters are in 2 miles
925
+ 1500.in_miles # converts meters to miles => 0.932056427001953
926
+ ```
1170
927
 
1171
- # later... like in `viewWillDisappear`. I'll use the alternative aliases here
1172
- text_view.off :change, :end, :begin
928
+ ### Sizes
929
+
930
+ Similar conversion methods for hard disk sizes. Uses the "mebi-byte" concepts,
931
+ e.g. 1024 bytes in a kilobyte.
932
+
933
+ ```ruby
934
+ 1.byte # => 1
935
+ 1.kilobyte # => 1024
936
+ 1.megabyte # => 1048576
937
+ 1.gigabyte # => 1073741824
938
+ 1.terabyte # => 1099511627776
939
+ 1.petabyte # => 1125899906842624
940
+ 1.exabyte # => 1152921504606846976
941
+
942
+ 1.megabyte.in_kilobytes # => 1024
943
+ ```
944
+
945
+ AttributedString
946
+ -----
947
+
948
+ > `require 'sugarcube-attributedstring'`
949
+
950
+ These are pretty fun! Check out [nsattributedstring_spec.rb][] for all the
951
+ supported attributes (in theory they are all supported, but there's weird
952
+ issues with missing constants).
953
+
954
+ [nsattributedstring.rb]: https://github.com/rubymotion/sugarcube/blob/master/lib/sugarcube-attributedstring/nsattributedstring.rb
955
+
956
+ ```ruby
957
+ 'test'.nsattributedstring({}) #=> NSAttributedString.alloc.initWithString('test', attributes:{})
958
+ 'test'.attrd # => alias for `nsattributedstring`
959
+ 'test'.bold # => NSAttributedString.alloc.initWithString('test', attributes:{NSFontAttributeName => :bold.uifont})
960
+ 'test'.italic # => NSAttributedString.alloc.initWithString('test', attributes:{NSFontAttributeName => :italic.uifont})
961
+ 'test'.underline # => NSAttributedString.alloc.initWithString('test', attributes:{NSUnderlineStyleAttributeName => NSUnderlineStyleSingle})
962
+
963
+ # you can chain 'em, too.
964
+ 'test'.bold.underline
965
+ # If you look up NSAttributedString Application Kit Additions, you can see all
966
+ # the constants. Each of those has a method on NSAttributedString.
967
+
968
+ # you can add 'em, but the FIRST one MUST be an NSAttributedString
969
+ 'test'.attrd + '-ing'.italic
970
+
971
+ # And there's where it gets FUN:
972
+ ('This'.italic + ' is going to be ' + 'FUN'.bold).underline
973
+ ```
974
+
975
+ And you can easily turn an attributed string into a label, if you include the
976
+ `sugarcube-uikit` package.
977
+
978
+ ```ruby
979
+ view << (("We just met\n".attrd +
980
+ "and this is " + "CRAZY".italic + "\n"
981
+ "But here's my " + "id_rsa.pub".monospace + " file,\n" +
982
+ "so give me SSH access.").uilabel
1173
983
  ```
1174
984
 
1175
- UILabel
1176
- ----------
985
+ 568
986
+ -----
987
+
988
+ > `require 'sugarcube-568'`
1177
989
 
1178
- Added simple `fit_to_size` function to the label, which will start at the supplied font size
1179
- and then squeeze down until all the text fits. This way you can assure any dynamic text will completely display
1180
- in a given label frame.
990
+ If you `require 'sugarcube-568'` in your Rakefile, you can use
991
+ `UIImage.imageNamed(name)` or `name.uiimage` to load images that are specific to
992
+ the 4" iphone.
1181
993
 
1182
- The font size changes instead of the frame size.
1183
994
  ```ruby
1184
- # this will try to make the containing text fit at font size 40, but squeeze as needed.
1185
- @label.fit_to_size(40)
1186
- puts @label.font.pointSize # => Will be 40 or less depending on the font type and label frame.
995
+ 'tall'.uiimage # => UIImage.imageNamed('tall')
996
+ # => tall.png on iphone 3g
997
+ # => tall@2x.png on iphone 4
998
+ # => tall-568h@2x.png on iphone 5
1187
999
  ```
1188
1000
 
1001
+ This code is ported from <https://github.com/gaj/imageNamed568>, which I had
1002
+ some problems with on RubyMotion (it worked, but not *always*. Very strange).
1003
+
1004
+ Files
1005
+ -----
1189
1006
 
1007
+ Methods to find document files, resource files, cache files, and to access
1008
+ entries out of the Info.plist file.
1190
1009
 
1191
- UIBarButtonItem
1192
- ----------------------
1010
+ > `require 'sugarcube-files'`
1193
1011
 
1194
1012
  ```ruby
1195
- # Get an instance containing the specified system item.
1196
- UIBarButtonItem.done do
1197
- self.dismissViewControllerAnimated true, completion:nil
1198
- end
1199
- # =>
1200
- UIBarButtonItem.alloc.initWithBarButtonSystemItem(:done.uibarbuttonitem, target:self, action:"action:")
1201
- # with 'action' defined as:
1202
- def action(sender)
1203
- self.dismissViewControllerAnimated true, completion:nil
1204
- end
1013
+ # file operations
1014
+ "my.plist".exists? # => NSFileManager.defaultManager.fileExistsAtPath("my.plist")
1015
+ "my.plist".document # => NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true)[0].stringByAppendingPathComponent("my.plist")
1016
+ "my.plist".cache # => NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, true)[0].stringByAppendingPathComponent("my.plist")
1017
+ "my.plist".remove! # => NSFileManager.defaultManager.removeItemAtPath("my.plist".document, error: error) (returns error, if any occurred)
1205
1018
 
1206
- # the method names are 1::1 with the uibarbuttonitem constants in symbol.rb
1207
- UIBarButtonItem.cancel { ... } => UIBarButtonItem.alloc.initWithBarButtonSystemItem(:cancel.uibarbuttonitem, target:self, action:"action:")
1208
- UIBarButtonItem.edit { ... } => UIBarButtonItem.alloc.initWithBarButtonSystemItem(:edit.uibarbuttonitem, target:self, action:"action:")
1209
- UIBarButtonItem.save { ... } => UIBarButtonItem.alloc.initWithBarButtonSystemItem(:save.uibarbuttonitem, target:self, action:"action:")
1210
- .
1211
- .
1212
- .
1213
- UIBarButtonItem.pagecurl { ... } => UIBarButtonItem.alloc.initWithBarButtonSystemItem(:pagecurl.uibarbuttonitem, target:self, action:"action:")
1019
+ # get the resource path, useful if you include json files or images you manipulate in the app
1020
+ "my.plist".resource # => NSBundle.mainBundle.resourcePath.stringByAppendingPathComponent("my.plist")
1021
+ # same, but get a URL instead - often used to display a static HTML page that is stored in resources
1022
+ "index.html".resource_url # => NSBundle.mainBundle.URLForResource("index", withExtension:"html")
1214
1023
 
1215
- # Create a UIBarButtonItem, specifying the title
1216
- UIBarButtonItem.titled('Close') do
1217
- self.dismissViewControllerAnimated(true, completion:nil)
1218
- end
1219
- # =>
1220
- UIBarButtonItem.alloc.initWithTitle('Close', style: :bordered.uibarbuttonstyle, target:self, action:"action:")
1221
- def action(sender)
1222
- self.dismissViewControllerAnimated(true, completion:nil)
1223
- end
1024
+ # access data from Info.plist
1025
+ "CFBundleVersion".info_plist # => NSBundle.mainBundle.infoDictionary["CFBundleVersion"]
1026
+ ```
1224
1027
 
1028
+ Localized
1029
+ -----
1225
1030
 
1226
- # You can also specify the style.
1227
- UIBarButtonItem.titled('Close', :plain) do # :plain, :bordered, :done
1228
- # ...
1229
- end
1031
+ > `require 'sugarcube-localized'`
1230
1032
 
1033
+ ```ruby
1034
+ # NSLocalizedString from string
1035
+ "hello".localized # => NSBundle.mainBundle.localizedStringForKey("hello", value:nil, table:nil)
1036
+ "hello"._ # == "hello".localized
1037
+ "hello".localized('Hello!', 'hello_table') # => ...("hello", value:'Hello!', table:'hello_table')
1231
1038
 
1232
- # Or specify the image instead
1233
- UIBarButtonItem.imaged('close_icon') do # 'close_icon' will be coerced into a UIImage
1234
- # ...
1235
- end
1236
- # =>
1237
- UIBarButtonItem.alloc.initWithImage('Close'.uiimage, style: :bordered.uibarbuttonstyle, ...)
1039
+ # If you have an NSError object, you can retrieve the localizedDescription the same way:
1040
+ error.localized
1041
+ error._
1042
+ ```
1238
1043
 
1239
- # And, like `titled`, specify the style
1240
- UIBarButtonItem.imaged('close'.uiimage, :done) do
1241
- # ...
1242
- end
1044
+ NSCoder
1045
+ -----
1243
1046
 
1244
- # If you provide two images, they will be used as the portrait and landscape images
1245
- UIBarButtonItem.imaged(['portrait'.uiimage, 'landscape'.uiimage) do
1246
- # ...
1247
- end
1248
- # =>
1249
- UIBarButtonItem.alloc.initWithImage('portrait'.uiimage, landscapeImagePhone:'landscape'.uiimage, style: :bordered.uibarbuttonstyle, target:self, action:"action:")
1047
+ Shorthands and hash-like access to the coder/decoder objects.
1048
+
1049
+ > `require 'sugarcube-nscoder'`
1050
+
1051
+ ```ruby
1052
+ # hash access is the handiest
1053
+ coder['key'] = self.value
1054
+ self.value = decoder['key']
1055
+
1056
+ # but if you want to store booleans and such (in their C form,
1057
+ # which will take up less space):
1058
+ coder.set('sugarcube_is_neat', toBool: self.sugarcube_is_neat?)
1059
+ self.sugarcube_is_neat = decoder.bool('sugarcube_is_neat')
1060
+
1061
+ coder.set('number_of_things', toInt:self.number_of_things)
1062
+ self.number_of_things = decoder.int('number_of_things')
1063
+
1064
+ # the entire list:
1065
+ coder.set(key, toBool:value)
1066
+ coder.set(key, toDouble:value)
1067
+ coder.set(key, toFloat:value)
1068
+ coder.set(key, toInt:value)
1069
+ coder.set(key, toPoint:value)
1070
+ coder.set(key, toRect:value)
1071
+ coder.set(key, toSize:value)
1072
+
1073
+ decoder.bool(key)
1074
+ decoder.double(key)
1075
+ decoder.float(key)
1076
+ decoder.int(key)
1077
+ decoder.point(key)
1078
+ decoder.rect(key)
1079
+ decoder.size(key)
1250
1080
  ```
1251
1081
 
1252
- Example Usage:
1082
+ NSData
1083
+ -----
1084
+
1085
+ > `require 'sugarcube-nsdata'`
1086
+
1087
+ Going to and from `NSData` is really useful when doing HTTP posts.
1088
+
1253
1089
  ```ruby
1254
- toolbar = UIToolbar.new
1255
- toolbar.items = [
1256
- @image_picker_button = UIBarButtonItem.camera { presentImagePickerController(self) },
1257
- UIBarButtonItem.flexiblespace,
1258
- @saveButton = UIBarButtonItem.save { save_photo(self) }
1259
- ]
1090
+ # default string encoding is UTF8, you can pass other encodings to this method
1091
+ string_data = 'String'.nsdata
1092
+
1093
+ # PNG data representation
1094
+ image = 'an image'.uiimage
1095
+ image_data = image.nsdata
1096
+
1097
+ # this will download the URL contents
1098
+ url = 'http://localhost'.nsurl
1099
+ url.nsdata # => NSData.dataWithContentsOfURL(self)
1100
+
1101
+ string_data.nsstring # => 'String'
1102
+ image_data.nsimage # => whatever 'an image' was
1260
1103
  ```
1261
1104
 
1262
- NSNotificationCenter
1263
- ----------------------
1105
+ NSDate ([wiki][NSDate Wiki])
1106
+ -----
1264
1107
 
1265
- Makes it easy to post a notification to some or all objects.
1108
+ > `require 'sugarcube-nsdate'`
1266
1109
 
1267
- ```ruby
1268
- # this one is handy, I think:
1269
- MyNotification = "my notification"
1270
- MyNotification.post_notification # => NSNotificationCenter.defaultCenter.postNotificationName(MyNotification, object:nil)
1271
- MyNotification.post_notification(obj) # => NSNotificationCenter.defaultCenter.postNotificationName(MyNotification, object:obj)
1272
- MyNotification.post_notification(obj, user: 'dict') # => NSNotificationCenter.defaultCenter.postNotificationName(MyNotification, object:obj, userInfo:{user: 'dict'})
1110
+ This package includes additions to the `NSDate` class, and related additions to
1111
+ `Fixnum` and `NSString`. There's a lot here, so check out the [wiki][NSDate Wiki]
1112
+ for detailed information.
1273
1113
 
1274
- # you can access the userInfo dictionary directly from the notification
1275
- def notified(notification)
1276
- notification[:user] # => 'dict'
1277
- end
1114
+ [NSDate Wiki]: https://github.com/rubymotion/sugarcube/wiki/NSDate
1278
1115
 
1279
- # very similar to add or remove an observer
1280
- MyNotification.add_observer(observer, :method_name)
1281
- MyNotification.add_observer(observer, :method_name, object)
1116
+ Foundation
1117
+ -----
1282
1118
 
1283
- # remove the observer
1284
- MyNotification.remove_observer(observer)
1285
- MyNotification.remove_observer(observer, object)
1119
+ Smaller additions to the CoreFoundation classes. Some extensions, like
1120
+ `NSDate`, are large enough that they get broken out into their own packages.
1121
+
1122
+ > `require 'sugarcube-foundation'`
1123
+
1124
+ ###### NSArray
1125
+ ```ruby
1126
+ [1, 3].nsindexpath # NSIndexPath.indexPathWithIndex(1).indexPathByAddingIndex(3)
1127
+ [1, 3].nsindexset
1128
+ [1, 3].nsset
1286
1129
  ```
1287
1130
 
1288
- NSTimer
1289
- ---------
1131
+ ###### NSIndexSet
1132
+ ```ruby
1133
+ index_set.to_a # => [1, 2, ...]
1134
+ ```
1290
1135
 
1136
+ ###### NSIndexPath
1291
1137
  ```ruby
1292
- # once
1293
- 1.second.later do
1294
- @view.shake
1295
- end
1296
- # repeating
1297
- 1.second.every do
1298
- @view.shake
1299
- end
1138
+ index_path.to_a # => [1, 2, ...]
1139
+ ```
1300
1140
 
1301
- # you can assign the return value (an NSTimer)
1302
- timer = 1.second.every do
1303
- @view.shake
1304
- end
1305
- # and invalidate it
1306
- timer.invalidate
1141
+ ###### NSString
1142
+ ```ruby
1143
+ 'https://github.com'.nsurl
1144
+ '/path/to/file'.fileurl
1145
+ 'https://google.com/search?q=' + 'search terms'.escape_url
1146
+ '%20'.unescape_url
1147
+ ```
1307
1148
 
1308
- # the `every` method is available in the SugarCube::Timer module,
1309
- # which you might find more readable
1310
- include SugarCube::Timer
1311
- every 1.minute do
1312
- puts "tick"
1313
- end
1149
+ ###### NSURL
1150
+ ```ruby
1151
+ url = 'https://github.com'.nsurl
1152
+ url.can_open? # => true, it will open in safari
1153
+ url.open # opens in safari
1154
+ url.nsurlrequest # convert to NSURLRequest object
1155
+ ```
1314
1156
 
1315
- # might as well make an alias
1316
- after 1.minute do
1317
- puts "ding!"
1318
- end
1157
+ IndexPath
1158
+ -----
1319
1159
 
1320
- # other time-related methods
1321
- # for compatibility with Time methods, the mins/secs (and min/sec) aliases are provided. Personally,
1322
- # I like the more verbose minutes/seconds.
1323
- 1.millisecond || 2.milliseconds
1324
- 1.millisec || 2.millisecs
1325
- 1.second || 2.seconds
1326
- 1.sec || 2.secs # aliases
1327
- 1.minute || 2.minutes # 1.minute = 60 seconds
1328
- 1.min || 2.mins # aliases
1329
- 1.hour || 2.hours # 1.hour = 60 minutes
1330
- 1.day || 2.days # 1.day = 24 hours
1331
- 1.week || 2.weeks # 1.week = 7 days
1332
- # sensible values for 'month' and 'year', even though we all know you can't
1333
- # **really** define them this way (go back to python if you find your brain
1334
- # hemorrhaging):
1335
- 1.month || 2.months # 1.month = 30 days
1336
- 1.year || 2.years # 1.year = 365 days
1160
+ > `require 'sugarcube-indexpath`
1337
1161
 
1338
- # some comparison methods
1339
- date1.today?
1340
- date2.same_day? date1
1162
+ Use the `IndexPath` class to match `NSIndexPath` objects, for instance in a
1163
+ `UITableViewDelegate`.
1164
+
1165
+ ```ruby
1166
+ index_path = [0, 2].nsindexpath
1167
+ case index_path
1168
+ when IndexPath[0]
1169
+ when IndexPath[1, 0..5]
1170
+ when IndexPath[1, 5..objects.length]
1171
+ end
1172
+ [0, 2].nsindexpath.to_a == [0, 2] # => true
1341
1173
  ```
1342
1174
 
1343
1175
  NSUserDefaults
1344
- ----------------
1176
+ -----
1177
+
1178
+ > `require 'sugarcube-nsuserdefaults'`
1345
1179
 
1346
1180
  This file does *one* thing very **DANGEROUS**... to "help" with defaults.
1347
1181
 
@@ -1386,6 +1220,9 @@ NSUserDefaults['test'] = test # saved
1386
1220
  CoreGraphics
1387
1221
  --------------
1388
1222
 
1223
+ *This package is installed automatically, because so many other packages depend
1224
+ on it. It does not add any methods to built-in classes.*
1225
+
1389
1226
  ###### Is it `CGMakeRect` or `CGRectMake`? What arguments does `CGRect.new` take?
1390
1227
 
1391
1228
  Instead, just use the coercion methods `Rect()`, `Size()` and `Point()`. They
@@ -1423,8 +1260,51 @@ f = Rect(p, [w, h])
1423
1260
  f = Rect([x, y], s)
1424
1261
  ```
1425
1262
 
1263
+ Pointer
1264
+ -----
1265
+
1266
+ > `require 'sugarcube-pointer'`
1267
+
1268
+ These are not UIKit-related, so I reverted to Ruby's preferred `to_foo`
1269
+ convention.
1270
+
1271
+ ```ruby
1272
+ [0.0, 1.1, 2.2].to_pointer(:float)
1273
+
1274
+ # is equivalent to
1275
+ floats = Pointer.new(:float, 3)
1276
+ floats[0] = 0.0
1277
+ floats[1] = 1.1
1278
+ floats[2] = 2.2
1279
+ ```
1280
+
1281
+ To_s
1282
+ -----
1283
+
1284
+ > `require 'sugarcube-to_s'`
1285
+
1286
+ `to_s` methods are defined on the following classes:
1287
+
1288
+ * NSError
1289
+ * NSIndexPath
1290
+ * NSLayoutConstraint
1291
+ * NSNotification
1292
+ * NSSet
1293
+ * NSURL
1294
+ * UIColor
1295
+ * UIEvent
1296
+ * UIView
1297
+ * UILabel
1298
+ * UITextField
1299
+ * UITouch
1300
+ * UIViewController
1301
+
1302
+ The output of these is (much?) more useful than the default.
1303
+
1426
1304
  CoreLocation
1427
- --------------
1305
+ -----
1306
+
1307
+ > `require 'sugarcube-corelocation'`
1428
1308
 
1429
1309
  Open up `CLLocationCoordinate2D` to provide handy-dandies
1430
1310
 
@@ -1435,7 +1315,7 @@ Open up `CLLocationCoordinate2D` to provide handy-dandies
1435
1315
  => #<CLLocationCoordinate2D latitude=39.2681274414062 longitude=-84.2576293945312>
1436
1316
  > denver_co.distance_to(loveland_oh)
1437
1317
  => 1773425.5 # in meters
1438
- > denver_co.distance_to(loveland_oh).miles
1318
+ > denver_co.distance_to(loveland_oh).in_miles
1439
1319
  => 1101.955078125
1440
1320
  > denver_co.delta_miles(1101.6, -32.556)
1441
1321
  => #<CLLocationCoordinate2D latitude=39.2681427001953 longitude=-84.2577209472656>
@@ -1445,399 +1325,95 @@ Open up `CLLocationCoordinate2D` to provide handy-dandies
1445
1325
  => 0.00502094626426697
1446
1326
  ```
1447
1327
 
1448
- REPL View adjustments
1449
- -----------------------
1450
-
1451
- Pixel pushing is an unfortunate but necessary evil. Well, at least we can make
1452
- it a little less painful. SugarCube provides a library that adds some methods
1453
- that are meant to be used in the REPL.
1454
-
1455
- `require "sugarcube-repl"`
1456
-
1457
- The actual code is, for historical reasons, in the `SugarCube::Adjust` module,
1458
- which is included by default. But to really be handy you'll want to require the
1459
- `sugarcube-repl` package.
1460
-
1461
- #### Finding the view you want.
1462
-
1463
- This is often touted as the *most useful feature* of SugarCube!
1464
-
1465
- ```
1466
- (main)> tree
1467
- 0: . UIWindow(#6e1f950: [[0.0, 0.0], [320.0, 480.0]])
1468
- 1: `-- UIView(#8b203b0: [[0.0, 20.0], [320.0, 460.0]])
1469
- 2: +-- UIButton(#d028de0: [[10.0, 10.0], [320.0, 463.400512695312]])
1470
- 3: | `-- UIImageView(#d02aaa0: [[0.0, 0.0], [320.0, 463.400512695312]])
1471
- 4: +-- UIRoundedRectButton(#d02adb0: [[55.0, 110.0], [210.0, 20.0]])
1472
- 5: | `-- UIButtonLabel(#d02af00: [[73.0, 0.0], [63.0, 19.0]], text: "Button 1")
1473
- 6: +-- UIRoundedRectButton(#d028550: [[60.0, 30.0], [200.0, 20.0]])
1474
- 7: | `-- UIButtonLabel(#d02afb0: [[68.0, 0.0], [63.0, 19.0]], text: "Button 2")
1475
- 8: `-- UIRoundedRectButton(#d02b220: [[70.0, 30.0], [300.0, 20.0]])
1476
- 9: `-- UIButtonLabel(#d02b300: [[118.0, 0.0], [63.0, 19.0]], text: "Button 3")
1477
- ```
1478
-
1479
- SugarCube provides lot of `to_s` methods on UIKit objects - that is so that this
1480
- tree view is really easy to find the view you want. Once you do find the one
1481
- you want, you can fetch it out of that list using the `adjust` method, which is
1482
- aliased to `a` to make it easy on the fingers.
1483
-
1484
- ```
1485
- (main)> a 6
1486
- => UIRoundedRectButton(#d028550: [[60.0, 30.0], [200.0, 20.0]]), child of UIView(#8b203b0)
1487
- ```
1488
-
1489
- Now that we've chose the button, it is available in the `a` method, *and* there
1490
- are a bunch of methods in the SugarCube::Adjust module that act on that object.
1491
- Most of these methods help you adjust the frame of a view.
1492
-
1493
- ```ruby
1494
- > up 1
1495
- > down 1 # same as `up -1`
1496
- > down # defaults to 1 anyway
1497
- > left 10
1498
- > right 10
1499
- > left # => left 1
1500
- > origin 10, 12 # move to x:10, y:12
1501
- > wider 15
1502
- > thinner 10
1503
- > taller # => taller 1
1504
- > shorter # => shorter 1
1505
- > size 100, 10 # set size to width:100, height: 10
1506
- > shadow(opacity: 0.5, offset: [0, 0], color: :black, radius: 1) # and path, which is a CGPath object.
1507
- > center # See `Centering` section below
1508
- > restore # original frame and shadow is saved when you first call `adjust`
1509
- ```
1510
-
1511
- Here are the short versions of those methods.
1512
-
1513
- ```ruby
1514
- > u # up, default value=1
1515
- > d # down
1516
- > l # left
1517
- > r # right
1518
- > w # wider
1519
- > n # thiNNer
1520
- > t # taller
1521
- > s # shorter
1522
- > o 10, 12 # origin
1523
- > o [10, 12]
1524
- > o CGPoint.new(10, 12)
1525
- > o Point(10, 12)
1526
- > z 100, 10 # siZe, also accepts an array, CGSize, or Size()
1527
- # and frame
1528
- > f [[0,0], [0,0]]
1529
- # sHadow
1530
- > h opacity: 0.5, offset: [0, 0], color: :black, radius: 1
1531
-
1532
- # frame, size, origin, and shadow can also be used as getters
1533
- > f
1534
- [[0, 0], [320, 568]]
1535
- > o # origin
1536
- [0, 0]
1537
- > z # size
1538
- [320, 568]
1539
- > h # this returns an object identical to what you can pass to `shadow`
1540
- {opacity: 0.5, offset: [0, 0], color: :black, radius: 1}
1541
-
1542
- # and of course the `a` method returns the current object
1543
- > a
1544
- => UITextField(#9ce6470, [[46, 214], [280, 33]], text: "hi!"), child of UIView(#10a6da20)
1545
- ```
1546
-
1547
- The most useful feature of the REPL adjustment is the ability to quickly
1548
- position and size your UI elements __visually__ and then paste the final values
1549
- into your code. In order to better accomodate that, `adjust` has an option to
1550
- modify the output format. Many thanks to [Thom Parkin][] for developing these
1551
- output formatters.
1552
-
1553
- ```
1554
- (main)> repl_format :ruby
1555
- ```
1556
-
1557
- Currently supported is:
1558
-
1559
- * RubyMotion (Default) (`:ruby`)
1560
- * Objective-C (`:objc`)
1561
- * JSON (`:json`)
1562
-
1563
- #### Objective-C style
1564
-
1565
- ```
1566
- (main)> repl_format :objc
1567
- (main)> tree
1568
- 0: . UIWindow(#6e27180: {{0, 0}, {320, 480}})
1569
- 1: `-- UIView(#8d631b0: {{0, 20}, {320, 460}})
1570
- 2: +-- UIButton(#6d6c090: {{10, 10}, {320, 463.401}})
1571
- 3: | `-- UIImageView(#8d67e00: {{0, 0}, {320, 463.401}})
1572
- 4: `-- UIRoundedRectButton(#8d68170: {{10, 30}, {30, 200}})
1573
- 5: `-- UIButtonLabel(#8d69c30: {{2, 90}, {26, 19}})
1574
- => UIWindow(#6e27180, {{0, 0}, {320, 480}},
1575
-
1576
- # you can pass the format into the adjust method:
1577
- (main)> a 4, :objc
1578
- => "UIRoundedRectButton(#8d68170: {{10.0, 30.0}, {200.0, 30.0}})"
1579
-
1580
- # it will continue to be used in subsequent calls
1581
- (main)> wider 15
1582
- {{10.0, 30.0}, {200.0, 45.0}}
1583
- => "UIRoundedRectButton(#8d68170: {{10.0, 30.0}, {200.0, 45.0}}) child of UIView(#8d631b0)"
1584
- ```
1585
-
1586
- #### JSON (or GeoMotion)
1587
-
1588
- ```
1589
- (main)> a 1, :json
1590
- => "UIView(#8d631b0: {x: 0.0, y: 20.0, height: 460.0, width: 320.0})"
1591
- (main)> wider 30
1592
- => "CGRect(#6e9c9f0: {x: 0.0, y: 20.0, height: 460.0, width: 350.0})"
1593
- (main)> right 130
1594
- => "CGRect(#8dc6a40: {x: 130.0, y: 20.0, height: 460.0, width: 350.0})"
1595
- (main)> tree
1596
- 0: . UIWindow(#6e27180: {x: 0.0, y: 0.0, height: 480.0, width: 320.0})
1597
- 1: `-- UIView(#8d631b0: {x: 130.0, y: 20.0, height: 460.0, width: 350.0})
1598
- 2: +-- UIButton(#6d6c090: {x: 10.0, y: 10.0, height: 463.400512695312, width: 320.0})
1599
- 3: | `-- UIImageView(#8d67e00: {x: 0.0, y: 0.0, height: 463.400512695312, width: 320.0})
1600
- 4: `-- UIRoundedRectButton(#8d68170: {x: 10.0, y: 30.0, height: 200.0, width: 45.0})
1601
- 5: `-- UIButtonLabel(#8d69c30: {x: 4.0, y: 90.0, height: 19.0, width: 37.0})
1602
- => UIWindow(#6e27180: {x: 0.0, y: 0.0, height: 480.0, width: 320.0})
1603
- ```
1604
-
1605
- ### CENTER (in parent frame)
1606
-
1607
- It is called as `center(which_index, of_total_number, direction)`. The order can
1608
- be changed, and all the arguments are optional. Default values are
1609
- `center(1, 1, 'h')` (center the item horizontally).
1610
-
1611
- You can set 'direction' using a string or symbol: 'horiz', 'vert', 'x', even 'x
1612
- and y'. The method searches for the letters `[xyhv]`.
1613
-
1614
- Here are a few examples:
1615
-
1616
- ```
1617
- (main)> center
1618
- [[145.0, 30.0], [30.0, 200.0]]
1619
- UIRoundedRectButton.origin = [145.0, 30.0]
1620
- => "[[145.0, 30.0], [30.0, 200.0]]"
1621
- ```
1622
-
1623
- In order to place that same button in the center of the screen - horizontally
1624
- and vertically - you can use this shorthand syntax:
1625
-
1626
- `center :xy`
1627
-
1628
- If you have three buttons and want them spaced evenly (vertically) across their
1629
- parent frame, you can accomplish that this way:
1630
-
1631
- ```
1632
- (main)> tree
1633
- 0: . UIWindow(#6e1f950: [[0.0, 0.0], [320.0, 480.0]])
1634
- 1: `-- UIView(#8b203b0: [[0.0, 20.0], [320.0, 460.0]])
1635
- 2: +-- UIButton(#d028de0: [[10.0, 10.0], [320.0, 464]])
1636
- 3: | `-- UIImageView(#d02aaa0: [[0.0, 0.0], [320.0, 464]])
1637
- 4: +-- UIRoundedRectButton(#d02adb0: [[55.0, 110.0], [210.0, 20.0]], text: "Button 1")
1638
- 5: | `-- UIButtonLabel(#d02af00: [[73.0, 0.0], [63.0, 19.0]])
1639
- 6: +-- UIRoundedRectButton(#d028550: [[60.0, 30.0], [200.0, 20.0]], text: "Button 2")
1640
- 7: | `-- UIButtonLabel(#d02afb0: [[68.0, 0.0], [63.0, 19.0]])
1641
- 8: `-- UIRoundedRectButton(#d02b220: [[70.0, 30.0], [300.0, 20.0]], text: "Button 3")
1642
- 9: `-- UIButtonLabel(#d02b300: [[118.0, 0.0], [63.0, 19.0]])
1643
- => UIWindow(#6e1f950, [[0.0, 0.0], [320.0, 480.0]])
1644
- # grab the first button, and center it vertically. It is the first of three buttons
1645
- (main)> a 4; center 1, 3, :vert; center
1646
- [[55.0, 110.0], [210.0, 20.0]]
1647
- UIRoundedRectButton.origin = [55.0, 110.0]
1648
- => "[[55.0, 110.0], [210.0, 20.0]]"
1649
- # grab the second button. The first parameter changes to `2`, because this
1650
- # button is in the second position.
1651
- (main)> a 6; center 2, 3, :vert; center
1652
- [[60.0, 220.0], [200.0, 20.0]]
1653
- UIRoundedRectButton.origin = [60.0, 220.0]
1654
- => "[[60.0, 220.0], [200.0, 20.0]]"
1655
- # grab the third button and place it in the third position
1656
- (main)> a 8; center 3, 3, :vert; center
1657
- [[10.0, 330.0], [300.0, 20.0]]
1658
- UIRoundedRectButton.origin = [10.0, 330.0]
1659
- => "[[10.0, 330.0], [300.0, 20.0]]"
1660
- ```
1661
-
1662
- The calculated positions (x,y) are in the REPL output
1663
-
1664
- #### Finding the *[controller,layer,...]* you want.
1665
-
1666
- **Don't stop there!**
1328
+ Pipes
1329
+ -----
1667
1330
 
1668
- You can analyze `UIViewController` and `CALayer` hierarchies, too. There's even
1669
- a handy `root` method to grab the `rootViewController`:
1331
+ This package short-circuits the `|` operator to perform coercion and filtering
1332
+ between all sorts of objects.
1670
1333
 
1671
- ```ruby
1672
- (main)> tree root
1673
- 0: . #<MainScreenController:0xac23b80>
1674
- 1: +-- #<ScheduleViewController:0x13185d00>
1675
- 2: | +-- #<ScheduleTableController:0x131862f0>
1676
- 3: | `-- #<ScheduleCalendarController:0x131bba90>
1677
- 4: +-- #<CameraViewController:0x13191380>
1678
- 5: +-- #<UINavigationController:0xac01ea0>
1679
- 6: | `-- #<UITableViewController:0xac04e30>
1680
- 7: +-- #<PicturesViewController:0x1403ede0>
1681
- 8: `-- #<MessagesViewController:0x131a1bc0>
1682
- => #<MainScreenController:0xac23b80>
1683
- ```
1334
+ ### Coercion
1684
1335
 
1685
- If you have a tree structure and you want to output it using `tree`, you can do
1686
- so by passing either a method name (that should return an array) or a block. The
1687
- block will be passed your object, and should return the children.
1336
+ Any object that defines a coercion method (image.uicolor, string.uiimage,
1337
+ :symbol.uifont) can use the `|` and the class name to perform the same method.
1688
1338
 
1689
1339
  ```ruby
1690
- class Foo
1691
- attr_accessor :children
1692
- end
1693
- ```
1694
- ```
1695
- (main)> foo = Foo.new
1696
- (main)> foo.children = [Foo.new,Foo.new,Foo.new]
1697
- (main)> tree foo, :children
1698
- (main)> tree foo, :children
1699
- 0: . #<Foo:0x12d6e0d0>
1700
- 1: +-- #<Foo:0x114146c0>
1701
- 2: +-- #<Foo:0x114149d0>
1702
- 3: `-- #<Foo:0x114149e0>
1703
-
1704
- => #<Foo:0x12d6e0d0 @children=[#<Foo:0x114146c0>, #<Foo:0x114149d0>, #<Foo:0x114149e0>]>
1705
- (main)> tree(foo) { |f| f.children }
1706
- 0: . #<Foo:0x12d6e0d0>
1707
- 1: +-- #<Foo:0x114146c0>
1708
- 2: +-- #<Foo:0x114149d0>
1709
- 3: `-- #<Foo:0x114149e0>
1710
-
1711
- => #<Foo:0x12d6e0d0 @children=[#<Foo:0x114146c0>, #<Foo:0x114149d0>, #<Foo:0x114149e0>]>
1340
+ :label | UIFont # => # :label.uifont
1341
+ "image_name" | UIImage # => "image_name".uiimage
1342
+ view | UIImage | UIColor # => view.uiimage.uicolor
1712
1343
  ```
1713
1344
 
1714
- ##### Global objects
1345
+ ### Filters
1715
1346
 
1716
- The adjust and tree methods act on global objects. Once either of these methods
1717
- is used, you can access that global if you want:
1347
+ You can pipe objects that have some idea of "filter", like using an image mask
1348
+ or image filter.
1718
1349
 
1719
1350
  ```ruby
1720
- $sugarcube_view # => the view (or any object) being 'adjusted' (accessible using `adjust` or `a`)
1721
- $sugarcube_items # => the list of views that was output using `tree`
1351
+ image | mask # => image.masked(mask)
1352
+ image | darken_cifilter # => image.apply_filter(darken_cifilter)
1353
+ # this one is... interesting!
1354
+ "My name is Mud" | /\w+$/ # => "Mud"
1355
+ "My name is Mud" | /\d+$/ # => nil
1356
+ "My name is Mud" | "Mud" # => "Mud"
1357
+ "My name is Mud" | "Bob" # => nil
1722
1358
  ```
1723
1359
 
1360
+ Awesome
1361
+ -----
1724
1362
 
1725
- Pointers
1726
- ----------
1727
-
1728
- These are not UIKit-related, so I reverted to Ruby's preferred `to_foo`
1729
- convention.
1730
-
1731
- ```ruby
1732
- [0.0, 1.1, 2.2].to_pointer(:float)
1733
-
1734
- # is equivalent to
1735
- floats = Pointer.new(:float, 3)
1736
- floats[0] = 0.0
1737
- floats[1] = 1.1
1738
- floats[2] = 2.2
1739
- ```
1363
+ > `require 'sugarcube-awesome'`
1740
1364
 
1741
- UUID
1742
- ------
1365
+ SugarCube adds support for [Motion-Awesome][motion-awesome]! The `awesome_icon`
1366
+ method is added to `Symbol`, which returns an NSAttributedString that uses the
1367
+ MotionAwesome font. You can pass in `:size` and `:color` options.
1743
1368
 
1744
- Quick wrapper for `CFUUIDCreate()` and `CFUUIDCreateString()`. Identical to the
1745
- `BubbleWrap::create_uuid` method.
1369
+ `sugarcube-attributedstring` is not required for this extension to function, but
1370
+ it adds `NSAttributedString` methods that really help. Below I'm usind `#+` and
1371
+ `#bold` and `#color` to construct an `NSAttributedString`.
1746
1372
 
1747
1373
  ```ruby
1748
- > SugarCube::UUID::uuid
1749
- "0A3A76C6-9738-4458-969E-3B9DF174A3D9"
1374
+ # in Rakefile
1375
+ require 'sugarcube-awesome'
1376
+ require 'sugarcube-attributedstring'
1750
1377
 
1751
- # or
1752
- > include SugarCube::UUID
1753
- > uuid
1754
- # => "0A3A76C6-9738-4458-969E-3B9DF174A3D9"
1378
+ # in your app
1379
+ label.attributedText = (:down_arrow.awesome_icon + ' Going down?'.bold).color(:white)
1380
+ # OR for buttons
1381
+ button.setAttributedTitle(:twitter.awesome_icon, forState:UIControlStateNormal)
1755
1382
  ```
1756
1383
 
1757
- Ruby on Rails Ripoffs (RoR-R?)
1758
- ---------------
1759
-
1760
- aka `ActiveSupport`. Now that Thomas Kadauke has released [motion-support][],
1761
- consider these extensions deprecated. They will be removed in version 1.0.
1762
-
1763
- ```ruby
1764
- # truthiness with `blank?`
1765
- nil.blank? # => true
1766
- false.blank? # => true
1767
- ''.blank? # => true
1768
- [].blank? # => true
1769
- {}.blank? # => true
1770
-
1771
- 0.blank? # => false
1772
- true.blank? # => false
1773
- 'a'.blank? # => false
1774
- ['a'].blank? # => false
1775
- {a: 'a'}.blank? # => false
1776
-
1777
- # and my favorite
1778
- 1.in? [1,2,3] # => true
1779
- 1.in? 4..5 # => false
1780
- ```
1384
+ [motion-awesome]: http://derailed.github.io/motion-awesome/
1781
1385
 
1782
- Gestures
1783
- --------
1386
+ Anonymous
1387
+ -----
1784
1388
 
1785
- Sugarcube's gesture support is very similar to BubbleWrap's, and it's entirely
1786
- possible that the two will be merged into one thing. But SugarCube is all about
1787
- extending base classes, whereas BubbleWrap tends to add *new* classes to do the
1788
- heavy lifting. Plus the options you pass to SugarCube are very different, and
1789
- the prefix is "on" instead of "when" (e.g. "on_pan" instead of "when_panned")
1389
+ > `require 'sugarcube-anonymous'`
1790
1390
 
1791
- Gestures are an "opt-in" extension. In your Rakefile, add
1792
- `require 'sugarcube-gestures'`.
1391
+ Convert `Hash`es into an "anonymous object". Existing keys will be able to be
1392
+ accessed using method names. Uses the `SugarCube::Anonymous` class to
1393
+ accomplish this, though the usual interface is via `Hash#to_object`.
1793
1394
 
1794
1395
  ```ruby
1795
- require 'sugarcube-gestures'
1796
-
1797
- view.on_pan { |gesture|
1798
- location = gesture.view.locationInView(view)
1799
- }
1800
-
1801
- # other gesture methods, with common options:
1802
- view.on_tap # use system defaults
1803
- view.on_tap(1) # number of taps
1804
- view.on_tap(taps: 1, fingers: 1) # number of taps and number of fingers
1805
-
1806
- view.on_pinch # no options
1807
- view.on_rotate # no options
1808
-
1809
- view.on_swipe # use system defaults
1810
- view.on_swipe :left
1811
- view.on_swipe(direction: :left, fingers: 1)
1812
- view.on_swipe(direction: UISwipeGestureRecognizerDirectionLeft, fingers: 1)
1396
+ h = { foo: 'FOO', 'bar' => 'BAR' }.to_object
1813
1397
 
1814
- view.on_pan # use system defaults
1815
- view.on_pan(2) # minimum and maximum fingers required
1816
- view.on_pan(fingers: 2)
1817
- view.on_pan(min_fingers: 2, max_fingers: 3)
1398
+ # You can use methods instead of keys.
1399
+ h.foo # => h[:foo]
1400
+ h.bar # => h['bar']
1401
+ h.foo = 'Foo' # => h[:foo] = 'Foo'
1402
+ h.bar = 'Bar' # => h['bar'] = 'Bar'
1818
1403
 
1819
- view.on_press # use system defaults
1820
- view.on_press(1.5) # duration
1821
- view.on_press(duration: 1.5, taps: 1, fingers: 1)
1404
+ # only existing keys are accessed this way
1405
+ h.baz # => NoMethodError
1406
+ h.baz = 'baz' # => NoMethodError
1822
1407
  ```
1823
1408
 
1824
1409
  Unholy
1825
- --------
1826
-
1827
- These methods are just about as opinionated as they get - even more than the RoR
1828
- additions. They are not included by default, so please don't freak out about
1829
- them. I add them here because I don't think anyone will notice if I do, and I
1830
- use these everywhere. :poop:
1831
-
1832
- ### `ivar`
1833
-
1834
- ###### Rakefile
1410
+ -----
1835
1411
 
1836
- ```ruby
1837
- require 'sugarcube-unholy'
1838
- ```
1412
+ > `require 'sugarcube-unholy'`
1839
1413
 
1840
- ###### Elsewhere
1414
+ I added these a long time ago to SugarCube, and so it's possible some people use
1415
+ these extensions. Honestly, I should have put them somewhere else, they are
1416
+ silly and not terribly useful, except maybe to me (I use them a lot!). :poop:
1841
1417
 
1842
1418
  ```ruby
1843
1419
  class Baz ; end
@@ -1847,10 +1423,25 @@ foo = Baz.new
1847
1423
  foo.instance_variable_set(:bar.ivar, value) # => foo.instance_variable_set(:@bar, value)
1848
1424
  foo.instance_variable_set(var_name.ivar, value) # => foo.instance_variable_set("@#{var_name}", value)
1849
1425
 
1426
+ # (:symbol || 'string').setter
1427
+ foo.send(varname.setter, 'value')
1428
+
1850
1429
  # (:symbol || 'string').cvar
1851
1430
  Baz.class_variable_set(var_name.cvar, value) # => Baz.class_variable_set("@@#{var_name}", value)
1852
1431
  ```
1853
1432
 
1433
+ Legacy
1434
+ -----
1435
+
1436
+ > `require 'sugarcube-legacy'`
1437
+
1438
+ This is where deprecated methods go to suffer a long, slow death.
1439
+
1440
+ Contributions
1441
+ =====
1442
+
1443
+ If you want to see new features, please fork, commit, and pull-request! :smiley:
1444
+
1854
1445
  [BubbleWrap]: https://github.com/rubymotion/BubbleWrap
1855
1446
  [sweettea]: https://github.com/colinta/sweettea
1856
1447
  [teacup]: https://github.com/rubymotion/teacup