From cd4333272dcdc76dcdb5e810c7b628d97cabcf91 Mon Sep 17 00:00:00 2001 From: Siqi LIU Date: Sat, 21 Sep 2013 12:36:55 +0200 Subject: fix iOS7 storyboard issues&popover position Change-Id: I6a3e03956b3616d2d43396843d852c5591423c9d --- .../Controllers/IASKAppSettingsViewController.m | 2 +- ios/iosremote/PopoverView/PopoverView.h | 208 ++++ ios/iosremote/PopoverView/PopoverView.m | 1055 ++++++++++++++++ .../PopoverView/PopoverViewCompatibility.h | 29 + .../PopoverView/PopoverView_Configuration.h | 128 ++ .../SWRevealViewController.h | 180 +++ .../SWRevealViewController.m | 1255 ++++++++++++++++++++ ios/iosremote/en.lproj/iPad_autosize.storyboard | 159 +-- .../en.lproj/iPad_autosize_old.storyboard | 177 +-- ios/iosremote/fr.lproj/iPad_autosize.storyboard | 161 +-- .../fr.lproj/iPad_autosize_old.storyboard | 165 +-- ios/iosremote/iosremote.xcodeproj/project.pbxproj | 14 +- .../AutoDismissKeyboardNavigationViewController.h | 14 + .../AutoDismissKeyboardNavigationViewController.m | 43 + .../iosremote/Categories/NSString+Base64.h | 16 + .../iosremote/Categories/NSString+Base64.m | 23 + .../iosremote/Categories/UIBarButtonItem+Theme.h | 15 + .../iosremote/Categories/UIBarButtonItem+Theme.m | 47 + .../iosremote/Categories/UIImage+Resize.m | 186 +++ .../Categories/UIImageView+setImageAnimated.h | 16 + .../Categories/UIImageView+setImageAnimated.m | 30 + .../Categories/UINavigationController+Theme.h | 13 + .../Categories/UINavigationController+Theme.m | 22 + .../iosremote/Categories/UIView+FadeInFadeOut.h | 13 + .../iosremote/Categories/UIView+FadeInFadeOut.m | 33 + .../iosremote/Categories/UIView+Shadowing.h | 16 + .../iosremote/Categories/UIView+Shadowing.m | 34 + .../Categories/UIViewController+LibOStyling.h | 28 + .../Categories/UIViewController+LibOStyling.m | 72 ++ .../iosremote/Classes/AboutViewController.h | 15 + .../iosremote/Classes/AboutViewController.m | 48 + ios/iosremote/iosremote/Classes/AppDelegate.h | 21 + ios/iosremote/iosremote/Classes/AppDelegate.m | 107 ++ ios/iosremote/iosremote/Classes/AppDelegate~ipad.h | 16 + ios/iosremote/iosremote/Classes/AppDelegate~ipad.m | 16 + .../Classes/BasePresentationViewController.h | 58 + .../Classes/BasePresentationViewController.m | 582 +++++++++ ios/iosremote/iosremote/Classes/ControlVariables.h | 53 + .../iosremote/Classes/EditableTableViewCell.h | 14 + .../iosremote/Classes/EditableTableViewCell.m | 55 + .../iosremote/Classes/HorizontalSlideCell.h | 16 + .../iosremote/Classes/HorizontalSlideCell.m | 64 + .../iosremote/Classes/LibONavigationController.h | 13 + .../iosremote/Classes/LibONavigationController.m | 44 + .../iosremote/Classes/MainSplitViewController.h | 17 + .../iosremote/Classes/MainSplitViewController.m | 79 ++ .../iosremote/Classes/NewServerViewController.h | 42 + .../iosremote/Classes/NewServerViewController.m | 253 ++++ .../Classes/PinValidationViewController.h | 16 + .../Classes/PinValidationViewController.m | 77 ++ .../iosremote/Classes/SWReavealMainController.h | 13 + .../iosremote/Classes/SWReavealMainController.m | 38 + .../iosremote/Classes/ServerListViewController.h | 20 + .../iosremote/Classes/ServerListViewController.m | 542 +++++++++ .../Classes/SlideShowPreviewViewController.h | 30 + .../Classes/SlideShowPreviewViewController.m | 107 ++ .../Classes/SlideShowPreviewViewController~ipad.h | 21 + .../Classes/SlideShowPreviewViewController~ipad.m | 86 ++ .../SlideShowPreviewViewController~iphone.h | 14 + .../SlideShowPreviewViewController~iphone.m | 58 + .../iosremote/Classes/SlideShowSwipeInList~ipad.h | 13 + .../iosremote/Classes/SlideShowSwipeInList~ipad.m | 155 +++ .../Classes/SlideShowSwipeInList~iphone.h | 18 + .../Classes/SlideShowSwipeInList~iphone.m | 307 +++++ .../iosremote/Classes/SlideShowViewController.h | 22 + .../iosremote/Classes/SlideShowViewController.m | 82 ++ .../Classes/SlideShowViewController~iphone.h | 30 + .../Classes/SlideShowViewController~iphone.m | 311 +++++ ios/iosremote/iosremote/Classes/StopWatch.h | 38 + ios/iosremote/iosremote/Classes/StopWatch.m | 175 +++ ios/iosremote/iosremote/Classes/Timer.h | 46 + ios/iosremote/iosremote/Classes/Timer.m | 230 ++++ .../Classes/WalkThroughContainerViewController.h | 15 + .../Classes/WalkThroughContainerViewController.m | 109 ++ .../Classes/WalkThroughPageViewController.h | 22 + .../Classes/WalkThroughPageViewController.m | 87 ++ ios/iosremote/iosremote/Classes/slideShow_vc.m | 146 +++ .../iosremote/Ressources/WTConnectionHotspot.png | Bin 0 -> 33674 bytes ios/iosremote/iosremote/Ressources/WTPairing.png | Bin 0 -> 71206 bytes .../iosremote/Ressources/WTPairingScreenshot.png | Bin 0 -> 81702 bytes .../iosremote/Ressources/WTconnecting.png | Bin 0 -> 41955 bytes .../iosremote/Ressources/WTconnection.png | Bin 0 -> 24521 bytes .../iosremote/Ressources/WTcontrol~ipad.png | Bin 0 -> 146037 bytes .../iosremote/Ressources/WTcontrol~iphone.png | Bin 0 -> 253574 bytes ios/iosremote/iosremote/Ressources/add.png | Bin 0 -> 2990 bytes ios/iosremote/iosremote/Ressources/add@2x.png | Bin 0 -> 2912 bytes ios/iosremote/iosremote/Ressources/backButton.png | Bin 0 -> 3118 bytes .../iosremote/Ressources/backButton@2x.png | Bin 0 -> 3574 bytes .../iosremote/Ressources/buttonBackground.png | Bin 0 -> 2975 bytes .../iosremote/Ressources/buttonBackground@2x.png | Bin 0 -> 3199 bytes .../iosremote/Ressources/forwardButton.png | Bin 0 -> 3091 bytes .../iosremote/Ressources/forwardButton@2x.png | Bin 0 -> 3439 bytes .../iosremote/Ressources/gear_transparent_bg.png | Bin 0 -> 3337 bytes .../Ressources/gear_transparent_bg@2x.png | Bin 0 -> 3866 bytes ios/iosremote/iosremote/Ressources/impress.jpeg | Bin 0 -> 7476 bytes .../iosremote/Ressources/iosremote_ipad_icon.png | Bin 0 -> 6105 bytes .../Ressources/iosremote_ipad_icon@2x.png | Bin 0 -> 6857 bytes .../iosremote/Ressources/iosremote_iphone_icon.png | Bin 0 -> 5552 bytes .../Ressources/iosremote_iphone_icon@2x.png | Bin 0 -> 8878 bytes ios/iosremote/iosremote/Ressources/more_icon.png | Bin 0 -> 3103 bytes .../iosremote/Ressources/more_icon@2x.png | Bin 0 -> 3135 bytes ios/iosremote/iosremote/Ressources/more_ipad.png | Bin 0 -> 3251 bytes .../iosremote/Ressources/more_ipad@2x.png | Bin 0 -> 3705 bytes .../iosremote/Ressources/navBarBackground.png | Bin 0 -> 2933 bytes .../iosremote/Ressources/navBarBackground@2x.png | Bin 0 -> 3488 bytes .../iosremote/Ressources/navBarButtonNormal@2x.png | Bin 0 -> 2843 bytes .../iosremote/Ressources/nextButton_normal.png | Bin 0 -> 3600 bytes .../iosremote/Ressources/nextButton_normal@2x.png | Bin 0 -> 4704 bytes .../iosremote/Ressources/nextButton_pressed.png | Bin 0 -> 3487 bytes .../iosremote/Ressources/nextButton_pressed@2x.png | Bin 0 -> 4454 bytes ios/iosremote/iosremote/Ressources/pointer.png | Bin 0 -> 4866 bytes .../iosremote/Ressources/pointer_pushed.png | Bin 0 -> 4859 bytes .../iosremote/Ressources/previousButton_normal.png | Bin 0 -> 3592 bytes .../Ressources/previousButton_normal@2x.png | Bin 0 -> 4655 bytes .../Ressources/previousButton_pressed.png | Bin 0 -> 3482 bytes .../Ressources/previousButton_pressed@2x.png | Bin 0 -> 4454 bytes .../iosremote/Ressources/pushed_next_button.png | Bin 0 -> 6574 bytes .../Ressources/pushed_previous_button.png | Bin 0 -> 6687 bytes .../iosremote/Ressources/released_next_button.png | Bin 0 -> 6592 bytes .../Ressources/released_previous_button.png | Bin 0 -> 6705 bytes .../iosremote/Ressources/slide_finished.png | Bin 0 -> 11049 bytes .../iosremote/Ressources/timer_clear_btn.png | Bin 0 -> 3521 bytes .../iosremote/Ressources/timer_clear_btn@2x.png | Bin 0 -> 4442 bytes .../iosremote/Ressources/timer_clear_btn_dark.png | Bin 0 -> 3502 bytes .../iosremote/Ressources/timer_pause_btn.png | Bin 0 -> 2884 bytes .../iosremote/Ressources/timer_pause_btn@2x.png | Bin 0 -> 3002 bytes .../iosremote/Ressources/timer_pause_btn_dark.png | Bin 0 -> 2894 bytes .../Ressources/timer_pause_btn_dark@2x.png | Bin 0 -> 3018 bytes .../iosremote/Ressources/timer_settime_btn.png | Bin 0 -> 3897 bytes .../iosremote/Ressources/timer_settime_btn@2x.png | Bin 0 -> 5148 bytes .../Ressources/timer_settime_btn_dark.png | Bin 0 -> 4066 bytes .../Ressources/timer_settime_btn_dark@2x.png | Bin 0 -> 5310 bytes .../iosremote/Ressources/timer_start_btn.png | Bin 0 -> 3139 bytes .../iosremote/Ressources/timer_start_btn@2x.png | Bin 0 -> 3728 bytes .../iosremote/Ressources/timer_start_btn_dark.png | Bin 0 -> 3191 bytes .../Ressources/timer_start_btn_dark@2x.png | Bin 0 -> 3750 bytes .../Views/WalkThroughPageViewController.xib | 470 ++++++++ .../WalkThroughPageViewMainImageController.xib | 401 +++++++ .../iosremote/en.lproj/iPhone_autoSize.storyboard | 132 +- .../en.lproj/iPhone_autoSize_old.storyboard | 132 +- .../iosremote/fr.lproj/iPhone_autoSize.storyboard | 130 +- .../fr.lproj/iPhone_autoSize_old.storyboard | 130 +- .../zh-Hans.lproj/iPhone_autoSize.storyboard | 130 +- .../zh-Hans.lproj/iPhone_autoSize_old.storyboard | 130 +- .../zh-Hans.lproj/iPad_autosize.storyboard | 161 +-- .../zh-Hans.lproj/iPad_autosize_old.storyboard | 165 +-- 146 files changed, 9193 insertions(+), 1283 deletions(-) create mode 100755 ios/iosremote/PopoverView/PopoverView.h create mode 100755 ios/iosremote/PopoverView/PopoverView.m create mode 100755 ios/iosremote/PopoverView/PopoverViewCompatibility.h create mode 100755 ios/iosremote/PopoverView/PopoverView_Configuration.h create mode 100755 ios/iosremote/SWRevealViewController/SWRevealViewController.h create mode 100755 ios/iosremote/SWRevealViewController/SWRevealViewController.m create mode 100644 ios/iosremote/iosremote/Categories/AutoDismissKeyboardNavigationViewController.h create mode 100644 ios/iosremote/iosremote/Categories/AutoDismissKeyboardNavigationViewController.m create mode 100644 ios/iosremote/iosremote/Categories/NSString+Base64.h create mode 100644 ios/iosremote/iosremote/Categories/NSString+Base64.m create mode 100644 ios/iosremote/iosremote/Categories/UIBarButtonItem+Theme.h create mode 100644 ios/iosremote/iosremote/Categories/UIBarButtonItem+Theme.m create mode 100644 ios/iosremote/iosremote/Categories/UIImage+Resize.m create mode 100644 ios/iosremote/iosremote/Categories/UIImageView+setImageAnimated.h create mode 100644 ios/iosremote/iosremote/Categories/UIImageView+setImageAnimated.m create mode 100644 ios/iosremote/iosremote/Categories/UINavigationController+Theme.h create mode 100644 ios/iosremote/iosremote/Categories/UINavigationController+Theme.m create mode 100644 ios/iosremote/iosremote/Categories/UIView+FadeInFadeOut.h create mode 100644 ios/iosremote/iosremote/Categories/UIView+FadeInFadeOut.m create mode 100644 ios/iosremote/iosremote/Categories/UIView+Shadowing.h create mode 100644 ios/iosremote/iosremote/Categories/UIView+Shadowing.m create mode 100644 ios/iosremote/iosremote/Categories/UIViewController+LibOStyling.h create mode 100644 ios/iosremote/iosremote/Categories/UIViewController+LibOStyling.m create mode 100644 ios/iosremote/iosremote/Classes/AboutViewController.h create mode 100644 ios/iosremote/iosremote/Classes/AboutViewController.m create mode 100644 ios/iosremote/iosremote/Classes/AppDelegate.h create mode 100644 ios/iosremote/iosremote/Classes/AppDelegate.m create mode 100644 ios/iosremote/iosremote/Classes/AppDelegate~ipad.h create mode 100644 ios/iosremote/iosremote/Classes/AppDelegate~ipad.m create mode 100644 ios/iosremote/iosremote/Classes/BasePresentationViewController.h create mode 100644 ios/iosremote/iosremote/Classes/BasePresentationViewController.m create mode 100644 ios/iosremote/iosremote/Classes/ControlVariables.h create mode 100644 ios/iosremote/iosremote/Classes/EditableTableViewCell.h create mode 100644 ios/iosremote/iosremote/Classes/EditableTableViewCell.m create mode 100644 ios/iosremote/iosremote/Classes/HorizontalSlideCell.h create mode 100644 ios/iosremote/iosremote/Classes/HorizontalSlideCell.m create mode 100644 ios/iosremote/iosremote/Classes/LibONavigationController.h create mode 100644 ios/iosremote/iosremote/Classes/LibONavigationController.m create mode 100644 ios/iosremote/iosremote/Classes/MainSplitViewController.h create mode 100644 ios/iosremote/iosremote/Classes/MainSplitViewController.m create mode 100644 ios/iosremote/iosremote/Classes/NewServerViewController.h create mode 100644 ios/iosremote/iosremote/Classes/NewServerViewController.m create mode 100644 ios/iosremote/iosremote/Classes/PinValidationViewController.h create mode 100644 ios/iosremote/iosremote/Classes/PinValidationViewController.m create mode 100644 ios/iosremote/iosremote/Classes/SWReavealMainController.h create mode 100644 ios/iosremote/iosremote/Classes/SWReavealMainController.m create mode 100644 ios/iosremote/iosremote/Classes/ServerListViewController.h create mode 100644 ios/iosremote/iosremote/Classes/ServerListViewController.m create mode 100644 ios/iosremote/iosremote/Classes/SlideShowPreviewViewController.h create mode 100644 ios/iosremote/iosremote/Classes/SlideShowPreviewViewController.m create mode 100644 ios/iosremote/iosremote/Classes/SlideShowPreviewViewController~ipad.h create mode 100644 ios/iosremote/iosremote/Classes/SlideShowPreviewViewController~ipad.m create mode 100644 ios/iosremote/iosremote/Classes/SlideShowPreviewViewController~iphone.h create mode 100644 ios/iosremote/iosremote/Classes/SlideShowPreviewViewController~iphone.m create mode 100644 ios/iosremote/iosremote/Classes/SlideShowSwipeInList~ipad.h create mode 100644 ios/iosremote/iosremote/Classes/SlideShowSwipeInList~ipad.m create mode 100644 ios/iosremote/iosremote/Classes/SlideShowSwipeInList~iphone.h create mode 100644 ios/iosremote/iosremote/Classes/SlideShowSwipeInList~iphone.m create mode 100644 ios/iosremote/iosremote/Classes/SlideShowViewController.h create mode 100644 ios/iosremote/iosremote/Classes/SlideShowViewController.m create mode 100644 ios/iosremote/iosremote/Classes/SlideShowViewController~iphone.h create mode 100644 ios/iosremote/iosremote/Classes/SlideShowViewController~iphone.m create mode 100644 ios/iosremote/iosremote/Classes/StopWatch.h create mode 100644 ios/iosremote/iosremote/Classes/StopWatch.m create mode 100644 ios/iosremote/iosremote/Classes/Timer.h create mode 100644 ios/iosremote/iosremote/Classes/Timer.m create mode 100644 ios/iosremote/iosremote/Classes/WalkThroughContainerViewController.h create mode 100644 ios/iosremote/iosremote/Classes/WalkThroughContainerViewController.m create mode 100644 ios/iosremote/iosremote/Classes/WalkThroughPageViewController.h create mode 100644 ios/iosremote/iosremote/Classes/WalkThroughPageViewController.m create mode 100644 ios/iosremote/iosremote/Classes/slideShow_vc.m create mode 100644 ios/iosremote/iosremote/Ressources/WTConnectionHotspot.png create mode 100644 ios/iosremote/iosremote/Ressources/WTPairing.png create mode 100644 ios/iosremote/iosremote/Ressources/WTPairingScreenshot.png create mode 100644 ios/iosremote/iosremote/Ressources/WTconnecting.png create mode 100644 ios/iosremote/iosremote/Ressources/WTconnection.png create mode 100644 ios/iosremote/iosremote/Ressources/WTcontrol~ipad.png create mode 100644 ios/iosremote/iosremote/Ressources/WTcontrol~iphone.png create mode 100644 ios/iosremote/iosremote/Ressources/add.png create mode 100644 ios/iosremote/iosremote/Ressources/add@2x.png create mode 100644 ios/iosremote/iosremote/Ressources/backButton.png create mode 100644 ios/iosremote/iosremote/Ressources/backButton@2x.png create mode 100644 ios/iosremote/iosremote/Ressources/buttonBackground.png create mode 100644 ios/iosremote/iosremote/Ressources/buttonBackground@2x.png create mode 100644 ios/iosremote/iosremote/Ressources/forwardButton.png create mode 100644 ios/iosremote/iosremote/Ressources/forwardButton@2x.png create mode 100644 ios/iosremote/iosremote/Ressources/gear_transparent_bg.png create mode 100644 ios/iosremote/iosremote/Ressources/gear_transparent_bg@2x.png create mode 100644 ios/iosremote/iosremote/Ressources/impress.jpeg create mode 100644 ios/iosremote/iosremote/Ressources/iosremote_ipad_icon.png create mode 100644 ios/iosremote/iosremote/Ressources/iosremote_ipad_icon@2x.png create mode 100644 ios/iosremote/iosremote/Ressources/iosremote_iphone_icon.png create mode 100644 ios/iosremote/iosremote/Ressources/iosremote_iphone_icon@2x.png create mode 100644 ios/iosremote/iosremote/Ressources/more_icon.png create mode 100644 ios/iosremote/iosremote/Ressources/more_icon@2x.png create mode 100644 ios/iosremote/iosremote/Ressources/more_ipad.png create mode 100644 ios/iosremote/iosremote/Ressources/more_ipad@2x.png create mode 100644 ios/iosremote/iosremote/Ressources/navBarBackground.png create mode 100644 ios/iosremote/iosremote/Ressources/navBarBackground@2x.png create mode 100644 ios/iosremote/iosremote/Ressources/navBarButtonNormal@2x.png create mode 100644 ios/iosremote/iosremote/Ressources/nextButton_normal.png create mode 100644 ios/iosremote/iosremote/Ressources/nextButton_normal@2x.png create mode 100644 ios/iosremote/iosremote/Ressources/nextButton_pressed.png create mode 100644 ios/iosremote/iosremote/Ressources/nextButton_pressed@2x.png create mode 100644 ios/iosremote/iosremote/Ressources/pointer.png create mode 100644 ios/iosremote/iosremote/Ressources/pointer_pushed.png create mode 100644 ios/iosremote/iosremote/Ressources/previousButton_normal.png create mode 100644 ios/iosremote/iosremote/Ressources/previousButton_normal@2x.png create mode 100644 ios/iosremote/iosremote/Ressources/previousButton_pressed.png create mode 100644 ios/iosremote/iosremote/Ressources/previousButton_pressed@2x.png create mode 100644 ios/iosremote/iosremote/Ressources/pushed_next_button.png create mode 100644 ios/iosremote/iosremote/Ressources/pushed_previous_button.png create mode 100644 ios/iosremote/iosremote/Ressources/released_next_button.png create mode 100644 ios/iosremote/iosremote/Ressources/released_previous_button.png create mode 100644 ios/iosremote/iosremote/Ressources/slide_finished.png create mode 100644 ios/iosremote/iosremote/Ressources/timer_clear_btn.png create mode 100644 ios/iosremote/iosremote/Ressources/timer_clear_btn@2x.png create mode 100644 ios/iosremote/iosremote/Ressources/timer_clear_btn_dark.png create mode 100644 ios/iosremote/iosremote/Ressources/timer_pause_btn.png create mode 100644 ios/iosremote/iosremote/Ressources/timer_pause_btn@2x.png create mode 100644 ios/iosremote/iosremote/Ressources/timer_pause_btn_dark.png create mode 100644 ios/iosremote/iosremote/Ressources/timer_pause_btn_dark@2x.png create mode 100644 ios/iosremote/iosremote/Ressources/timer_settime_btn.png create mode 100644 ios/iosremote/iosremote/Ressources/timer_settime_btn@2x.png create mode 100644 ios/iosremote/iosremote/Ressources/timer_settime_btn_dark.png create mode 100644 ios/iosremote/iosremote/Ressources/timer_settime_btn_dark@2x.png create mode 100644 ios/iosremote/iosremote/Ressources/timer_start_btn.png create mode 100644 ios/iosremote/iosremote/Ressources/timer_start_btn@2x.png create mode 100644 ios/iosremote/iosremote/Ressources/timer_start_btn_dark.png create mode 100644 ios/iosremote/iosremote/Ressources/timer_start_btn_dark@2x.png create mode 100644 ios/iosremote/iosremote/Views/WalkThroughPageViewController.xib create mode 100644 ios/iosremote/iosremote/Views/WalkThroughPageViewMainImageController.xib (limited to 'ios') diff --git a/ios/iosremote/InAppSettingsKit/Controllers/IASKAppSettingsViewController.m b/ios/iosremote/InAppSettingsKit/Controllers/IASKAppSettingsViewController.m index 7ae7af0ae8fc..52afa8891a22 100755 --- a/ios/iosremote/InAppSettingsKit/Controllers/IASKAppSettingsViewController.m +++ b/ios/iosremote/InAppSettingsKit/Controllers/IASKAppSettingsViewController.m @@ -358,7 +358,7 @@ CGRect IASKCGRectSwap(CGRect rect); if ((title = [self tableView:tableView titleForHeaderInSection:section])) { CGSize size = [title sizeWithFont:[UIFont boldSystemFontOfSize:[UIFont labelFontSize]] constrainedToSize:CGSizeMake(tableView.frame.size.width - 2*kIASKHorizontalPaddingGroupTitles, INFINITY) - lineBreakMode:UILineBreakModeWordWrap]; + lineBreakMode:NSLineBreakByWordWrapping]; return size.height+kIASKVerticalPaddingGroupTitles; } return 0; diff --git a/ios/iosremote/PopoverView/PopoverView.h b/ios/iosremote/PopoverView/PopoverView.h new file mode 100755 index 000000000000..6766ab1fbccd --- /dev/null +++ b/ios/iosremote/PopoverView/PopoverView.h @@ -0,0 +1,208 @@ +// +// PopoverView +// https://github.com/runway20/PopoverView +// +//(MIT Licensed) +// +//Copyright (c) 2012 Runway 20 Inc. +// +//Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +// +//The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +// +//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +#import +#import "PopoverViewCompatibility.h" + + +/**************** Support both ARC and non-ARC ********************/ + +#ifndef SUPPORT_ARC +#define SUPPORT_ARC + +#if __has_feature(objc_arc_weak) //objc_arc_weak +#define WEAK weak +#define __WEAK __weak +#define STRONG strong + +#define AUTORELEASE self +#define RELEASE self +#define RETAIN self +#define CFTYPECAST(exp) (__bridge exp) +#define TYPECAST(exp) (__bridge_transfer exp) +#define CFRELEASE(exp) CFRelease(exp) +#define DEALLOC self + +#elif __has_feature(objc_arc) //objc_arc +#define WEAK unsafe_unretained +#define __WEAK __unsafe_unretained +#define STRONG strong + +#define AUTORELEASE self +#define RELEASE self +#define RETAIN self +#define CFTYPECAST(exp) (__bridge exp) +#define TYPECAST(exp) (__bridge_transfer exp) +#define CFRELEASE(exp) CFRelease(exp) +#define DEALLOC self + +#else //none +#define WEAK assign +#define __WEAK +#define STRONG retain + +#define AUTORELEASE autorelease +#define RELEASE release +#define RETAIN retain +#define CFTYPECAST(exp) (exp) +#define TYPECAST(exp) (exp) +#define CFRELEASE(exp) CFRelease(exp) +#define DEALLOC dealloc + +#endif +#endif + +/******************************************************************/ + + +@class PopoverView; + +@protocol PopoverViewDelegate + +@optional + +//Delegate receives this call as soon as the item has been selected +- (void)popoverView:(PopoverView *)popoverView didSelectItemAtIndex:(NSInteger)index; + +//Delegate receives this call once the popover has begun the dismissal animation +- (void)popoverViewDidDismiss:(PopoverView *)popoverView; + +@end + +@interface PopoverView : UIView { + CGRect boxFrame; + CGSize contentSize; + CGPoint arrowPoint; + + BOOL above; + + __WEAK id delegate; + + UIView *parentView; + + UIView *topView; + + NSArray *subviewsArray; + + NSArray *dividerRects; + + UIView *contentView; + + UIView *titleView; + + UIActivityIndicatorView *activityIndicator; + + //Instance variable that can change at runtime + BOOL showDividerRects; +} + +@property (nonatomic, STRONG) UIView *titleView; + +@property (nonatomic, STRONG) UIView *contentView; + +@property (nonatomic, STRONG) NSArray *subviewsArray; + +@property (nonatomic, WEAK) id delegate; + +#pragma mark - Class Static Showing Methods + +//These are the main static methods you can use to display the popover. +//Simply call [PopoverView show...] with your arguments, and the popover will be generated, added to the view stack, and notify you when it's done. + ++ (PopoverView *)showPopoverAtPoint:(CGPoint)point inView:(UIView *)view withText:(NSString *)text delegate:(id)delegate; + ++ (PopoverView *)showPopoverAtPoint:(CGPoint)point inView:(UIView *)view withTitle:(NSString *)title withText:(NSString *)text delegate:(id)delegate; + ++ (PopoverView *)showPopoverAtPoint:(CGPoint)point inView:(UIView *)view withViewArray:(NSArray *)viewArray delegate:(id)delegate; + ++ (PopoverView *)showPopoverAtPoint:(CGPoint)point inView:(UIView *)view withTitle:(NSString *)title withViewArray:(NSArray *)viewArray delegate:(id)delegate; + ++ (PopoverView *)showPopoverAtPoint:(CGPoint)point inView:(UIView *)view withStringArray:(NSArray *)stringArray delegate:(id)delegate; + ++ (PopoverView *)showPopoverAtPoint:(CGPoint)point inView:(UIView *)view withTitle:(NSString *)title withStringArray:(NSArray *)stringArray delegate:(id)delegate; + ++ (PopoverView *)showPopoverAtPoint:(CGPoint)point inView:(UIView *)view withStringArray:(NSArray *)stringArray withImageArray:(NSArray *)imageArray delegate:(id)delegate; + ++ (PopoverView *)showPopoverAtPoint:(CGPoint)point inView:(UIView *)view withTitle:(NSString *)title withStringArray:(NSArray *)stringArray withImageArray:(NSArray *)imageArray delegate:(id)delegate; + ++ (PopoverView *)showPopoverAtPoint:(CGPoint)point inView:(UIView *)view withTitle:(NSString *)title withContentView:(UIView *)cView delegate:(id)delegate; + ++ (PopoverView *)showPopoverAtPoint:(CGPoint)point inView:(UIView *)view withContentView:(UIView *)cView delegate:(id)delegate; + +#pragma mark - Instance Showing Methods + +//Adds/animates in the popover to the top of the view stack with the arrow pointing at the "point" +//within the specified view. The contentView will be added to the popover, and should have either +//a clear color backgroundColor, or perhaps a rounded corner bg rect (radius 4.f if you're going to +//round). +- (void)showAtPoint:(CGPoint)point inView:(UIView *)view withContentView:(UIView *)contentView; + +//Calls above method with a UILabel containing the text you deliver to this method. +- (void)showAtPoint:(CGPoint)point inView:(UIView *)view withText:(NSString *)text; + +//Calls top method with an array of UIView objects. This method will stack these views vertically +//with kBoxPadding padding between each view in the y-direction. +- (void)showAtPoint:(CGPoint)point inView:(UIView *)view withViewArray:(NSArray *)viewArray; + +//Does same as above, but adds a title label at top of the popover. +- (void)showAtPoint:(CGPoint)point inView:(UIView *)view withTitle:(NSString *)title withViewArray:(NSArray *)viewArray; + +//Calls the viewArray method with an array of UILabels created with the strings +//in stringArray. All contents of stringArray must be NSStrings. +- (void)showAtPoint:(CGPoint)point inView:(UIView *)view withStringArray:(NSArray *)stringArray; + +//This method does same as above, but with a title label at the top of the popover. +- (void)showAtPoint:(CGPoint)point inView:(UIView *)view withTitle:(NSString *)title withStringArray:(NSArray *)stringArray; + +//Draws a vertical list of the NSString elements of stringArray with UIImages +//from imageArray placed centered above them. +- (void)showAtPoint:(CGPoint)point inView:(UIView *)view withStringArray:(NSArray *)stringArray withImageArray:(NSArray *)imageArray; + +//Does the same as above, but with a title +- (void)showAtPoint:(CGPoint)point inView:(UIView *)view withTitle:(NSString *)title withStringArray:(NSArray *)stringArray withImageArray:(NSArray *)imageArray; + +//Lays out the PopoverView at a point once all of the views have already been setup elsewhere +- (void)layoutAtPoint:(CGPoint)point inView:(UIView *)view; + +#pragma mark - Other Interaction +//This method animates the rotation of the PopoverView to a new point +- (void)animateRotationToNewPoint:(CGPoint)point inView:(UIView *)view withDuration:(NSTimeInterval)duration; + +#pragma mark - Dismissal +//Dismisses the view, and removes it from the view stack. +- (void)dismiss; +- (void)dismiss:(BOOL)animated; + +#pragma mark - Activity Indicator Methods + +//Shows the activity indicator, and changes the title (if the title is available, and is a UILabel). +- (void)showActivityIndicatorWithMessage:(NSString *)msg; + +//Hides the activity indicator, and changes the title (if the title is available) to the msg +- (void)hideActivityIndicatorWithMessage:(NSString *)msg; + +#pragma mark - Custom Image Showing + +//Animate in, and display the image provided here. +- (void)showImage:(UIImage *)image withMessage:(NSString *)msg; + +#pragma mark - Error/Success Methods + +//Shows (and animates in) an error X in the contentView +- (void)showError; + +//Shows (and animates in) a success checkmark in the contentView +- (void)showSuccess; + +@end diff --git a/ios/iosremote/PopoverView/PopoverView.m b/ios/iosremote/PopoverView/PopoverView.m new file mode 100755 index 000000000000..4f810cf72df8 --- /dev/null +++ b/ios/iosremote/PopoverView/PopoverView.m @@ -0,0 +1,1055 @@ +// +// PopoverView +// https://github.com/runway20/PopoverView +// +//(MIT Licensed) +// +//Copyright (c) 2012 Runway 20 Inc. +// +//Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +// +//The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +// +//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +#import "PopoverView.h" +#import "PopoverView_Configuration.h" +#import + +#pragma mark - Implementation + +@implementation PopoverView + +@synthesize subviewsArray; +@synthesize contentView; +@synthesize titleView; +@synthesize delegate; + +#pragma mark - Static Methods + ++ (PopoverView *)showPopoverAtPoint:(CGPoint)point inView:(UIView *)view withText:(NSString *)text delegate:(id)delegate { + PopoverView *popoverView = [[PopoverView alloc] initWithFrame:CGRectZero]; + [popoverView showAtPoint:point inView:view withText:text]; + popoverView.delegate = delegate; + [popoverView RELEASE]; + return popoverView; +} + ++ (PopoverView *)showPopoverAtPoint:(CGPoint)point inView:(UIView *)view withTitle:(NSString *)title withText:(NSString *)text delegate:(id)delegate { + PopoverView *popoverView = [[PopoverView alloc] initWithFrame:CGRectZero]; + [popoverView showAtPoint:point inView:view withTitle:title withText:text]; + popoverView.delegate = delegate; + [popoverView RELEASE]; + return popoverView; +} + ++ (PopoverView *)showPopoverAtPoint:(CGPoint)point inView:(UIView *)view withViewArray:(NSArray *)viewArray delegate:(id)delegate { + PopoverView *popoverView = [[PopoverView alloc] initWithFrame:CGRectZero]; + [popoverView showAtPoint:point inView:view withViewArray:viewArray]; + popoverView.delegate = delegate; + [popoverView RELEASE]; + return popoverView; +} + ++ (PopoverView *)showPopoverAtPoint:(CGPoint)point inView:(UIView *)view withTitle:(NSString *)title withViewArray:(NSArray *)viewArray delegate:(id)delegate { + PopoverView *popoverView = [[PopoverView alloc] initWithFrame:CGRectZero]; + [popoverView showAtPoint:point inView:view withTitle:title withViewArray:viewArray]; + popoverView.delegate = delegate; + [popoverView RELEASE]; + return popoverView; +} + ++ (PopoverView *)showPopoverAtPoint:(CGPoint)point inView:(UIView *)view withStringArray:(NSArray *)stringArray delegate:(id)delegate { + PopoverView *popoverView = [[PopoverView alloc] initWithFrame:CGRectZero]; + [popoverView showAtPoint:point inView:view withStringArray:stringArray]; + popoverView.delegate = delegate; + [popoverView RELEASE]; + return popoverView; +} + ++ (PopoverView *)showPopoverAtPoint:(CGPoint)point inView:(UIView *)view withTitle:(NSString *)title withStringArray:(NSArray *)stringArray delegate:(id)delegate { + PopoverView *popoverView = [[PopoverView alloc] initWithFrame:CGRectZero]; + [popoverView showAtPoint:point inView:view withTitle:title withStringArray:stringArray]; + popoverView.delegate = delegate; + [popoverView RELEASE]; + return popoverView; +} + ++ (PopoverView *)showPopoverAtPoint:(CGPoint)point inView:(UIView *)view withStringArray:(NSArray *)stringArray withImageArray:(NSArray *)imageArray delegate:(id)delegate { + PopoverView *popoverView = [[PopoverView alloc] initWithFrame:CGRectZero]; + [popoverView showAtPoint:point inView:view withStringArray:stringArray withImageArray:imageArray]; + popoverView.delegate = delegate; + [popoverView RELEASE]; + return popoverView; +} + ++ (PopoverView *)showPopoverAtPoint:(CGPoint)point inView:(UIView *)view withTitle:(NSString *)title withStringArray:(NSArray *)stringArray withImageArray:(NSArray *)imageArray delegate:(id)delegate { + PopoverView *popoverView = [[PopoverView alloc] initWithFrame:CGRectZero]; + [popoverView showAtPoint:point inView:view withTitle:title withStringArray:stringArray withImageArray:imageArray]; + popoverView.delegate = delegate; + [popoverView RELEASE]; + return popoverView; +} + ++ (PopoverView *)showPopoverAtPoint:(CGPoint)point inView:(UIView *)view withTitle:(NSString *)title withContentView:(UIView *)cView delegate:(id)delegate { + PopoverView *popoverView = [[PopoverView alloc] initWithFrame:CGRectZero]; + [popoverView showAtPoint:point inView:view withTitle:title withContentView:cView]; + popoverView.delegate = delegate; + [popoverView RELEASE]; + return popoverView; +} + ++ (PopoverView *)showPopoverAtPoint:(CGPoint)point inView:(UIView *)view withContentView:(UIView *)cView delegate:(id)delegate { + PopoverView *popoverView = [[PopoverView alloc] initWithFrame:CGRectZero]; + [popoverView showAtPoint:point inView:view withContentView:cView]; + popoverView.delegate = delegate; + [popoverView RELEASE]; + return popoverView; +} + +#pragma mark - View Lifecycle + +- (id)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + if (self) { + // Initialization code + + self.backgroundColor = [UIColor clearColor]; + + self.titleView = nil; + self.contentView = nil; + + showDividerRects = kShowDividersBetweenViews; + } + return self; +} + +- (void)dealloc +{ + self.subviewsArray = nil; + + if (dividerRects) { + [dividerRects RELEASE]; + dividerRects = nil; + } + + self.contentView = nil; + self.titleView = nil; + + [super DEALLOC]; +} + + + +#pragma mark - Display methods + +// get the screen size, adjusted for orientation and status bar display +// see http://stackoverflow.com/questions/7905432/how-to-get-orientation-dependent-height-and-width-of-the-screen/7905540#7905540 +- (CGSize) screenSize +{ + UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation; + CGSize size = [UIScreen mainScreen].bounds.size; + UIApplication *application = [UIApplication sharedApplication]; + if (UIInterfaceOrientationIsLandscape(orientation)) + { + size = CGSizeMake(size.height, size.width); + } + if (application.statusBarHidden == NO) + { + size.height -= MIN(application.statusBarFrame.size.width, application.statusBarFrame.size.height); + } + return size; +} + +- (void)showAtPoint:(CGPoint)point inView:(UIView *)view withText:(NSString *)text +{ + UIFont *font = UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ? kSmallTextFontPad : kTextFontPhone; + + CGSize screenSize = [self screenSize]; + CGSize textSize = [text sizeWithFont:font constrainedToSize:CGSizeMake(screenSize.width - kHorizontalMargin*4.f, 1000.f) lineBreakMode:UILineBreakModeWordWrap]; + + UILabel *textView = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, textSize.width, textSize.height)]; + textView.backgroundColor = [UIColor clearColor]; + textView.userInteractionEnabled = NO; + [textView setNumberOfLines:0]; //This is so the label word wraps instead of cutting off the text + textView.font = font; + textView.textAlignment = kTextAlignmentLeft; + textView.textColor = kTextColor; + textView.text = text; + + [self showAtPoint:point inView:view withViewArray:[NSArray arrayWithObject:[textView AUTORELEASE]]]; +} + +- (void)showAtPoint:(CGPoint)point inView:(UIView *)view withTitle:(NSString *)title withText:(NSString *)text +{ + UIFont *font = UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ? kTextFontPad : kTextFontPhone; + + CGSize screenSize = [self screenSize]; + CGSize textSize = [text sizeWithFont:font constrainedToSize:CGSizeMake(screenSize.width - kHorizontalMargin*4.f, 1000.f) lineBreakMode:UILineBreakModeWordWrap]; + + UILabel *textView = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, textSize.width, textSize.height)]; + textView.backgroundColor = [UIColor clearColor]; + textView.userInteractionEnabled = NO; + [textView setNumberOfLines:0]; //This is so the label word wraps instead of cutting off the text + textView.font = font; + textView.textAlignment = kTextAlignment; + textView.textColor = kTextColor; + textView.text = text; + + [self showAtPoint:point inView:view withTitle:title withViewArray:[NSArray arrayWithObject:[textView AUTORELEASE]]]; +} + +- (void)showAtPoint:(CGPoint)point inView:(UIView *)view withViewArray:(NSArray *)viewArray +{ + UIView *container = [[UIView alloc] initWithFrame:CGRectZero]; + + float totalHeight = 0.f; + float totalWidth = 0.f; + + int i = 0; + + //Position each view the first time, and identify which view has the largest width that controls + //the sizing of the popover. + for (UIView *view in viewArray) { + + view.frame = CGRectMake(0, totalHeight, view.frame.size.width, view.frame.size.height); + //Only add padding below the view if it's not the last item + float padding = (i == viewArray.count-1) ? 0 : kBoxPadding; + + totalHeight += view.frame.size.height + padding; + + if (view.frame.size.width > totalWidth) { + totalWidth = view.frame.size.width; + } + + [container addSubview:view]; + + i++; + } + + //If dividers are enabled, then we allocate the divider rect array. This will hold NSValues + if (kShowDividersBetweenViews) { + dividerRects = [[NSMutableArray alloc] initWithCapacity:viewArray.count-1]; + } + + container.frame = CGRectMake(0, 0, totalWidth, totalHeight); + + i = 0; + + totalHeight = 0; + + //Now we actually change the frame element for each subview, and center the views horizontally. + for (UIView *view in viewArray) { + if ([view autoresizingMask] == UIViewAutoresizingFlexibleWidth) { + //Now make sure all flexible views are the full width + view.frame = CGRectMake(view.frame.origin.x, view.frame.origin.y, totalWidth, view.frame.size.height); + } else { + //If the view is not flexible width, then we position it centered in the view + //without stretching it. + view.frame = CGRectMake(floorf(CGRectGetMinX(boxFrame) + totalWidth*0.5f - view.frame.size.width*0.5f), view.frame.origin.y, view.frame.size.width, view.frame.size.height); + } + + //and if dividers are enabled, we record their position for the drawing methods + if (kShowDividersBetweenViews && i != viewArray.count-1) { + CGRect dividerRect = CGRectMake(view.frame.origin.x, floorf(view.frame.origin.y + view.frame.size.height + kBoxPadding*0.5f), view.frame.size.width, 0.5f); + + [((NSMutableArray *)dividerRects) addObject:[NSValue valueWithCGRect:dividerRect]]; + } + + //Only add padding below the view if it's not the last item + float padding = (i == viewArray.count-1) ? 0.f : kBoxPadding; + + totalHeight += view.frame.size.height + padding; + + i++; + } + + self.subviewsArray = viewArray; + + [self showAtPoint:point inView:view withContentView:[container AUTORELEASE]]; +} + +- (void)showAtPoint:(CGPoint)point inView:(UIView *)view withTitle:(NSString *)title withViewArray:(NSArray *)viewArray +{ + UIView *container = [[UIView alloc] initWithFrame:CGRectZero]; + + //Create a label for the title text. + CGSize titleSize = [title sizeWithFont: UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ? kTitleFontPad : kTitleFontPhone]; + UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0.f, 0.f, titleSize.width, titleSize.height)]; + titleLabel.backgroundColor = [UIColor clearColor]; + titleLabel.font = UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ? kTitleFontPad : kTitleFontPhone; + titleLabel.textAlignment = UITextAlignmentCenter; + titleLabel.textColor = kTitleColor; + titleLabel.text = title; + + //Make sure that the title's label will have non-zero height. If it has zero height, then we don't allocate any space + //for it in the positioning of the views. + float titleHeightOffset = (titleSize.height > 0.f ? kBoxPadding : 0.f); + + float totalHeight = titleSize.height + titleHeightOffset + kBoxPadding; + float totalWidth = titleSize.width; + + int i = 0; + + //Position each view the first time, and identify which view has the largest width that controls + //the sizing of the popover. + for (UIView *view in viewArray) { + + view.frame = CGRectMake(0, totalHeight, view.frame.size.width, view.frame.size.height); + + //Only add padding below the view if it's not the last item. + float padding = (i == viewArray.count-1) ? 0.f : kBoxPadding; + + totalHeight += view.frame.size.height + padding; + + if (view.frame.size.width > totalWidth) { + totalWidth = view.frame.size.width; + } + + [container addSubview:view]; + + i++; + } + + //If dividers are enabled, then we allocate the divider rect array. This will hold NSValues + if (kShowDividersBetweenViews) { + dividerRects = [[NSMutableArray alloc] initWithCapacity:viewArray.count-1]; + } + + i = 0; + + for (UIView *view in viewArray) { + if ([view autoresizingMask] == UIViewAutoresizingFlexibleWidth) { + //Now make sure all flexible views are the full width + view.frame = CGRectMake(view.frame.origin.x, view.frame.origin.y, totalWidth, view.frame.size.height); + } else { + //If the view is not flexible width, then we position it centered in the view + //without stretching it. + view.frame = CGRectMake(floorf(CGRectGetMinX(boxFrame) + totalWidth*0.5f - view.frame.size.width*0.5f), view.frame.origin.y, view.frame.size.width, view.frame.size.height); + } + + //and if dividers are enabled, we record their position for the drawing methods + if (kShowDividersBetweenViews && i != viewArray.count-1) { + CGRect dividerRect = CGRectMake(view.frame.origin.x, floorf(view.frame.origin.y + view.frame.size.height + kBoxPadding*0.5f), view.frame.size.width, 0.5f); + + [((NSMutableArray *)dividerRects) addObject:[NSValue valueWithCGRect:dividerRect]]; + } + + i++; + } + + titleLabel.frame = CGRectMake(floorf(totalWidth*0.5f - titleSize.width*0.5f), 0, titleSize.width, titleSize.height); + + //Store the titleView as an instance variable if it is larger than 0 height (not an empty string) + if (titleSize.height > 0) { + self.titleView = titleLabel; + } + + [container addSubview:[titleLabel AUTORELEASE]]; + + container.frame = CGRectMake(0, 0, totalWidth, totalHeight); + + self.subviewsArray = viewArray; + + [self showAtPoint:point inView:view withContentView:[container AUTORELEASE]]; +} + +- (void)showAtPoint:(CGPoint)point inView:(UIView *)view withStringArray:(NSArray *)stringArray +{ + NSMutableArray *labelArray = [[NSMutableArray alloc] initWithCapacity:stringArray.count]; + + UIFont *font = UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ? kTextFontPad : kTextFontPhone; + + for (NSString *string in stringArray) { + CGSize textSize = [string sizeWithFont:font]; + UIButton *textButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, textSize.width, textSize.height)]; + textButton.backgroundColor = [UIColor clearColor]; + textButton.titleLabel.font = font; + textButton.titleLabel.textAlignment = kTextAlignment; + textButton.titleLabel.textColor = kTextColor; + [textButton setTitle:string forState:UIControlStateNormal]; + textButton.layer.cornerRadius = 4.f; + [textButton setTitleColor:kTextColor forState:UIControlStateNormal]; + [textButton setTitleColor:kTextHighlightColor forState:UIControlStateHighlighted]; + [textButton addTarget:self action:@selector(didTapButton:) forControlEvents:UIControlEventTouchUpInside]; + + [labelArray addObject:[textButton AUTORELEASE]]; + } + + [self showAtPoint:point inView:view withViewArray:[labelArray AUTORELEASE]]; +} + +- (void)showAtPoint:(CGPoint)point inView:(UIView *)view withTitle:(NSString *)title withStringArray:(NSArray *)stringArray + { + NSMutableArray *labelArray = [[NSMutableArray alloc] initWithCapacity:stringArray.count]; + + UIFont *font = UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ? kTextFontPad : kTextFontPhone; + + for (NSString *string in stringArray) { + CGSize textSize = [string sizeWithFont:font]; + UIButton *textButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, textSize.width, textSize.height)]; + textButton.backgroundColor = [UIColor clearColor]; + textButton.titleLabel.font = font; + textButton.titleLabel.textAlignment = kTextAlignment; + textButton.titleLabel.textColor = kTextColor; + [textButton setTitle:string forState:UIControlStateNormal]; + textButton.layer.cornerRadius = 4.f; + [textButton setTitleColor:kTextColor forState:UIControlStateNormal]; + [textButton setTitleColor:kTextHighlightColor forState:UIControlStateHighlighted]; + [textButton addTarget:self action:@selector(didTapButton:) forControlEvents:UIControlEventTouchUpInside]; + + [labelArray addObject:[textButton AUTORELEASE]]; + } + + [self showAtPoint:point inView:view withTitle:title withViewArray:[labelArray AUTORELEASE]]; +} + +- (void)showAtPoint:(CGPoint)point inView:(UIView *)view withStringArray:(NSArray *)stringArray withImageArray:(NSArray *)imageArray +{ + //Here we do something pretty similar to the stringArray method above. + //We create an array of subviews that contains the strings and images centered above a label. + + NSAssert((stringArray.count == imageArray.count), @"stringArray.count should equal imageArray.count"); + NSMutableArray* tempViewArray = [self makeTempViewsWithStrings:stringArray andImages:imageArray]; + + [self showAtPoint:point inView:view withViewArray:tempViewArray]; +} + +- (void)showAtPoint:(CGPoint)point inView:(UIView *)view withTitle:(NSString *)title withStringArray:(NSArray *)stringArray withImageArray:(NSArray *)imageArray +{ + NSAssert((stringArray.count == imageArray.count), @"stringArray.count should equal imageArray.count"); + NSMutableArray* tempViewArray = [self makeTempViewsWithStrings:stringArray andImages:imageArray]; + + [self showAtPoint:point inView:view withTitle:title withViewArray:tempViewArray]; +} + +- (NSMutableArray*) makeTempViewsWithStrings:(NSArray *)stringArray andImages:(NSArray *)imageArray +{ + NSMutableArray *tempViewArray = [[NSMutableArray alloc] initWithCapacity:stringArray.count]; + + UIFont *font = UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ? kTextFontPad : kTextFontPhone; + + for (int i = 0; i < stringArray.count; i++) { + NSString *string = [stringArray objectAtIndex:i]; + + //First we build a label for the text to set in. + CGSize textSize = [string sizeWithFont:font]; + UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, textSize.width, textSize.height)]; + label.backgroundColor = [UIColor clearColor]; + label.font = font; + label.textAlignment = kTextAlignment; + label.textColor = kTextColor; + label.text = string; + label.layer.cornerRadius = 4.f; + + //Now we grab the image at the same index in the imageArray, and create + //a UIImageView for it. + UIImage *image = [imageArray objectAtIndex:i]; + UIImageView *imageView = [[UIImageView alloc] initWithImage:image]; + + //Take the larger of the two widths as the width for the container + float containerWidth = MAX(imageView.frame.size.width, label.frame.size.width); + float containerHeight = label.frame.size.height + kImageTopPadding + kImageBottomPadding + imageView.frame.size.height; + + //This container will hold both the image and the label + UIView *containerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, containerWidth, containerHeight)]; + + //Now we do the frame manipulations to put the imageView on top of the label, both centered + imageView.frame = CGRectMake(floorf(containerWidth*0.5f - imageView.frame.size.width*0.5f), kImageTopPadding, imageView.frame.size.width, imageView.frame.size.height); + label.frame = CGRectMake(floorf(containerWidth*0.5f - label.frame.size.width*0.5f), imageView.frame.size.height + kImageBottomPadding + kImageTopPadding, label.frame.size.width, label.frame.size.height); + + [containerView addSubview:imageView]; + [containerView addSubview:label]; + + [label RELEASE]; + [imageView RELEASE]; + + [tempViewArray addObject:containerView]; + [containerView RELEASE]; + } + + return [tempViewArray AUTORELEASE]; +} + +- (void)showAtPoint:(CGPoint)point inView:(UIView *)view withTitle:(NSString *)title withContentView:(UIView *)cView +{ + [self showAtPoint:point inView:view withTitle:title withViewArray:[NSArray arrayWithObject:cView]]; +} + +- (void)showAtPoint:(CGPoint)point inView:(UIView *)view withContentView:(UIView *)cView { + + //NSLog(@"point:%f,%f", point.x, point.y); + + self.contentView = cView; + parentView = view; + + // get the top view + // http://stackoverflow.com/questions/3843411/getting-reference-to-the-top-most-view-window-in-ios-application/8045804#8045804 + topView = [[[[UIApplication sharedApplication] keyWindow] subviews] lastObject]; + + [self setupLayout:point inView:view]; + + // Make the view small and transparent before animation + self.alpha = 0.f; + self.transform = CGAffineTransformMakeScale(0.1f, 0.1f); + + // animate into full size + // First stage animates to 1.05x normal size, then second stage animates back down to 1x size. + // This two-stage animation creates a little "pop" on open. + [UIView animateWithDuration:0.2f delay:0.f options:UIViewAnimationOptionCurveEaseInOut animations:^{ + self.alpha = 1.f; + self.transform = CGAffineTransformMakeScale(1.05f, 1.05f); + } completion:^(BOOL finished) { + [UIView animateWithDuration:0.08f delay:0.f options:UIViewAnimationOptionCurveEaseInOut animations:^{ + self.transform = CGAffineTransformIdentity; + } completion:nil]; + }]; +} + +- (void)layoutAtPoint:(CGPoint)point inView:(UIView *)view +{ + // make transparent + self.alpha = 0.f; + + [self setupLayout:point inView:view]; + + // animate back to full opacity + [UIView animateWithDuration:0.2f delay:0.f options:UIViewAnimationOptionCurveEaseInOut animations:^{ + self.alpha = 1.f; + } completion:nil]; +} + +-(void)setupLayout:(CGPoint)point inView:(UIView*)view +{ + CGPoint topPoint = [topView convertPoint:point fromView:view]; + + arrowPoint = topPoint; + + //NSLog(@"arrowPoint:%f,%f", arrowPoint.x, arrowPoint.y); + + CGRect topViewBounds = topView.bounds; + //NSLog(@"topViewBounds %@", NSStringFromCGRect(topViewBounds)); + + float contentHeight = contentView.frame.size.height; + float contentWidth = contentView.frame.size.width; + + float padding = kBoxPadding; + + float boxHeight = contentHeight + 2.f*padding; + float boxWidth = contentWidth + 2.f*padding; + + float xOrigin = 0.f; + + //Make sure the arrow point is within the drawable bounds for the popover. + if (arrowPoint.x + kArrowHeight > topViewBounds.size.width - kHorizontalMargin - kBoxRadius - kArrowHorizontalPadding) {//Too far to the right + arrowPoint.x = topViewBounds.size.width - kHorizontalMargin - kBoxRadius - kArrowHorizontalPadding - kArrowHeight; + //NSLog(@"Correcting Arrow Point because it's too far to the right"); + } else if (arrowPoint.x - kArrowHeight < kHorizontalMargin + kBoxRadius + kArrowHorizontalPadding) {//Too far to the left + arrowPoint.x = kHorizontalMargin + kArrowHeight + kBoxRadius + kArrowHorizontalPadding; + //NSLog(@"Correcting Arrow Point because it's too far to the left"); + } + + //NSLog(@"arrowPoint:%f,%f", arrowPoint.x, arrowPoint.y); + + xOrigin = floorf(arrowPoint.x - boxWidth*0.5f); + + //Check to see if the centered xOrigin value puts the box outside of the normal range. + if (xOrigin < CGRectGetMinX(topViewBounds) + kHorizontalMargin) { + xOrigin = CGRectGetMinX(topViewBounds) + kHorizontalMargin; + } else if (xOrigin + boxWidth > CGRectGetMaxX(topViewBounds) - kHorizontalMargin) { + //Check to see if the positioning puts the box out of the window towards the left + xOrigin = CGRectGetMaxX(topViewBounds) - kHorizontalMargin - boxWidth; + } + + float arrowHeight = kArrowHeight; + + float topPadding = kTopMargin; + + above = YES; + + if (topPoint.y - contentHeight - arrowHeight - topPadding < CGRectGetMinY(topViewBounds)) { + //Position below because it won't fit above. + above = NO; + + boxFrame = CGRectMake(xOrigin, arrowPoint.y + arrowHeight, boxWidth, boxHeight); + } else { + //Position above. + above = YES; + + boxFrame = CGRectMake(xOrigin, arrowPoint.y - arrowHeight - boxHeight, boxWidth, boxHeight); + } + + //NSLog(@"boxFrame:(%f,%f,%f,%f)", boxFrame.origin.x, boxFrame.origin.y, boxFrame.size.width, boxFrame.size.height); + + CGRect contentFrame = CGRectMake(boxFrame.origin.x + padding, boxFrame.origin.y + padding, contentWidth, contentHeight); + contentView.frame = contentFrame; + + //We set the anchorPoint here so the popover will "grow" out of the arrowPoint specified by the user. + //You have to set the anchorPoint before setting the frame, because the anchorPoint property will + //implicitly set the frame for the view, which we do not want. + self.layer.anchorPoint = CGPointMake(arrowPoint.x / topViewBounds.size.width, arrowPoint.y / topViewBounds.size.height); + self.frame = topViewBounds; + [self setNeedsDisplay]; + + [self addSubview:contentView]; + [topView addSubview:self]; + + //Add a tap gesture recognizer to the large invisible view (self), which will detect taps anywhere on the screen. + UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapped:)]; + tap.cancelsTouchesInView = NO; // Allow touches through to a UITableView or other touchable view, as suggested by Dimajp. + [self addGestureRecognizer:tap]; + [tap RELEASE]; + + self.userInteractionEnabled = YES; +} + + +#pragma mark - Activity Indicator + +//Animates in a progress indicator, and removes +- (void)showActivityIndicatorWithMessage:(NSString *)msg +{ + if ([titleView isKindOfClass:[UILabel class]]) { + ((UILabel *)titleView).text = msg; + } + + if (subviewsArray && (subviewsArray.count > 0)) { + [UIView animateWithDuration:0.2f animations:^{ + for (UIView *view in subviewsArray) { + view.alpha = 0.f; + } + }]; + + if (showDividerRects) { + showDividerRects = NO; + [self setNeedsDisplay]; + } + } + + if (activityIndicator) { + [activityIndicator RELEASE]; + [activityIndicator removeFromSuperview]; + activityIndicator = nil; + } + + activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; + activityIndicator.frame = CGRectMake(CGRectGetMidX(contentView.bounds) - 10.f, CGRectGetMidY(contentView.bounds) - 10.f + 20.f, 20.f, 20.f); + [contentView addSubview:activityIndicator]; + + [activityIndicator startAnimating]; +} + +- (void)hideActivityIndicatorWithMessage:(NSString *)msg +{ + if ([titleView isKindOfClass:[UILabel class]]) { + ((UILabel *)titleView).text = msg; + } + + [activityIndicator stopAnimating]; + [UIView animateWithDuration:0.1f animations:^{ + activityIndicator.alpha = 0.f; + } completion:^(BOOL finished) { + [activityIndicator RELEASE]; + [activityIndicator removeFromSuperview]; + activityIndicator = nil; + }]; +} + +- (void)showImage:(UIImage *)image withMessage:(NSString *)msg +{ + UIImageView *imageView = [[UIImageView alloc] initWithImage:image]; + imageView.alpha = 0.f; + imageView.frame = CGRectMake(floorf(CGRectGetMidX(contentView.bounds) - image.size.width*0.5f), floorf(CGRectGetMidY(contentView.bounds) - image.size.height*0.5f + ((self.titleView) ? 20 : 0.f)), image.size.width, image.size.height); + imageView.transform = CGAffineTransformMakeScale(0.1f, 0.1f); + + [contentView addSubview:[imageView AUTORELEASE]]; + + if (subviewsArray && (subviewsArray.count > 0)) { + [UIView animateWithDuration:0.2f animations:^{ + for (UIView *view in subviewsArray) { + view.alpha = 0.f; + } + }]; + + if (showDividerRects) { + showDividerRects = NO; + [self setNeedsDisplay]; + } + } + + if (msg) { + if ([titleView isKindOfClass:[UILabel class]]) { + ((UILabel *)titleView).text = msg; + } + } + + [UIView animateWithDuration:0.2f delay:0.2f options:UIViewAnimationOptionCurveEaseOut animations:^{ + imageView.alpha = 1.f; + imageView.transform = CGAffineTransformIdentity; + } completion:^(BOOL finished) { + //[imageView removeFromSuperview]; + }]; +} + +- (void)showError +{ + UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"error"]]; + imageView.alpha = 0.f; + imageView.frame = CGRectMake(CGRectGetMidX(contentView.bounds) - 20.f, CGRectGetMidY(contentView.bounds) - 20.f + ((self.titleView) ? 20 : 0.f), 40.f, 40.f); + imageView.transform = CGAffineTransformMakeScale(0.1f, 0.1f); + + [contentView addSubview:[imageView AUTORELEASE]]; + + if (subviewsArray && (subviewsArray.count > 0)) { + [UIView animateWithDuration:0.1f animations:^{ + for (UIView *view in subviewsArray) { + view.alpha = 0.f; + } + }]; + + if (showDividerRects) { + showDividerRects = NO; + [self setNeedsDisplay]; + } + } + + [UIView animateWithDuration:0.1f delay:0.1f options:UIViewAnimationOptionCurveEaseOut animations:^{ + imageView.alpha = 1.f; + imageView.transform = CGAffineTransformIdentity; + } completion:^(BOOL finished) { + //[imageView removeFromSuperview]; + }]; + +} + +- (void)showSuccess +{ + UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"success"]]; + imageView.alpha = 0.f; + imageView.frame = CGRectMake(CGRectGetMidX(contentView.bounds) - 20.f, CGRectGetMidY(contentView.bounds) - 20.f + ((self.titleView) ? 20 : 0.f), 40.f, 40.f); + imageView.transform = CGAffineTransformMakeScale(0.1f, 0.1f); + + [contentView addSubview:[imageView AUTORELEASE]]; + + if (subviewsArray && (subviewsArray.count > 0)) { + [UIView animateWithDuration:0.1f animations:^{ + for (UIView *view in subviewsArray) { + view.alpha = 0.f; + } + }]; + + if (showDividerRects) { + showDividerRects = NO; + [self setNeedsDisplay]; + } + } + + [UIView animateWithDuration:0.1f delay:0.1f options:UIViewAnimationOptionCurveEaseOut animations:^{ + imageView.alpha = 1.f; + imageView.transform = CGAffineTransformIdentity; + } completion:^(BOOL finished) { + //[imageView removeFromSuperview]; + }]; + +} + +#pragma mark - User Interaction + +- (void)tapped:(UITapGestureRecognizer *)tap +{ + CGPoint point = [tap locationInView:contentView]; + + //NSLog(@"point:(%f,%f)", point.x, point.y); + + BOOL found = NO; + + //NSLog(@"subviewsArray:%@", subviewsArray); + + for (int i = 0; i < subviewsArray.count && !found; i++) { + UIView *view = [subviewsArray objectAtIndex:i]; + + //NSLog(@"Rect:(%f,%f,%f,%f)", view.frame.origin.x, view.frame.origin.y, view.frame.size.width, view.frame.size.height); + + if (CGRectContainsPoint(view.frame, point)) { + //The tap was within this view, so we notify the delegate, and break the loop. + + found = YES; + + //NSLog(@"Tapped subview:%d", i); + + if ([view isKindOfClass:[UIButton class]]) { + return; + } + + if (delegate && [delegate respondsToSelector:@selector(popoverView:didSelectItemAtIndex:)]) { + [delegate popoverView:self didSelectItemAtIndex:i]; + } + + break; + } + } + + if (!found && CGRectContainsPoint(contentView.bounds, point)) { + found = YES; + //NSLog(@"popover box contains point, ignoring user input"); + } + + if (!found) { + [self dismiss:YES]; + } + +} + +- (void)didTapButton:(UIButton *)sender +{ + int index = [subviewsArray indexOfObject:sender]; + + if (index == NSNotFound) { + return; + } + + if (delegate && [delegate respondsToSelector:@selector(popoverView:didSelectItemAtIndex:)]) { + [delegate popoverView:self didSelectItemAtIndex:index]; + } +} + +- (void)dismiss +{ + [self dismiss:YES]; +} + +- (void)dismiss:(BOOL)animated +{ + if (!animated) + { + [self dismissComplete]; + } + else + { + [UIView animateWithDuration:0.3f animations:^{ + self.alpha = 0.1f; + self.transform = CGAffineTransformMakeScale(0.1f, 0.1f); + } completion:^(BOOL finished) { + [self dismissComplete]; + }]; + } +} + +- (void)dismissComplete +{ + [self removeFromSuperview]; + + if (self.delegate && [self.delegate respondsToSelector:@selector(popoverViewDidDismiss:)]) { + [delegate popoverViewDidDismiss:self]; + } +} + +- (void)animateRotationToNewPoint:(CGPoint)point inView:(UIView *)view withDuration:(NSTimeInterval)duration +{ + [self layoutAtPoint:point inView:view]; +} + +#pragma mark - Drawing Routines + +// Only override drawRect: if you perform custom drawing. +// An empty implementation adversely affects performance during animation. +- (void)drawRect:(CGRect)rect +{ + // Drawing code + + // Build the popover path + CGRect frame = boxFrame; + + float xMin = CGRectGetMinX(frame); + float yMin = CGRectGetMinY(frame); + + float xMax = CGRectGetMaxX(frame); + float yMax = CGRectGetMaxY(frame); + + float radius = kBoxRadius; //Radius of the curvature. + + float cpOffset = kCPOffset; //Control Point Offset. Modifies how "curved" the corners are. + + + /* + LT2 RT1 + LT1⌜⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⌝RT2 + | | + | popover | + | | + LB2⌞_______________⌟RB1 + LB1 RB2 + + Traverse rectangle in clockwise order, starting at LT1 + L = Left + R = Right + T = Top + B = Bottom + 1,2 = order of traversal for any given corner + + */ + + UIBezierPath *popoverPath = [UIBezierPath bezierPath]; + [popoverPath moveToPoint:CGPointMake(CGRectGetMinX(frame), CGRectGetMinY(frame) + radius)];//LT1 + [popoverPath addCurveToPoint:CGPointMake(xMin + radius, yMin) controlPoint1:CGPointMake(xMin, yMin + radius - cpOffset) controlPoint2:CGPointMake(xMin + radius - cpOffset, yMin)];//LT2 + + //If the popover is positioned below (!above) the arrowPoint, then we know that the arrow must be on the top of the popover. + //In this case, the arrow is located between LT2 and RT1 + if (!above) { + [popoverPath addLineToPoint:CGPointMake(arrowPoint.x - kArrowHeight, yMin)];//left side + [popoverPath addCurveToPoint:arrowPoint controlPoint1:CGPointMake(arrowPoint.x - kArrowHeight + kArrowCurvature, yMin) controlPoint2:arrowPoint];//actual arrow point + [popoverPath addCurveToPoint:CGPointMake(arrowPoint.x + kArrowHeight, yMin) controlPoint1:arrowPoint controlPoint2:CGPointMake(arrowPoint.x + kArrowHeight - kArrowCurvature, yMin)];//right side + } + + [popoverPath addLineToPoint:CGPointMake(xMax - radius, yMin)];//RT1 + [popoverPath addCurveToPoint:CGPointMake(xMax, yMin + radius) controlPoint1:CGPointMake(xMax - radius + cpOffset, yMin) controlPoint2:CGPointMake(xMax, yMin + radius - cpOffset)];//RT2 + [popoverPath addLineToPoint:CGPointMake(xMax, yMax - radius)];//RB1 + [popoverPath addCurveToPoint:CGPointMake(xMax - radius, yMax) controlPoint1:CGPointMake(xMax, yMax - radius + cpOffset) controlPoint2:CGPointMake(xMax - radius + cpOffset, yMax)];//RB2 + + //If the popover is positioned above the arrowPoint, then we know that the arrow must be on the bottom of the popover. + //In this case, the arrow is located somewhere between LB1 and RB2 + if (above) { + [popoverPath addLineToPoint:CGPointMake(arrowPoint.x + kArrowHeight, yMax)];//right side + [popoverPath addCurveToPoint:arrowPoint controlPoint1:CGPointMake(arrowPoint.x + kArrowHeight - kArrowCurvature, yMax) controlPoint2:arrowPoint];//arrow point + [popoverPath addCurveToPoint:CGPointMake(arrowPoint.x - kArrowHeight, yMax) controlPoint1:arrowPoint controlPoint2:CGPointMake(arrowPoint.x - kArrowHeight + kArrowCurvature, yMax)]; + } + + [popoverPath addLineToPoint:CGPointMake(xMin + radius, yMax)];//LB1 + [popoverPath addCurveToPoint:CGPointMake(xMin, yMax - radius) controlPoint1:CGPointMake(xMin + radius - cpOffset, yMax) controlPoint2:CGPointMake(xMin, yMax - radius + cpOffset)];//LB2 + [popoverPath closePath]; + + //// General Declarations + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CGContextRef context = UIGraphicsGetCurrentContext(); + + //// Shadow Declarations + UIColor* shadow = [UIColor colorWithWhite:0.0f alpha:kShadowAlpha]; + CGSize shadowOffset = CGSizeMake(0, 1); + CGFloat shadowBlurRadius = kShadowBlur; + + //// Gradient Declarations + NSArray* gradientColors = [NSArray arrayWithObjects: + (id)kGradientTopColor.CGColor, + (id)kGradientBottomColor.CGColor, nil]; + CGFloat gradientLocations[] = {0, 1}; + CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (CFTYPECAST(CFArrayRef)gradientColors), gradientLocations); + + + //These floats are the top and bottom offsets for the gradient drawing so the drawing includes the arrows. + float bottomOffset = (above ? kArrowHeight : 0.f); + float topOffset = (!above ? kArrowHeight : 0.f); + + //Draw the actual gradient and shadow. + CGContextSaveGState(context); + CGContextSetShadowWithColor(context, shadowOffset, shadowBlurRadius, shadow.CGColor); + CGContextBeginTransparencyLayer(context, NULL); + [popoverPath addClip]; + CGContextDrawLinearGradient(context, gradient, CGPointMake(CGRectGetMidX(frame), CGRectGetMinY(frame) - topOffset), CGPointMake(CGRectGetMidX(frame), CGRectGetMaxY(frame) + bottomOffset), 0); + CGContextEndTransparencyLayer(context); + CGContextRestoreGState(context); + + //// Cleanup + CGGradientRelease(gradient); + CGColorSpaceRelease(colorSpace); + + + //Draw the title background + if (kDrawTitleGradient) { + //Calculate the height of the title bg + float titleBGHeight = -1; + + //NSLog(@"titleView:%@", titleView); + + if (titleView != nil) { + titleBGHeight = kBoxPadding*2.f + titleView.frame.size.height; + } + + + //Draw the title bg height, but only if we need to. + if (titleBGHeight > 0.f) { + CGPoint startingPoint = CGPointMake(xMin, yMin + titleBGHeight); + CGPoint endingPoint = CGPointMake(xMax, yMin + titleBGHeight); + + UIBezierPath *titleBGPath = [UIBezierPath bezierPath]; + [titleBGPath moveToPoint:startingPoint]; + [titleBGPath addLineToPoint:CGPointMake(CGRectGetMinX(frame), CGRectGetMinY(frame) + radius)];//LT1 + [titleBGPath addCurveToPoint:CGPointMake(xMin + radius, yMin) controlPoint1:CGPointMake(xMin, yMin + radius - cpOffset) controlPoint2:CGPointMake(xMin + radius - cpOffset, yMin)];//LT2 + + //If the popover is positioned below (!above) the arrowPoint, then we know that the arrow must be on the top of the popover. + //In this case, the arrow is located between LT2 and RT1 + if (!above) { + [titleBGPath addLineToPoint:CGPointMake(arrowPoint.x - kArrowHeight, yMin)];//left side + [titleBGPath addCurveToPoint:arrowPoint controlPoint1:CGPointMake(arrowPoint.x - kArrowHeight + kArrowCurvature, yMin) controlPoint2:arrowPoint];//actual arrow point + [titleBGPath addCurveToPoint:CGPointMake(arrowPoint.x + kArrowHeight, yMin) controlPoint1:arrowPoint controlPoint2:CGPointMake(arrowPoint.x + kArrowHeight - kArrowCurvature, yMin)];//right side + } + + [titleBGPath addLineToPoint:CGPointMake(xMax - radius, yMin)];//RT1 + [titleBGPath addCurveToPoint:CGPointMake(xMax, yMin + radius) controlPoint1:CGPointMake(xMax - radius + cpOffset, yMin) controlPoint2:CGPointMake(xMax, yMin + radius - cpOffset)];//RT2 + [titleBGPath addLineToPoint:endingPoint]; + [titleBGPath addLineToPoint:startingPoint]; + [titleBGPath closePath]; + + //// General Declarations + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CGContextRef context = UIGraphicsGetCurrentContext(); + + //// Gradient Declarations + NSArray* gradientColors = [NSArray arrayWithObjects: + (id)kGradientTitleTopColor.CGColor, + (id)kGradientTitleBottomColor.CGColor, nil]; + CGFloat gradientLocations[] = {0, 1}; + CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (CFTYPECAST(CFArrayRef)gradientColors), gradientLocations); + + + //These floats are the top and bottom offsets for the gradient drawing so the drawing includes the arrows. + float topOffset = (!above ? kArrowHeight : 0.f); + + //Draw the actual gradient and shadow. + CGContextSaveGState(context); + CGContextBeginTransparencyLayer(context, NULL); + [titleBGPath addClip]; + CGContextDrawLinearGradient(context, gradient, CGPointMake(CGRectGetMidX(frame), CGRectGetMinY(frame) - topOffset), CGPointMake(CGRectGetMidX(frame), CGRectGetMinY(frame) + titleBGHeight), 0); + CGContextEndTransparencyLayer(context); + CGContextRestoreGState(context); + + UIBezierPath *dividerLine = [UIBezierPath bezierPathWithRect:CGRectMake(startingPoint.x, startingPoint.y, (endingPoint.x - startingPoint.x), 0.5f)]; + [[UIColor colorWithRed:0.741 green:0.741 blue:0.741 alpha:0.5f] setFill]; + [dividerLine fill]; + + //// Cleanup + CGGradientRelease(gradient); + CGColorSpaceRelease(colorSpace); + } + } + + + + //Draw the divider rects if we need to + { + if (kShowDividersBetweenViews && showDividerRects) { + if (dividerRects && dividerRects.count > 0) { + for (NSValue *value in dividerRects) { + CGRect rect = value.CGRectValue; + rect.origin.x += contentView.frame.origin.x; + rect.origin.y += contentView.frame.origin.y; + + UIBezierPath *dividerPath = [UIBezierPath bezierPathWithRect:rect]; + [kDividerColor setFill]; + [dividerPath fill]; + } + } + } + } + + //Draw border if we need to + //The border is done last because it needs to be drawn on top of everything else + if (kDrawBorder) { + [kBorderColor setStroke]; + popoverPath.lineWidth = kBorderWidth; + [popoverPath stroke]; + } + +} + +@end diff --git a/ios/iosremote/PopoverView/PopoverViewCompatibility.h b/ios/iosremote/PopoverView/PopoverViewCompatibility.h new file mode 100755 index 000000000000..b53d9f94f481 --- /dev/null +++ b/ios/iosremote/PopoverView/PopoverViewCompatibility.h @@ -0,0 +1,29 @@ +// +// PopoverView +// https://github.com/runway20/PopoverView +// +//(MIT Licensed) +// +//Copyright (c) 2012 Runway 20 Inc. +// +//Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +// +//The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +// +//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +#ifndef popover_PopoverViewCompatibility_h +#define popover_PopoverViewCompatibility_h + +#ifdef __IPHONE_6_0 + +#define UITextAlignmentCenter NSTextAlignmentCenter +#define UITextAlignmentLeft NSTextAlignmentLeft +#define UITextAlignmentRight NSTextAlignmentRight +#define UILineBreakModeTailTruncation NSLineBreakByTruncatingTail +#define UILineBreakModeMiddleTruncation NSLineBreakByTruncatingMiddle +#define UILineBreakModeWordWrap NSLineBreakByWordWrapping + +#endif + +#endif diff --git a/ios/iosremote/PopoverView/PopoverView_Configuration.h b/ios/iosremote/PopoverView/PopoverView_Configuration.h new file mode 100755 index 000000000000..7b43683e6a42 --- /dev/null +++ b/ios/iosremote/PopoverView/PopoverView_Configuration.h @@ -0,0 +1,128 @@ +// +// PopoverView +// https://github.com/runway20/PopoverView +// +//(MIT Licensed) +// +//Copyright (c) 2012 Runway 20 Inc. +// +//Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +// +//The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +// +//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +#pragma mark Constants - Configure look/feel + +// BOX GEOMETRY + +//Height/width of the actual arrow +#define kArrowHeight 12.f + +//padding within the box for the contentView +#define kBoxPadding 10.f + +//control point offset for rounding corners of the main popover box +#define kCPOffset 1.8f + +//radius for the rounded corners of the main popover box +#define kBoxRadius 4.f + +//Curvature value for the arrow. Set to 0.f to make it linear. +#define kArrowCurvature 6.f + +//Minimum distance from the side of the arrow to the beginning of curvature for the box +#define kArrowHorizontalPadding 5.f + +//Alpha value for the shadow behind the PopoverView +#define kShadowAlpha 0.4f + +//Blur for the shadow behind the PopoverView +#define kShadowBlur 3.f; + +//Box gradient bg alpha +#define kBoxAlpha 0.95f + +//Padding along top of screen to allow for any nav/status bars +#define kTopMargin 50.f + +//margin along the left and right of the box +#define kHorizontalMargin 10.f + +//padding along top of icons/images +#define kImageTopPadding 3.f + +//padding along bottom of icons/images +#define kImageBottomPadding 3.f + + +// DIVIDERS BETWEEN VIEWS + +//Bool that turns off/on the dividers +#define kShowDividersBetweenViews YES + +//color for the divider fill +#define kDividerColor [UIColor colorWithRed:0.329 green:0.341 blue:0.353 alpha:0.15f] + + +// BACKGROUND GRADIENT + +//bottom color white in gradient bg +#define kGradientBottomColor [UIColor colorWithRed:0.98f green:0.98f blue:0.98f alpha:kBoxAlpha] + +//top color white value in gradient bg +#define kGradientTopColor [UIColor colorWithRed:1.f green:1.f blue:1.f alpha:kBoxAlpha] + + +// TITLE GRADIENT + +//bool that turns off/on title gradient +#define kDrawTitleGradient YES + +//bottom color white value in title gradient bg +#define kGradientTitleBottomColor [UIColor colorWithRed:0.93f green:0.93f blue:0.93f alpha:kBoxAlpha] + +//top color white value in title gradient bg +#define kGradientTitleTopColor [UIColor colorWithRed:1.f green:1.f blue:1.f alpha:kBoxAlpha] + + +// FONTS + +//normal text font +#define kTextFontPhone [UIFont fontWithName:@"HelveticaNeue" size:16.f] + +//normal text font +#define kTextFontPad [UIFont fontWithName:@"HelveticaNeue" size:20.f] + +#define kSmallTextFontPad [UIFont fontWithName:@"HelveticaNeue" size:16.f] + +//normal text color +#define kTextColor [UIColor colorWithRed:0.329 green:0.341 blue:0.353 alpha:1] +// highlighted text color +#define kTextHighlightColor [UIColor colorWithRed:0.098 green:0.102 blue:0.106 alpha:1.000] + +//normal text alignment +#define kTextAlignment UITextAlignmentCenter + +//normal text alignment +#define kTextAlignmentLeft UITextAlignmentLeft + +//title font +#define kTitleFontPhone [UIFont fontWithName:@"HelveticaNeue-Bold" size:16.f] + +#define kTitleFontPad [UIFont fontWithName:@"HelveticaNeue-Bold" size:22.f] + +//title text color +#define kTitleColor [UIColor colorWithRed:0.329 green:0.341 blue:0.353 alpha:1] + + +// BORDER + +//bool that turns off/on the border +#define kDrawBorder NO + +//border color +#define kBorderColor [UIColor blackColor] + +//border width +#define kBorderWidth 1.f \ No newline at end of file diff --git a/ios/iosremote/SWRevealViewController/SWRevealViewController.h b/ios/iosremote/SWRevealViewController/SWRevealViewController.h new file mode 100755 index 000000000000..1610fef5d497 --- /dev/null +++ b/ios/iosremote/SWRevealViewController/SWRevealViewController.h @@ -0,0 +1,180 @@ +/* + + Copyright (c) 2013 Joan Lluch + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is furnished + to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + Early code inspired on a similar class by Philip Kluz (Philip.Kluz@zuui.org) + +*/ + + +#import +#import "stopWatch.h" +#import "Timer.h" + +@class SWRevealViewController; +@protocol SWRevealViewControllerDelegate; + +#pragma mark - SWRevealViewController Class + +// Enum values for setFrontViewPosition:animated: +typedef enum +{ + FrontViewPositionLeftSideMostRemoved, + FrontViewPositionLeftSideMost, + FrontViewPositionLeftSide, + + // Left position, rear view is hidden behind front controller + FrontViewPositionLeft, + + // Right possition, front view is presented right-offseted by rearViewRevealWidth + FrontViewPositionRight, + + // Right most possition, front view is presented right-offseted by rearViewRevealWidth+rearViewRevealOverdraw + FrontViewPositionRightMost, + + // Front controller is removed from view. Animated transitioning from this state will cause the same + // effect than animating from FrontViewPositionRightMost. Use this instead of FrontViewPositionRightMost when + // you intent to remove the front controller view to be removed from the view hierarchy. + FrontViewPositionRightMostRemoved, + +} FrontViewPosition; + + +@interface SWRevealViewController : UIViewController + +- (void)startTimePickerwithTimer:(Timer *) timer; + +// Object instance init and rear view setting +- (id)initWithRearViewController:(UIViewController *)rearViewController frontViewController:(UIViewController *)frontViewController; + +// Rear view controller, can be nil if not used +@property (strong, nonatomic) UIViewController *rearViewController; + +// Optional right view controller, can be nil if not used +@property (strong, nonatomic) UIViewController *rightViewController; + +// Front view controller, can be nil on initialization but must be supplied by the time its view is loaded +@property (strong, nonatomic) UIViewController *frontViewController; + +// Sets the frontViewController using a default set of chained animations consisting on moving the +// presented frontViewController to the top most right, replacing it, and moving it back to the left position +- (void)setFrontViewController:(UIViewController *)frontViewController animated:(BOOL)animated; + +// Front view position, use this to set a particular position state on the controller +// On initialization it is set to FrontViewPositionLeft +@property (assign, nonatomic) FrontViewPosition frontViewPosition; + +// Chained animation of the frontViewController position. You can call it several times in a row to achieve +// any set of animations you wish. Animations will be chained and performed one after the other. +- (void)setFrontViewPosition:(FrontViewPosition)frontViewPosition animated:(BOOL)animated; + +// Toogles the current state of the front controller between Left or Right and fully visible +// Use setFrontViewPosition to set a particular position +- (void)revealToggleAnimated:(BOOL)animated; +- (void)rightRevealToggleAnimated:(BOOL)animated; + +// The following methods are meant to be directly connected to the action method of a button +// to perform user triggered postion change of the controller views. This is ussually added to a +// button on top left or right of the frontViewController +- (void)revealToggle:(id)sender; +- (void)rightRevealToggle:(id)sender; + +// The following method will provide a panGestureRecognizer suitable to be added to any view on the frontController +// in order to perform usual drag and swipe gestures on the frontViewController to reveal the rear views. This +// is usually added on the top bar of a front controller. +- (UIPanGestureRecognizer*)panGestureRecognizer; + +// The following properties are provided for further customization, they are set to default values on initialization, +// you should not generally have to set them + +// Defines how much of the rear view is shown, default is 260. +@property (assign, nonatomic) CGFloat rearViewRevealWidth; +@property (assign, nonatomic) CGFloat rightViewRevealWidth; + +// Defines how much of an overdraw can occur when dragging further than 'rearViewRevealWidth', default is 60. +@property (assign, nonatomic) CGFloat rearViewRevealOverdraw; +@property (assign, nonatomic) CGFloat rightViewRevealOverdraw; + +// If YES (the default) the controller will bounce to the Left position when dragging further than 'rearViewRevealWidth' +@property (assign, nonatomic) BOOL bounceBackOnOverdraw; +@property (assign, nonatomic) BOOL bounceBackOnLeftOverdraw; + +// If YES (default is NO) the controller will allow permanent dragging up to the rightMostPosition +@property (assign, nonatomic) BOOL stableDragOnOverdraw; +@property (assign, nonatomic) BOOL stableDragOnLeftOverdraw; + +// Velocity required for the controller to toggle its state based on a swipe movement, default is 300 +@property (assign, nonatomic) CGFloat quickFlickVelocity; + +// Default duration for the revealToggle animation, default is 0.25 +@property (assign, nonatomic) NSTimeInterval toggleAnimationDuration; + +// Defines the radius of the front view's shadow, default is 2.5f +@property (assign, nonatomic) CGFloat frontViewShadowRadius; + +// Defines the radius of the front view's shadow offset default is {0.0f,2.5f} +@property (assign, nonatomic) CGSize frontViewShadowOffset; + +// The class properly handles all the relevant calls to appearance methods on the contained controllers. +// Moreover you can assign a delegate to let the class inform you on positions and animation activity. + +// Delegate +@property (weak, nonatomic) id delegate; + +@end + +#pragma mark - SWRevealViewControllerDelegate Protocol + +@protocol SWRevealViewControllerDelegate + +@optional + +- (void)revealController:(SWRevealViewController *)revealController willMoveToPosition:(FrontViewPosition)position; +- (void)revealController:(SWRevealViewController *)revealController didMoveToPosition:(FrontViewPosition)position; + +- (void)revealController:(SWRevealViewController *)revealController animateToPosition:(FrontViewPosition)position; + +- (void)revealControllerPanGestureBegan:(SWRevealViewController *)revealController; +- (void)revealControllerPanGestureEnded:(SWRevealViewController *)revealController; + +@end + + +#pragma mark - UIViewController(SWRevealViewController) Category + +// We add a category of UIViewController to let childViewControllers easily access their parent SWRevealViewController +@interface UIViewController(SWRevealViewController) + +- (SWRevealViewController*)revealViewController; + +@end + + +// This will allow the class to be defined on a storyboard +#pragma mark - SWRevealViewControllerSegue + +@interface SWRevealViewControllerSegue : UIStoryboardSegue + +@property (strong) void(^performBlock)( SWRevealViewControllerSegue* segue, UIViewController* svc, UIViewController* dvc ); + +@end + + diff --git a/ios/iosremote/SWRevealViewController/SWRevealViewController.m b/ios/iosremote/SWRevealViewController/SWRevealViewController.m new file mode 100755 index 000000000000..feab034b3cee --- /dev/null +++ b/ios/iosremote/SWRevealViewController/SWRevealViewController.m @@ -0,0 +1,1255 @@ +/* + + Copyright (c) 2013 Joan Lluch + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is furnished + to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + Early code inspired on a similar class by Philip Kluz (Philip.Kluz@zuui.org) + +*/ + +#import +#import + +#import "SWRevealViewController.h" +#import "SlideShowSwipeInList~iphone.h" +#import "UIViewController+LibOStyling.h" + +#pragma mark - SWDirectionPanGestureRecognizer + +typedef enum +{ + SWDirectionPanGestureRecognizerVertical, + SWDirectionPanGestureRecognizerHorizontal + +} SWDirectionPanGestureRecognizerDirection; + +@interface SWDirectionPanGestureRecognizer : UIPanGestureRecognizer + +@property (nonatomic, assign) SWDirectionPanGestureRecognizerDirection direction; + +@end + + +@implementation SWDirectionPanGestureRecognizer +{ + BOOL _dragging; + BOOL _disabled; + CGPoint _init; +} + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event +{ + [super touchesBegan:touches withEvent:event]; + + UITouch *touch = [touches anyObject]; + if ([touch locationInView:self.view].x > 60.0) { + _disabled = YES; + return; + } + _disabled = NO; + _init = [touch locationInView:self.view]; + _dragging = NO; +} + + +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event +{ + if (_disabled) { + return; + } + [super touchesMoved:touches withEvent:event]; + + if (self.state == UIGestureRecognizerStateFailed) + return; + + if ( _dragging ) + return; + + const int kDirectionPanThreshold = 5; + + UITouch *touch = [touches anyObject]; + CGPoint nowPoint = [touch locationInView:self.view]; + + CGFloat moveX = nowPoint.x - _init.x; + CGFloat moveY = nowPoint.y - _init.y; + + if (abs(moveX) > kDirectionPanThreshold) + { + if (_direction == SWDirectionPanGestureRecognizerHorizontal) + _dragging = YES; + else + self.state = UIGestureRecognizerStateFailed; + } + else if (abs(moveY) > kDirectionPanThreshold) + { + if (_direction == SWDirectionPanGestureRecognizerVertical) + _dragging = YES ; + else + self.state = UIGestureRecognizerStateFailed; + } +} + +@end + + +#pragma mark - SWRevealView Class + +@interface SWRevealView: UIView +{ + __weak SWRevealViewController *_c; +} + +@property (nonatomic, readonly) UIView *rearView; +@property (nonatomic, readonly) UIView *rightView; +@property (nonatomic, readonly) UIView *frontView; +@property (nonatomic, assign) BOOL disableLayout; + +@end + + +@interface SWRevealViewController() +- (void)_getRevealWidth:(CGFloat*)pRevealWidth revealOverDraw:(CGFloat*)pRevealOverdraw forSymetry:(int)symetry; +- (void)_getBounceBack:(BOOL*)pBounceBack pStableDrag:(BOOL*)pStableDrag forSymetry:(int)symetry; +- (void)_getAdjustedFrontViewPosition:(FrontViewPosition*)frontViewPosition forSymetry:(int)symetry; +@end + + +@implementation SWRevealView + +- (id)initWithFrame:(CGRect)frame controller:(SWRevealViewController*)controller +{ + self = [super initWithFrame:frame]; + if ( self ) + { + _c = controller; + CGRect bounds = self.bounds; + + _frontView = [[UIView alloc] initWithFrame:bounds]; + _frontView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight; + + [self addSubview:_frontView]; + + CALayer *frontViewLayer = _frontView.layer; + frontViewLayer.masksToBounds = NO; + frontViewLayer.shadowColor = [UIColor blackColor].CGColor; + frontViewLayer.shadowOpacity = 1.0f; + frontViewLayer.shadowOffset = _c.frontViewShadowOffset; + frontViewLayer.shadowRadius = _c.frontViewShadowRadius; + } + + return self; +} + + +- (void)layoutSubviews +{ + if ( _disableLayout ) return; + + [self _layoutRearViews]; + + CGRect bounds = self.bounds; + + CGFloat xPosition = [self frontLocationForPosition:_c.frontViewPosition]; + _frontView.frame = CGRectMake(xPosition, 0.0f, bounds.size.width, bounds.size.height); + + UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRect:_frontView.bounds]; + _frontView.layer.shadowPath = shadowPath.CGPath; +} + + +- (void)prepareRearViewForPosition:(FrontViewPosition)newPosition +{ + if ( _rearView == nil ) + { + _rearView = [[UIView alloc] initWithFrame:self.bounds]; + _rearView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight; + [self insertSubview:_rearView belowSubview:_frontView]; + } + [self _layoutRearViews]; + [self _prepareForNewPosition:newPosition]; +} + + +- (CGFloat)frontLocationForPosition:(FrontViewPosition)frontViewPosition +{ + CGFloat revealWidth; + CGFloat revealOverdraw; + + CGFloat location = 0.0f; + + int symetry = frontViewPosition FrontViewPositionRight ) + location = revealWidth + revealOverdraw; + + return location*symetry; +} + + +- (void)dragFrontViewToXPosition:(CGFloat)xPosition +{ + CGRect bounds = self.bounds; + xPosition = [self _adjustedDragLocationForLocation:xPosition]; + _frontView.frame = CGRectMake(xPosition, 0.0f, bounds.size.width, bounds.size.height); +} + + +- (void)prepareRightViewForPosition:(FrontViewPosition)newPosition +{ + if ( _rightView == nil ) + { + _rightView = [[UIView alloc] initWithFrame:self.bounds]; + _rightView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight; + [self insertSubview:_rightView belowSubview:_frontView]; + } + [self _layoutRearViews]; + [self _prepareForNewPosition:newPosition]; +} + + +# pragma mark private + +- (void)_layoutRearViews +{ + CGRect bounds = self.bounds; + + CGFloat rearWidth = _c.rearViewRevealWidth + _c.rearViewRevealOverdraw; + _rearView.frame = CGRectMake(0.0, 0.0, rearWidth, bounds.size.height); + + CGFloat rightWidth = _c.rightViewRevealWidth + _c.rightViewRevealOverdraw; + _rightView.frame = CGRectMake(bounds.size.width-rightWidth, 0.0f, rightWidth, bounds.size.height); +} + + +- (void)_prepareForNewPosition:(FrontViewPosition)newPosition; +{ + if ( _rearView == nil || _rightView == nil ) + return; + + int symetry = newPosition 0 && rearIndex < rightIndex) ) + [self exchangeSubviewAtIndex:rightIndex withSubviewAtIndex:rearIndex]; +} + + +- (CGFloat)_adjustedDragLocationForLocation:(CGFloat)x +{ + CGFloat result; + + CGFloat revealWidth; + CGFloat revealOverdraw; + BOOL bounceBack; + BOOL stableDrag; + FrontViewPosition position = _c.frontViewPosition; + + int symetry = x<0 ? -1 : 1; + + [_c _getRevealWidth:&revealWidth revealOverDraw:&revealOverdraw forSymetry:symetry]; + [_c _getBounceBack:&bounceBack pStableDrag:&stableDrag forSymetry:symetry]; + + BOOL stableTrack = !bounceBack || stableDrag || position==FrontViewPositionRightMost || position==FrontViewPositionLeftSideMost; + if ( stableTrack ) + { + revealWidth += revealOverdraw; + revealOverdraw = 0.0f; + } + + x = x * symetry; + + if (x <= revealWidth) + result = x; // Translate linearly. + + else if (x <= revealWidth+2*revealOverdraw) + result = revealWidth + (x-revealWidth)/2; // slow down translation by halph the movement. + + else + result = revealWidth+revealOverdraw; // keep at the rightMost location. + + return result * symetry; +} + +@end + + +#pragma mark - SWRevealViewController Class + +@interface SWRevealViewController() +{ + SWRevealView *_contentView; + UIPanGestureRecognizer *_panGestureRecognizer; + FrontViewPosition _frontViewPosition; + FrontViewPosition _rearViewPosition; + FrontViewPosition _rightViewPosition; +} +@end + + +@implementation SWRevealViewController +{ + FrontViewPosition _panInitialFrontPosition; + NSMutableArray *_animationQueue; + BOOL _userInteractionStore; +} + +const int FrontViewPositionNone = 0xff; + + +#pragma mark - Init + +- (id)initWithCoder:(NSCoder *)aDecoder +{ + self = [super initWithCoder:aDecoder]; + if ( self ) + { + [self _initDefaultProperties]; + } + return self; +} + + +- (id)init +{ + return [self initWithRearViewController:nil frontViewController:nil]; +} + + +- (id)initWithRearViewController:(UIViewController *)rearViewController frontViewController:(UIViewController *)frontViewController; +{ + self = [super init]; + if ( self ) + { + [self _initDefaultProperties]; + [self _setRearViewController:rearViewController]; + [self _setFrontViewController:frontViewController]; + } + return self; +} + + +- (void)_initDefaultProperties +{ + _frontViewPosition = FrontViewPositionLeft; + _rearViewPosition = FrontViewPositionLeft; + _rightViewPosition = FrontViewPositionLeft; + _rearViewRevealWidth = 220.0f; + _rearViewRevealOverdraw = 60.0f; + _rightViewRevealWidth = 260.0f; + _rightViewRevealOverdraw = 60.0f; + _bounceBackOnOverdraw = YES; + _bounceBackOnLeftOverdraw = YES; + _stableDragOnOverdraw = NO; + _stableDragOnLeftOverdraw = NO; + _quickFlickVelocity = 250.0f; + _toggleAnimationDuration = 0.25; + _frontViewShadowRadius = 2.5f; + _frontViewShadowOffset = CGSizeMake(0.0f, 2.5f); + _userInteractionStore = YES; + _animationQueue = [NSMutableArray array]; +} + +#pragma mark popup timer +- (void)changeDate:(UIDatePicker *)sender { + NSLog(@"Time left: %f", sender.countDownDuration); + [[(SlideShowSwipeInList_iphone *) self.rearViewController timer] setSecondsLeft: sender.countDownDuration]; +} + +- (void)removeViews:(id)object { + [[self.view viewWithTag:89] removeFromSuperview]; + [[self.view viewWithTag:90] removeFromSuperview]; + [[self.view viewWithTag:91] removeFromSuperview]; +} + +- (void)dismissDatePicker:(id)sender { + CGRect toolbarTargetFrame = CGRectMake(0, self.view.bounds.size.height, 320, 44); + CGRect datePickerTargetFrame = CGRectMake(0, self.view.bounds.size.height+44, 320, 216); + [UIView beginAnimations:@"MoveOut" context:nil]; + [self.view viewWithTag:89].alpha = 0; + [self.view viewWithTag:90].frame = datePickerTargetFrame; + [self.view viewWithTag:91].frame = toolbarTargetFrame; + [UIView setAnimationDelegate:self]; + [UIView setAnimationDidStopSelector:@selector(removeViews:)]; + [UIView commitAnimations]; +} + +- (IBAction)callDP:(id)sender { + if ([self.view viewWithTag:89]) { + return; + } + CGRect toolbarTargetFrame = CGRectMake(0, self.view.bounds.size.height-216-44, 320, 44); + CGRect datePickerTargetFrame = CGRectMake(0, self.view.bounds.size.height-216, 320, 216); + + UIView *darkView = [[UIView alloc] initWithFrame:self.view.bounds]; + darkView.alpha = 0; + darkView.backgroundColor = [UIColor blackColor]; + darkView.tag = 89; + UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(dismissDatePicker:)]; + [darkView addGestureRecognizer:tapGesture]; + [self.view addSubview:darkView]; + + UIDatePicker *datePicker = [[UIDatePicker alloc] initWithFrame:CGRectMake(0, self.view.bounds.size.height+44, 320, 216)]; + datePicker.tag = 90; + [datePicker setDatePickerMode:UIDatePickerModeCountDownTimer]; + [datePicker addTarget:self action:@selector(changeDate:) forControlEvents:UIControlEventValueChanged]; + [self.view addSubview:datePicker]; + + UIToolbar *toolBar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, self.view.bounds.size.height, 320, 44)]; + toolBar.tag = 91; + toolBar.barStyle = UIBarStyleBlackTranslucent; + UIBarButtonItem *spacer = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil]; + UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(dismissDatePicker:)]; + [toolBar setItems:[NSArray arrayWithObjects:spacer, doneButton, nil]]; + [self.view addSubview:toolBar]; + + [UIView beginAnimations:@"MoveIn" context:nil]; + toolBar.frame = toolbarTargetFrame; + datePicker.frame = datePickerTargetFrame; + darkView.alpha = 0.5; + [UIView commitAnimations]; +} + +- (void)startTimePickerwithTimer:(Timer *) timer +{ + [self callDP:self]; +} + + +#pragma mark storyboard support + +static NSString * const SWSegueRearIdentifier = @"sw_rear"; +static NSString * const SWSegueFrontIdentifier = @"sw_front"; +static NSString * const SWSegueRightIdentifier = @"sw_right"; + +- (void)prepareForSegue:(SWRevealViewControllerSegue *)segue sender:(id)sender +{ + // $ using a custom segue we can get access to the storyboard-loaded rear/front view controllers + // the trick is to define segues of type SWRevealViewControllerSegue on the storyboard + // connecting the SWRevealViewController to the desired front/rear controllers, + // and setting the identifiers to "sw_rear" and "sw_front" + + // $ these segues are invoked manually in the loadView method if a storyboard + // was used to instantiate the SWRevealViewController + + // $ none of this would be necessary if Apple exposed "relationship" segues for container view controllers. + + NSString *identifier = segue.identifier; + if ( [segue isKindOfClass:[SWRevealViewControllerSegue class]] && sender == nil ) + { + if ( [identifier isEqualToString:SWSegueRearIdentifier] ) + { + segue.performBlock = ^(SWRevealViewControllerSegue* rvc_segue, UIViewController* svc, UIViewController* dvc) + { + [self _setRearViewController:dvc]; + }; + } + else if ( [identifier isEqualToString:SWSegueFrontIdentifier] ) + { + segue.performBlock = ^(SWRevealViewControllerSegue* rvc_segue, UIViewController* svc, UIViewController* dvc) + { + [self _setFrontViewController:dvc]; + }; + } + else if ( [identifier isEqualToString:SWSegueRightIdentifier] ) + { + segue.performBlock = ^(SWRevealViewControllerSegue* rvc_segue, UIViewController* svc, UIViewController* dvc) + { + [self _setRightViewController:dvc]; + }; + } + } +} + + +#pragma mark - View lifecycle + +- (void)loadView +{ + // Do not call super, to prevent the apis from unfruitful looking for inexistent xibs! + + // This is what Apple tells us to set as the initial frame, which is of course totally irrelevant + // with the modern view controller containment patterns, let's leave it for the sake of it! + CGRect frame = [[UIScreen mainScreen] applicationFrame]; + + // create a custom content view for the controller + _contentView = [[SWRevealView alloc] initWithFrame:frame controller:self]; + + // set the content view to resize along with its superview + [_contentView setAutoresizingMask:UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight]; + + // set our contentView to the controllers view + self.view = _contentView; + + // load any defined front/rear controllers from the storyboard + if ( self.storyboard && _rearViewController == nil ) + { + @try + { + [self performSegueWithIdentifier:SWSegueRearIdentifier sender:nil]; + [self performSegueWithIdentifier:SWSegueFrontIdentifier sender:nil]; + [self performSegueWithIdentifier:SWSegueRightIdentifier sender:nil]; + } + @catch(NSException *exception) + { + //NSLog(@"Caught %@: %@", [exception name], [exception reason]); + } + } + + // Apple also tells us to do this: + _contentView.backgroundColor = [UIColor blackColor]; + + // we set the current frontViewPosition to none before seting the + // desired initial position, this will force proper controller reload + FrontViewPosition initialPosition = _frontViewPosition; + _frontViewPosition = FrontViewPositionNone; + _rearViewPosition = FrontViewPositionNone; + _rightViewPosition = FrontViewPositionNone; + + // now set the desired initial position + [self _setFrontViewPosition:initialPosition withDuration:0.0]; +} + + +- (void)viewDidAppear:(BOOL)animated +{ + [super viewDidAppear:animated]; + + // Uncomment the following code if you want the child controllers + // to be loaded at this point. + // + // We leave this commented out because we think loading childs here is conceptually wrong. + // Instead, we refrain view loads until necesary, for example we may never load + // the rear controller view -or the front controller view- if it is never displayed. + // + // If you need to manipulate views of any of your child controllers in an override + // of this method, you can load yourself the views explicitly on your overriden method. + // However we discourage it as an app following the MVC principles should never need to do so + +// [_frontViewController view]; +// [_rearViewController view]; + + // we store at this point the view's user interaction state as we may temporarily disable it + // and resume it back to the previous state, it is possible to override this behaviour by + // intercepting it on the panGestureBegan and panGestureEnded delegates + _userInteractionStore = _contentView.userInteractionEnabled; +} + + +- (NSUInteger)supportedInterfaceOrientations +{ + return UIInterfaceOrientationMaskAll; +} + +// Support for earlier than iOS 6.0 +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation +{ + return YES; +} + + + +#pragma mark - Public methods and property accessors + +- (void)setFrontViewController:(UIViewController *)frontViewController +{ + [self setFrontViewController:frontViewController animated:NO]; +} + + +- (void)setFrontViewController:(UIViewController *)frontViewController animated:(BOOL)animated +{ + if ( ![self isViewLoaded]) + { + [self _setFrontViewController:frontViewController]; + return; + } + + [self _dispatchSetFrontViewController:frontViewController animated:animated]; +} + + +- (void)setRearViewController:(UIViewController *)rightViewController +{ + if ( ![self isViewLoaded]) + { + [self _setRearViewController:rightViewController]; + return; + } + + [self _dispatchSetRearViewController:rightViewController]; +} + + +- (void)setRightViewController:(UIViewController *)rightViewController +{ + if ( ![self isViewLoaded]) + { + [self _setRightViewController:rightViewController]; + return; + } + + [self _dispatchSetRightViewController:rightViewController]; +} + + +- (void)revealToggleAnimated:(BOOL)animated +{ + FrontViewPosition toogledFrontViewPosition = FrontViewPositionLeft; + if (_frontViewPosition <= FrontViewPositionLeft) + toogledFrontViewPosition = FrontViewPositionRight; + + [self setFrontViewPosition:toogledFrontViewPosition animated:animated]; +} + +- (void)rightRevealToggleAnimated:(BOOL)animated +{ + FrontViewPosition toogledFrontViewPosition = FrontViewPositionLeft; + if (_frontViewPosition >= FrontViewPositionLeft) + toogledFrontViewPosition = FrontViewPositionLeftSide; + + [self setFrontViewPosition:toogledFrontViewPosition animated:animated]; +} + + +- (void)setFrontViewPosition:(FrontViewPosition)frontViewPosition +{ + [self setFrontViewPosition:frontViewPosition animated:NO]; +} + + +- (void)setFrontViewPosition:(FrontViewPosition)frontViewPosition animated:(BOOL)animated +{ + if ( ![self isViewLoaded] ) + { + _frontViewPosition = frontViewPosition; + _rearViewPosition = frontViewPosition; + _rightViewPosition = frontViewPosition; + return; + } + + [self _dispatchSetFrontViewPosition:frontViewPosition animated:animated]; +} + + +- (UIPanGestureRecognizer*)panGestureRecognizer +{ + if ( _panGestureRecognizer == nil ) + { + SWDirectionPanGestureRecognizer *customRecognizer = + [[SWDirectionPanGestureRecognizer alloc] initWithTarget:self action:@selector(_handleRevealGesture:)]; + + customRecognizer.direction = SWDirectionPanGestureRecognizerHorizontal; + customRecognizer.delegate = self; + _panGestureRecognizer = customRecognizer ; + } + return _panGestureRecognizer; +} + + +#pragma mark - Provided acction methods + +- (void)revealToggle:(id)sender +{ + [self revealToggleAnimated:YES]; +} + +- (void)rightRevealToggle:(id)sender +{ + [self rightRevealToggleAnimated:YES]; +} + + +#pragma mark - UserInteractionEnabling + +// disable userInteraction on the entire control +- (void)_disableUserInteraction +{ + //_userInteractionStore = _contentView.userInteractionEnabled; + [_contentView setUserInteractionEnabled:NO]; + [_contentView setDisableLayout:YES]; + + if ( [_delegate respondsToSelector:@selector(revealControllerPanGestureBegan:)] ) + [_delegate revealControllerPanGestureBegan:self]; +} + +// restore userInteraction on the control +- (void)_restoreUserInteraction +{ + // we use the stored userInteraction state just in case a developer decided + // to have our view interaction disabled beforehand + [_contentView setUserInteractionEnabled:_userInteractionStore]; + [_contentView setDisableLayout:NO]; + + if ( [_delegate respondsToSelector:@selector(revealControllerPanGestureEnded:) ] ) + [_delegate revealControllerPanGestureEnded:self]; +} + + +#pragma mark - Symetry + +- (void)_getRevealWidth:(CGFloat*)pRevealWidth revealOverDraw:(CGFloat*)pRevealOverdraw forSymetry:(int)symetry +{ + if ( symetry < 0 ) *pRevealWidth = _rightViewRevealWidth, *pRevealOverdraw = _rightViewRevealOverdraw; + else *pRevealWidth = _rearViewRevealWidth, *pRevealOverdraw = _rearViewRevealOverdraw; +} + +- (void)_getBounceBack:(BOOL*)pBounceBack pStableDrag:(BOOL*)pStableDrag forSymetry:(int)symetry +{ + if ( symetry < 0 ) *pBounceBack = _bounceBackOnLeftOverdraw, *pStableDrag = _stableDragOnLeftOverdraw; + else *pBounceBack = _bounceBackOnOverdraw, *pStableDrag = _stableDragOnOverdraw; +} + +- (void)_getAdjustedFrontViewPosition:(FrontViewPosition*)frontViewPosition forSymetry:(int)symetry +{ + if ( symetry < 0 ) *frontViewPosition = FrontViewPositionLeft + symetry*(*frontViewPosition-FrontViewPositionLeft); +} + + +#pragma mark - Deferred block execution queue + +// Define a convenience macro to enqueue single statements +#define _enqueue(code) [self _enqueueBlock:^{code;}]; + +// Defers the execution of the passed in block until a paired _dequeue call is received, +// or executes the block right away if no pending requests are present. +- (void)_enqueueBlock:(void (^)(void))block +{ + [_animationQueue insertObject:block atIndex:0]; + if ( _animationQueue.count == 1) + { + block(); + } +} + +// Removes the top most block in the queue and executes the following one if any. +// Calls to this method must be paired with calls to _enqueueBlock, particularly it may be called +// from within a block passed to _enqueueBlock to remove itself when done with animations. +- (void)_dequeue +{ + [_animationQueue removeLastObject]; + + if ( _animationQueue.count > 0 ) + { + void (^block)(void) = [_animationQueue lastObject]; + block(); + } +} + + +#pragma mark - Gesture Delegate + +- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer +{ + // only allow gesture if no previous request is in process + return ( gestureRecognizer == _panGestureRecognizer && _animationQueue.count == 0) ; +} + + +#pragma mark - Gesture Based Reveal + +- (void)_handleRevealGesture:(UIPanGestureRecognizer *)recognizer +{ + switch ( recognizer.state ) + { + case UIGestureRecognizerStateBegan: + [self _handleRevealGestureStateBeganWithRecognizer:recognizer]; + break; + + case UIGestureRecognizerStateChanged: + [self _handleRevealGestureStateChangedWithRecognizer:recognizer]; + break; + + case UIGestureRecognizerStateEnded: + [self _handleRevealGestureStateEndedWithRecognizer:recognizer]; + break; + + case UIGestureRecognizerStateCancelled: + //case UIGestureRecognizerStateFailed: + [self _handleRevealGestureStateCancelledWithRecognizer:recognizer]; + break; + + default: + break; + } +} + + +- (void)_handleRevealGestureStateBeganWithRecognizer:(UIPanGestureRecognizer *)recognizer +{ + // we know that we will not get here unless the animationQueue is empty because the recognizer + // delegate prevents it, however we do not want any forthcoming programatic actions to disturb + // the gesture, so we just enqueue a dummy block to ensure any programatic acctions will be + // scheduled after the gesture is completed + [self _enqueueBlock:^{}]; // <-- dummy block + + // we store the initial position and initialize a target position + _panInitialFrontPosition = _frontViewPosition; + + // we disable user interactions on the views, however programatic accions will still be + // enqueued to be performed after the gesture completes + [self _disableUserInteraction]; +} + + +- (void)_handleRevealGestureStateChangedWithRecognizer:(UIPanGestureRecognizer *)recognizer +{ + CGFloat translation = [recognizer translationInView:_contentView].x; + + CGFloat baseLocation = [_contentView frontLocationForPosition:_panInitialFrontPosition]; + CGFloat xPosition = baseLocation + translation; + + if ( xPosition < 0 ) + { + if ( _rightViewController == nil ) xPosition = 0; + [self _rightViewDeploymentForNewFrontViewPosition:FrontViewPositionLeftSide](); + [self _rearViewDeploymentForNewFrontViewPosition:FrontViewPositionLeftSide](); + } + + if ( xPosition > 0 ) + { + if ( _rearViewController == nil ) xPosition = 0; + [self _rightViewDeploymentForNewFrontViewPosition:FrontViewPositionRight](); + [self _rearViewDeploymentForNewFrontViewPosition:FrontViewPositionRight](); + } + + [_contentView dragFrontViewToXPosition:xPosition]; +} + + +- (void)_handleRevealGestureStateEndedWithRecognizer:(UIPanGestureRecognizer *)recognizer +{ + UIView *frontView = _contentView.frontView; + + CGFloat xPosition = frontView.frame.origin.x; + CGFloat velocity = [recognizer velocityInView:_contentView].x; + //NSLog( @"Velocity:%1.4f", velocity); + + // depending on position we compute a simetric replacement of widths and positions + int symetry = xPosition<0 ? -1 : 1; + + // simetring computing of widths + CGFloat revealWidth ; + CGFloat revealOverdraw ; + BOOL bounceBack; + BOOL stableDrag; + + [self _getRevealWidth:&revealWidth revealOverDraw:&revealOverdraw forSymetry:symetry]; + [self _getBounceBack:&bounceBack pStableDrag:&stableDrag forSymetry:symetry]; + + // simetric replacement of position + xPosition = xPosition * symetry; + + // initially we assume drag to left and default duration + FrontViewPosition frontViewPosition = FrontViewPositionLeft; + NSTimeInterval duration = _toggleAnimationDuration; + + // Velocity driven change: + if (fabsf(velocity) > _quickFlickVelocity) + { + // we may need to set the drag position and to adjust the animation duration + CGFloat journey = xPosition; + if (velocity*symetry > 0.0f) + { + frontViewPosition = FrontViewPositionRight; + journey = revealWidth - xPosition; + if (xPosition > revealWidth) + { + if (!bounceBack && stableDrag /*&& xPosition > _rearViewRevealWidth+_rearViewRevealOverdraw*0.5f*/) + { + frontViewPosition = FrontViewPositionRightMost; + journey = revealWidth+revealOverdraw - xPosition; + } + } + } + + duration = fabsf(journey/velocity); + } + + // Position driven change: + else + { + // we may need to set the drag position + if (xPosition > revealWidth*0.5f) + { + frontViewPosition = FrontViewPositionRight; + if (xPosition > revealWidth) + { + if (bounceBack) + frontViewPosition = FrontViewPositionLeft; + + else if (stableDrag && xPosition > revealWidth+revealOverdraw*0.5f) + frontViewPosition = FrontViewPositionRightMost; + } + } + } + + // symetric replacement of frontViewPosition + [self _getAdjustedFrontViewPosition:&frontViewPosition forSymetry:symetry]; + + // restore user interaction and animate to the final position + [self _restoreUserInteraction]; + [self _setFrontViewPosition:frontViewPosition withDuration:duration]; +} + + +- (void)_handleRevealGestureStateCancelledWithRecognizer:(UIPanGestureRecognizer *)recognizer +{ + [self _restoreUserInteraction]; + [self _dequeue]; +} + + +#pragma mark Enqueued position and controller setup + +- (void)_dispatchSetFrontViewPosition:(FrontViewPosition)frontViewPosition animated:(BOOL)animated +{ + NSTimeInterval duration = animated?_toggleAnimationDuration:0.0; + __weak SWRevealViewController *theSelf = self; + _enqueue( [theSelf _setFrontViewPosition:frontViewPosition withDuration:duration] ); +} + + +- (void)_dispatchSetFrontViewController:(UIViewController *)newFrontViewController animated:(BOOL)animated +{ + int initialPosDif = FrontViewPositionRightMost - _frontViewPosition; + + NSTimeInterval firstDuration ; + if ( initialPosDif <= 0 ) firstDuration = 0.0; + else if ( initialPosDif == 1 ) firstDuration = _toggleAnimationDuration*0.5; + else firstDuration = _toggleAnimationDuration; + + NSTimeInterval duration = animated?_toggleAnimationDuration:0.0; + + __weak SWRevealViewController *theSelf = self; + if ( animated ) + { + _enqueue( [theSelf _setFrontViewPosition:FrontViewPositionRightMost withDuration:firstDuration] ); + _enqueue( [theSelf _setFrontViewController:newFrontViewController] ); + _enqueue( [theSelf _setFrontViewPosition:FrontViewPositionLeft withDuration:duration] ); + } + else + { + _enqueue( [theSelf _setFrontViewController:newFrontViewController] ); + } +} + + +- (void)_dispatchSetRearViewController:(UIViewController *)newRearViewController +{ + __weak SWRevealViewController *theSelf = self; + _enqueue( [theSelf _setRearViewController:newRearViewController] ); +} + + +- (void)_dispatchSetRightViewController:(UIViewController *)newRightViewController +{ + __weak SWRevealViewController *theSelf = self; + _enqueue( [theSelf _setRightViewController:newRightViewController] ); +} + + +#pragma mark animated view controller deployment and layout + +// Primitive method for view controller deployment and animated layout to the given position. +- (void)_setFrontViewPosition:(FrontViewPosition)newPosition withDuration:(NSTimeInterval)duration +{ + void (^rearDeploymentCompletion)() = [self _rearViewDeploymentForNewFrontViewPosition:newPosition]; + void (^rightDeploymentCompletion)() = [self _rightViewDeploymentForNewFrontViewPosition:newPosition]; + void (^frontDeploymentCompletion)() = [self _frontViewDeploymentForNewFrontViewPosition:newPosition]; + + void (^animations)() = ^() + { + // We call the layoutSubviews method on the contentView view and send a delegate, which will + // occur inside of an animation block if any animated transition is being performed + [_contentView layoutSubviews]; + + if ([_delegate respondsToSelector:@selector(revealController:animateToPosition:)]) + [_delegate revealController:self animateToPosition:_frontViewPosition]; + }; + + void (^completion)(BOOL) = ^(BOOL finished) + { + rearDeploymentCompletion(); + rightDeploymentCompletion(); + frontDeploymentCompletion(); + [self _dequeue]; + }; + + if ( duration > 0.0f ) + { + [UIView animateWithDuration:duration delay:0.0 + options:UIViewAnimationOptionCurveEaseOut + animations:animations completion:completion]; + } + else + { + animations(); + completion(YES); + } +} + +// primitive method for front controller transition +- (void)_setFrontViewController:(UIViewController*)newFrontViewController +{ + UIViewController *old = _frontViewController; + _frontViewController = newFrontViewController; + [self _transitionFromViewController:old toViewController:newFrontViewController inView:_contentView.frontView](); + [self _dequeue]; +} + + +// Primitive method for rear controller transition +- (void)_setRearViewController:(UIViewController*)newRearViewController +{ + UIViewController *old = _rearViewController; + _rearViewController = newRearViewController; + [self _transitionFromViewController:old toViewController:newRearViewController inView:_contentView.frontView](); + [self _dequeue]; +} + +// Primitive method for right controller transition +- (void)_setRightViewController:(UIViewController*)newRightViewController +{ + UIViewController *old = _rightViewController; + _rightViewController = newRightViewController; + [self _transitionFromViewController:old toViewController:newRightViewController inView:_contentView.rightView](); + [self _dequeue]; + +// UIViewController *old = _rightViewController; +// void (^completion)() = [self _transitionRearController:old toController:newRightViewController inView:_contentView.rightView]; +// [newRightViewController.view setAlpha:0.0]; +// [UIView animateWithDuration:_toggleAnimationDuration +// animations:^ +// { +// [old.view setAlpha:0.0f]; +// [newRightViewController.view setAlpha:1.0]; +// } +// completion:^(BOOL finished) +// { +// completion(); +// [self _dequeue]; +// }]; +} + + +#pragma mark Position based view controller deployment + +// Deploy/Undeploy of the front view controller following the containment principles. Returns a block +// that must be invoked on animation completion in order to finish deployment +- (void (^)(void))_frontViewDeploymentForNewFrontViewPosition:(FrontViewPosition)newPosition +{ + if ( (_rightViewController == nil && newPosition < FrontViewPositionLeft) || + (_rearViewController == nil && newPosition > FrontViewPositionLeft) ) + newPosition = FrontViewPositionLeft; + + BOOL positionIsChanging = (_frontViewPosition != newPosition); + + BOOL appear = + (_frontViewPosition >= FrontViewPositionRightMostRemoved || _frontViewPosition <= FrontViewPositionLeftSideMostRemoved) && + (newPosition < FrontViewPositionRightMostRemoved && newPosition > FrontViewPositionLeftSideMostRemoved); + + BOOL disappear = + (newPosition >= FrontViewPositionRightMostRemoved || newPosition <= FrontViewPositionLeftSideMostRemoved ) && + (_frontViewPosition < FrontViewPositionRightMostRemoved && _frontViewPosition > FrontViewPositionLeftSideMostRemoved); + + if ( positionIsChanging ) + { + if ( [_delegate respondsToSelector:@selector(revealController:willMoveToPosition:)] ) + [_delegate revealController:self willMoveToPosition:newPosition]; + } + + _frontViewPosition = newPosition; + + void (^deploymentCompletion)() = + [self _deploymentForViewController:_frontViewController inView:_contentView.frontView appear:appear disappear:disappear]; + + void (^completion)() = ^() + { + deploymentCompletion(); + if ( positionIsChanging ) + { + if ( [_delegate respondsToSelector:@selector(revealController:didMoveToPosition:)] ) + [_delegate revealController:self didMoveToPosition:newPosition]; + } + }; + + return completion; +} + +// Deploy/Undeploy of the left view controller following the containment principles. Returns a block +// that must be invoked on animation completion in order to finish deployment +- (void (^)(void))_rearViewDeploymentForNewFrontViewPosition:(FrontViewPosition)newPosition +{ + if ( _rearViewController == nil && newPosition > FrontViewPositionLeft ) + newPosition = FrontViewPositionLeft; + + BOOL appear = (_rearViewPosition <= FrontViewPositionLeft || _rearViewPosition == FrontViewPositionNone) && newPosition > FrontViewPositionLeft; + BOOL disappear = (newPosition <= FrontViewPositionLeft || newPosition == FrontViewPositionNone) && _rearViewPosition > FrontViewPositionLeft; + + if ( appear ) + [_contentView prepareRearViewForPosition:newPosition]; + + _rearViewPosition = newPosition; + + return [self _deploymentForViewController:_rearViewController inView:_contentView.rearView appear:appear disappear:disappear]; +} + +// Deploy/Undeploy of the right view controller following the containment principles. Returns a block +// that must be invoked on animation completion in order to finish deployment +- (void (^)(void))_rightViewDeploymentForNewFrontViewPosition:(FrontViewPosition)newPosition +{ + if ( _rightViewController == nil && newPosition < FrontViewPositionLeft ) + newPosition = FrontViewPositionLeft; + + BOOL appear = _rightViewPosition >= FrontViewPositionLeft && newPosition < FrontViewPositionLeft ; + BOOL disappear = newPosition >= FrontViewPositionLeft && _rightViewPosition < FrontViewPositionLeft; + + if ( appear ) + [_contentView prepareRightViewForPosition:newPosition]; + + _rightViewPosition = newPosition; + + return [self _deploymentForViewController:_rightViewController inView:_contentView.rightView appear:appear disappear:disappear]; +} + + +- (void (^)(void)) _deploymentForViewController:(UIViewController*)controller inView:(UIView*)view appear:(BOOL)appear disappear:(BOOL)disappear +{ + if ( appear ) return [self _deployForViewController:controller inView:view]; + if ( disappear ) return [self _undeployForViewController:controller]; + return ^{}; +} + + +#pragma mark Containment view controller deployment and transition + +// Containment Deploy method. Returns a block to be invoked at the +// animation completion, or right after return in case of non-animated deployment. +- (void (^)(void))_deployForViewController:(UIViewController*)controller inView:(UIView*)view +{ + if ( !controller || !view ) + return ^(void){}; + + CGRect tmpFrame = view.frame; + view.frame = _contentView.bounds; + + UIView *controllerView = controller.view; + controllerView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + controllerView.frame = view.bounds; + + [view addSubview:controllerView]; + view.frame = tmpFrame; + + void (^completionBlock)(void) = ^(void) + { + // nothing to do on completion at this stage + }; + + return completionBlock; +} + +// Containment Undeploy method. Returns a block to be invoked at the +// animation completion, or right after return in case of non-animated deployment. +- (void (^)(void))_undeployForViewController:(UIViewController*)controller +{ + if (!controller) + return ^(void){}; + + // nothing to do before completion at this stage + + void (^completionBlock)(void) = ^(void) + { + [controller.view removeFromSuperview]; + }; + + return completionBlock; +} + +// Containment Transition method. Returns a block to be invoked at the +// animation completion, or right after return in case of non-animated transition. +- (void(^)(void))_transitionFromViewController:(UIViewController*)fromController toViewController:(UIViewController*)toController inView:(UIView*)view +{ + if ( fromController == toController ) + return ^(void){}; + + if ( toController ) [self addChildViewController:toController]; + + void (^deployCompletion)() = [self _deployForViewController:toController inView:view]; + + [fromController willMoveToParentViewController:nil]; + + void (^undeployCompletion)() = [self _undeployForViewController:fromController]; + + void (^completionBlock)(void) = ^(void) + { + undeployCompletion() ; + [fromController removeFromParentViewController]; + + deployCompletion() ; + [toController didMoveToParentViewController:self]; + }; + return completionBlock; +} + + +@end + + +#pragma mark - UIViewController(SWRevealViewController) Category + +@implementation UIViewController(SWRevealViewController) + +- (SWRevealViewController*)revealViewController +{ + UIViewController *parent = self; + Class revealClass = [SWRevealViewController class]; + + while ( nil != (parent = [parent parentViewController]) && ![parent isKindOfClass:revealClass] ) + { + } + + return (id)parent; +} + +@end + + +#pragma mark - SWRevealViewControllerSegue Class + +@implementation SWRevealViewControllerSegue + +- (void)perform +{ + if ( _performBlock != nil ) + { + _performBlock( self, self.sourceViewController, self.destinationViewController ); + } +} + +@end + diff --git a/ios/iosremote/en.lproj/iPad_autosize.storyboard b/ios/iosremote/en.lproj/iPad_autosize.storyboard index ffb97be77937..57700f3c78b7 100644 --- a/ios/iosremote/en.lproj/iPad_autosize.storyboard +++ b/ios/iosremote/en.lproj/iPad_autosize.storyboard @@ -1,8 +1,8 @@ - + - - + + @@ -39,27 +39,26 @@ - + - - + + - - + @@ -79,7 +78,7 @@ - + @@ -108,7 +107,7 @@ - - - -