rubymotionr 0.0.1

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 (139) hide show
  1. data/.gitignore +2 -0
  2. data/.rspec +2 -0
  3. data/Gemfile +5 -0
  4. data/Gemfile.lock +24 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +31 -0
  7. data/Rakefile +1 -0
  8. data/lib/rubymotionr.rb +7 -0
  9. data/lib/rubymotionr/project.rb +18 -0
  10. data/lib/rubymotionr/repo.rb +18 -0
  11. data/lib/rubymotionr/version.rb +3 -0
  12. data/rubymotionr.gemspec +19 -0
  13. data/spec/.DS_Store +0 -0
  14. data/spec/multiple-rubymotion-apps/Beers/README +5 -0
  15. data/spec/multiple-rubymotion-apps/Beers/Rakefile +8 -0
  16. data/spec/multiple-rubymotion-apps/Beers/app/app_delegate.rb +16 -0
  17. data/spec/multiple-rubymotion-apps/Beers/app/beer.rb +22 -0
  18. data/spec/multiple-rubymotion-apps/Beers/app/beer_details_controller.rb +15 -0
  19. data/spec/multiple-rubymotion-apps/Beers/app/beer_list_controller.rb +40 -0
  20. data/spec/multiple-rubymotion-apps/Beers/app/beer_map_controller.rb +51 -0
  21. data/spec/multiple-rubymotion-apps/Beers/resources/.gitignore +0 -0
  22. data/spec/multiple-rubymotion-apps/Beers/resources/list.png +0 -0
  23. data/spec/multiple-rubymotion-apps/Beers/resources/map.png +0 -0
  24. data/spec/multiple-rubymotion-apps/CircleLayout/.gitignore +5 -0
  25. data/spec/multiple-rubymotion-apps/CircleLayout/README +5 -0
  26. data/spec/multiple-rubymotion-apps/CircleLayout/Rakefile +9 -0
  27. data/spec/multiple-rubymotion-apps/CircleLayout/app/app_delegate.rb +8 -0
  28. data/spec/multiple-rubymotion-apps/CircleLayout/app/cell.rb +13 -0
  29. data/spec/multiple-rubymotion-apps/CircleLayout/app/circle_layout.rb +41 -0
  30. data/spec/multiple-rubymotion-apps/CircleLayout/app/view_controller.rb +33 -0
  31. data/spec/multiple-rubymotion-apps/Fonts/README +7 -0
  32. data/spec/multiple-rubymotion-apps/Fonts/Rakefile +8 -0
  33. data/spec/multiple-rubymotion-apps/Fonts/app/app_delegate.rb +9 -0
  34. data/spec/multiple-rubymotion-apps/Fonts/app/detail_controller.rb +50 -0
  35. data/spec/multiple-rubymotion-apps/Fonts/app/root_controller.rb +65 -0
  36. data/spec/multiple-rubymotion-apps/Fonts/resources/.gitkeep +0 -0
  37. data/spec/multiple-rubymotion-apps/GestureTable/README +8 -0
  38. data/spec/multiple-rubymotion-apps/GestureTable/Rakefile +8 -0
  39. data/spec/multiple-rubymotion-apps/GestureTable/app/UIColor_extensions.rb +32 -0
  40. data/spec/multiple-rubymotion-apps/GestureTable/app/UITableView_extensions.rb +7 -0
  41. data/spec/multiple-rubymotion-apps/GestureTable/app/app_delegate.rb +9 -0
  42. data/spec/multiple-rubymotion-apps/GestureTable/app/gesture_recognizer.rb +361 -0
  43. data/spec/multiple-rubymotion-apps/GestureTable/app/transformable_cells.rb +92 -0
  44. data/spec/multiple-rubymotion-apps/GestureTable/app/view_controller.rb +173 -0
  45. data/spec/multiple-rubymotion-apps/GestureTable/resources/.gitignore +0 -0
  46. data/spec/multiple-rubymotion-apps/Hello/README +3 -0
  47. data/spec/multiple-rubymotion-apps/Hello/Rakefile +8 -0
  48. data/spec/multiple-rubymotion-apps/Hello/app/app_delegate.rb +9 -0
  49. data/spec/multiple-rubymotion-apps/Hello/app/hello_view.rb +33 -0
  50. data/spec/multiple-rubymotion-apps/Hello/app/hello_view_controller.rb +5 -0
  51. data/spec/multiple-rubymotion-apps/Hello/resources/.gitignore +0 -0
  52. data/spec/multiple-rubymotion-apps/Hello/resources/icon.png +0 -0
  53. data/spec/multiple-rubymotion-apps/HelloGL/README +9 -0
  54. data/spec/multiple-rubymotion-apps/HelloGL/Rakefile +11 -0
  55. data/spec/multiple-rubymotion-apps/HelloGL/app/app_delegate.rb +16 -0
  56. data/spec/multiple-rubymotion-apps/HelloGL/app/index_data.rb +19 -0
  57. data/spec/multiple-rubymotion-apps/HelloGL/app/motiongl_controller.rb +139 -0
  58. data/spec/multiple-rubymotion-apps/HelloGL/app/vertex_data.rb +22 -0
  59. data/spec/multiple-rubymotion-apps/HelloGL/resources/rubymotion.png +0 -0
  60. data/spec/multiple-rubymotion-apps/Locations/README +3 -0
  61. data/spec/multiple-rubymotion-apps/Locations/Rakefile +8 -0
  62. data/spec/multiple-rubymotion-apps/Locations/app/app_delegate.rb +11 -0
  63. data/spec/multiple-rubymotion-apps/Locations/app/location.rb +21 -0
  64. data/spec/multiple-rubymotion-apps/Locations/app/locations_controller.rb +66 -0
  65. data/spec/multiple-rubymotion-apps/Locations/app/locations_store.rb +61 -0
  66. data/spec/multiple-rubymotion-apps/Locations/resources/.gitignore +0 -0
  67. data/spec/multiple-rubymotion-apps/Mustache/README +5 -0
  68. data/spec/multiple-rubymotion-apps/Mustache/Rakefile +10 -0
  69. data/spec/multiple-rubymotion-apps/Mustache/app/app_delegate.rb +9 -0
  70. data/spec/multiple-rubymotion-apps/Mustache/app/mustache_view_controller.rb +99 -0
  71. data/spec/multiple-rubymotion-apps/Mustache/resources/.gitignore +0 -0
  72. data/spec/multiple-rubymotion-apps/Mustache/resources/guido.jpg +0 -0
  73. data/spec/multiple-rubymotion-apps/Mustache/resources/jmccolor.jpg +0 -0
  74. data/spec/multiple-rubymotion-apps/Mustache/resources/kay.jpg +0 -0
  75. data/spec/multiple-rubymotion-apps/Mustache/resources/matz.jpg +0 -0
  76. data/spec/multiple-rubymotion-apps/Mustache/resources/mustache.png +0 -0
  77. data/spec/multiple-rubymotion-apps/Paint/README +3 -0
  78. data/spec/multiple-rubymotion-apps/Paint/Rakefile +8 -0
  79. data/spec/multiple-rubymotion-apps/Paint/app/app_delegate.rb +9 -0
  80. data/spec/multiple-rubymotion-apps/Paint/app/paint_view.rb +57 -0
  81. data/spec/multiple-rubymotion-apps/Paint/app/paint_view_controller.rb +13 -0
  82. data/spec/multiple-rubymotion-apps/Paint/resources/.gitignore +0 -0
  83. data/spec/multiple-rubymotion-apps/Paint/resources/erase.caf +0 -0
  84. data/spec/multiple-rubymotion-apps/PaintHTML/README +8 -0
  85. data/spec/multiple-rubymotion-apps/PaintHTML/Rakefile +10 -0
  86. data/spec/multiple-rubymotion-apps/PaintHTML/app/app_delegate.rb +10 -0
  87. data/spec/multiple-rubymotion-apps/PaintHTML/app/web_view_controller.rb +48 -0
  88. data/spec/multiple-rubymotion-apps/PaintHTML/resources/index.html +211 -0
  89. data/spec/multiple-rubymotion-apps/PaintHTML/resources/index2.html +14 -0
  90. data/spec/multiple-rubymotion-apps/README.rdoc +47 -0
  91. data/spec/multiple-rubymotion-apps/StoryboardCustoms/README.md +42 -0
  92. data/spec/multiple-rubymotion-apps/StoryboardCustoms/Rakefile +7 -0
  93. data/spec/multiple-rubymotion-apps/StoryboardCustoms/app/app_delegate.rb +12 -0
  94. data/spec/multiple-rubymotion-apps/StoryboardCustoms/app/custom_view_controller1.rb +31 -0
  95. data/spec/multiple-rubymotion-apps/StoryboardCustoms/app/custom_view_controller2.rb +29 -0
  96. data/spec/multiple-rubymotion-apps/StoryboardCustoms/app/helpers.rb +4 -0
  97. data/spec/multiple-rubymotion-apps/StoryboardCustoms/resources/Master.storyboard +127 -0
  98. data/spec/multiple-rubymotion-apps/TicTacToe/.gitignore +5 -0
  99. data/spec/multiple-rubymotion-apps/TicTacToe/Rakefile +7 -0
  100. data/spec/multiple-rubymotion-apps/TicTacToe/app/app_delegate.rb +8 -0
  101. data/spec/multiple-rubymotion-apps/TicTacToe/app/board.rb +59 -0
  102. data/spec/multiple-rubymotion-apps/TicTacToe/app/board_view_controller.rb +86 -0
  103. data/spec/multiple-rubymotion-apps/Timer/README +3 -0
  104. data/spec/multiple-rubymotion-apps/Timer/Rakefile +7 -0
  105. data/spec/multiple-rubymotion-apps/Timer/app/app_delegate.rb +9 -0
  106. data/spec/multiple-rubymotion-apps/Timer/app/timer_controller.rb +38 -0
  107. data/spec/multiple-rubymotion-apps/Timer/resources/.gitignore +0 -0
  108. data/spec/multiple-rubymotion-apps/Trollify/README +6 -0
  109. data/spec/multiple-rubymotion-apps/Trollify/Rakefile +9 -0
  110. data/spec/multiple-rubymotion-apps/Trollify/app/app_delegate.rb +9 -0
  111. data/spec/multiple-rubymotion-apps/Trollify/app/image_composition_controller.rb +193 -0
  112. data/spec/multiple-rubymotion-apps/Trollify/app/uiimage_extension.rb +27 -0
  113. data/spec/multiple-rubymotion-apps/Trollify/resources/.gitignore +0 -0
  114. data/spec/multiple-rubymotion-apps/Trollify/resources/ballmer.jpg +0 -0
  115. data/spec/multiple-rubymotion-apps/Trollify/resources/icon.png +0 -0
  116. data/spec/multiple-rubymotion-apps/Trollify/resources/icon@2x.png +0 -0
  117. data/spec/multiple-rubymotion-apps/Trollify/resources/trollface.png +0 -0
  118. data/spec/multiple-rubymotion-apps/Tweets/README +3 -0
  119. data/spec/multiple-rubymotion-apps/Tweets/Rakefile +7 -0
  120. data/spec/multiple-rubymotion-apps/Tweets/app/app_delegate.rb +9 -0
  121. data/spec/multiple-rubymotion-apps/Tweets/app/data_parser.rb +10 -0
  122. data/spec/multiple-rubymotion-apps/Tweets/app/json_parser.rb +12 -0
  123. data/spec/multiple-rubymotion-apps/Tweets/app/tweet.rb +11 -0
  124. data/spec/multiple-rubymotion-apps/Tweets/app/tweet_cell.rb +51 -0
  125. data/spec/multiple-rubymotion-apps/Tweets/app/tweets_controller.rb +72 -0
  126. data/spec/multiple-rubymotion-apps/Tweets/resources/.gitignore +0 -0
  127. data/spec/repo_spec.rb +27 -0
  128. data/spec/rubymotion-example/.DS_Store +0 -0
  129. data/spec/rubymotion-example/README +3 -0
  130. data/spec/rubymotion-example/Rakefile +7 -0
  131. data/spec/rubymotion-example/app/app_delegate.rb +9 -0
  132. data/spec/rubymotion-example/app/data_parser.rb +10 -0
  133. data/spec/rubymotion-example/app/json_parser.rb +12 -0
  134. data/spec/rubymotion-example/app/tweet.rb +11 -0
  135. data/spec/rubymotion-example/app/tweet_cell.rb +51 -0
  136. data/spec/rubymotion-example/app/tweets_controller.rb +72 -0
  137. data/spec/rubymotion-example/resources/.gitignore +0 -0
  138. data/spec/spec_helper.rb +9 -0
  139. metadata +309 -0
