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,22 @@
1
+ class VertexData
2
+ attr_accessor :ptr
3
+
4
+ def initialize(data)
5
+ @vertex_size = data[0].count
6
+ @num_vertices = data.count
7
+ @ptr = Pointer.new(:float, @vertex_size * @num_vertices)
8
+ set_data(data)
9
+ end
10
+
11
+ def set_data(vertices)
12
+ vertices.each_with_index do |vertex,idx|
13
+ vertex.each_with_index do |component, component_idx|
14
+ @ptr[idx * @vertex_size + component_idx] = component
15
+ end
16
+ end
17
+ end
18
+
19
+ def size
20
+ @vertex_size * @num_vertices
21
+ end
22
+ end
@@ -0,0 +1,3 @@
1
+ Locations.app
2
+
3
+ This sample demonstrates the following concepts: Table View, Core Location, Core Data.
@@ -0,0 +1,8 @@
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 = 'Locations'
7
+ app.frameworks += ['CoreData', 'CoreLocation']
8
+ end
@@ -0,0 +1,11 @@
1
+ class AppDelegate
2
+ def application(application, didFinishLaunchingWithOptions:launchOptions)
3
+ nav = UINavigationController.alloc.initWithRootViewController(LocationsController.alloc.init)
4
+ nav.wantsFullScreenLayout = true
5
+ nav.toolbarHidden = true
6
+ @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
7
+ @window.rootViewController = nav
8
+ @window.makeKeyAndVisible
9
+ true
10
+ end
11
+ end
@@ -0,0 +1,21 @@
1
+ class Location < NSManagedObject
2
+ def self.entity
3
+ @entity ||= begin
4
+ # Create the entity for our Location class. The entity has 3 properties. CoreData will appropriately define accessor methods for the properties.
5
+ entity = NSEntityDescription.alloc.init
6
+ entity.name = 'Location'
7
+ entity.managedObjectClassName = 'Location'
8
+ entity.properties =
9
+ ['creation_date', NSDateAttributeType,
10
+ 'latitude', NSDoubleAttributeType,
11
+ 'longitude', NSDoubleAttributeType].each_slice(2).map do |name, type|
12
+ property = NSAttributeDescription.alloc.init
13
+ property.name = name
14
+ property.attributeType = type
15
+ property.optional = false
16
+ property
17
+ end
18
+ entity
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,66 @@
1
+ class LocationsController < UITableViewController
2
+ def viewDidLoad
3
+ view.dataSource = view.delegate = self
4
+ end
5
+
6
+ def viewWillAppear(animated)
7
+ navigationItem.title = 'Locations'
8
+ navigationItem.leftBarButtonItem = editButtonItem
9
+ navigationItem.rightBarButtonItem = UIBarButtonItem.alloc.initWithBarButtonSystemItem(UIBarButtonSystemItemAdd, target:self, action:'addLocation')
10
+
11
+ # The Add button is disabled by default, and will be enabled once the location manager is ready to return the current location.
12
+ navigationItem.rightBarButtonItem.enabled = false
13
+ @location_manager ||= CLLocationManager.alloc.init.tap do |lm|
14
+ lm.desiredAccuracy = KCLLocationAccuracyNearestTenMeters
15
+ lm.startUpdatingLocation
16
+ lm.delegate = self
17
+ end
18
+ end
19
+
20
+ def addLocation
21
+ LocationsStore.shared.add_location do |location|
22
+ # We set up our new Location object here.
23
+ coordinate = @location_manager.location.coordinate
24
+ location.creation_date = NSDate.date
25
+ location.latitude = coordinate.latitude
26
+ location.longitude = coordinate.longitude
27
+ end
28
+ view.reloadData
29
+ end
30
+
31
+ def tableView(tableView, numberOfRowsInSection:section)
32
+ LocationsStore.shared.locations.size
33
+ end
34
+
35
+ CellID = 'CellIdentifier'
36
+ def tableView(tableView, cellForRowAtIndexPath:indexPath)
37
+ cell = tableView.dequeueReusableCellWithIdentifier(CellID) || UITableViewCell.alloc.initWithStyle(UITableViewCellStyleSubtitle, reuseIdentifier:CellID)
38
+ location = LocationsStore.shared.locations[indexPath.row]
39
+
40
+ @date_formatter ||= NSDateFormatter.alloc.init.tap do |df|
41
+ df.timeStyle = NSDateFormatterMediumStyle
42
+ df.dateStyle = NSDateFormatterMediumStyle
43
+ end
44
+ cell.textLabel.text = @date_formatter.stringFromDate(location.creation_date)
45
+ cell.detailTextLabel.text = "%0.3f, %0.3f" % [location.latitude, location.longitude]
46
+ cell
47
+ end
48
+
49
+ def tableView(tableView, editingStyleForRowAtIndexPath:indexPath)
50
+ UITableViewCellEditingStyleDelete
51
+ end
52
+
53
+ def tableView(tableView, commitEditingStyle:editingStyle, forRowAtIndexPath:indexPath)
54
+ location = LocationsStore.shared.locations[indexPath.row]
55
+ LocationsStore.shared.remove_location(location)
56
+ tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation:UITableViewRowAnimationFade)
57
+ end
58
+
59
+ def locationManager(manager, didUpdateToLocation:newLocation, fromLocation:oldLocation)
60
+ navigationItem.rightBarButtonItem.enabled = true
61
+ end
62
+
63
+ def locationManager(manager, didFailWithError:error)
64
+ navigationItem.rightBarButtonItem.enabled = false
65
+ end
66
+ end
@@ -0,0 +1,61 @@
1
+ class LocationsStore
2
+ def self.shared
3
+ # Our store is a singleton object.
4
+ @shared ||= LocationsStore.new
5
+ end
6
+
7
+ def locations
8
+ @locations ||= begin
9
+ # Fetch all locations from the model, sorting by the creation date.
10
+ request = NSFetchRequest.alloc.init
11
+ request.entity = NSEntityDescription.entityForName('Location', inManagedObjectContext:@context)
12
+ request.sortDescriptors = [NSSortDescriptor.alloc.initWithKey('creation_date', ascending:false)]
13
+
14
+ error_ptr = Pointer.new(:object)
15
+ data = @context.executeFetchRequest(request, error:error_ptr)
16
+ if data == nil
17
+ raise "Error when fetching data: #{error_ptr[0].description}"
18
+ end
19
+ data
20
+ end
21
+ end
22
+
23
+ def add_location
24
+ # Yield a blank, newly created Location entity, then save the model.
25
+ yield NSEntityDescription.insertNewObjectForEntityForName('Location', inManagedObjectContext:@context)
26
+ save
27
+ end
28
+
29
+ def remove_location(location)
30
+ # Delete the given entity, then save the model.
31
+ @context.deleteObject(location)
32
+ save
33
+ end
34
+
35
+ private
36
+
37
+ def initialize
38
+ # Create the model programmatically. Our model has only one entity, the Location class, and the data will be stored in a SQLite database, inside the application's Documents folder.
39
+ model = NSManagedObjectModel.alloc.init
40
+ model.entities = [Location.entity]
41
+
42
+ store = NSPersistentStoreCoordinator.alloc.initWithManagedObjectModel(model)
43
+ store_url = NSURL.fileURLWithPath(File.join(NSHomeDirectory(), 'Documents', 'Locations.sqlite'))
44
+ error_ptr = Pointer.new(:object)
45
+ unless store.addPersistentStoreWithType(NSSQLiteStoreType, configuration:nil, URL:store_url, options:nil, error:error_ptr)
46
+ raise "Can't add persistent SQLite store: #{error_ptr[0].description}"
47
+ end
48
+
49
+ context = NSManagedObjectContext.alloc.init
50
+ context.persistentStoreCoordinator = store
51
+ @context = context
52
+ end
53
+
54
+ def save
55
+ error_ptr = Pointer.new(:object)
56
+ unless @context.save(error_ptr)
57
+ raise "Error when saving the model: #{error_ptr[0].description}"
58
+ end
59
+ @locations = nil
60
+ end
61
+ end
@@ -0,0 +1,5 @@
1
+ Mustache.app
2
+
3
+ This sample demonstrates the following concepts: Images, Face Recognition, Gestures.
4
+
5
+ The code was inspired from a similar MacRuby sample.
@@ -0,0 +1,10 @@
1
+ $:.unshift("/Library/RubyMotion/lib")
2
+ require 'motion/project'
3
+
4
+ #ENV['debug'] ||= '0' # Because the REPL cannot be loaded with CoreImage yet.
5
+
6
+ Motion::Project::App.setup do |app|
7
+ # Use `rake config' to see complete project settings.
8
+ app.name = 'Mustache'
9
+ app.frameworks += ['QuartzCore', 'CoreImage']
10
+ end
@@ -0,0 +1,9 @@
1
+ class AppDelegate
2
+ def application(application, didFinishLaunchingWithOptions:launchOptions)
3
+ @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
4
+ @window.rootViewController = MustacheViewController.alloc.init
5
+ @window.rootViewController.wantsFullScreenLayout = true
6
+ @window.makeKeyAndVisible
7
+ true
8
+ end
9
+ end
@@ -0,0 +1,99 @@
1
+ class MustacheViewController < UIViewController
2
+ def loadView
3
+ self.view = UIImageView.alloc.init
4
+ @debug_face = false # Set to true to debug face features.
5
+ end
6
+
7
+ def viewDidLoad
8
+ @images = %w{matz guido kay jmccolor}.map { |name| UIImage.imageNamed(name + '.jpg') }
9
+ view.image = @images.first
10
+ view.contentMode = UIViewContentModeScaleAspectFit
11
+ view.userInteractionEnabled = true
12
+
13
+ previousGesture = UISwipeGestureRecognizer.alloc.initWithTarget(self, action:'swipePreviousGesture:')
14
+ previousGesture.direction = UISwipeGestureRecognizerDirectionLeft
15
+ view.addGestureRecognizer(previousGesture)
16
+ nextGesture = UISwipeGestureRecognizer.alloc.initWithTarget(self, action:'swipeNextGesture:')
17
+ nextGesture.direction = UISwipeGestureRecognizerDirectionRight
18
+ view.addGestureRecognizer(nextGesture)
19
+ end
20
+
21
+ def viewDidAppear(animated)
22
+ mustachify
23
+ end
24
+
25
+ def mustachify
26
+ # Remove previous mustaches.
27
+ view.subviews.each { |v| v.removeFromSuperview }
28
+
29
+ # CoreImage used a coordinate system which is flipped on the Y axis
30
+ # compared to UIKit. Also, a UIImageView can return an image larger than
31
+ # itself. To properly translate points, we use an affine transform.
32
+ transform = CGAffineTransformMakeScale(view.bounds.size.width / view.image.size.width, -1 * (view.bounds.size.height / view.image.size.height))
33
+ transform = CGAffineTransformTranslate(transform, 0, -view.image.size.height)
34
+
35
+ image = CIImage.imageWithCGImage(view.image.CGImage)
36
+ @detector ||= CIDetector.detectorOfType CIDetectorTypeFace, context:nil, options: { CIDetectorAccuracy: CIDetectorAccuracyHigh }
37
+ @detector.featuresInImage(image).each do |feature|
38
+ # We need the mouth and eyes positions to determine where the mustache
39
+ # should be added.
40
+ next unless feature.hasMouthPosition and feature.hasLeftEyePosition and feature.hasRightEyePosition
41
+
42
+ if @debug_face
43
+ [feature.leftEyePosition,feature.rightEyePosition,feature.mouthPosition].each do |pt|
44
+ v = UIView.alloc.initWithFrame CGRectMake(0, 0, 20, 20)
45
+ v.backgroundColor = UIColor.greenColor.colorWithAlphaComponent(0.2)
46
+ pt = CGPointApplyAffineTransform(pt, transform)
47
+ v.center = pt
48
+ view.addSubview(v)
49
+ end
50
+ end
51
+
52
+ # Create the mustache view.
53
+ mustacheView = UIImageView.alloc.init
54
+ mustacheView.image = UIImage.imageNamed('mustache')
55
+ mustacheView.contentMode = UIViewContentModeScaleAspectFit
56
+
57
+ # Compute its location and size, based on the position of the eyes and
58
+ # mouth.
59
+ w = feature.bounds.size.width
60
+ h = feature.bounds.size.height / 5
61
+ x = (feature.mouthPosition.x + (feature.leftEyePosition.x + feature.rightEyePosition.x) / 2) / 2 - w / 2
62
+ y = feature.mouthPosition.y
63
+ mustacheView.frame = CGRectApplyAffineTransform([[x, y], [w, h]], transform)
64
+
65
+ # Apply a rotation on the mustache, based on the face inclination.
66
+ mustacheAngle = Math.atan2(feature.leftEyePosition.x - feature.rightEyePosition.x, feature.leftEyePosition.y - feature.rightEyePosition.y) + Math::PI/2
67
+ mustacheView.transform = CGAffineTransformMakeRotation(mustacheAngle)
68
+
69
+ view.addSubview(mustacheView)
70
+ end
71
+ end
72
+
73
+ def shouldAutorotateToInterfaceOrientation(*)
74
+ mustachify
75
+ true
76
+ end
77
+
78
+ def swipePreviousGesture(gesture)
79
+ idx = @images.index(view.image)
80
+ view.image =
81
+ if idx == 0
82
+ @images.last
83
+ else
84
+ @images[idx - 1]
85
+ end
86
+ mustachify
87
+ end
88
+
89
+ def swipeNextGesture(gesture)
90
+ idx = @images.index(view.image)
91
+ view.image =
92
+ if idx == @images.size - 1
93
+ @images.first
94
+ else
95
+ @images[idx + 1]
96
+ end
97
+ mustachify
98
+ end
99
+ end
@@ -0,0 +1,3 @@
1
+ Paint.app
2
+
3
+ This sample demonstrates the following concepts: Custom View Drawing, Bezier Paths, Gestures, Audio Player.
@@ -0,0 +1,8 @@
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 = 'Paint'
7
+ app.frameworks << 'AVFoundation'
8
+ end
@@ -0,0 +1,9 @@
1
+ class AppDelegate
2
+ def application(application, didFinishLaunchingWithOptions:launchOptions)
3
+ @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
4
+ @window.rootViewController = PaintViewController.alloc.init
5
+ @window.rootViewController.wantsFullScreenLayout = true
6
+ @window.makeKeyAndVisible
7
+ true
8
+ end
9
+ end
@@ -0,0 +1,57 @@
1
+ class PaintView < UIView
2
+ def initWithFrame(ect)
3
+ if super
4
+ path = NSBundle.mainBundle.pathForResource('erase', ofType:'caf')
5
+ url = NSURL.fileURLWithPath(path)
6
+ error_ptr = Pointer.new(:id)
7
+ @eraseSound = AVAudioPlayer.alloc.initWithContentsOfURL(url,
8
+ error:error_ptr)
9
+ unless @eraseSound
10
+ raise "Can't open sound file: #{error_ptr[0].description}"
11
+ end
12
+ @paths = []
13
+ end
14
+ self
15
+ end
16
+
17
+ def drawRect(rect)
18
+ UIColor.blackColor.set
19
+ UIBezierPath.bezierPathWithRect(rect).fill
20
+ @paths.each do |path, color|
21
+ color.set
22
+ path.stroke
23
+ end
24
+ end
25
+
26
+ def touchesBegan(touches, withEvent:event)
27
+ bp = UIBezierPath.alloc.init
28
+ bp.lineWidth = 3.0
29
+ random_color = begin
30
+ red, green, blue = rand(100)/100.0, rand(100)/100.0, rand(100)/100.0
31
+ UIColor.alloc.initWithRed(red/100.0, green:green, blue:blue, alpha:1.0)
32
+ end
33
+ @paths << [bp, random_color]
34
+ end
35
+
36
+ def touchesMoved(touches, withEvent:event)
37
+ touch = event.touchesForView(self).anyObject
38
+ point = touch.locationInView(self)
39
+ if @previousPoint and !@paths.empty?
40
+ bp = @paths.last.first
41
+ bp.moveToPoint(@previousPoint)
42
+ bp.addLineToPoint(point)
43
+ end
44
+ @previousPoint = point
45
+ setNeedsDisplay
46
+ end
47
+
48
+ def touchesEnded(touches, withEvent:event)
49
+ @previousPoint = nil
50
+ end
51
+
52
+ def eraseContent
53
+ @paths.clear
54
+ @eraseSound.play
55
+ setNeedsDisplay
56
+ end
57
+ end
@@ -0,0 +1,13 @@
1
+ class PaintViewController < UIViewController
2
+ def loadView
3
+ self.view = PaintView.alloc.init
4
+ end
5
+
6
+ def canBecomeFirstResponder
7
+ true
8
+ end
9
+
10
+ def motionEnded(motion, withEvent:event)
11
+ self.view.eraseContent if motion == UIEventSubtypeMotionShake
12
+ end
13
+ end