From b571cdf74872ec8d9fa1e087b93c2e19f86e1690 Mon Sep 17 00:00:00 2001 From: Maxim Makhun Date: Mon, 4 Mar 2019 22:24:31 +0100 Subject: [PATCH] Further refactoring changes. --- ARPlayer.xcodeproj/project.pbxproj | 144 ++++++++++++---- ARPlayer/Categories/NSDateFormatter+Helpers.h | 2 +- ARPlayer/Categories/NSDateFormatter+Helpers.m | 28 ++- ...terial+Colors.h => SCNMaterial+Contents.h} | 4 +- ...terial+Colors.m => SCNMaterial+Contents.m} | 6 +- .../UIImpactFeedbackGenerator+Helpers.h | 23 +++ .../UIImpactFeedbackGenerator+Helpers.m | 29 ++++ .../Categories/UIViewController+Helpers.m | 18 +- ARPlayer/Gestures/GestureHandler.h | 29 ---- ARPlayer/Gestures/GestureHandler.m | 143 ---------------- ARPlayer/Gestures/NodeInsertionHandler.h | 19 +++ ARPlayer/Gestures/NodeInsertionHandler.m | 41 +++++ ARPlayer/Gestures/NodePlaybackHandler.h | 19 +++ ARPlayer/Gestures/NodePlaybackHandler.m | 54 ++++++ ARPlayer/Gestures/NodePositionHandler.h | 19 +++ ARPlayer/Gestures/NodePositionHandler.m | 42 +++++ ARPlayer/Gestures/NodeRotationHandler.h | 19 +++ ARPlayer/Gestures/NodeRotationHandler.m | 54 ++++++ ARPlayer/Gestures/NodeScaleHandler.h | 19 +++ ARPlayer/Gestures/NodeScaleHandler.m | 53 ++++++ .../Protocols/GestureHandleProtocol.h | 20 +++ .../Gestures/SceneGestureRecognizerDelegate.h | 21 +++ .../Gestures/SceneGestureRecognizerDelegate.m | 110 ++++++++++++ ARPlayer/Nodes/ControlNodes/ControlNode.h | 27 +++ ARPlayer/Nodes/ControlNodes/ControlNode.m | 44 +++++ ARPlayer/Nodes/ControlNodes/PlayNode.h | 34 ++++ ARPlayer/Nodes/{ => ControlNodes}/PlayNode.m | 18 +- ARPlayer/Nodes/{ => ControlNodes}/StopNode.h | 8 +- ARPlayer/Nodes/ControlNodes/StopNode.m | 40 +++++ .../SwitchTrackNodes/NextTrackNode.h | 4 + .../SwitchTrackNodes/NextTrackNode.m | 10 +- .../SwitchTrackNodes/PreviousTrackNode.h | 4 + .../SwitchTrackNodes/PreviousTrackNode.m | 10 +- .../SwitchTrackNodes/SwitchTrackNode.h | 8 +- .../SwitchTrackNodes/SwitchTrackNode.m | 5 +- ARPlayer/Nodes/CurrentTimeNode.h | 12 ++ ARPlayer/Nodes/CurrentTimeNode.m | 20 ++- ARPlayer/Nodes/MediaPlayerNode.h | 41 ++++- ARPlayer/Nodes/MediaPlayerNode.m | 63 ++++--- ARPlayer/Nodes/PlaneRendererNode.h | 25 ++- ARPlayer/Nodes/PlaneRendererNode.m | 2 +- ARPlayer/Nodes/PlayNode.h | 24 --- ...yerNodeProtocol.h => AnimatableProtocol.h} | 8 +- ARPlayer/Nodes/StopNode.m | 36 ---- ARPlayer/Nodes/TVNode.h | 14 +- ARPlayer/Nodes/TVNode.m | 34 ++-- ARPlayer/Services/PlaylistService.h | 19 +++ ARPlayer/Services/PlaylistService.m | 21 +++ ARPlayer/Settings/SettingsManager.h | 5 + ARPlayer/Utils/Utils.h | 17 -- ARPlayer/Utils/Utils.m | 46 ----- .../Delegates/PlaneRendererDelegate.h | 17 ++ .../Delegates/PlaneRendererDelegate.m | 90 ++++++++++ .../ViewControllers/PlayerViewController.m | 160 ++++-------------- .../ViewControllers/SettingsViewController.m | 31 ++-- ARPlayerTests/NSDateFormatter+HelpersTests.m | 52 ++++++ ARPlayerTests/SCNMaterial+ColorsTests.m | 2 +- 57 files changed, 1298 insertions(+), 569 deletions(-) rename ARPlayer/Categories/{SCNMaterial+Colors.h => SCNMaterial+Contents.h} (81%) rename ARPlayer/Categories/{SCNMaterial+Colors.m => SCNMaterial+Contents.m} (74%) create mode 100644 ARPlayer/Categories/UIImpactFeedbackGenerator+Helpers.h create mode 100644 ARPlayer/Categories/UIImpactFeedbackGenerator+Helpers.m delete mode 100644 ARPlayer/Gestures/GestureHandler.h delete mode 100644 ARPlayer/Gestures/GestureHandler.m create mode 100644 ARPlayer/Gestures/NodeInsertionHandler.h create mode 100644 ARPlayer/Gestures/NodeInsertionHandler.m create mode 100644 ARPlayer/Gestures/NodePlaybackHandler.h create mode 100644 ARPlayer/Gestures/NodePlaybackHandler.m create mode 100644 ARPlayer/Gestures/NodePositionHandler.h create mode 100644 ARPlayer/Gestures/NodePositionHandler.m create mode 100644 ARPlayer/Gestures/NodeRotationHandler.h create mode 100644 ARPlayer/Gestures/NodeRotationHandler.m create mode 100644 ARPlayer/Gestures/NodeScaleHandler.h create mode 100644 ARPlayer/Gestures/NodeScaleHandler.m create mode 100644 ARPlayer/Gestures/Protocols/GestureHandleProtocol.h create mode 100644 ARPlayer/Gestures/SceneGestureRecognizerDelegate.h create mode 100644 ARPlayer/Gestures/SceneGestureRecognizerDelegate.m create mode 100644 ARPlayer/Nodes/ControlNodes/ControlNode.h create mode 100644 ARPlayer/Nodes/ControlNodes/ControlNode.m create mode 100644 ARPlayer/Nodes/ControlNodes/PlayNode.h rename ARPlayer/Nodes/{ => ControlNodes}/PlayNode.m (85%) rename ARPlayer/Nodes/{ => ControlNodes}/StopNode.h (58%) create mode 100644 ARPlayer/Nodes/ControlNodes/StopNode.m rename ARPlayer/Nodes/{ => ControlNodes}/SwitchTrackNodes/NextTrackNode.h (74%) rename ARPlayer/Nodes/{ => ControlNodes}/SwitchTrackNodes/NextTrackNode.m (64%) rename ARPlayer/Nodes/{ => ControlNodes}/SwitchTrackNodes/PreviousTrackNode.h (73%) rename ARPlayer/Nodes/{ => ControlNodes}/SwitchTrackNodes/PreviousTrackNode.m (62%) rename ARPlayer/Nodes/{ => ControlNodes}/SwitchTrackNodes/SwitchTrackNode.h (56%) rename ARPlayer/Nodes/{ => ControlNodes}/SwitchTrackNodes/SwitchTrackNode.m (91%) delete mode 100644 ARPlayer/Nodes/PlayNode.h rename ARPlayer/Nodes/Protocols/{PlayerNodeProtocol.h => AnimatableProtocol.h} (56%) delete mode 100644 ARPlayer/Nodes/StopNode.m create mode 100644 ARPlayer/Services/PlaylistService.h create mode 100644 ARPlayer/Services/PlaylistService.m delete mode 100644 ARPlayer/Utils/Utils.h delete mode 100644 ARPlayer/Utils/Utils.m create mode 100644 ARPlayer/ViewControllers/Delegates/PlaneRendererDelegate.h create mode 100644 ARPlayer/ViewControllers/Delegates/PlaneRendererDelegate.m create mode 100644 ARPlayerTests/NSDateFormatter+HelpersTests.m diff --git a/ARPlayer.xcodeproj/project.pbxproj b/ARPlayer.xcodeproj/project.pbxproj index d1416c6..9612db5 100644 --- a/ARPlayer.xcodeproj/project.pbxproj +++ b/ARPlayer.xcodeproj/project.pbxproj @@ -17,16 +17,25 @@ 6D1BB4201F77D2C500E5175E /* SettingsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D1BB41E1F77D2C500E5175E /* SettingsViewController.xib */; }; 6D1BB4241F77D3B100E5175E /* PlayerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6D1BB4221F77D3B100E5175E /* PlayerViewController.m */; }; 6D1BB4251F77D3B100E5175E /* PlayerViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D1BB4231F77D3B100E5175E /* PlayerViewController.xib */; }; - 6D1CAD511F7713E800D250CB /* Utils.m in Sources */ = {isa = PBXBuildFile; fileRef = 6D1CAD501F7713E800D250CB /* Utils.m */; }; 6D22FD051F768EAE006CDBD5 /* SettingsManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 6D22FD041F768EAE006CDBD5 /* SettingsManager.m */; }; 6D2A5E321F73233F0095F03A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6D2A5E311F73233F0095F03A /* AppDelegate.m */; }; 6D2A5E3A1F73233F0095F03A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6D2A5E391F73233F0095F03A /* Assets.xcassets */; }; 6D2A5E3D1F73233F0095F03A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6D2A5E3B1F73233F0095F03A /* LaunchScreen.storyboard */; }; 6D2A5E401F73233F0095F03A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 6D2A5E3F1F73233F0095F03A /* main.m */; }; 6D2A5E481F7323930095F03A /* MediaPlayerNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 6D2A5E471F7323920095F03A /* MediaPlayerNode.m */; }; - 6D8E92841F81014900046E25 /* GestureHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 6D8E92831F81014900046E25 /* GestureHandler.m */; }; 6DA2E01E1F7E655100B82663 /* Art.scnassets in Resources */ = {isa = PBXBuildFile; fileRef = 6DA2E01D1F7E655100B82663 /* Art.scnassets */; }; 6DCC3AB81F85785300E4C94B /* NextTrackNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 6DCC3AB71F85785300E4C94B /* NextTrackNode.m */; }; + 93DA5A36222D3BA800CA1B8B /* PlaylistService.m in Sources */ = {isa = PBXBuildFile; fileRef = 93DA5A35222D3BA800CA1B8B /* PlaylistService.m */; }; + 93DA5A3A222D3F1600CA1B8B /* ControlNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 93DA5A39222D3F1600CA1B8B /* ControlNode.m */; }; + 93DA5A3D222D458100CA1B8B /* PlaneRendererDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 93DA5A3C222D458100CA1B8B /* PlaneRendererDelegate.m */; }; + 93DA5A40222D48F900CA1B8B /* SceneGestureRecognizerDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 93DA5A3F222D48F900CA1B8B /* SceneGestureRecognizerDelegate.m */; }; + 93DA5A5C222D73B300CA1B8B /* NodePlaybackHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 93DA5A5B222D73B300CA1B8B /* NodePlaybackHandler.m */; }; + 93DA5A5F222D76F800CA1B8B /* NodeInsertionHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 93DA5A5E222D76F800CA1B8B /* NodeInsertionHandler.m */; }; + 93DA5A62222D77D200CA1B8B /* NodePositionHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 93DA5A61222D77D200CA1B8B /* NodePositionHandler.m */; }; + 93DA5A65222D788800CA1B8B /* NodeRotationHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 93DA5A64222D788800CA1B8B /* NodeRotationHandler.m */; }; + 93DA5A68222D797A00CA1B8B /* NodeScaleHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 93DA5A67222D797A00CA1B8B /* NodeScaleHandler.m */; }; + 93DA5A6E222D99C900CA1B8B /* UIImpactFeedbackGenerator+Helpers.m in Sources */ = {isa = PBXBuildFile; fileRef = 93DA5A6D222D99C900CA1B8B /* UIImpactFeedbackGenerator+Helpers.m */; }; + 93DA5A72222DC3F600CA1B8B /* NSDateFormatter+HelpersTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 93DA5A71222DC3F600CA1B8B /* NSDateFormatter+HelpersTests.m */; }; 93E260E2222C35FB00C4C81D /* UIViewController+Helpers.m in Sources */ = {isa = PBXBuildFile; fileRef = 93E260E1222C35FB00C4C81D /* UIViewController+Helpers.m */; }; 93E260E5222C37F400C4C81D /* SCNNode+Helpers.m in Sources */ = {isa = PBXBuildFile; fileRef = 93E260E4222C37F400C4C81D /* SCNNode+Helpers.m */; }; 93E26102222C3C2800C4C81D /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 93E26101222C3C2800C4C81D /* AppDelegate.m */; }; @@ -46,10 +55,10 @@ 93E2613C222C5B2400C4C81D /* Constants.m in Sources */ = {isa = PBXBuildFile; fileRef = 93E2612C222C42CD00C4C81D /* Constants.m */; }; 93ED08E0222C71910061433C /* SwitchTrackNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 93ED08DF222C71910061433C /* SwitchTrackNode.m */; }; 93ED08E3222C72630061433C /* PreviousTrackNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 93ED08E2222C72630061433C /* PreviousTrackNode.m */; }; - 93ED08EB222C8A3E0061433C /* SCNMaterial+Colors.m in Sources */ = {isa = PBXBuildFile; fileRef = 93ED08EA222C8A3E0061433C /* SCNMaterial+Colors.m */; }; + 93ED08EB222C8A3E0061433C /* SCNMaterial+Contents.m in Sources */ = {isa = PBXBuildFile; fileRef = 93ED08EA222C8A3E0061433C /* SCNMaterial+Contents.m */; }; 93ED08ED222C8C830061433C /* SCNMaterial+ColorsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 93ED08EC222C8C830061433C /* SCNMaterial+ColorsTests.m */; }; - 93ED08EE222C8CAA0061433C /* SCNMaterial+Colors.m in Sources */ = {isa = PBXBuildFile; fileRef = 93ED08EA222C8A3E0061433C /* SCNMaterial+Colors.m */; }; - 93ED08EF222C8CAB0061433C /* SCNMaterial+Colors.m in Sources */ = {isa = PBXBuildFile; fileRef = 93ED08EA222C8A3E0061433C /* SCNMaterial+Colors.m */; }; + 93ED08EE222C8CAA0061433C /* SCNMaterial+Contents.m in Sources */ = {isa = PBXBuildFile; fileRef = 93ED08EA222C8A3E0061433C /* SCNMaterial+Contents.m */; }; + 93ED08EF222C8CAB0061433C /* SCNMaterial+Contents.m in Sources */ = {isa = PBXBuildFile; fileRef = 93ED08EA222C8A3E0061433C /* SCNMaterial+Contents.m */; }; 93ED08F2222C909A0061433C /* NSDateFormatter+Helpers.m in Sources */ = {isa = PBXBuildFile; fileRef = 93ED08F1222C909A0061433C /* NSDateFormatter+Helpers.m */; }; 93ED08F3222C90E10061433C /* NSDateFormatter+Helpers.m in Sources */ = {isa = PBXBuildFile; fileRef = 93ED08F1222C909A0061433C /* NSDateFormatter+Helpers.m */; }; 93ED08F4222C90E20061433C /* NSDateFormatter+Helpers.m in Sources */ = {isa = PBXBuildFile; fileRef = 93ED08F1222C909A0061433C /* NSDateFormatter+Helpers.m */; }; @@ -83,8 +92,6 @@ 6D1BB4211F77D3B100E5175E /* PlayerViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PlayerViewController.h; sourceTree = ""; }; 6D1BB4221F77D3B100E5175E /* PlayerViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PlayerViewController.m; sourceTree = ""; }; 6D1BB4231F77D3B100E5175E /* PlayerViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = PlayerViewController.xib; sourceTree = ""; }; - 6D1CAD4F1F7713E800D250CB /* Utils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Utils.h; sourceTree = ""; }; - 6D1CAD501F7713E800D250CB /* Utils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Utils.m; sourceTree = ""; }; 6D22FD031F768EAE006CDBD5 /* SettingsManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SettingsManager.h; sourceTree = ""; }; 6D22FD041F768EAE006CDBD5 /* SettingsManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SettingsManager.m; sourceTree = ""; }; 6D2A5E2D1F73233F0095F03A /* ARPlayer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ARPlayer.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -96,11 +103,32 @@ 6D2A5E3F1F73233F0095F03A /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 6D2A5E461F7323920095F03A /* MediaPlayerNode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MediaPlayerNode.h; sourceTree = ""; }; 6D2A5E471F7323920095F03A /* MediaPlayerNode.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MediaPlayerNode.m; sourceTree = ""; }; - 6D8E92821F81014900046E25 /* GestureHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GestureHandler.h; sourceTree = ""; }; - 6D8E92831F81014900046E25 /* GestureHandler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GestureHandler.m; sourceTree = ""; }; 6DA2E01D1F7E655100B82663 /* Art.scnassets */ = {isa = PBXFileReference; lastKnownFileType = wrapper.scnassets; path = Art.scnassets; sourceTree = ""; }; 6DCC3AB61F85785300E4C94B /* NextTrackNode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NextTrackNode.h; sourceTree = ""; }; 6DCC3AB71F85785300E4C94B /* NextTrackNode.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NextTrackNode.m; sourceTree = ""; }; + 93DA5A34222D3BA800CA1B8B /* PlaylistService.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PlaylistService.h; sourceTree = ""; }; + 93DA5A35222D3BA800CA1B8B /* PlaylistService.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PlaylistService.m; sourceTree = ""; }; + 93DA5A37222D3EB100CA1B8B /* AnimatableProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AnimatableProtocol.h; sourceTree = ""; }; + 93DA5A38222D3F1600CA1B8B /* ControlNode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ControlNode.h; sourceTree = ""; }; + 93DA5A39222D3F1600CA1B8B /* ControlNode.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ControlNode.m; sourceTree = ""; }; + 93DA5A3B222D458100CA1B8B /* PlaneRendererDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PlaneRendererDelegate.h; sourceTree = ""; }; + 93DA5A3C222D458100CA1B8B /* PlaneRendererDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PlaneRendererDelegate.m; sourceTree = ""; }; + 93DA5A3E222D48F900CA1B8B /* SceneGestureRecognizerDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SceneGestureRecognizerDelegate.h; sourceTree = ""; }; + 93DA5A3F222D48F900CA1B8B /* SceneGestureRecognizerDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SceneGestureRecognizerDelegate.m; sourceTree = ""; }; + 93DA5A56222D670300CA1B8B /* GestureHandleProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GestureHandleProtocol.h; sourceTree = ""; }; + 93DA5A5A222D73B300CA1B8B /* NodePlaybackHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NodePlaybackHandler.h; sourceTree = ""; }; + 93DA5A5B222D73B300CA1B8B /* NodePlaybackHandler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NodePlaybackHandler.m; sourceTree = ""; }; + 93DA5A5D222D76F800CA1B8B /* NodeInsertionHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NodeInsertionHandler.h; sourceTree = ""; }; + 93DA5A5E222D76F800CA1B8B /* NodeInsertionHandler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NodeInsertionHandler.m; sourceTree = ""; }; + 93DA5A60222D77D200CA1B8B /* NodePositionHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NodePositionHandler.h; sourceTree = ""; }; + 93DA5A61222D77D200CA1B8B /* NodePositionHandler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NodePositionHandler.m; sourceTree = ""; }; + 93DA5A63222D788800CA1B8B /* NodeRotationHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NodeRotationHandler.h; sourceTree = ""; }; + 93DA5A64222D788800CA1B8B /* NodeRotationHandler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NodeRotationHandler.m; sourceTree = ""; }; + 93DA5A66222D797A00CA1B8B /* NodeScaleHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NodeScaleHandler.h; sourceTree = ""; }; + 93DA5A67222D797A00CA1B8B /* NodeScaleHandler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NodeScaleHandler.m; sourceTree = ""; }; + 93DA5A6C222D99C900CA1B8B /* UIImpactFeedbackGenerator+Helpers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIImpactFeedbackGenerator+Helpers.h"; sourceTree = ""; }; + 93DA5A6D222D99C900CA1B8B /* UIImpactFeedbackGenerator+Helpers.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIImpactFeedbackGenerator+Helpers.m"; sourceTree = ""; }; + 93DA5A71222DC3F600CA1B8B /* NSDateFormatter+HelpersTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSDateFormatter+HelpersTests.m"; sourceTree = ""; }; 93E260E0222C35FB00C4C81D /* UIViewController+Helpers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIViewController+Helpers.h"; sourceTree = ""; }; 93E260E1222C35FB00C4C81D /* UIViewController+Helpers.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIViewController+Helpers.m"; sourceTree = ""; }; 93E260E3222C37F400C4C81D /* SCNNode+Helpers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SCNNode+Helpers.h"; sourceTree = ""; }; @@ -125,9 +153,8 @@ 93ED08DF222C71910061433C /* SwitchTrackNode.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SwitchTrackNode.m; sourceTree = ""; }; 93ED08E1222C72630061433C /* PreviousTrackNode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PreviousTrackNode.h; sourceTree = ""; }; 93ED08E2222C72630061433C /* PreviousTrackNode.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PreviousTrackNode.m; sourceTree = ""; }; - 93ED08E4222C74DD0061433C /* PlayerNodeProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PlayerNodeProtocol.h; sourceTree = ""; }; - 93ED08E9222C8A3E0061433C /* SCNMaterial+Colors.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SCNMaterial+Colors.h"; sourceTree = ""; }; - 93ED08EA222C8A3E0061433C /* SCNMaterial+Colors.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "SCNMaterial+Colors.m"; sourceTree = ""; }; + 93ED08E9222C8A3E0061433C /* SCNMaterial+Contents.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SCNMaterial+Contents.h"; sourceTree = ""; }; + 93ED08EA222C8A3E0061433C /* SCNMaterial+Contents.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "SCNMaterial+Contents.m"; sourceTree = ""; }; 93ED08EC222C8C830061433C /* SCNMaterial+ColorsTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "SCNMaterial+ColorsTests.m"; sourceTree = ""; }; 93ED08F0222C909A0061433C /* NSDateFormatter+Helpers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSDateFormatter+Helpers.h"; sourceTree = ""; }; 93ED08F1222C909A0061433C /* NSDateFormatter+Helpers.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSDateFormatter+Helpers.m"; sourceTree = ""; }; @@ -172,6 +199,7 @@ 6D1BB4151F77BDE100E5175E /* ViewControllers */ = { isa = PBXGroup; children = ( + 93DA5A41222D490300CA1B8B /* Delegates */, 6D1BB4211F77D3B100E5175E /* PlayerViewController.h */, 6D1BB4221F77D3B100E5175E /* PlayerViewController.m */, 6D1BB4231F77D3B100E5175E /* PlayerViewController.xib */, @@ -186,17 +214,13 @@ isa = PBXGroup; children = ( 93ED08E7222C79AC0061433C /* Protocols */, - 93ED08E8222C86240061433C /* SwitchTrackNodes */, + 93DA5A6F222D9F9B00CA1B8B /* ControlNodes */, 6D2A5E461F7323920095F03A /* MediaPlayerNode.h */, 6D2A5E471F7323920095F03A /* MediaPlayerNode.m */, 6D06790D1F804CDB0081574B /* TVNode.h */, 6D06790E1F804CDB0081574B /* TVNode.m */, 6D15BA2E1F744F4C002FABB0 /* PlaneRendererNode.h */, 6D15BA2F1F744F4C002FABB0 /* PlaneRendererNode.m */, - 6D0679041F803D8F0081574B /* PlayNode.h */, - 6D0679051F803D8F0081574B /* PlayNode.m */, - 6D0679071F803DA20081574B /* StopNode.h */, - 6D0679081F803DA20081574B /* StopNode.m */, 6D06790A1F803DB00081574B /* CurrentTimeNode.h */, 6D06790B1F803DB00081574B /* CurrentTimeNode.m */, ); @@ -226,6 +250,7 @@ 6D2A5E2F1F73233F0095F03A /* ARPlayer */ = { isa = PBXGroup; children = ( + 93DA5A33222D3B9100CA1B8B /* Services */, 6D1BB4151F77BDE100E5175E /* ViewControllers */, 6D1CAD521F77162C00D250CB /* Nodes */, 93E260DF222C324E00C4C81D /* Categories */, @@ -241,6 +266,46 @@ path = ARPlayer; sourceTree = ""; }; + 93DA5A33222D3B9100CA1B8B /* Services */ = { + isa = PBXGroup; + children = ( + 93DA5A34222D3BA800CA1B8B /* PlaylistService.h */, + 93DA5A35222D3BA800CA1B8B /* PlaylistService.m */, + ); + path = Services; + sourceTree = ""; + }; + 93DA5A41222D490300CA1B8B /* Delegates */ = { + isa = PBXGroup; + children = ( + 93DA5A3B222D458100CA1B8B /* PlaneRendererDelegate.h */, + 93DA5A3C222D458100CA1B8B /* PlaneRendererDelegate.m */, + ); + path = Delegates; + sourceTree = ""; + }; + 93DA5A6F222D9F9B00CA1B8B /* ControlNodes */ = { + isa = PBXGroup; + children = ( + 93ED08E8222C86240061433C /* SwitchTrackNodes */, + 93DA5A38222D3F1600CA1B8B /* ControlNode.h */, + 93DA5A39222D3F1600CA1B8B /* ControlNode.m */, + 6D0679041F803D8F0081574B /* PlayNode.h */, + 6D0679051F803D8F0081574B /* PlayNode.m */, + 6D0679071F803DA20081574B /* StopNode.h */, + 6D0679081F803DA20081574B /* StopNode.m */, + ); + path = ControlNodes; + sourceTree = ""; + }; + 93DA5A70222DBA4200CA1B8B /* Protocols */ = { + isa = PBXGroup; + children = ( + 93DA5A56222D670300CA1B8B /* GestureHandleProtocol.h */, + ); + path = Protocols; + sourceTree = ""; + }; 93E260DF222C324E00C4C81D /* Categories */ = { isa = PBXGroup; children = ( @@ -248,10 +313,12 @@ 93E260E1222C35FB00C4C81D /* UIViewController+Helpers.m */, 93E260E3222C37F400C4C81D /* SCNNode+Helpers.h */, 93E260E4222C37F400C4C81D /* SCNNode+Helpers.m */, - 93ED08E9222C8A3E0061433C /* SCNMaterial+Colors.h */, - 93ED08EA222C8A3E0061433C /* SCNMaterial+Colors.m */, + 93ED08E9222C8A3E0061433C /* SCNMaterial+Contents.h */, + 93ED08EA222C8A3E0061433C /* SCNMaterial+Contents.m */, 93ED08F0222C909A0061433C /* NSDateFormatter+Helpers.h */, 93ED08F1222C909A0061433C /* NSDateFormatter+Helpers.m */, + 93DA5A6C222D99C900CA1B8B /* UIImpactFeedbackGenerator+Helpers.h */, + 93DA5A6D222D99C900CA1B8B /* UIImpactFeedbackGenerator+Helpers.m */, ); path = Categories; sourceTree = ""; @@ -278,6 +345,7 @@ 93E26125222C407500C4C81D /* SCNNode+HelpersTests.m */, 93E26131222C4D0500C4C81D /* SettingsTests.m */, 93ED08EC222C8C830061433C /* SCNMaterial+ColorsTests.m */, + 93DA5A71222DC3F600CA1B8B /* NSDateFormatter+HelpersTests.m */, 93E2611B222C3C2A00C4C81D /* Info.plist */, ); path = ARPlayerTests; @@ -286,8 +354,6 @@ 93E26127222C40A300C4C81D /* Utils */ = { isa = PBXGroup; children = ( - 6D1CAD4F1F7713E800D250CB /* Utils.h */, - 6D1CAD501F7713E800D250CB /* Utils.m */, 93E2612B222C42CD00C4C81D /* Constants.h */, 93E2612C222C42CD00C4C81D /* Constants.m */, ); @@ -306,8 +372,19 @@ 93E26134222C4EDD00C4C81D /* Gestures */ = { isa = PBXGroup; children = ( - 6D8E92821F81014900046E25 /* GestureHandler.h */, - 6D8E92831F81014900046E25 /* GestureHandler.m */, + 93DA5A70222DBA4200CA1B8B /* Protocols */, + 93DA5A3E222D48F900CA1B8B /* SceneGestureRecognizerDelegate.h */, + 93DA5A3F222D48F900CA1B8B /* SceneGestureRecognizerDelegate.m */, + 93DA5A5A222D73B300CA1B8B /* NodePlaybackHandler.h */, + 93DA5A5B222D73B300CA1B8B /* NodePlaybackHandler.m */, + 93DA5A5D222D76F800CA1B8B /* NodeInsertionHandler.h */, + 93DA5A5E222D76F800CA1B8B /* NodeInsertionHandler.m */, + 93DA5A60222D77D200CA1B8B /* NodePositionHandler.h */, + 93DA5A61222D77D200CA1B8B /* NodePositionHandler.m */, + 93DA5A63222D788800CA1B8B /* NodeRotationHandler.h */, + 93DA5A64222D788800CA1B8B /* NodeRotationHandler.m */, + 93DA5A66222D797A00CA1B8B /* NodeScaleHandler.h */, + 93DA5A67222D797A00CA1B8B /* NodeScaleHandler.m */, ); path = Gestures; sourceTree = ""; @@ -315,7 +392,7 @@ 93ED08E7222C79AC0061433C /* Protocols */ = { isa = PBXGroup; children = ( - 93ED08E4222C74DD0061433C /* PlayerNodeProtocol.h */, + 93DA5A37222D3EB100CA1B8B /* AnimatableProtocol.h */, ); path = Protocols; sourceTree = ""; @@ -470,27 +547,35 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 93DA5A5F222D76F800CA1B8B /* NodeInsertionHandler.m in Sources */, 6D2A5E401F73233F0095F03A /* main.m in Sources */, 93ED08E0222C71910061433C /* SwitchTrackNode.m in Sources */, 6D0679061F803D8F0081574B /* PlayNode.m in Sources */, + 93DA5A3D222D458100CA1B8B /* PlaneRendererDelegate.m in Sources */, 6D2A5E481F7323930095F03A /* MediaPlayerNode.m in Sources */, 93E260E2222C35FB00C4C81D /* UIViewController+Helpers.m in Sources */, 93E260E5222C37F400C4C81D /* SCNNode+Helpers.m in Sources */, 6D1BB41F1F77D2C500E5175E /* SettingsViewController.m in Sources */, + 93DA5A5C222D73B300CA1B8B /* NodePlaybackHandler.m in Sources */, + 93DA5A36222D3BA800CA1B8B /* PlaylistService.m in Sources */, 6D1BB4241F77D3B100E5175E /* PlayerViewController.m in Sources */, 6D15BA301F744F4C002FABB0 /* PlaneRendererNode.m in Sources */, 93E2612D222C42CD00C4C81D /* Constants.m in Sources */, 93ED08F2222C909A0061433C /* NSDateFormatter+Helpers.m in Sources */, - 6D8E92841F81014900046E25 /* GestureHandler.m in Sources */, 93ED08E3222C72630061433C /* PreviousTrackNode.m in Sources */, 6D22FD051F768EAE006CDBD5 /* SettingsManager.m in Sources */, + 93DA5A40222D48F900CA1B8B /* SceneGestureRecognizerDelegate.m in Sources */, 6D06790F1F804CDB0081574B /* TVNode.m in Sources */, + 93DA5A65222D788800CA1B8B /* NodeRotationHandler.m in Sources */, 6D0679091F803DA20081574B /* StopNode.m in Sources */, - 93ED08EB222C8A3E0061433C /* SCNMaterial+Colors.m in Sources */, + 93ED08EB222C8A3E0061433C /* SCNMaterial+Contents.m in Sources */, 6DCC3AB81F85785300E4C94B /* NextTrackNode.m in Sources */, - 6D1CAD511F7713E800D250CB /* Utils.m in Sources */, + 93DA5A68222D797A00CA1B8B /* NodeScaleHandler.m in Sources */, 6D2A5E321F73233F0095F03A /* AppDelegate.m in Sources */, 6D06790C1F803DB00081574B /* CurrentTimeNode.m in Sources */, + 93DA5A3A222D3F1600CA1B8B /* ControlNode.m in Sources */, + 93DA5A6E222D99C900CA1B8B /* UIImpactFeedbackGenerator+Helpers.m in Sources */, + 93DA5A62222D77D200CA1B8B /* NodePositionHandler.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -498,7 +583,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 93ED08EF222C8CAB0061433C /* SCNMaterial+Colors.m in Sources */, + 93ED08EF222C8CAB0061433C /* SCNMaterial+Contents.m in Sources */, 93E2612F222C4A2E00C4C81D /* SCNNode+Helpers.m in Sources */, 93E26105222C3C2800C4C81D /* ViewController.m in Sources */, 93E26110222C3C2A00C4C81D /* main.m in Sources */, @@ -513,9 +598,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 93ED08EE222C8CAA0061433C /* SCNMaterial+Colors.m in Sources */, + 93ED08EE222C8CAA0061433C /* SCNMaterial+Contents.m in Sources */, 93E2613C222C5B2400C4C81D /* Constants.m in Sources */, 93ED08ED222C8C830061433C /* SCNMaterial+ColorsTests.m in Sources */, + 93DA5A72222DC3F600CA1B8B /* NSDateFormatter+HelpersTests.m in Sources */, 93E26130222C4A3600C4C81D /* SCNNode+Helpers.m in Sources */, 93E26139222C5B1700C4C81D /* SettingsManager.m in Sources */, 93ED08F3222C90E10061433C /* NSDateFormatter+Helpers.m in Sources */, diff --git a/ARPlayer/Categories/NSDateFormatter+Helpers.h b/ARPlayer/Categories/NSDateFormatter+Helpers.h index d9b3bee..9fe9644 100644 --- a/ARPlayer/Categories/NSDateFormatter+Helpers.h +++ b/ARPlayer/Categories/NSDateFormatter+Helpers.h @@ -12,7 +12,7 @@ NS_ASSUME_NONNULL_BEGIN @interface NSDateFormatter (Helpers) -+ (NSString *)toString:(CMTime)time; ++ (NSString *)currentTimeStringForTime:(CMTime)time; @end diff --git a/ARPlayer/Categories/NSDateFormatter+Helpers.m b/ARPlayer/Categories/NSDateFormatter+Helpers.m index 8afbc3b..33682aa 100644 --- a/ARPlayer/Categories/NSDateFormatter+Helpers.m +++ b/ARPlayer/Categories/NSDateFormatter+Helpers.m @@ -12,14 +12,32 @@ @implementation NSDateFormatter (Helpers) -+ (NSString *)toString:(CMTime)time { ++ (instancetype)dateFormatter { + static NSDateFormatter *dateFormatter; + static dispatch_once_t onceToken; + + dispatch_once(&onceToken, ^{ + dateFormatter = [NSDateFormatter new]; + [dateFormatter setTimeZone:[NSTimeZone timeZoneWithName:@"UTC"]]; + }); + + return dateFormatter; +} + ++ (NSString *)currentTimeStringForTime:(CMTime)time { Float64 seconds = CMTimeGetSeconds(time); NSDate *date = [[NSDate alloc] initWithTimeIntervalSince1970:seconds]; - NSDateFormatter *dateFormatter = [NSDateFormatter new]; - [dateFormatter setDateFormat:(NSUInteger)(seconds / 3600) > 0 ? @"HH:mm:ss" : @"mm:ss"]; - [dateFormatter setTimeZone:[NSTimeZone timeZoneWithName:@"UTC"]]; + NSDateFormatter *dateFormatter = [NSDateFormatter dateFormatter]; + + NSString *dateFormat = @"mm:ss"; + NSUInteger hours = seconds / 3600; + if (hours > 0) { + dateFormat = @"HH:mm:ss"; + } + + [dateFormatter setDateFormat:dateFormat]; NSString *currentTime = [dateFormatter stringFromDate:date]; - + return currentTime; } diff --git a/ARPlayer/Categories/SCNMaterial+Colors.h b/ARPlayer/Categories/SCNMaterial+Contents.h similarity index 81% rename from ARPlayer/Categories/SCNMaterial+Colors.h rename to ARPlayer/Categories/SCNMaterial+Contents.h index d0e5415..6f2f5d2 100644 --- a/ARPlayer/Categories/SCNMaterial+Colors.h +++ b/ARPlayer/Categories/SCNMaterial+Contents.h @@ -1,5 +1,5 @@ // -// SCNMaterial+Colors.h +// SCNMaterial+Contents.h // ARPlayer // // Created by Maxim Makhun on 03/03/2019. @@ -11,7 +11,7 @@ NS_ASSUME_NONNULL_BEGIN -@interface SCNMaterial (Colors) +@interface SCNMaterial (Contents) + (instancetype)materialWithColor:(UIColor *)color; diff --git a/ARPlayer/Categories/SCNMaterial+Colors.m b/ARPlayer/Categories/SCNMaterial+Contents.m similarity index 74% rename from ARPlayer/Categories/SCNMaterial+Colors.m rename to ARPlayer/Categories/SCNMaterial+Contents.m index d89213d..a9a8ee7 100644 --- a/ARPlayer/Categories/SCNMaterial+Colors.m +++ b/ARPlayer/Categories/SCNMaterial+Contents.m @@ -1,14 +1,14 @@ // -// SCNMaterial+Colors.m +// SCNMaterial+Contents.m // ARPlayer // // Created by Maxim Makhun on 03/03/2019. // Copyright © 2019 Maxim Makhun. All rights reserved. // -#import "SCNMaterial+Colors.h" +#import "SCNMaterial+Contents.h" -@implementation SCNMaterial (Colors) +@implementation SCNMaterial (Contents) + (SCNMaterial *)materialWithColor:(UIColor *)color { SCNMaterial *material = [SCNMaterial new]; diff --git a/ARPlayer/Categories/UIImpactFeedbackGenerator+Helpers.h b/ARPlayer/Categories/UIImpactFeedbackGenerator+Helpers.h new file mode 100644 index 0000000..cc365ea --- /dev/null +++ b/ARPlayer/Categories/UIImpactFeedbackGenerator+Helpers.h @@ -0,0 +1,23 @@ +// +// UIImpactFeedbackGenerator+Helpers.h +// ARPlayer +// +// Created by Maxim Makhun on 04/03/2019. +// Copyright © 2019 Maxim Makhun. All rights reserved. +// + +@import UIKit; + +NS_ASSUME_NONNULL_BEGIN + +@interface UIImpactFeedbackGenerator (Helpers) + ++ (void)lightImpactOccurred; + ++ (void)mediumImpactOccurred; + ++ (void)heavyImpactOccurred; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ARPlayer/Categories/UIImpactFeedbackGenerator+Helpers.m b/ARPlayer/Categories/UIImpactFeedbackGenerator+Helpers.m new file mode 100644 index 0000000..1e0b10f --- /dev/null +++ b/ARPlayer/Categories/UIImpactFeedbackGenerator+Helpers.m @@ -0,0 +1,29 @@ +// +// UIImpactFeedbackGenerator+Helpers.m +// ARPlayer +// +// Created by Maxim Makhun on 04/03/2019. +// Copyright © 2019 Maxim Makhun. All rights reserved. +// + +#import "UIImpactFeedbackGenerator+Helpers.h" + +@implementation UIImpactFeedbackGenerator (Helpers) + ++ (UIImpactFeedbackGenerator *)impactFeedbackGeneratorWithStyle:(UIImpactFeedbackStyle)style { + return [[UIImpactFeedbackGenerator alloc] initWithStyle:style]; +} + ++ (void)lightImpactOccurred { + [[UIImpactFeedbackGenerator impactFeedbackGeneratorWithStyle:UIImpactFeedbackStyleLight] impactOccurred]; +} + ++ (void)mediumImpactOccurred { + [[UIImpactFeedbackGenerator impactFeedbackGeneratorWithStyle:UIImpactFeedbackStyleMedium] impactOccurred]; +} + ++ (void)heavyImpactOccurred { + [[UIImpactFeedbackGenerator impactFeedbackGeneratorWithStyle:UIImpactFeedbackStyleHeavy] impactOccurred]; +} + +@end diff --git a/ARPlayer/Categories/UIViewController+Helpers.m b/ARPlayer/Categories/UIViewController+Helpers.m index 10f0eae..85f40f6 100644 --- a/ARPlayer/Categories/UIViewController+Helpers.m +++ b/ARPlayer/Categories/UIViewController+Helpers.m @@ -11,14 +11,16 @@ @implementation UIViewController (Helpers) - (void)showMessage:(NSString *)message { - UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"ARPlayer" - message:message - preferredStyle:UIAlertControllerStyleAlert]; - UIAlertAction *action = [UIAlertAction actionWithTitle:@"OK" - style:UIAlertActionStyleDefault - handler:nil]; - [alertController addAction:action]; - [self presentViewController:alertController animated:YES completion:nil]; + dispatch_async(dispatch_get_main_queue(), ^{ + UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"ARPlayer" + message:message + preferredStyle:UIAlertControllerStyleAlert]; + UIAlertAction *action = [UIAlertAction actionWithTitle:@"OK" + style:UIAlertActionStyleDefault + handler:nil]; + [alertController addAction:action]; + [self presentViewController:alertController animated:YES completion:nil]; + }); } @end diff --git a/ARPlayer/Gestures/GestureHandler.h b/ARPlayer/Gestures/GestureHandler.h deleted file mode 100644 index 2848b5f..0000000 --- a/ARPlayer/Gestures/GestureHandler.h +++ /dev/null @@ -1,29 +0,0 @@ -// -// GestureHandler.h -// ARPlayer -// -// Created by Maxim Makhun on 10/1/17. -// Copyright © 2017 Maxim Makhun. All rights reserved. -// - -@import Foundation; -@import ARKit; - -@interface GestureHandler : NSObject - -+ (void)handlePlayback:(UITapGestureRecognizer *)recognizer - inSceneView:(ARSCNView *)sceneView; - -+ (void)handleInsertion:(UILongPressGestureRecognizer *)recognizer - inSceneView:(ARSCNView *)sceneView; - -+ (void)handleScale:(UIPinchGestureRecognizer *)recognizer - inSceneView:(ARSCNView *)sceneView; - -+ (void)handleRotation:(UIRotationGestureRecognizer *)recognizer - inSceneView:(ARSCNView *)sceneView; - -+ (void)handlePosition:(UIPanGestureRecognizer *)recognizer - inSceneView:(ARSCNView *)sceneView; - -@end diff --git a/ARPlayer/Gestures/GestureHandler.m b/ARPlayer/Gestures/GestureHandler.m deleted file mode 100644 index f04e0de..0000000 --- a/ARPlayer/Gestures/GestureHandler.m +++ /dev/null @@ -1,143 +0,0 @@ -// -// GestureHandler.m -// ARPlayer -// -// Created by Maxim Makhun on 10/1/17. -// Copyright © 2017 Maxim Makhun. All rights reserved. -// - -@import SceneKit; - -#import "GestureHandler.h" - -// Nodes -#import "MediaPlayerNode.h" - -// Utils -#import "Utils.h" -#import "Constants.h" -#import "SettingsManager.h" - -@implementation GestureHandler - -+ (void)handlePlayback:(UITapGestureRecognizer *)recognizer - inSceneView:(ARSCNView *)sceneView { - CGPoint tapPoint = [recognizer locationInView:sceneView]; - NSArray *result = [sceneView hitTest:tapPoint options:nil]; - if (result.count != 0) { - SCNHitTestResult *hitResult = [result firstObject]; - MediaPlayerNode *mediaPlayerNode = (MediaPlayerNode *)[sceneView.scene.rootNode childNodeWithName:kMediaPlayerNode - recursively:NO]; - - if ([hitResult.node.name isEqualToString:kVideoRendererNode]) { - [GestureHandler performPlayback:mediaPlayerNode]; - } else if ([hitResult.node.name isEqualToString:kStopNode]) { - [Utils handleTouch:hitResult.node]; - [mediaPlayerNode stop]; - } else if ([hitResult.node.name isEqualToString:kPlayNode]) { - [Utils handleTouch:hitResult.node]; - [GestureHandler performPlayback:mediaPlayerNode]; - } else if ([hitResult.node.name isEqualToString:kNextTrackNode]) { - [Utils handleTouch:hitResult.node]; - [mediaPlayerNode toNextTrack]; - } else if ([hitResult.node.name isEqualToString:kPreviousTrackNode]) { - [Utils handleTouch:hitResult.node]; - [mediaPlayerNode toPreviousTrack]; - } - } -} - -+ (void)performPlayback:(MediaPlayerNode *)node { - if (node.playerPaused) { - [node play]; - } else { - [node pause]; - } -} - -+ (void)handleInsertion:(UILongPressGestureRecognizer *)recognizer - inSceneView:(ARSCNView *)sceneView { - if (recognizer.state == UIGestureRecognizerStateBegan) { - CGPoint tapPoint = [recognizer locationInView:sceneView]; - NSArray *arHitTestResults = [sceneView hitTest:tapPoint - types:ARHitTestResultTypeExistingPlaneUsingExtent]; - - if (arHitTestResults.count != 0) { - ARHitTestResult *hitResult = [arHitTestResults firstObject]; - simd_float4 column = hitResult.anchor.transform.columns[3]; - - MediaPlayerNode *mediaPlayerNode = [[MediaPlayerNode alloc] initWithPlaylist:[Utils playlist]]; - mediaPlayerNode.position = SCNVector3Make(column.x, column.y, column.z); - [mediaPlayerNode play]; - [sceneView.scene.rootNode addChildNode:mediaPlayerNode]; - } - } -} - -+ (SCNNode *)findNodeInSceneView:(ARSCNView *)sceneView - forRecognizer:(UIGestureRecognizer *)recognizer { - SCNHitTestResult *hitResult = [[sceneView hitTest:[recognizer locationInView:sceneView] - options:nil] firstObject]; - if ([hitResult.node.name isEqualToString:kTVNode] || - ([hitResult.node.name isEqualToString:kVideoRendererNode])) { - return [sceneView.scene.rootNode childNodeWithName:kMediaPlayerNode - recursively:NO]; - } - - return nil; -} - -+ (void)handleScale:(UIPinchGestureRecognizer *)recognizer - inSceneView:(ARSCNView *)sceneView { - if ([SettingsManager instance].scaleAllowed) { - static SCNNode *node; - if (recognizer.state == UIGestureRecognizerStateBegan) { - node = [GestureHandler findNodeInSceneView:sceneView - forRecognizer:recognizer]; - } else if (recognizer.state == UIGestureRecognizerStateChanged) { - CGFloat pinchScaleX = recognizer.scale * node.scale.x; - CGFloat pinchScaleY = recognizer.scale * node.scale.y; - CGFloat pinchScaleZ = recognizer.scale * node.scale.z; - node.scale = SCNVector3Make(pinchScaleX, pinchScaleY, pinchScaleZ); - recognizer.scale = 1.0f; - } - } -} - -+ (void)handleRotation:(UIRotationGestureRecognizer *)recognizer - inSceneView:(ARSCNView *)sceneView { - if ([SettingsManager instance].rotationAllowed) { - static SCNNode *node; - static CGFloat lastRotation = 0.0f; - CGFloat currentRotation = recognizer.rotation * (- 180 / M_PI); - if (recognizer.state == UIGestureRecognizerStateBegan) { - node = [GestureHandler findNodeInSceneView:sceneView - forRecognizer:recognizer]; - } else if (recognizer.state == UIGestureRecognizerStateChanged) { - [node runAction:[SCNAction rotateByX:0 - y:currentRotation - lastRotation - z:0 - duration:0.0f]]; - } - lastRotation = currentRotation; - } -} - -+ (void)handlePosition:(UIPanGestureRecognizer *)recognizer - inSceneView:(ARSCNView *)sceneView { - if ([SettingsManager instance].repositionAllowed) { - CGPoint tapPoint = [recognizer locationInView:sceneView]; - if (recognizer.state == UIGestureRecognizerStateChanged) { - NSArray *arHitTestResults = [sceneView hitTest:tapPoint - types:ARHitTestResultTypeExistingPlaneUsingExtent]; - if (arHitTestResults.count != 0) { - ARHitTestResult *hitResult = [arHitTestResults firstObject]; - MediaPlayerNode *mediaPlayerNode = (MediaPlayerNode *)[sceneView.scene.rootNode childNodeWithName:kMediaPlayerNode - recursively:NO]; - [mediaPlayerNode setSimdTransform:hitResult.worldTransform]; - } - } - } -} - -@end diff --git a/ARPlayer/Gestures/NodeInsertionHandler.h b/ARPlayer/Gestures/NodeInsertionHandler.h new file mode 100644 index 0000000..b8b846e --- /dev/null +++ b/ARPlayer/Gestures/NodeInsertionHandler.h @@ -0,0 +1,19 @@ +// +// NodeInsertionHandler.h +// ARPlayer +// +// Created by Maxim Makhun on 04/03/2019. +// Copyright © 2019 Maxim Makhun. All rights reserved. +// + +@import UIKit; + +#import "GestureHandleProtocol.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface NodeInsertionHandler : NSObject + +@end + +NS_ASSUME_NONNULL_END diff --git a/ARPlayer/Gestures/NodeInsertionHandler.m b/ARPlayer/Gestures/NodeInsertionHandler.m new file mode 100644 index 0000000..b839b1c --- /dev/null +++ b/ARPlayer/Gestures/NodeInsertionHandler.m @@ -0,0 +1,41 @@ +// +// NodeInsertionHandler.m +// ARPlayer +// +// Created by Maxim Makhun on 04/03/2019. +// Copyright © 2019 Maxim Makhun. All rights reserved. +// + +@import ARKit; + +#import "NodeInsertionHandler.h" + +// Nodes +#import "MediaPlayerNode.h" + +// Utils +#import "PlaylistService.h" + +@implementation NodeInsertionHandler + +- (void)handleGesture:(UIGestureRecognizer *)gesture inSceneView:(ARSCNView *)sceneView { + NSAssert([gesture isKindOfClass:[UILongPressGestureRecognizer class]], @"Different class type is expected."); + + if (gesture.state == UIGestureRecognizerStateBegan) { + CGPoint tapPoint = [gesture locationInView:sceneView]; + NSArray *arHitTestResults = [sceneView hitTest:tapPoint + types:ARHitTestResultTypeExistingPlaneUsingExtent]; + + if (arHitTestResults.count != 0) { + ARHitTestResult *hitResult = [arHitTestResults firstObject]; + simd_float4 column = hitResult.anchor.transform.columns[3]; + + MediaPlayerNode *mediaPlayerNode = [[MediaPlayerNode alloc] initWithPlaylist:[PlaylistService playlist]]; + mediaPlayerNode.position = SCNVector3Make(column.x, column.y, column.z); + [mediaPlayerNode play]; + [sceneView.scene.rootNode addChildNode:mediaPlayerNode]; + } + } +} + +@end diff --git a/ARPlayer/Gestures/NodePlaybackHandler.h b/ARPlayer/Gestures/NodePlaybackHandler.h new file mode 100644 index 0000000..739be54 --- /dev/null +++ b/ARPlayer/Gestures/NodePlaybackHandler.h @@ -0,0 +1,19 @@ +// +// NodePlaybackHandler.h +// ARPlayer +// +// Created by Maxim Makhun on 04/03/2019. +// Copyright © 2019 Maxim Makhun. All rights reserved. +// + +@import UIKit; + +#import "GestureHandleProtocol.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface NodePlaybackHandler : NSObject + +@end + +NS_ASSUME_NONNULL_END diff --git a/ARPlayer/Gestures/NodePlaybackHandler.m b/ARPlayer/Gestures/NodePlaybackHandler.m new file mode 100644 index 0000000..4f01f37 --- /dev/null +++ b/ARPlayer/Gestures/NodePlaybackHandler.m @@ -0,0 +1,54 @@ +// +// NodePlaybackHandler.m +// ARPlayer +// +// Created by Maxim Makhun on 04/03/2019. +// Copyright © 2019 Maxim Makhun. All rights reserved. +// + +@import ARKit; + +#import "NodePlaybackHandler.h" + +// Nodes +#import "MediaPlayerNode.h" +#import "SwitchTrackNode.h" +#import "ControlNode.h" + +// Utils +#import "Constants.h" + +@implementation NodePlaybackHandler + +- (void)handleGesture:(UIGestureRecognizer *)gesture inSceneView:(ARSCNView *)sceneView { + NSAssert([gesture isKindOfClass:[UITapGestureRecognizer class]], @"Different class type is expected."); + + CGPoint tapPoint = [gesture locationInView:sceneView]; + NSArray *result = [sceneView hitTest:tapPoint options:nil]; + if (result.count == 0) { + return; + } + + SCNHitTestResult *hitResult = [result firstObject]; + MediaPlayerNode *mediaPlayerNode = (MediaPlayerNode *)[sceneView.scene.rootNode childNodeWithName:kMediaPlayerNode + recursively:NO]; + + if ([hitResult.node isKindOfClass:[ControlNode class]]) { + ControlNode *node = (ControlNode *)hitResult.node; + [node animate]; + } + + if ([hitResult.node.name isEqualToString:kVideoRendererNode]) { + [mediaPlayerNode togglePlay]; + } else if ([hitResult.node.name isEqualToString:kStopNode]) { + [mediaPlayerNode stop]; + } else if ([hitResult.node.name isEqualToString:kPlayNode]) { + [mediaPlayerNode togglePlay]; + } else if ([hitResult.node.name isEqualToString:kNextTrackNode]) { + [mediaPlayerNode toNextTrack]; + } else if ([hitResult.node.name isEqualToString:kPreviousTrackNode]) { + [mediaPlayerNode toPreviousTrack]; + } +} + +@end diff --git a/ARPlayer/Gestures/NodePositionHandler.h b/ARPlayer/Gestures/NodePositionHandler.h new file mode 100644 index 0000000..78537dd --- /dev/null +++ b/ARPlayer/Gestures/NodePositionHandler.h @@ -0,0 +1,19 @@ +// +// NodePositionHandler.h +// ARPlayer +// +// Created by Maxim Makhun on 04/03/2019. +// Copyright © 2019 Maxim Makhun. All rights reserved. +// + +@import UIKit; + +#import "GestureHandleProtocol.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface NodePositionHandler : NSObject + +@end + +NS_ASSUME_NONNULL_END diff --git a/ARPlayer/Gestures/NodePositionHandler.m b/ARPlayer/Gestures/NodePositionHandler.m new file mode 100644 index 0000000..c5ec9d4 --- /dev/null +++ b/ARPlayer/Gestures/NodePositionHandler.m @@ -0,0 +1,42 @@ +// +// NodePositionHandler.m +// ARPlayer +// +// Created by Maxim Makhun on 04/03/2019. +// Copyright © 2019 Maxim Makhun. All rights reserved. +// + +@import ARKit; + +#import "NodePositionHandler.h" + +// Nodes +#import "MediaPlayerNode.h" + +// Utils +#import "SettingsManager.h" +#import "Constants.h" + +@implementation NodePositionHandler + +- (void)handleGesture:(UIGestureRecognizer *)gesture inSceneView:(ARSCNView *)sceneView { + NSAssert([gesture isKindOfClass:[UIPanGestureRecognizer class]], @"Different class type is expected."); + + if (![SettingsManager instance].repositionAllowed) { + return; + } + + CGPoint tapPoint = [gesture locationInView:sceneView]; + if (gesture.state == UIGestureRecognizerStateChanged) { + NSArray *arHitTestResults = [sceneView hitTest:tapPoint + types:ARHitTestResultTypeExistingPlaneUsingExtent]; + if (arHitTestResults.count != 0) { + ARHitTestResult *hitResult = [arHitTestResults firstObject]; + MediaPlayerNode *mediaPlayerNode = (MediaPlayerNode *)[sceneView.scene.rootNode childNodeWithName:kMediaPlayerNode + recursively:NO]; + [mediaPlayerNode setSimdTransform:hitResult.worldTransform]; + } + } +} + +@end diff --git a/ARPlayer/Gestures/NodeRotationHandler.h b/ARPlayer/Gestures/NodeRotationHandler.h new file mode 100644 index 0000000..33b0081 --- /dev/null +++ b/ARPlayer/Gestures/NodeRotationHandler.h @@ -0,0 +1,19 @@ +// +// NodeRotationHandler.h +// ARPlayer +// +// Created by Maxim Makhun on 04/03/2019. +// Copyright © 2019 Maxim Makhun. All rights reserved. +// + +@import UIKit; + +#import "GestureHandleProtocol.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface NodeRotationHandler : NSObject + +@end + +NS_ASSUME_NONNULL_END diff --git a/ARPlayer/Gestures/NodeRotationHandler.m b/ARPlayer/Gestures/NodeRotationHandler.m new file mode 100644 index 0000000..a79e404 --- /dev/null +++ b/ARPlayer/Gestures/NodeRotationHandler.m @@ -0,0 +1,54 @@ +// +// NodeRotationHandler.m +// ARPlayer +// +// Created by Maxim Makhun on 04/03/2019. +// Copyright © 2019 Maxim Makhun. All rights reserved. +// + +@import ARKit; + +#import "NodeRotationHandler.h" + +// Utils +#import "SettingsManager.h" +#import "Constants.h" + +@implementation NodeRotationHandler + +- (void)handleGesture:(UIGestureRecognizer *)gesture inSceneView:(ARSCNView *)sceneView { + NSAssert([gesture isKindOfClass:[UIRotationGestureRecognizer class]], @"Different class type is expected."); + + if (![SettingsManager instance].rotationAllowed) { + return; + } + + static SCNNode *node; + static CGFloat lastRotation = 0.0f; + CGFloat currentRotation = ((UIRotationGestureRecognizer *)gesture).rotation * (- 180 / M_PI); + if (gesture.state == UIGestureRecognizerStateBegan) { + node = [NodeRotationHandler findNodeInSceneView:sceneView + forGesture:gesture]; + } else if (gesture.state == UIGestureRecognizerStateChanged) { + [node runAction:[SCNAction rotateByX:0 + y:currentRotation - lastRotation + z:0 + duration:0.0f]]; + } + lastRotation = currentRotation; +} + ++ (SCNNode *)findNodeInSceneView:(ARSCNView *)sceneView + forGesture:(UIGestureRecognizer *)gesture { + SCNHitTestResult *hitResult = [[sceneView hitTest:[gesture locationInView:sceneView] + options:nil] firstObject]; + if ([hitResult.node.name isEqualToString:kTVNode] || + [hitResult.node.name isEqualToString:kVideoRendererNode]) { + return [sceneView.scene.rootNode childNodeWithName:kMediaPlayerNode + recursively:NO]; + } + + return nil; +} + +@end diff --git a/ARPlayer/Gestures/NodeScaleHandler.h b/ARPlayer/Gestures/NodeScaleHandler.h new file mode 100644 index 0000000..b98417a --- /dev/null +++ b/ARPlayer/Gestures/NodeScaleHandler.h @@ -0,0 +1,19 @@ +// +// NodeScaleHandler.h +// ARPlayer +// +// Created by Maxim Makhun on 04/03/2019. +// Copyright © 2019 Maxim Makhun. All rights reserved. +// + +@import UIKit; + +#import "GestureHandleProtocol.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface NodeScaleHandler : NSObject + +@end + +NS_ASSUME_NONNULL_END diff --git a/ARPlayer/Gestures/NodeScaleHandler.m b/ARPlayer/Gestures/NodeScaleHandler.m new file mode 100644 index 0000000..a8575d4 --- /dev/null +++ b/ARPlayer/Gestures/NodeScaleHandler.m @@ -0,0 +1,53 @@ +// +// NodeScaleHandler.m +// ARPlayer +// +// Created by Maxim Makhun on 04/03/2019. +// Copyright © 2019 Maxim Makhun. All rights reserved. +// + +@import ARKit; + +#import "NodeScaleHandler.h" + +// Utils +#import "SettingsManager.h" +#import "Constants.h" + +@implementation NodeScaleHandler + +- (void)handleGesture:(UIGestureRecognizer *)gesture inSceneView:(ARSCNView *)sceneView { + NSAssert([gesture isKindOfClass:[UIPinchGestureRecognizer class]], @"Different class type is expected."); + + if (![SettingsManager instance].scaleAllowed) { + return; + } + + static SCNNode *node; + UIPinchGestureRecognizer *pinchGesture = (UIPinchGestureRecognizer *)gesture; + if (pinchGesture.state == UIGestureRecognizerStateBegan) { + node = [NodeScaleHandler findNodeInSceneView:sceneView + forGesture:pinchGesture]; + } else if (pinchGesture.state == UIGestureRecognizerStateChanged) { + CGFloat pinchScaleX = pinchGesture.scale * node.scale.x; + CGFloat pinchScaleY = pinchGesture.scale * node.scale.y; + CGFloat pinchScaleZ = pinchGesture.scale * node.scale.z; + node.scale = SCNVector3Make(pinchScaleX, pinchScaleY, pinchScaleZ); + pinchGesture.scale = 1.0f; + } +} + ++ (SCNNode *)findNodeInSceneView:(ARSCNView *)sceneView + forGesture:(UIGestureRecognizer *)gesture { + SCNHitTestResult *hitResult = [[sceneView hitTest:[gesture locationInView:sceneView] + options:nil] firstObject]; + if ([hitResult.node.name isEqualToString:kTVNode] || + [hitResult.node.name isEqualToString:kVideoRendererNode]) { + return [sceneView.scene.rootNode childNodeWithName:kMediaPlayerNode + recursively:NO]; + } + + return nil; +} + +@end diff --git a/ARPlayer/Gestures/Protocols/GestureHandleProtocol.h b/ARPlayer/Gestures/Protocols/GestureHandleProtocol.h new file mode 100644 index 0000000..80d6ceb --- /dev/null +++ b/ARPlayer/Gestures/Protocols/GestureHandleProtocol.h @@ -0,0 +1,20 @@ +// +// GestureHandleProtocol.h +// ARPlayer +// +// Created by Maxim Makhun on 04/03/2019. +// Copyright © 2019 Maxim Makhun. All rights reserved. +// + +@import Foundation; +@import ARKit; + +NS_ASSUME_NONNULL_BEGIN + +@protocol GestureHandleProtocol + +- (void)handleGesture:(UIGestureRecognizer *)gesture inSceneView:(ARSCNView *)sceneView; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ARPlayer/Gestures/SceneGestureRecognizerDelegate.h b/ARPlayer/Gestures/SceneGestureRecognizerDelegate.h new file mode 100644 index 0000000..3f000df --- /dev/null +++ b/ARPlayer/Gestures/SceneGestureRecognizerDelegate.h @@ -0,0 +1,21 @@ +// +// SceneGestureRecognizerDelegate.h +// ARPlayer +// +// Created by Maxim Makhun on 04/03/2019. +// Copyright © 2019 Maxim Makhun. All rights reserved. +// + +@import UIKit; + +NS_ASSUME_NONNULL_BEGIN + +@interface SceneGestureRecognizerDelegate : NSObject + +- (instancetype)initWithSceneView:(ARSCNView *)sceneView NS_DESIGNATED_INITIALIZER; + +- (instancetype)init __attribute__((unavailable("Use initWithSceneView: to create instance of this class."))); + +@end + +NS_ASSUME_NONNULL_END diff --git a/ARPlayer/Gestures/SceneGestureRecognizerDelegate.m b/ARPlayer/Gestures/SceneGestureRecognizerDelegate.m new file mode 100644 index 0000000..b40779d --- /dev/null +++ b/ARPlayer/Gestures/SceneGestureRecognizerDelegate.m @@ -0,0 +1,110 @@ +// +// SceneGestureRecognizerDelegate.m +// ARPlayer +// +// Created by Maxim Makhun on 04/03/2019. +// Copyright © 2019 Maxim Makhun. All rights reserved. +// + +@import UIKit; +@import ARKit; + +#import "SceneGestureRecognizerDelegate.h" + +// Nodes +#import "MediaPlayerNode.h" +#import "NodePlaybackHandler.h" +#import "NodeInsertionHandler.h" +#import "NodePositionHandler.h" +#import "NodeRotationHandler.h" +#import "NodeScaleHandler.h" + +// Services +#import "PlaylistService.h" + +// Utils +#import "Constants.h" +#import "SettingsManager.h" + +// Protocols +#import "GestureHandleProtocol.h" + +@interface SceneGestureRecognizerDelegate () + +@property (nonatomic, weak) ARSCNView *sceneView; + +@end + +@implementation SceneGestureRecognizerDelegate + +- (instancetype)initWithSceneView:(ARSCNView *)sceneView { + self = [super init]; + + if (self) { + self.sceneView = sceneView; + [self setupGestureRecognizers]; + } + + return self; +} + +- (void)setupGestureRecognizers { + UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self + action:@selector(handleGesture:)]; + tapGestureRecognizer.delegate = self; + tapGestureRecognizer.numberOfTapsRequired = 1; + [self.sceneView addGestureRecognizer:tapGestureRecognizer]; + + UILongPressGestureRecognizer *longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self + action:@selector(handleGesture:)]; + longPressGestureRecognizer.delegate = self; + longPressGestureRecognizer.minimumPressDuration = 1.0f; + [self.sceneView addGestureRecognizer:longPressGestureRecognizer]; + + UIPinchGestureRecognizer *pinchGestureRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self + action:@selector(handleGesture:)]; + pinchGestureRecognizer.delegate = self; + [self.sceneView addGestureRecognizer:pinchGestureRecognizer]; + + UIRotationGestureRecognizer *rotationGestureRecognizer = [[UIRotationGestureRecognizer alloc] initWithTarget:self + action:@selector(handleGesture:)]; + rotationGestureRecognizer.delegate = self; + [self.sceneView addGestureRecognizer:rotationGestureRecognizer]; + + UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self + action:@selector(handleGesture:)]; + panGestureRecognizer.delegate = self; + [self.sceneView addGestureRecognizer:panGestureRecognizer]; +} + +#pragma mark - UISceneGestureRecognizerDelegate methods + +- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer +shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { + if ([gestureRecognizer isKindOfClass:[UIPinchGestureRecognizer class]] || + [otherGestureRecognizer isKindOfClass:[UIRotationGestureRecognizer class]]) { + return YES; + } + + return NO; +} + +- (void)handleGesture:(UIGestureRecognizer *)gestureRecognizer { + id handler = nil; + if ([gestureRecognizer isKindOfClass:UITapGestureRecognizer.class]) { + handler = [NodePlaybackHandler new]; + } else if (([gestureRecognizer isKindOfClass:UILongPressGestureRecognizer.class])) { + handler = [NodeInsertionHandler new]; + } else if ([gestureRecognizer isKindOfClass:UIPinchGestureRecognizer.class]) { + handler = [NodeScaleHandler new]; + } else if ([gestureRecognizer isKindOfClass:UIRotationGestureRecognizer.class]) { + handler = [NodeRotationHandler new]; + } else if ([gestureRecognizer isKindOfClass:UIPanGestureRecognizer.class]) { + handler = [NodePositionHandler new]; + } + + NSAssert(handler != nil, @"Handler should not be nil"); + [handler handleGesture:gestureRecognizer inSceneView:self.sceneView]; +} + +@end diff --git a/ARPlayer/Nodes/ControlNodes/ControlNode.h b/ARPlayer/Nodes/ControlNodes/ControlNode.h new file mode 100644 index 0000000..029f127 --- /dev/null +++ b/ARPlayer/Nodes/ControlNodes/ControlNode.h @@ -0,0 +1,27 @@ +// +// ControlNode.h +// ARPlayer +// +// Created by Maxim Makhun on 04/03/2019. +// Copyright © 2019 Maxim Makhun. All rights reserved. +// + +@import SceneKit; + +NS_ASSUME_NONNULL_BEGIN + +/*! + @class ControlNode + @abstract Node which is used as a base class for all nodes which can control video playback. + */ +@interface ControlNode : SCNNode + +/*! + @method animate + @abstract Animate control media player action depending on settings. + */ +- (void)animate; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ARPlayer/Nodes/ControlNodes/ControlNode.m b/ARPlayer/Nodes/ControlNodes/ControlNode.m new file mode 100644 index 0000000..0bc7d5a --- /dev/null +++ b/ARPlayer/Nodes/ControlNodes/ControlNode.m @@ -0,0 +1,44 @@ +// +// ControlNode.m +// ARPlayer +// +// Created by Maxim Makhun on 04/03/2019. +// Copyright © 2019 Maxim Makhun. All rights reserved. +// + +#import "ControlNode.h" + +// Protocols +#import "AnimatableProtocol.h" + +// Utils +#import "SettingsManager.h" + +// Categories +#import "UIImpactFeedbackGenerator+Helpers.h" + +@interface ControlNode () + +@end + +@implementation ControlNode + +- (void)animate { + if ([SettingsManager instance].vibrateOnTouch) { + [UIImpactFeedbackGenerator heavyImpactOccurred]; + } + + if ([SettingsManager instance].animateOnTouch) { + SCNAction *moveDown = [SCNAction moveBy:SCNVector3Make(0.0f, -0.03f, 0.0f) + duration:0.2f]; + moveDown.timingMode = SCNActionTimingModeEaseInEaseOut; + SCNAction *moveUp = [SCNAction moveBy:SCNVector3Make(0.0f, 0.03f, 0.0f) + duration:0.2f]; + moveUp.timingMode = SCNActionTimingModeEaseInEaseOut; + SCNAction *moveAction = [SCNAction repeatAction:[SCNAction sequence:@[moveDown, moveUp]] + count:1]; + [self runAction:moveAction]; + } +} + +@end diff --git a/ARPlayer/Nodes/ControlNodes/PlayNode.h b/ARPlayer/Nodes/ControlNodes/PlayNode.h new file mode 100644 index 0000000..a663f4d --- /dev/null +++ b/ARPlayer/Nodes/ControlNodes/PlayNode.h @@ -0,0 +1,34 @@ +// +// PlayNode.h +// ARPlayer +// +// Created by Maxim Makhun on 9/30/17. +// Copyright © 2017 Maxim Makhun. All rights reserved. +// + +@import SceneKit; + +#import "ControlNode.h" + +NS_ASSUME_NONNULL_BEGIN + +typedef NS_ENUM(NSUInteger, PlayerState) { + Playing, + Paused, +}; + +/*! + @class PlayNode + @abstract Node by pressing which playback can be toggled - either paused on moved to playing state. + */ +@interface PlayNode : ControlNode + +/*! + @property state + @abstract Current playback state - can be either paused or playing. + */ +@property (nonatomic) PlayerState state; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ARPlayer/Nodes/PlayNode.m b/ARPlayer/Nodes/ControlNodes/PlayNode.m similarity index 85% rename from ARPlayer/Nodes/PlayNode.m rename to ARPlayer/Nodes/ControlNodes/PlayNode.m index f8e2215..1570612 100644 --- a/ARPlayer/Nodes/PlayNode.m +++ b/ARPlayer/Nodes/ControlNodes/PlayNode.m @@ -17,14 +17,18 @@ - (instancetype)init { self = [super init]; if (self) { - self.eulerAngles = SCNVector3Make(M_PI_2, 0.0f, 0.0f); - self.position = SCNVector3Make(-0.07f, 0.0f, 0.08f); - self.name = kPlayNode; + [self setupNode]; } return self; } +- (void)setupNode { + self.eulerAngles = SCNVector3Make(M_PI_2, 0.0f, 0.0f); + self.position = SCNVector3Make(-0.07f, 0.0f, 0.08f); + self.name = kPlayNode; +} + - (SCNShape *)playShape { UIBezierPath *path = [UIBezierPath bezierPath]; [path moveToPoint:CGPointZero]; @@ -63,12 +67,14 @@ - (SCNShape *)pauseShape { return shape; } -- (void)updateState:(PlayerState)state { +- (void)setState:(PlayerState)state { + _state = state; + switch (state) { - case StatePlaying: + case Playing: [self setGeometry:[self pauseShape]]; break; - case StatePaused: + case Paused: [self setGeometry:[self playShape]]; break; } diff --git a/ARPlayer/Nodes/StopNode.h b/ARPlayer/Nodes/ControlNodes/StopNode.h similarity index 58% rename from ARPlayer/Nodes/StopNode.h rename to ARPlayer/Nodes/ControlNodes/StopNode.h index a5a8cb1..ed920d9 100644 --- a/ARPlayer/Nodes/StopNode.h +++ b/ARPlayer/Nodes/ControlNodes/StopNode.h @@ -8,9 +8,15 @@ @import SceneKit; +#import "ControlNode.h" + NS_ASSUME_NONNULL_BEGIN -@interface StopNode : SCNNode +/*! + @class StopNode + @abstract Node by pressing on which playback can be stopped. + */ +@interface StopNode : ControlNode @end diff --git a/ARPlayer/Nodes/ControlNodes/StopNode.m b/ARPlayer/Nodes/ControlNodes/StopNode.m new file mode 100644 index 0000000..a839e2f --- /dev/null +++ b/ARPlayer/Nodes/ControlNodes/StopNode.m @@ -0,0 +1,40 @@ +// +// StopNode.m +// ARPlayer +// +// Created by Maxim Makhun on 9/30/17. +// Copyright © 2017 Maxim Makhun. All rights reserved. +// + +#import "StopNode.h" + +// Utils +#import "Constants.h" + +// Categories +#import "SCNMaterial+Contents.h" + +@implementation StopNode + +- (instancetype)init { + self = [super init]; + + if (self) { + [self setupNode]; + } + + return self; +} + +- (void)setupNode { + SCNBox *geometry = [SCNBox boxWithWidth:0.04f + height:0.02f + length:0.04f + chamferRadius:0.0f]; + self.geometry = geometry; + self.position = SCNVector3Make(0.05f, 0.0f, 0.1f); + self.geometry.firstMaterial = [SCNMaterial materialWithColor:[UIColor blackColor]]; + self.name = kStopNode; +} + +@end diff --git a/ARPlayer/Nodes/SwitchTrackNodes/NextTrackNode.h b/ARPlayer/Nodes/ControlNodes/SwitchTrackNodes/NextTrackNode.h similarity index 74% rename from ARPlayer/Nodes/SwitchTrackNodes/NextTrackNode.h rename to ARPlayer/Nodes/ControlNodes/SwitchTrackNodes/NextTrackNode.h index 4cb2149..1e8218f 100644 --- a/ARPlayer/Nodes/SwitchTrackNodes/NextTrackNode.h +++ b/ARPlayer/Nodes/ControlNodes/SwitchTrackNodes/NextTrackNode.h @@ -12,6 +12,10 @@ NS_ASSUME_NONNULL_BEGIN +/*! + @class NextTrackNode + @abstract Node by pressing on which switch to next track will occur. + */ @interface NextTrackNode : SwitchTrackNode @end diff --git a/ARPlayer/Nodes/SwitchTrackNodes/NextTrackNode.m b/ARPlayer/Nodes/ControlNodes/SwitchTrackNodes/NextTrackNode.m similarity index 64% rename from ARPlayer/Nodes/SwitchTrackNodes/NextTrackNode.m rename to ARPlayer/Nodes/ControlNodes/SwitchTrackNodes/NextTrackNode.m index ea66c9b..f236b2e 100644 --- a/ARPlayer/Nodes/SwitchTrackNodes/NextTrackNode.m +++ b/ARPlayer/Nodes/ControlNodes/SwitchTrackNodes/NextTrackNode.m @@ -18,12 +18,16 @@ - (instancetype)init { self = [super init]; if (self) { - self.position = SCNVector3Make(0.12f, 0.0f, 0.08f); - self.eulerAngles = SCNVector3Make(M_PI_2, 0.0f, 0.0f); - self.name = kNextTrackNode; + [self setupNode]; } return self; } +- (void)setupNode { + self.position = SCNVector3Make(0.12f, 0.0f, 0.08f); + self.eulerAngles = SCNVector3Make(M_PI_2, 0.0f, 0.0f); + self.name = kNextTrackNode; +} + @end diff --git a/ARPlayer/Nodes/SwitchTrackNodes/PreviousTrackNode.h b/ARPlayer/Nodes/ControlNodes/SwitchTrackNodes/PreviousTrackNode.h similarity index 73% rename from ARPlayer/Nodes/SwitchTrackNodes/PreviousTrackNode.h rename to ARPlayer/Nodes/ControlNodes/SwitchTrackNodes/PreviousTrackNode.h index 8e2d90b..0e0f105 100644 --- a/ARPlayer/Nodes/SwitchTrackNodes/PreviousTrackNode.h +++ b/ARPlayer/Nodes/ControlNodes/SwitchTrackNodes/PreviousTrackNode.h @@ -12,6 +12,10 @@ NS_ASSUME_NONNULL_BEGIN +/*! + @class PreviousTrackNode + @abstract Node by pressing on which switch to previous track will occur. + */ @interface PreviousTrackNode : SwitchTrackNode @end diff --git a/ARPlayer/Nodes/SwitchTrackNodes/PreviousTrackNode.m b/ARPlayer/Nodes/ControlNodes/SwitchTrackNodes/PreviousTrackNode.m similarity index 62% rename from ARPlayer/Nodes/SwitchTrackNodes/PreviousTrackNode.m rename to ARPlayer/Nodes/ControlNodes/SwitchTrackNodes/PreviousTrackNode.m index 9ffee5d..9431d56 100644 --- a/ARPlayer/Nodes/SwitchTrackNodes/PreviousTrackNode.m +++ b/ARPlayer/Nodes/ControlNodes/SwitchTrackNodes/PreviousTrackNode.m @@ -17,12 +17,16 @@ - (instancetype)init { self = [super init]; if (self) { - self.position = SCNVector3Make(-0.12f, 0.0f, 0.12f); - self.eulerAngles = SCNVector3Make(M_PI_2, M_PI, 0.0f); - self.name = kPreviousTrackNode; + [self setupNode]; } return self; } +- (void)setupNode { + self.position = SCNVector3Make(-0.12f, 0.0f, 0.12f); + self.eulerAngles = SCNVector3Make(M_PI_2, M_PI, 0.0f); + self.name = kPreviousTrackNode; +} + @end diff --git a/ARPlayer/Nodes/SwitchTrackNodes/SwitchTrackNode.h b/ARPlayer/Nodes/ControlNodes/SwitchTrackNodes/SwitchTrackNode.h similarity index 56% rename from ARPlayer/Nodes/SwitchTrackNodes/SwitchTrackNode.h rename to ARPlayer/Nodes/ControlNodes/SwitchTrackNodes/SwitchTrackNode.h index de28e2d..0a07196 100644 --- a/ARPlayer/Nodes/SwitchTrackNodes/SwitchTrackNode.h +++ b/ARPlayer/Nodes/ControlNodes/SwitchTrackNodes/SwitchTrackNode.h @@ -8,9 +8,15 @@ @import SceneKit; +#import "ControlNode.h" + NS_ASSUME_NONNULL_BEGIN -@interface SwitchTrackNode : SCNNode +/*! + @class SwitchTrackNode + @abstract Node which is used as a base class for switch track nodes. + */ +@interface SwitchTrackNode : ControlNode @end diff --git a/ARPlayer/Nodes/SwitchTrackNodes/SwitchTrackNode.m b/ARPlayer/Nodes/ControlNodes/SwitchTrackNodes/SwitchTrackNode.m similarity index 91% rename from ARPlayer/Nodes/SwitchTrackNodes/SwitchTrackNode.m rename to ARPlayer/Nodes/ControlNodes/SwitchTrackNodes/SwitchTrackNode.m index 8e9537e..89b5354 100644 --- a/ARPlayer/Nodes/SwitchTrackNodes/SwitchTrackNode.m +++ b/ARPlayer/Nodes/ControlNodes/SwitchTrackNodes/SwitchTrackNode.m @@ -8,10 +8,7 @@ #import "SwitchTrackNode.h" -// Protocols -#import "PlayerNodeProtocol.h" - -@interface SwitchTrackNode () +@interface SwitchTrackNode () @end diff --git a/ARPlayer/Nodes/CurrentTimeNode.h b/ARPlayer/Nodes/CurrentTimeNode.h index 653b09e..f3c18bb 100644 --- a/ARPlayer/Nodes/CurrentTimeNode.h +++ b/ARPlayer/Nodes/CurrentTimeNode.h @@ -11,10 +11,22 @@ NS_ASSUME_NONNULL_BEGIN +/*! + @class CurrentTimeNode + @abstract Node which is placed on top of vide surface to show current playback time. + */ @interface CurrentTimeNode : SCNNode +/*! + @method subscribeForPlayerTimeUpdates: + @abstract Subscribe to time playback updates of AVPlayer instance. + */ - (void)subscribeForPlayerTimeUpdates:(AVPlayer *)player; +/*! + @method resetTimeForPlayer: + @abstract Reset current time information in CurrentTimeNode. + */ - (void)resetTimeForPlayer:(AVPlayer *)player; @end diff --git a/ARPlayer/Nodes/CurrentTimeNode.m b/ARPlayer/Nodes/CurrentTimeNode.m index 4c2a238..a13024b 100644 --- a/ARPlayer/Nodes/CurrentTimeNode.m +++ b/ARPlayer/Nodes/CurrentTimeNode.m @@ -10,6 +10,7 @@ // Categories #import "NSDateFormatter+Helpers.h" +#import "SCNMaterial+Contents.h" static NSUInteger const kTimeInterval = 1.0f; static NSString * const kTimeFormat = @"00:00"; @@ -26,19 +27,20 @@ - (instancetype)init { self = [super init]; if (self) { - SCNText *geometry = [self createTimeGeometryWithFrame:CGRectMake(0.0f, 0.0f, 1.8f, 1.5f)]; - geometry.alignmentMode = kCAAlignmentCenter; - - SCNMaterial *mainMaterial = [SCNMaterial new]; - mainMaterial.diffuse.contents = [UIColor whiteColor]; - geometry.firstMaterial = mainMaterial; - self.scale = SCNVector3Make(0.02f, 0.02f, 0.02f); - [self setGeometry:geometry]; + [self setupNode]; } return self; } +- (void)setupNode { + SCNText *geometry = [self createTimeGeometryWithFrame:CGRectMake(0.0f, 0.0f, 1.8f, 1.5f)]; + geometry.alignmentMode = kCAAlignmentCenter; + geometry.firstMaterial = [SCNMaterial materialWithColor:[UIColor whiteColor]]; + self.scale = SCNVector3Make(0.02f, 0.02f, 0.02f); + [self setGeometry:geometry]; +} + - (SCNText *)createTimeGeometryWithFrame:(CGRect)frame { SCNText *geometry = [SCNText textWithString:kTimeFormat extrusionDepth:0.1f]; geometry.font = [UIFont systemFontOfSize:0.9f]; @@ -55,7 +57,7 @@ - (void)subscribeForPlayerTimeUpdates:(AVPlayer *)player { usingBlock:^(CMTime time) { __strong typeof(weakSelf) strongSelf = weakSelf; SCNText *textGeometry = (SCNText *)strongSelf.geometry; - textGeometry.string = [NSDateFormatter toString:time]; + textGeometry.string = [NSDateFormatter currentTimeStringForTime:time]; strongSelf.geometry = textGeometry; }]; } diff --git a/ARPlayer/Nodes/MediaPlayerNode.h b/ARPlayer/Nodes/MediaPlayerNode.h index 73d7709..0fa50a5 100644 --- a/ARPlayer/Nodes/MediaPlayerNode.h +++ b/ARPlayer/Nodes/MediaPlayerNode.h @@ -10,22 +10,55 @@ NS_ASSUME_NONNULL_BEGIN +/*! + @class MediaPlayerNode + @abstract MediaPlayerNode main video playback node. + @discussion It is holder class for all other media playback related nodes. For example: + Play, pause, switch tracks, playback time etc + */ @interface MediaPlayerNode : SCNNode +/*! + @method initWithPlaylist: + @abstract Creates and initializes media player node instance with the specified playlist. + @param playlist Array of NSURLs. + */ - (instancetype)initWithPlaylist:(NSArray *)playlist; -@property(nonatomic, strong) NSArray *playlist; - -@property(nonatomic, readonly) BOOL playerPaused; - +/*! + @method pause + @abstract Pauses playback of media player. + */ - (void)pause; +/*! + @method play + @abstract Starts media playback. + */ - (void)play; +/*! + @method togglePlay + @abstract Either starts or pauses playback. + */ +- (void)togglePlay; + +/*! + @method stop + @abstract Stop media playback. + */ - (void)stop; +/*! + @method toNextTrack + @abstract Switch to next track (if present). + */ - (void)toNextTrack; +/*! + @method toPreviousTrack + @abstract Switch to previous track (if present). + */ - (void)toPreviousTrack; @end diff --git a/ARPlayer/Nodes/MediaPlayerNode.m b/ARPlayer/Nodes/MediaPlayerNode.m index d709463..5acf504 100644 --- a/ARPlayer/Nodes/MediaPlayerNode.m +++ b/ARPlayer/Nodes/MediaPlayerNode.m @@ -24,6 +24,7 @@ @interface MediaPlayerNode () @property (nonatomic, strong) StopNode *stopNode; @property (nonatomic, strong) NextTrackNode *nextTrackNode; @property (nonatomic, strong) PreviousTrackNode *previousTrackNode; +@property (nonatomic, strong) NSArray *playlist; @property (nonatomic) NSInteger currentPlaybackIndex; @end @@ -37,27 +38,20 @@ - (instancetype)initWithPlaylist:(NSArray *)playlist { if (self) { self.playlist = playlist; - } - - return self; -} - -- (instancetype)init { - self = [super init]; - - if (self) { [self constructNodes]; + [self setupMediaPlayerNode]; } return self; } -- (void)constructNodes { +- (void)setupMediaPlayerNode { self.currentPlaybackIndex = 0; - _playerPaused = YES; self.physicsBody = [SCNPhysicsBody bodyWithType:SCNPhysicsBodyTypeStatic shape:nil]; self.name = kMediaPlayerNode; - +} + +- (void)constructNodes { self.tvNode = [TVNode node]; [self addChildNode:self.tvNode]; @@ -77,33 +71,54 @@ - (void)constructNodes { #pragma mark - Video player control logic - (void)pause { - _playerPaused = YES; - - [self.playNode updateState:StatePaused]; + self.playNode.state = Paused; [self.tvNode.player pause]; } +- (void)playAnimated:(BOOL)animated { + if (self.playlist.count != 0) { + if (self.tvNode.player == NULL) { + [self switchToTrackWithIndex:self.currentPlaybackIndex]; + } else { + [self.tvNode.player play]; + } + + self.playNode.state = Playing; + } else { + NSLog(@"[%s] Playlist empty, playback won't be started.", __FUNCTION__); + } +} + - (void)play { if (self.playlist.count != 0) { - _playerPaused = NO; - if (self.tvNode.player == NULL) { [self switchToTrackWithIndex:self.currentPlaybackIndex]; } else { [self.tvNode.player play]; } - [self.playNode updateState:StatePlaying]; + self.playNode.state = Playing; } else { NSLog(@"[%s] Playlist empty, playback won't be started.", __FUNCTION__); } } +- (void)togglePlay { + switch (self.playNode.state) { + case Playing: + [self pause]; + break; + case Paused: + [self play]; + break; + default: + break; + } +} + - (void)stop { - _playerPaused = YES; - - [self.playNode updateState:StatePaused]; - [self.tvNode updateVideoNodeWithPlayer:nil]; + self.playNode.state = Paused; + self.tvNode.player = nil; } #pragma mark - Video player track switching logic @@ -124,10 +139,10 @@ - (void)switchToTrackWithIndex:(NSUInteger)index { AVPlayerItem *playerItem = [AVPlayerItem playerItemWithURL:self.playlist[index]]; if (self.tvNode.player.currentItem != playerItem) { - [self.tvNode updateVideoNodeWithPlayer:[AVPlayer playerWithPlayerItem:playerItem]]; + self.tvNode.player = [AVPlayer playerWithPlayerItem:playerItem]; } - [self.playNode updateState:StatePlaying]; + self.playNode.state = Playing; } @end diff --git a/ARPlayer/Nodes/PlaneRendererNode.h b/ARPlayer/Nodes/PlaneRendererNode.h index 7157c70..1412d11 100644 --- a/ARPlayer/Nodes/PlaneRendererNode.h +++ b/ARPlayer/Nodes/PlaneRendererNode.h @@ -11,14 +11,37 @@ NS_ASSUME_NONNULL_BEGIN +/*! + @class PlaneRendererNode + @abstract Node which is used to render horizontal planes. + */ @interface PlaneRendererNode : SCNNode -- (instancetype)initWithAnchor:(ARPlaneAnchor *)anchor visible:(BOOL)visible; +/*! + @method initWithAnchor + @abstract It's possible to init PlaneRendererNode only using this method. Every PlaneRendererNode is + attached to specific ARPlaneAnchor and can be either visible or not (depending on settings). + */ +- (instancetype)initWithAnchor:(ARPlaneAnchor *)anchor visible:(BOOL)visible NS_DESIGNATED_INITIALIZER; +- (instancetype)init __attribute__((unavailable("Use initWithAnchor:visible: to create instance of this class."))); + +/*! + @method update: + @abstract Update location of PlaneRendererNode depending on location of ARPlaneAnchor. + */ - (void)update:(ARPlaneAnchor *)anchor; +/*! + @method hide + @abstract Hide current PlaneRendererNode. + */ - (void)hide; +/*! + @method show + @abstract Show current PlaneRendererNode. + */ - (void)show; @end diff --git a/ARPlayer/Nodes/PlaneRendererNode.m b/ARPlayer/Nodes/PlaneRendererNode.m index 3343000..6592438 100644 --- a/ARPlayer/Nodes/PlaneRendererNode.m +++ b/ARPlayer/Nodes/PlaneRendererNode.m @@ -9,7 +9,7 @@ #import "PlaneRendererNode.h" // Categories -#import "SCNMaterial+Colors.h" +#import "SCNMaterial+Contents.h" // Utils #import "Constants.h" diff --git a/ARPlayer/Nodes/PlayNode.h b/ARPlayer/Nodes/PlayNode.h deleted file mode 100644 index 66af03a..0000000 --- a/ARPlayer/Nodes/PlayNode.h +++ /dev/null @@ -1,24 +0,0 @@ -// -// PlayNode.h -// ARPlayer -// -// Created by Maxim Makhun on 9/30/17. -// Copyright © 2017 Maxim Makhun. All rights reserved. -// - -@import SceneKit; - -NS_ASSUME_NONNULL_BEGIN - -typedef NS_ENUM(NSUInteger, PlayerState) { - StatePlaying, - StatePaused, -}; - -@interface PlayNode : SCNNode - -- (void)updateState:(PlayerState)state; - -@end - -NS_ASSUME_NONNULL_END diff --git a/ARPlayer/Nodes/Protocols/PlayerNodeProtocol.h b/ARPlayer/Nodes/Protocols/AnimatableProtocol.h similarity index 56% rename from ARPlayer/Nodes/Protocols/PlayerNodeProtocol.h rename to ARPlayer/Nodes/Protocols/AnimatableProtocol.h index 3cd7f91..0b65c23 100644 --- a/ARPlayer/Nodes/Protocols/PlayerNodeProtocol.h +++ b/ARPlayer/Nodes/Protocols/AnimatableProtocol.h @@ -1,8 +1,8 @@ // -// PlayerNodeProtocol.h +// AnimatableProtocol.h // ARPlayer // -// Created by Maxim Makhun on 03/03/2019. +// Created by Maxim Makhun on 04/03/2019. // Copyright © 2019 Maxim Makhun. All rights reserved. // @@ -11,9 +11,9 @@ NS_ASSUME_NONNULL_BEGIN -@protocol PlayerNodeProtocol +@protocol AnimatableProtocol -- (SCNGeometry *)shape; +- (void)animate; @end diff --git a/ARPlayer/Nodes/StopNode.m b/ARPlayer/Nodes/StopNode.m deleted file mode 100644 index 3cf9698..0000000 --- a/ARPlayer/Nodes/StopNode.m +++ /dev/null @@ -1,36 +0,0 @@ -// -// StopNode.m -// ARPlayer -// -// Created by Maxim Makhun on 9/30/17. -// Copyright © 2017 Maxim Makhun. All rights reserved. -// - -#import "StopNode.h" - -// Utils -#import "Constants.h" - -@implementation StopNode - -- (instancetype)init { - self = [super init]; - - if (self) { - SCNBox *geometry = [SCNBox boxWithWidth:0.04f - height:0.02f - length:0.04f - chamferRadius:0.0f]; - self.geometry = geometry; - self.position = SCNVector3Make(0.05f, 0.0f, 0.1f); - - SCNMaterial *mainMaterial = [SCNMaterial new]; - mainMaterial.diffuse.contents = [UIColor blackColor]; - self.geometry.firstMaterial = mainMaterial; - self.name = kStopNode; - } - - return self; -} - -@end diff --git a/ARPlayer/Nodes/TVNode.h b/ARPlayer/Nodes/TVNode.h index d495792..d8373bd 100644 --- a/ARPlayer/Nodes/TVNode.h +++ b/ARPlayer/Nodes/TVNode.h @@ -13,13 +13,17 @@ NS_ASSUME_NONNULL_BEGIN +/*! + @class TVNode + @abstract TVNode - node which contains media playback surface and TV node. + */ @interface TVNode : SCNNode -@property(nonatomic, strong, readonly) AVPlayer *player; - -@property(nonatomic, strong, readonly) CurrentTimeNode *currentTimeNode; - -- (void)updateVideoNodeWithPlayer:(nullable AVPlayer *)player; +/*! + @property player + @abstract AVPlayer instance, using which it's possible to control playback. + */ +@property (nonatomic, strong, nullable) AVPlayer *player; @end diff --git a/ARPlayer/Nodes/TVNode.m b/ARPlayer/Nodes/TVNode.m index 87f1f25..e4291e4 100644 --- a/ARPlayer/Nodes/TVNode.m +++ b/ARPlayer/Nodes/TVNode.m @@ -15,11 +15,13 @@ // Categories #import "SCNNode+Helpers.h" +#import "SCNMaterial+Contents.h" @interface TVNode () @property (nonatomic, strong) SCNNode *tvNode; @property (nonatomic, strong) SCNNode *videoRendererNode; +@property (nonatomic, strong) CurrentTimeNode *currentTimeNode; @end @@ -39,7 +41,7 @@ - (instancetype)init { - (void)createTvNode { self.tvNode = [[SCNScene sceneNamed:@"Art.scnassets/tv_scene.scn"].rootNode childNodeWithName:kTVNode recursively:NO]; - self.tvNode.geometry.firstMaterial.diffuse.contents = [[UIColor darkGrayColor] colorWithAlphaComponent:1.0f]; + self.tvNode.geometry.firstMaterial.diffuse.contents = [[UIColor blackColor] colorWithAlphaComponent:1.0f]; self.tvNode.movabilityHint = SCNMovabilityHintFixed; self.tvNode.name = kTVNode; @@ -58,32 +60,30 @@ - (void)createVideoRendererNode { self.videoRendererNode.eulerAngles = SCNVector3Make(M_PI_2, 0.0f, 0.0f); self.videoRendererNode.name = kVideoRendererNode; - self.videoRendererNode.geometry.firstMaterial = [self mainMaterial]; + self.videoRendererNode.geometry.firstMaterial = [SCNMaterial materialWithColor:[UIColor blackColor]]; [self.tvNode addChildNode:self.videoRendererNode]; - _currentTimeNode = [CurrentTimeNode node]; - _currentTimeNode.position = SCNVector3Make(-0.025f, -0.1f, 0.003f); - [self.videoRendererNode addChildNode:_currentTimeNode]; + self.currentTimeNode = [CurrentTimeNode node]; + self.currentTimeNode.position = SCNVector3Make(-0.025f, -0.1f, 0.003f); + [self.videoRendererNode addChildNode:self.currentTimeNode]; } -- (void)updateVideoNodeWithPlayer:(nullable AVPlayer *)player { - SCNMaterial *mainMaterial = [self mainMaterial]; +- (void)setPlayer:(AVPlayer * )player { + SCNMaterial *mainMaterial = [SCNMaterial materialWithColor:[UIColor blackColor]]; [_player pause]; [_player replaceCurrentItemWithPlayerItem:nil]; - [_currentTimeNode resetTimeForPlayer:_player]; + [self.currentTimeNode resetTimeForPlayer:_player]; if (player == nil) { - self.videoRendererNode.geometry.firstMaterial = [self mainMaterial]; + self.videoRendererNode.geometry.firstMaterial = mainMaterial; _player = nil; } else { _player = player; - SCNMaterial *playerMaterial = [SCNMaterial new]; - playerMaterial.diffuse.contents = player; - self.videoRendererNode.geometry.materials = @[playerMaterial, mainMaterial, mainMaterial, mainMaterial, mainMaterial, mainMaterial]; - [_currentTimeNode subscribeForPlayerTimeUpdates:_player]; + self.videoRendererNode.geometry.materials = @[[self materialWithPlayer:_player], mainMaterial, mainMaterial, mainMaterial, mainMaterial, mainMaterial]; + [self.currentTimeNode subscribeForPlayerTimeUpdates:_player]; [_player play]; } @@ -91,11 +91,11 @@ - (void)updateVideoNodeWithPlayer:(nullable AVPlayer *)player { #pragma mark - Helper methods -- (SCNMaterial *)mainMaterial { - SCNMaterial *mainMaterial = [SCNMaterial new]; - mainMaterial.diffuse.contents = [[UIColor blackColor] colorWithAlphaComponent:1.0f]; +- (SCNMaterial *)materialWithPlayer:(AVPlayer *)player { + SCNMaterial *material = [SCNMaterial new]; + material.diffuse.contents = player; - return mainMaterial; + return material; } @end diff --git a/ARPlayer/Services/PlaylistService.h b/ARPlayer/Services/PlaylistService.h new file mode 100644 index 0000000..8126b80 --- /dev/null +++ b/ARPlayer/Services/PlaylistService.h @@ -0,0 +1,19 @@ +// +// PlaylistService.h +// ARPlayer +// +// Created by Maxim Makhun on 04/03/2019. +// Copyright © 2019 Maxim Makhun. All rights reserved. +// + +@import Foundation; + +NS_ASSUME_NONNULL_BEGIN + +@interface PlaylistService : NSObject + ++ (NSArray *)playlist; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ARPlayer/Services/PlaylistService.m b/ARPlayer/Services/PlaylistService.m new file mode 100644 index 0000000..37317b5 --- /dev/null +++ b/ARPlayer/Services/PlaylistService.m @@ -0,0 +1,21 @@ +// +// PlaylistService.m +// ARPlayer +// +// Created by Maxim Makhun on 04/03/2019. +// Copyright © 2019 Maxim Makhun. All rights reserved. +// + +#import "PlaylistService.h" + +@implementation PlaylistService + ++ (NSArray *)playlist { + return [NSArray arrayWithObjects: + [NSURL URLWithString:@"https://devstreaming-cdn.apple.com/videos/tutorials/20181030/209vnatfwjud/Brining_your_apps_to_iPadPro/Brining_your_apps_to_iPadPro_sd.mp4"], + [NSURL URLWithString:@"https://devstreaming-cdn.apple.com/videos/wwdc/2018/412zw88j5aa4mr9/412/412_sd_advanced_debugging_with_xcode_and_lldb.mp4"], + [NSURL URLWithString:@"http://devstreaming.apple.com/videos/wwdc/2014/609xxkxq1v95fju/609/609_sd_whats_new_in_scenekit.mov"], + [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"sample_video_iTunes" ofType:@"mov"]], nil]; +} + +@end diff --git a/ARPlayer/Settings/SettingsManager.h b/ARPlayer/Settings/SettingsManager.h index 47ed759..eacafee 100644 --- a/ARPlayer/Settings/SettingsManager.h +++ b/ARPlayer/Settings/SettingsManager.h @@ -8,6 +8,11 @@ @import Foundation; +/*! + @class SettingsManager + @abstract Manager which is responsible for saving app-related settings. There can only be one + instance of such manager during lifetime of an app. + */ @interface SettingsManager : NSObject + (instancetype)instance; diff --git a/ARPlayer/Utils/Utils.h b/ARPlayer/Utils/Utils.h deleted file mode 100644 index 03deedb..0000000 --- a/ARPlayer/Utils/Utils.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// Utils.h -// ARPlayer -// -// Created by Maxim Makhun on 9/24/17. -// Copyright © 2017 Maxim Makhun. All rights reserved. -// - -@import Foundation; - -@interface Utils : NSObject - -+ (void)handleTouch:(SCNNode *)node; - -+ (NSArray *)playlist; - -@end diff --git a/ARPlayer/Utils/Utils.m b/ARPlayer/Utils/Utils.m deleted file mode 100644 index 7ed62ca..0000000 --- a/ARPlayer/Utils/Utils.m +++ /dev/null @@ -1,46 +0,0 @@ -// -// Utils.m -// ARPlayer -// -// Created by Maxim Makhun on 9/24/17. -// Copyright © 2017 Maxim Makhun. All rights reserved. -// - -@import SceneKit; -@import AudioToolbox; - -// Utils -#import "Utils.h" -#import "SettingsManager.h" - -@implementation Utils - -+ (void)handleTouch:(SCNNode *)node { - if ([SettingsManager instance].vibrateOnTouch) { - AudioServicesPlaySystemSound(kSystemSoundID_Vibrate); - } - - if ([SettingsManager instance].animateOnTouch) { - SCNAction *moveDown = [SCNAction moveBy:SCNVector3Make(0.0f, - -0.03f, - 0.0f) - duration:0.2f]; - moveDown.timingMode = SCNActionTimingModeEaseInEaseOut; - SCNAction *moveUp = [SCNAction moveBy:SCNVector3Make(0.0f, - 0.03f, - 0.0f) - duration:0.2f]; - moveUp.timingMode = SCNActionTimingModeEaseInEaseOut; - SCNAction *moveAction = [SCNAction repeatAction:[SCNAction sequence:@[moveDown, moveUp]] - count:1]; - [node runAction:moveAction]; - } -} - -+ (NSArray *)playlist { - return [NSArray arrayWithObjects: - [NSURL URLWithString:@"http://devstreaming.apple.com/videos/wwdc/2014/609xxkxq1v95fju/609/609_sd_whats_new_in_scenekit.mov"], - [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"sample_video_iTunes" ofType:@"mov"]], nil]; -} - -@end diff --git a/ARPlayer/ViewControllers/Delegates/PlaneRendererDelegate.h b/ARPlayer/ViewControllers/Delegates/PlaneRendererDelegate.h new file mode 100644 index 0000000..3efabe9 --- /dev/null +++ b/ARPlayer/ViewControllers/Delegates/PlaneRendererDelegate.h @@ -0,0 +1,17 @@ +// +// PlaneRendererDelegate.h +// ARPlayer +// +// Created by Maxim Makhun on 04/03/2019. +// Copyright © 2019 Maxim Makhun. All rights reserved. +// + +@import Foundation; + +NS_ASSUME_NONNULL_BEGIN + +@interface PlaneRendererDelegate : NSObject + +@end + +NS_ASSUME_NONNULL_END diff --git a/ARPlayer/ViewControllers/Delegates/PlaneRendererDelegate.m b/ARPlayer/ViewControllers/Delegates/PlaneRendererDelegate.m new file mode 100644 index 0000000..f6831eb --- /dev/null +++ b/ARPlayer/ViewControllers/Delegates/PlaneRendererDelegate.m @@ -0,0 +1,90 @@ +// +// PlaneRendererDelegate.m +// ARPlayer +// +// Created by Maxim Makhun on 04/03/2019. +// Copyright © 2019 Maxim Makhun. All rights reserved. +// + +@import ARKit; + +#import "PlaneRendererDelegate.h" + +// Nodes +#import "PlaneRendererNode.h" + +// Utils +#import "Constants.h" +#import "SettingsManager.h" + +@interface PlaneRendererDelegate () + +@property (nonatomic, strong) NSMutableDictionary *planes; + +@end + +@implementation PlaneRendererDelegate + +- (instancetype)init { + self = [super init]; + + if (self) { + self.planes = [NSMutableDictionary new]; + [self subscribeForNotifications]; + } + + return self; +} + +#pragma mark - ARSCNViewDelegate methods + +- (void)renderer:(id )renderer + didAddNode:(SCNNode *)node + forAnchor:(ARAnchor *)anchor { + if (![anchor isKindOfClass:[ARPlaneAnchor class]]) { + return; + } + + PlaneRendererNode *plane = [[PlaneRendererNode alloc] initWithAnchor:(ARPlaneAnchor *)anchor + visible:[SettingsManager instance].showPlanes]; + [self.planes setObject:plane forKey:anchor.identifier]; + [node addChildNode:plane]; +} + +- (void)renderer:(id )renderer + didUpdateNode:(SCNNode *)node + forAnchor:(ARAnchor *)anchor { + PlaneRendererNode *plane = [self.planes objectForKey:anchor.identifier]; + if (plane == nil) { + return; + } + + [plane update:(ARPlaneAnchor *)anchor]; +} + +- (void)renderer:(id )renderer + didRemoveNode:(SCNNode *)node + forAnchor:(ARAnchor *)anchor { + [self.planes removeObjectForKey:anchor.identifier]; +} + +#pragma mark - Helper methods + +- (void)subscribeForNotifications { + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(showPlanes:) + name:kNotificationShowPlanes + object:nil]; +} + +- (void)showPlanes:(NSNotification *)notification { + for (PlaneRendererNode *plane in [self.planes allValues]) { + if ([SettingsManager instance].showPlanes) { + [plane show]; + } else { + [plane hide]; + } + } +} + +@end diff --git a/ARPlayer/ViewControllers/PlayerViewController.m b/ARPlayer/ViewControllers/PlayerViewController.m index 54c5edd..dded69b 100644 --- a/ARPlayer/ViewControllers/PlayerViewController.m +++ b/ARPlayer/ViewControllers/PlayerViewController.m @@ -13,20 +13,22 @@ #import "PlayerViewController.h" #import "SettingsViewController.h" -// Nodes -#import "PlaneRendererNode.h" +// Delegates +#import "PlaneRendererDelegate.h" +#import "SceneGestureRecognizerDelegate.h" // Utils #import "SettingsManager.h" -#import "Utils.h" -#import "GestureHandler.h" #import "Constants.h" + +// Categories #import "UIViewController+Helpers.h" -@interface PlayerViewController () +@interface PlayerViewController () @property (nonatomic, weak) IBOutlet ARSCNView *sceneView; -@property (nonatomic, strong) NSMutableDictionary *planes; +@property (nonatomic, strong) PlaneRendererDelegate *planeRendererDelegate; +@property (nonatomic, strong) SceneGestureRecognizerDelegate *sceneGestureRecognizerDelegate; @end @@ -36,30 +38,21 @@ @implementation PlayerViewController - (void)viewDidLoad { [super viewDidLoad]; - + [self setupScene]; - [self setupGestureRecognizers]; - [self subscribeForNotifications]; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; - if (ARConfiguration.isSupported) { - ARWorldTrackingConfiguration *configuration = [ARWorldTrackingConfiguration new]; - configuration.planeDetection = ARPlaneDetectionHorizontal; - self.sceneView.automaticallyUpdatesLighting = YES; - [self.sceneView.session runWithConfiguration:configuration]; - } else { - NSLog(@"[%s] ARConfiguration is not supported.", __FUNCTION__); - } + [self setupConfiguration]; } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; - + if (!ARConfiguration.isSupported) { - [self showMessage:@"ARConfiguration is not supported"]; + [self showMessage:@"ARConfiguration is not supported."]; } } @@ -75,51 +68,28 @@ - (BOOL)prefersStatusBarHidden { #pragma mark - Setting up methods +- (void)setupConfiguration { + if (ARConfiguration.isSupported) { + ARWorldTrackingConfiguration *configuration = [ARWorldTrackingConfiguration new]; + configuration.planeDetection = ARPlaneDetectionHorizontal; + self.sceneView.automaticallyUpdatesLighting = YES; + [self.sceneView.session runWithConfiguration:configuration]; + } else { + NSLog(@"[%s] ARConfiguration is not supported.", __FUNCTION__); + } +} + - (void)setupScene { self.view.backgroundColor = [UIColor blackColor]; - self.planes = [NSMutableDictionary new]; - - self.sceneView.delegate = self; + + self.planeRendererDelegate = [PlaneRendererDelegate new]; + self.sceneView.delegate = self.planeRendererDelegate; self.sceneView.showsStatistics = NO; - + SCNScene *scene = [SCNScene scene]; self.sceneView.scene = scene; -} -- (void)setupGestureRecognizers { - UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self - action:@selector(handleGesture:)]; - tapGestureRecognizer.delegate = self; - tapGestureRecognizer.numberOfTapsRequired = 1; - [self.sceneView addGestureRecognizer:tapGestureRecognizer]; - - UILongPressGestureRecognizer *longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self - action:@selector(handleGesture:)]; - longPressGestureRecognizer.delegate = self; - longPressGestureRecognizer.minimumPressDuration = 1.0f; - [self.sceneView addGestureRecognizer:longPressGestureRecognizer]; - - UIPinchGestureRecognizer *pinchGestureRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self - action:@selector(handleGesture:)]; - pinchGestureRecognizer.delegate = self; - [self.sceneView addGestureRecognizer:pinchGestureRecognizer]; - - UIRotationGestureRecognizer *rotationGestureRecognizer = [[UIRotationGestureRecognizer alloc] initWithTarget:self - action:@selector(handleGesture:)]; - rotationGestureRecognizer.delegate = self; - [self.sceneView addGestureRecognizer:rotationGestureRecognizer]; - - UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self - action:@selector(handleGesture:)]; - panGestureRecognizer.delegate = self; - [self.sceneView addGestureRecognizer:panGestureRecognizer]; -} - -- (void)subscribeForNotifications { - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(showPlanes:) - name:kNotificationShowPlanes - object:nil]; + self.sceneGestureRecognizerDelegate = [[SceneGestureRecognizerDelegate alloc] initWithSceneView:self.sceneView]; } #pragma mark - Action handlers @@ -131,83 +101,11 @@ - (IBAction)showSettings:(UIButton *)sender { sender.frame.size.height + 5, 0, 0); - settingsViewController.preferredContentSize = CGSizeMake(self.view.frame.size.width - 100, - self.view.frame.size.height - 200); + settingsViewController.preferredContentSize = CGSizeMake(self.view.frame.size.width - 100, 250); settingsViewController.popoverPresentationController.permittedArrowDirections = UIPopoverArrowDirectionUp; [self presentViewController:settingsViewController animated:YES completion:nil]; } -- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer -shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { - if ([gestureRecognizer isKindOfClass:[UIPinchGestureRecognizer class]] || - [otherGestureRecognizer isKindOfClass:[UIRotationGestureRecognizer class]]) { - return YES; - } - - return NO; -} - -- (void)handleGesture:(UIGestureRecognizer *)recognizer { - if ([recognizer isKindOfClass:UITapGestureRecognizer.class]) { - [GestureHandler handlePlayback:(UITapGestureRecognizer *)recognizer - inSceneView:self.sceneView]; - } else if (([recognizer isKindOfClass:UILongPressGestureRecognizer.class])) { - [GestureHandler handleInsertion:(UILongPressGestureRecognizer *)recognizer - inSceneView:self.sceneView]; - } else if ([recognizer isKindOfClass:UIPinchGestureRecognizer.class]) { - [GestureHandler handleScale:(UIPinchGestureRecognizer *)recognizer - inSceneView:self.sceneView]; - } else if ([recognizer isKindOfClass:UIRotationGestureRecognizer.class]) { - [GestureHandler handleRotation:(UIRotationGestureRecognizer *)recognizer - inSceneView:self.sceneView]; - } else if ([recognizer isKindOfClass:UIPanGestureRecognizer.class]) { - [GestureHandler handlePosition:(UIPanGestureRecognizer *)recognizer - inSceneView:self.sceneView]; - } -} - -- (void)showPlanes:(NSNotification *)notification { - for (PlaneRendererNode *plane in [self.planes allValues]) { - if ([SettingsManager instance].showPlanes) { - [plane show]; - } else { - [plane hide]; - } - } -} - -#pragma mark - ARSCNViewDelegate methods - -- (void)renderer:(id )renderer - didAddNode:(SCNNode *)node - forAnchor:(ARAnchor *)anchor { - if (![anchor isKindOfClass:[ARPlaneAnchor class]]) { - return; - } - - PlaneRendererNode *plane = [[PlaneRendererNode alloc] initWithAnchor:(ARPlaneAnchor *)anchor - visible:[SettingsManager instance].showPlanes]; - [self.planes setObject:plane forKey:anchor.identifier]; - [node addChildNode:plane]; -} - -- (void)renderer:(id )renderer - didUpdateNode:(SCNNode *)node - forAnchor:(ARAnchor *)anchor { - PlaneRendererNode *plane = [self.planes objectForKey:anchor.identifier]; - if (plane == nil) { - return; - } - - [plane update:(ARPlaneAnchor *)anchor]; -} - -- (void)renderer:(id )renderer - didRemoveNode:(SCNNode *)node - forAnchor:(ARAnchor *)anchor { - [self.planes removeObjectForKey:anchor.identifier]; -} - #pragma mark - ARSessionObserver methods - (void)session:(ARSession *)session didFailWithError:(NSError *)error { diff --git a/ARPlayer/ViewControllers/SettingsViewController.m b/ARPlayer/ViewControllers/SettingsViewController.m index d5867c9..b73887b 100644 --- a/ARPlayer/ViewControllers/SettingsViewController.m +++ b/ARPlayer/ViewControllers/SettingsViewController.m @@ -26,26 +26,35 @@ @implementation SettingsViewController - (instancetype)init { if (self = [super init]) { - self.modalPresentationStyle = UIModalPresentationPopover; - self.popoverPresentationController.delegate = self; - self.view.backgroundColor = [UIColor whiteColor]; - - [self.showPlanesSwitch setOn:[SettingsManager instance].showPlanes]; - [self.vibrateOnTouchSwitch setOn:[SettingsManager instance].vibrateOnTouch]; - [self.animateOnTouchSwitch setOn:[SettingsManager instance].animateOnTouch]; - [self.scaleAllowedSwitch setOn:[SettingsManager instance].scaleAllowed]; - [self.rotationAllowedSwitch setOn:[SettingsManager instance].rotationAllowed]; - [self.repositionAllowedSwitch setOn:[SettingsManager instance].repositionAllowed]; + [self setupUI]; + [self setupSettings]; } return self; } +#pragma mark - Setting-up methods + +- (void)setupUI { + self.modalPresentationStyle = UIModalPresentationPopover; + self.popoverPresentationController.delegate = self; + self.view.backgroundColor = [UIColor whiteColor]; +} + +- (void)setupSettings { + [self.showPlanesSwitch setOn:[SettingsManager instance].showPlanes]; + [self.vibrateOnTouchSwitch setOn:[SettingsManager instance].vibrateOnTouch]; + [self.animateOnTouchSwitch setOn:[SettingsManager instance].animateOnTouch]; + [self.scaleAllowedSwitch setOn:[SettingsManager instance].scaleAllowed]; + [self.rotationAllowedSwitch setOn:[SettingsManager instance].rotationAllowed]; + [self.repositionAllowedSwitch setOn:[SettingsManager instance].repositionAllowed]; +} + - (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller { return UIModalPresentationNone; } -#pragma mark - Settings switching logic +#pragma mark - Settings switching methods - (IBAction)showPlanes:(UISwitch *)sender { [SettingsManager instance].showPlanes = sender.isOn; diff --git a/ARPlayerTests/NSDateFormatter+HelpersTests.m b/ARPlayerTests/NSDateFormatter+HelpersTests.m new file mode 100644 index 0000000..cf16eb7 --- /dev/null +++ b/ARPlayerTests/NSDateFormatter+HelpersTests.m @@ -0,0 +1,52 @@ +// +// NSDateFormatter+HelpersTests.m +// ARPlayerTests +// +// Created by Maxim Makhun on 04/03/2019. +// Copyright © 2019 Maxim Makhun. All rights reserved. +// + +@import XCTest; +@import CoreMedia; + +#import "NSDateFormatter+Helpers.h" + +@interface NSDateFormatter_HelpersTests : XCTestCase + +@end + +@implementation NSDateFormatter_HelpersTests + +- (void)setUp { + +} + +- (void)tearDown { + +} + +- (void)test1_ZeroSeconds { + CMTime time = CMTimeMakeWithSeconds(0, NSEC_PER_SEC); + NSString *result = [NSDateFormatter currentTimeStringForTime:time]; + XCTAssertTrue([result isEqualToString:@"00:00"]); +} + +- (void)test2_SeveralSeconds { + CMTime time = CMTimeMakeWithSeconds(7, NSEC_PER_SEC); + NSString *result = [NSDateFormatter currentTimeStringForTime:time]; + XCTAssertTrue([result isEqualToString:@"00:07"]); +} + +- (void)test3_SecondsAndMinutes { + CMTime time = CMTimeMakeWithSeconds(66, NSEC_PER_SEC); + NSString *result = [NSDateFormatter currentTimeStringForTime:time]; + XCTAssertTrue([result isEqualToString:@"01:06"]); +} + +- (void)test4_SecondsAndMinutesAndHours { + CMTime time = CMTimeMakeWithSeconds(1287, NSEC_PER_SEC); + NSString *result = [NSDateFormatter currentTimeStringForTime:time]; + XCTAssertTrue([result isEqualToString:@"21:27"]); +} + +@end diff --git a/ARPlayerTests/SCNMaterial+ColorsTests.m b/ARPlayerTests/SCNMaterial+ColorsTests.m index fda1874..5eee4d0 100644 --- a/ARPlayerTests/SCNMaterial+ColorsTests.m +++ b/ARPlayerTests/SCNMaterial+ColorsTests.m @@ -8,7 +8,7 @@ @import XCTest; -#import "SCNMaterial+Colors.h" +#import "SCNMaterial+Contents.h" @interface SCNMaterial_ColorsTests : XCTestCase