rubymotionr 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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