@@ -0,0 +1,27 @@
1
+ class UIImage
2
+ def imageRotatedByRadians(radians)
3
+ # Calculate the size of the rotated view's containing box for our drawing space.
4
+ rotatedViewBox = UIView.alloc.initWithFrame(CGRectMake(0,0,self.size.width, self.size.height))
5
+ t = CGAffineTransformMakeRotation(radians)
6
+ rotatedViewBox.transform = t
7
+ rotatedSize = rotatedViewBox.frame.size
8
+
9
+ # Create the bitmap context.
10
+ UIGraphicsBeginImageContext(rotatedSize)
11
+ bitmap = UIGraphicsGetCurrentContext()
12
+
13
+ # Move the origin to the middle of the image so we will rotate and scale around the center.
14
+ CGContextTranslateCTM(bitmap, rotatedSize.width/2, rotatedSize.height/2)
15
+
16
+ # Rotate the image context.
17
+ CGContextRotateCTM(bitmap, radians)
18
+
19
+ # Now, draw the rotated/scaled image into the context.
20
+ CGContextScaleCTM(bitmap, 1.0, -1.0)
21
+ CGContextDrawImage(bitmap, CGRectMake(-self.size.width / 2, -self.size.height / 2, self.size.width, self.size.height), self.CGImage)
22
+
23
+ newImage = UIGraphicsGetImageFromCurrentImageContext()
24
+ UIGraphicsEndImageContext()
25
+ newImage
26
+ end
27
+ end
@@ -0,0 +1,3 @@
1
+ Tweets.app
2
+
3
+ This sample demonstrates the following concepts: Table View, Custom Table View Cells, Grand Central Dispatch, JSON.
@@ -0,0 +1,7 @@
1
+ $:.unshift("/Library/RubyMotion/lib")
2
+ require 'motion/project'
3
+
4
+ Motion::Project::App.setup do |app|
5
+ # Use `rake config' to see complete project settings.
6
+ app.name = 'Tweets'
7
+ end
@@ -0,0 +1,9 @@
1
+ class AppDelegate
2
+ def application(application, didFinishLaunchingWithOptions:launchOptions)
3
+ @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.applicationFrame)
4
+ @window.rootViewController = TweetsController.alloc.initWithStyle(UITableViewStylePlain)
5
+ @window.rootViewController.wantsFullScreenLayout = true
6
+ @window.makeKeyAndVisible
7
+ true
8
+ end
9
+ end
@@ -0,0 +1,10 @@
1
+ class DataParser
2
+ def self.parse(url)
3
+ error_ptr = Pointer.new(:object)
4
+ data = NSData.alloc.initWithContentsOfURL(NSURL.URLWithString(url), options:NSDataReadingUncached, error:error_ptr)
5
+ unless data
6
+ raise error_ptr[0]
7
+ end
8
+ data
9
+ end
10
+ end
@@ -0,0 +1,12 @@
1
+ class JSONParser
2
+ def self.parse_from_url(url)
3
+ data = DataParser.parse(url)
4
+
5
+ error_ptr = Pointer.new(:object)
6
+ json = NSJSONSerialization.JSONObjectWithData(data, options:0, error:error_ptr)
7
+ unless json
8
+ raise error_ptr[0]
9
+ end
10
+ json
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ class Tweet
2
+ attr_reader :author, :message, :profile_image_url
3
+ attr_accessor :profile_image
4
+
5
+ def initialize(dict)
6
+ @author = dict['from_user_name']
7
+ @message = dict['text']
8
+ @profile_image_url = dict['profile_image_url']
9
+ @profile_image = nil
10
+ end
11
+ end
@@ -0,0 +1,51 @@
1
+ class TweetCell < UITableViewCell
2
+ CellID = 'CellIdentifier'
3
+ MessageFontSize = 14
4
+
5
+ def self.cellForTweet(tweet, inTableView:tableView)
6
+ cell = tableView.dequeueReusableCellWithIdentifier(TweetCell::CellID) || TweetCell.alloc.initWithStyle(UITableViewCellStyleDefault, reuseIdentifier:CellID)
7
+ cell.fillWithTweet(tweet, inTableView:tableView)
8
+ cell
9
+ end
10
+
11
+ def initWithStyle(style, reuseIdentifier:cellid)
12
+ if super
13
+ self.textLabel.numberOfLines = 0
14
+ self.textLabel.font = UIFont.systemFontOfSize(MessageFontSize)
15
+ end
16
+ self
17
+ end
18
+
19
+ def fillWithTweet(tweet, inTableView:tableView)
20
+ self.textLabel.text = tweet.message
21
+
22
+ unless tweet.profile_image
23
+ self.imageView.image = nil
24
+ Dispatch::Queue.concurrent.async do
25
+ profile_image_data = NSData.alloc.initWithContentsOfURL(NSURL.URLWithString(tweet.profile_image_url))
26
+ if profile_image_data
27
+ tweet.profile_image = UIImage.alloc.initWithData(profile_image_data)
28
+ Dispatch::Queue.main.sync do
29
+ self.imageView.image = tweet.profile_image
30
+ tableView.delegate.reloadRowForTweet(tweet)
31
+ end
32
+ end
33
+ end
34
+ else
35
+ self.imageView.image = tweet.profile_image
36
+ end
37
+ end
38
+
39
+ def self.heightForTweet(tweet, width)
40
+ constrain = CGSize.new(width - 57, 1000)
41
+ size = tweet.message.sizeWithFont(UIFont.systemFontOfSize(MessageFontSize), constrainedToSize:constrain)
42
+ [57, size.height + 8].max
43
+ end
44
+
45
+ def layoutSubviews
46
+ super
47
+ self.imageView.frame = CGRectMake(2, 2, 49, 49)
48
+ label_size = self.frame.size
49
+ self.textLabel.frame = CGRectMake(57, 0, label_size.width - 59, label_size.height)
50
+ end
51
+ end
@@ -0,0 +1,72 @@
1
+ class TweetsController < UITableViewController
2
+ def viewDidLoad
3
+ @tweets = []
4
+ searchBar = UISearchBar.alloc.initWithFrame(CGRectMake(0, 0, self.tableView.frame.size.width, 0))
5
+ searchBar.delegate = self;
6
+ searchBar.showsCancelButton = true;
7
+ searchBar.sizeToFit
8
+ view.tableHeaderView = searchBar
9
+ view.dataSource = view.delegate = self
10
+
11
+ searchBar.text = 'xcode crash'
12
+ searchBarSearchButtonClicked(searchBar)
13
+ end
14
+
15
+ def searchBarSearchButtonClicked(searchBar)
16
+ query = searchBar.text.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
17
+ url = "http://search.twitter.com/search.json?q=#{query}"
18
+
19
+ @tweets.clear
20
+ Dispatch::Queue.concurrent.async do
21
+ json = nil
22
+ begin
23
+ json = JSONParser.parse_from_url(url)
24
+ rescue RuntimeError => e
25
+ presentError e.message
26
+ end
27
+
28
+ new_tweets = []
29
+ json['results'].each do |dict|
30
+ new_tweets << Tweet.new(dict)
31
+ end
32
+
33
+ Dispatch::Queue.main.sync { load_tweets(new_tweets) }
34
+ end
35
+
36
+ searchBar.resignFirstResponder
37
+ end
38
+
39
+ def searchBarCancelButtonClicked(searchBar)
40
+ searchBar.resignFirstResponder
41
+ end
42
+
43
+ def load_tweets(tweets)
44
+ @tweets = tweets
45
+ view.reloadData
46
+ end
47
+
48
+ def presentError(error)
49
+ # TODO
50
+ $stderr.puts error.description
51
+ end
52
+
53
+ def tableView(tableView, numberOfRowsInSection:section)
54
+ @tweets.size
55
+ end
56
+
57
+ def tableView(tableView, heightForRowAtIndexPath:indexPath)
58
+ TweetCell.heightForTweet(@tweets[indexPath.row], tableView.frame.size.width)
59
+ end
60
+
61
+ def tableView(tableView, cellForRowAtIndexPath:indexPath)
62
+ tweet = @tweets[indexPath.row]
63
+ TweetCell.cellForTweet(tweet, inTableView:tableView)
64
+ end
65
+
66
+ def reloadRowForTweet(tweet)
67
+ row = @tweets.index(tweet)
68
+ if row
69
+ view.reloadRowsAtIndexPaths([NSIndexPath.indexPathForRow(row, inSection:0)], withRowAnimation:false)
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ describe Rubymotionr::Repo do
4
+ describe "point it at a rubymotion project with no dependencies and one project at the root" do
5
+ let(:repo) {
6
+ Rubymotionr::Repo.new(File.join(File.dirname(__FILE__), "rubymotion-example"))
7
+ }
8
+ it 'should parse one project' do
9
+ repo.projects.length.should == 1
10
+ end
11
+ it 'should find the path to be in the spec folder' do
12
+ repo.projects.first.path.should == File.join(File.dirname(__FILE__), "rubymotion-example/")
13
+ end
14
+ end
15
+
16
+ describe "point it at a rubymotion project with no dependencies and many projects at the root" do
17
+ let(:repo) {
18
+ Rubymotionr::Repo.new(File.join(File.dirname(__FILE__), "multiple-rubymotion-apps/"))
19
+ }
20
+ it 'should parse one project' do
21
+ repo.projects.length.should == 15
22
+ end
23
+ it 'should have found 15 paths all within multiple-rubymotion-apps directory' do
24
+ Dir.glob(File.join(File.dirname(__FILE__), "multiple-rubymotion-apps/*/")).sort.should == repo.projects.collect { |proj| proj.path }.sort
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,3 @@
1
+ Tweets.app
2
+
3
+ This sample demonstrates the following concepts: Table View, Custom Table View Cells, Grand Central Dispatch, JSON.
@@ -0,0 +1,7 @@
1
+ $:.unshift("/Library/RubyMotion/lib")
2
+ require 'motion/project'
3
+
4
+ Motion::Project::App.setup do |app|
5
+ # Use `rake config' to see complete project settings.
6
+ app.name = 'Tweets'
7
+ end
@@ -0,0 +1,9 @@
1
+ class AppDelegate
2
+ def application(application, didFinishLaunchingWithOptions:launchOptions)
3
+ @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.applicationFrame)
4
+ @window.rootViewController = TweetsController.alloc.initWithStyle(UITableViewStylePlain)
5
+ @window.rootViewController.wantsFullScreenLayout = true
6
+ @window.makeKeyAndVisible
7
+ true
8
+ end
9
+ end
@@ -0,0 +1,10 @@
1
+ class DataParser
2
+ def self.parse(url)
3
+ error_ptr = Pointer.new(:object)
4
+ data = NSData.alloc.initWithContentsOfURL(NSURL.URLWithString(url), options:NSDataReadingUncached, error:error_ptr)
5
+ unless data
6
+ raise error_ptr[0]
7
+ end
8
+ data
9
+ end
10
+ end
@@ -0,0 +1,12 @@
1
+ class JSONParser
2
+ def self.parse_from_url(url)
3
+ data = DataParser.parse(url)
4
+
5
+ error_ptr = Pointer.new(:object)
6
+ json = NSJSONSerialization.JSONObjectWithData(data, options:0, error:error_ptr)
7
+ unless json
8
+ raise error_ptr[0]
9
+ end
10
+ json
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ class Tweet
2
+ attr_reader :author, :message, :profile_image_url
3
+ attr_accessor :profile_image
4
+
5
+ def initialize(dict)
6
+ @author = dict['from_user_name']
7
+ @message = dict['text']
8
+ @profile_image_url = dict['profile_image_url']
9
+ @profile_image = nil
10
+ end
11
+ end
@@ -0,0 +1,51 @@
1
+ class TweetCell < UITableViewCell
2
+ CellID = 'CellIdentifier'
3
+ MessageFontSize = 14
4
+
5
+ def self.cellForTweet(tweet, inTableView:tableView)
6
+ cell = tableView.dequeueReusableCellWithIdentifier(TweetCell::CellID) || TweetCell.alloc.initWithStyle(UITableViewCellStyleDefault, reuseIdentifier:CellID)
7
+ cell.fillWithTweet(tweet, inTableView:tableView)
8
+ cell
9
+ end
10
+
11
+ def initWithStyle(style, reuseIdentifier:cellid)
12
+ if super
13
+ self.textLabel.numberOfLines = 0
14
+ self.textLabel.font = UIFont.systemFontOfSize(MessageFontSize)
15
+ end
16
+ self
17
+ end
18
+
19
+ def fillWithTweet(tweet, inTableView:tableView)
20
+ self.textLabel.text = tweet.message
21
+
22
+ unless tweet.profile_image
23
+ self.imageView.image = nil
24
+ Dispatch::Queue.concurrent.async do
25
+ profile_image_data = NSData.alloc.initWithContentsOfURL(NSURL.URLWithString(tweet.profile_image_url))
26
+ if profile_image_data
27
+ tweet.profile_image = UIImage.alloc.initWithData(profile_image_data)
28
+ Dispatch::Queue.main.sync do
29
+ self.imageView.image = tweet.profile_image
30
+ tableView.delegate.reloadRowForTweet(tweet)
31
+ end
32
+ end
33
+ end
34
+ else
35
+ self.imageView.image = tweet.profile_image
36
+ end
37
+ end
38
+
39
+ def self.heightForTweet(tweet, width)
40
+ constrain = CGSize.new(width - 57, 1000)
41
+ size = tweet.message.sizeWithFont(UIFont.systemFontOfSize(MessageFontSize), constrainedToSize:constrain)
42
+ [57, size.height + 8].max
43
+ end
44
+
45
+ def layoutSubviews
46
+ super
47
+ self.imageView.frame = CGRectMake(2, 2, 49, 49)
48
+ label_size = self.frame.size
49
+ self.textLabel.frame = CGRectMake(57, 0, label_size.width - 59, label_size.height)
50
+ end
51
+ end
@@ -0,0 +1,72 @@
1
+ class TweetsController < UITableViewController
2
+ def viewDidLoad
3
+ @tweets = []
4
+ searchBar = UISearchBar.alloc.initWithFrame(CGRectMake(0, 0, self.tableView.frame.size.width, 0))
5
+ searchBar.delegate = self;
6
+ searchBar.showsCancelButton = true;
7
+ searchBar.sizeToFit
8
+ view.tableHeaderView = searchBar
9
+ view.dataSource = view.delegate = self
10
+
11
+ searchBar.text = 'xcode crash'
12
+ searchBarSearchButtonClicked(searchBar)
13
+ end
14
+
15
+ def searchBarSearchButtonClicked(searchBar)
16
+ query = searchBar.text.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
17
+ url = "http://search.twitter.com/search.json?q=#{query}"
18
+
19
+ @tweets.clear
20
+ Dispatch::Queue.concurrent.async do
21
+ json = nil
22
+ begin
23
+ json = JSONParser.parse_from_url(url)
24
+ rescue RuntimeError => e
25
+ presentError e.message
26
+ end
27
+
28
+ new_tweets = []
29
+ json['results'].each do |dict|
30
+ new_tweets << Tweet.new(dict)
31
+ end
32
+
33
+ Dispatch::Queue.main.sync { load_tweets(new_tweets) }
34
+ end
35
+
36
+ searchBar.resignFirstResponder
37
+ end
38
+
39
+ def searchBarCancelButtonClicked(searchBar)
40
+ searchBar.resignFirstResponder
41
+ end
42
+
43
+ def load_tweets(tweets)
44
+ @tweets = tweets
45
+ view.reloadData
46
+ end
47
+
48
+ def presentError(error)
49
+ # TODO
50
+ $stderr.puts error.description
51
+ end
52
+
53
+ def tableView(tableView, numberOfRowsInSection:section)
54
+ @tweets.size
55
+ end
56
+
57
+ def tableView(tableView, heightForRowAtIndexPath:indexPath)
58
+ TweetCell.heightForTweet(@tweets[indexPath.row], tableView.frame.size.width)
59
+ end
60
+
61
+ def tableView(tableView, cellForRowAtIndexPath:indexPath)
62
+ tweet = @tweets[indexPath.row]
63
+ TweetCell.cellForTweet(tweet, inTableView:tableView)
64
+ end
65
+
66
+ def reloadRowForTweet(tweet)
67
+ row = @tweets.index(tweet)
68
+ if row
69
+ view.reloadRowsAtIndexPaths([NSIndexPath.indexPathForRow(row, inSection:0)], withRowAnimation:false)
70
+ end
71
+ end
72
+ end