diff --git a/sample/trollify/README b/sample/trollify/README new file mode 100644 index 00000000..ae3e6e13 --- /dev/null +++ b/sample/trollify/README @@ -0,0 +1,2 @@ +This example was contributed by Johannes Fahrenkrug, http://springenwerk.com +The app/uiimage_extension.rb file is derived from code by Hardy Macia, Catamount Software. diff --git a/sample/trollify/Rakefile b/sample/trollify/Rakefile new file mode 100644 index 00000000..0d349445 --- /dev/null +++ b/sample/trollify/Rakefile @@ -0,0 +1,9 @@ +$:.unshift("/Users/lrz/src/rubixir/lib") +require 'motion/project' + +Motion::Project::App.setup do |app| + # Use `rake config' to see complete project settings. + app.name = 'trollify' + app.frameworks += ['QuartzCore', 'AssetsLibrary', 'MobileCoreServices'] + app.icons = ['icon.png', 'icon@2x.png'] +end diff --git a/sample/trollify/app/app_delegate.rb b/sample/trollify/app/app_delegate.rb new file mode 100644 index 00000000..26463cef --- /dev/null +++ b/sample/trollify/app/app_delegate.rb @@ -0,0 +1,9 @@ +class AppDelegate + def application(application, didFinishLaunchingWithOptions:launchOptions) + @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds) + @window.rootViewController = UINavigationController.alloc.initWithRootViewController(ImageCompositionController.alloc.init) + @window.rootViewController.wantsFullScreenLayout = true + @window.makeKeyAndVisible + return true + end +end diff --git a/sample/trollify/app/image_composition_controller.rb b/sample/trollify/app/image_composition_controller.rb new file mode 100644 index 00000000..13b8550e --- /dev/null +++ b/sample/trollify/app/image_composition_controller.rb @@ -0,0 +1,193 @@ +class ImageCompositionController < UIViewController + def loadView + self.view = UIImageView.alloc.init + view.contentMode = UIViewContentModeScaleAspectFill + view.userInteractionEnabled = true + + view.image = UIImage.imageNamed("ballmer.jpg") + view.addSubview(troll_image_view) + + rotationGesture = UIRotationGestureRecognizer.alloc.initWithTarget(self, action: 'rotate_image:') + @troll_image_view.addGestureRecognizer(rotationGesture) + + panGesture = UIPanGestureRecognizer.alloc.initWithTarget(self, action: 'pan_image:') + panGesture.maximumNumberOfTouches = 2 + panGesture.delegate = self + @troll_image_view.addGestureRecognizer(panGesture) + + pinchGesture = UIPinchGestureRecognizer.alloc.initWithTarget(self, action:'scale_image:') + pinchGesture.delegate = self + @troll_image_view.addGestureRecognizer(pinchGesture) + + navigationItem.leftBarButtonItem = UIBarButtonItem.alloc.initWithTitle("Pick", style:UIBarButtonItemStylePlain, target:self, action:'show_source_sheet') + navigationItem.rightBarButtonItem = UIBarButtonItem.alloc.initWithTitle("Save", style:UIBarButtonItemStylePlain, target:self, action:'save_image') + end + + def viewWillAppear(animated) + navigationController.setNavigationBarHidden(false, animated:true) + troll_image_view.center = CGPointMake(CGRectGetMidX(self.view.bounds), CGRectGetMidY(self.view.bounds)) + end + + def troll_image_view + @troll_image_view ||= begin + image = UIImageView.alloc.init + image.image = UIImage.imageNamed("trollface.png") + image.frame = CGRectMake(0, 0, image.image.size.width, image.image.size.height) + image.userInteractionEnabled = true + + # Set initial scale of the trollface to a quarter. + image.transform = CGAffineTransformMakeScale(0.25, 0.25) + image + end + end + + # Scale and rotation transforms are applied relative to the layer's anchor point. + # This method moves a gesture recognizer's view's anchor point between the user's fingers. + def adjust_anchor_point_for_gesture_recognizer(gestureRecognizer) + if gestureRecognizer.state == UIGestureRecognizerStateBegan + locationInView = gestureRecognizer.locationInView(troll_image_view) + locationInSuperview = gestureRecognizer.locationInView(troll_image_view.superview) + + troll_image_view.layer.anchorPoint = CGPointMake(locationInView.x / troll_image_view.bounds.size.width, locationInView.y / troll_image_view.bounds.size.height) + troll_image_view.center = locationInSuperview + end + end + + def rotate_image(gestureRecognizer) + adjust_anchor_point_for_gesture_recognizer(gestureRecognizer) + if gestureRecognizer.state == UIGestureRecognizerStateBegan || gestureRecognizer.state == UIGestureRecognizerStateChanged + gestureRecognizer.view.transform = CGAffineTransformRotate(gestureRecognizer.view.transform, gestureRecognizer.rotation) + gestureRecognizer.rotation = 0 + end + end + + def pan_image(gestureRecognizer) + adjust_anchor_point_for_gesture_recognizer(gestureRecognizer) + if gestureRecognizer.state == UIGestureRecognizerStateBegan || gestureRecognizer.state == UIGestureRecognizerStateChanged + translation = gestureRecognizer.translationInView(troll_image_view.superview) + troll_image_view.center = CGPointMake(troll_image_view.center.x + translation.x, troll_image_view.center.y + translation.y) + gestureRecognizer.setTranslation(CGPointZero, inView:troll_image_view.superview) + end + end + + def scale_image(gestureRecognizer) + adjust_anchor_point_for_gesture_recognizer(gestureRecognizer) + if gestureRecognizer.state == UIGestureRecognizerStateBegan || gestureRecognizer.state == UIGestureRecognizerStateChanged + gestureRecognizer.view.transform = CGAffineTransformScale(gestureRecognizer.view.transform, gestureRecognizer.scale, gestureRecognizer.scale); + gestureRecognizer.scale = 1 + end + end + + def gestureRecognizer(gestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer:otherGestureRecognizer) + true + end + + def show_source_sheet + popupQuery = UIActionSheet.alloc.initWithTitle("", delegate:self, cancelButtonTitle:'Cancel', destructiveButtonTitle:nil, otherButtonTitles:"Choose Existing", "Take Picture", nil) + popupQuery.delegate = self + popupQuery.actionSheetStyle = UIActionSheetStyleBlackOpaque + popupQuery.showInView(view) + end + + def actionSheet(actionSheet, clickedButtonAtIndex:buttonIndex) + case buttonIndex + when 0 + pick_image + when 1 + take_image + when 2 + # cancelled + end + end + + def pick_image + pick_image_with_source(UIImagePickerControllerSourceTypePhotoLibrary) + end + + def take_image + pick_image_with_source(UIImagePickerControllerSourceTypeCamera) + end + + def pick_image_with_source(source_type) + # Create and show the image picker. + imagePicker = UIImagePickerController.alloc.init + imagePicker.sourceType = source_type + imagePicker.mediaTypes = [KUTTypeImage] + imagePicker.delegate = self + imagePicker.allowsImageEditing = false + presentModalViewController(imagePicker, animated:true) + end + + # UIImagePickerControllerDelegate methods + + def imagePickerControllerDidCancel(picker) + dismissModalViewControllerAnimated(true) + end + + def imagePickerController(picker, didFinishPickingMediaWithInfo:info) + mediaType = info[UIImagePickerControllerMediaType] + if mediaType == KUTTypeImage + editedImage = info[UIImagePickerControllerEditedImage] + originalImage = info[UIImagePickerControllerOriginalImage] + view.image = editedImage || originalImage + end + dismissModalViewControllerAnimated(true) + end + + def save_image + image = view.image + + troll_image_transform = @troll_image_view.transform; + trollfaceRotation = Math.atan2(troll_image_transform.b, troll_image_transform.a); + trollfaceScaleX = Math.sqrt(troll_image_transform.a**2 + troll_image_transform.c**2); + trollfaceScaleY = Math.sqrt(troll_image_transform.b**2 + troll_image_transform.d**2); + + # We are displaying the image in AspectFill mode. This will give us the scale. + scale = [image.size.width/view.frame.size.width, image.size.height/view.frame.size.height].min + + targetWidth = image.size.width; + targetHeight = image.size.height; + + # Image Rect and Image Context in the size of the image. + imageRect = CGRectMake(0, 0, image.size.width, image.size.height); + + UIGraphicsBeginImageContextWithOptions(CGSizeMake(targetWidth, targetHeight), true, UIScreen.mainScreen.scale); + c = UIGraphicsGetCurrentContext(); + image.drawInRect(imageRect) + + # Potential landscape rotation. + CGContextSaveGState(c) + + # Get the (possibly rotated) image. + trollface_image = @troll_image_view.image + if trollfaceRotation != 0 + trollface_image = @troll_image_view.image.imageRotatedByRadians(trollfaceRotation) + end + + # Move the context down and left (or either or) to match where the trollface view was on the screen + CGContextTranslateCTM(c, (image.size.width-view.frame.size.width*scale)/2, (image.size.height-view.frame.size.height*scale)/2) + # Scale the coordinate context, + CGContextScaleCTM(c, scale, scale) + + # Draw the trollface image, correctly scaled. + trollface_image.drawInRect(CGRectMake(@troll_image_view.frame.origin.x, @troll_image_view.frame.origin.y, trollface_image.size.width * trollfaceScaleX, trollface_image.size.height * trollfaceScaleY)) + + CGContextRestoreGState(c) + + newImage = UIGraphicsGetImageFromCurrentImageContext() # UIImage returned + + UIGraphicsEndImageContext() + + # Save to camera roll. + library = ALAssetsLibrary.alloc.init + library.writeImageToSavedPhotosAlbum(newImage.CGImage, orientation:ALAssetOrientationUp, completionBlock: lambda do |assetURL, error| + if error + alert = UIAlertView.alloc.init + alert.title = "Error When Saving Picture" + alert.message = error.localizedDescription + alert.addButtonWithTitle('OK') + alert.show + end + end) + end +end diff --git a/sample/trollify/app/uiimage_extension.rb b/sample/trollify/app/uiimage_extension.rb new file mode 100644 index 00000000..cb148886 --- /dev/null +++ b/sample/trollify/app/uiimage_extension.rb @@ -0,0 +1,27 @@ +class UIImage + def imageRotatedByRadians(radians) + # Calculate the size of the rotated view's containing box for our drawing space. + rotatedViewBox = UIView.alloc.initWithFrame(CGRectMake(0,0,self.size.width, self.size.height)) + t = CGAffineTransformMakeRotation(radians) + rotatedViewBox.transform = t + rotatedSize = rotatedViewBox.frame.size + + # Create the bitmap context. + UIGraphicsBeginImageContext(rotatedSize) + bitmap = UIGraphicsGetCurrentContext() + + # Move the origin to the middle of the image so we will rotate and scale around the center. + CGContextTranslateCTM(bitmap, rotatedSize.width/2, rotatedSize.height/2) + + # Rotate the image context. + CGContextRotateCTM(bitmap, radians) + + # Now, draw the rotated/scaled image into the context. + CGContextScaleCTM(bitmap, 1.0, -1.0) + CGContextDrawImage(bitmap, CGRectMake(-self.size.width / 2, -self.size.height / 2, self.size.width, self.size.height), self.CGImage) + + newImage = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + newImage + end +end diff --git a/sample/trollify/resources/ballmer.jpg b/sample/trollify/resources/ballmer.jpg new file mode 100644 index 00000000..811fc0b9 Binary files /dev/null and b/sample/trollify/resources/ballmer.jpg differ diff --git a/sample/trollify/resources/icon.png b/sample/trollify/resources/icon.png new file mode 100644 index 00000000..b9a64839 Binary files /dev/null and b/sample/trollify/resources/icon.png differ diff --git a/sample/trollify/resources/icon@2x.png b/sample/trollify/resources/icon@2x.png new file mode 100644 index 00000000..9c214f86 Binary files /dev/null and b/sample/trollify/resources/icon@2x.png differ diff --git a/sample/trollify/resources/trollface.png b/sample/trollify/resources/trollface.png new file mode 100644 index 00000000..5749a8f3 Binary files /dev/null and b/sample/trollify/resources/trollface.png differ diff --git a/sample/trollify/spec/main_spec.rb b/sample/trollify/spec/main_spec.rb new file mode 100644 index 00000000..4a378d29 --- /dev/null +++ b/sample/trollify/spec/main_spec.rb @@ -0,0 +1,9 @@ +describe "Application 'trollify'" do + before do + @app = UIApplication.sharedApplication + end + + it "has one window" do + @app.windows.size.should == 1 + end +end