sugarcube 0.20.25 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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