diff --git a/iOS/Hydra-Info.plist b/iOS/Hydra-Info.plist index ad533dd5..bf2fcd00 100644 --- a/iOS/Hydra-Info.plist +++ b/iOS/Hydra-Info.plist @@ -27,7 +27,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.2 + 2.0.1 CFBundleSignature ???? CFBundleURLTypes @@ -40,11 +40,23 @@ CFBundleVersion - 1.2.2 + 2.0.15 FacebookAppID 146947948791011 LSRequiresIPhoneOS + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + ugent.be + + NSIncludesSubdomains + + NSTemporaryExceptionAllowsInsecureHTTPLoads + + + NSLocationWhenInUseUsageDescription We hebben uw locatie nodig om deze te kunnen tonen op de kaarten. UIBackgroundModes @@ -52,17 +64,28 @@ audio UILaunchStoryboardName - DashboardViewController + MainStoryboard UIPrerenderedIcon + UIRequiresFullScreen + UIStatusBarHidden + UIStatusBarHidden~ipad + UIStatusBarStyle - UIStatusBarStyleBlackOpaque + UIStatusBarStyleLightContent UISupportedInterfaceOrientations UIInterfaceOrientationPortrait + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + UIViewControllerBasedStatusBarAppearance diff --git a/iOS/Hydra.xcodeproj/project.pbxproj b/iOS/Hydra.xcodeproj/project.pbxproj index 5df87ff1..3e3cd47f 100644 --- a/iOS/Hydra.xcodeproj/project.pbxproj +++ b/iOS/Hydra.xcodeproj/project.pbxproj @@ -9,9 +9,7 @@ /* Begin PBXBuildFile section */ 395C117A16272D8000B99F9B /* NewsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 395C117916272D8000B99F9B /* NewsViewController.m */; }; 395C117D1627437000B99F9B /* NewsDetailViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 395C117C1627437000B99F9B /* NewsDetailViewController.m */; }; - 3B0D77EC15BB0945002B23C1 /* BadgedButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BAFFE7D15BB04B600921E7D /* BadgedButton.m */; }; 3B436DF815B8915300984D3F /* InfoViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B436DF715B8915200984D3F /* InfoViewController.m */; }; - 3B7AD3B915BC830F0026BB62 /* RestoMenuView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B7AD3B815BC830F0026BB62 /* RestoMenuView.m */; }; 3B93FB9F1630939200C962DC /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B93FB9E1630939200C962DC /* AudioToolbox.framework */; }; 3B93FBA7163095F900C962DC /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B93FB9E1630939200C962DC /* AudioToolbox.framework */; }; 3B93FBAA1630969900C962DC /* UrgentPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B93FBA91630969900C962DC /* UrgentPlayer.m */; }; @@ -20,24 +18,65 @@ 445C81FC16C45D7A0080819C /* button-settings.png in Resources */ = {isa = PBXBuildFile; fileRef = 445C81FA16C45D7A0080819C /* button-settings.png */; }; 445C81FD16C45D7A0080819C /* button-settings@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 445C81FB16C45D7A0080819C /* button-settings@2x.png */; }; 44A29C58162DA88E001A573C /* EventKitUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 44A29C57162DA88E001A573C /* EventKitUI.framework */; }; + 9004F58F1B7F2FB60041FAA6 /* resto-map-icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 9004F58D1B7F2FB60041FAA6 /* resto-map-icon@2x.png */; }; + 9004F5901B7F2FB60041FAA6 /* resto-map-icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 9004F58E1B7F2FB60041FAA6 /* resto-map-icon.png */; }; 9015D0FA1A8ABA4B00228ADC /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 908E536419BE4F3900F1DA57 /* Images.xcassets */; }; + 901603DC1B716DED002E0D60 /* HomeNewsItemCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 901603DB1B716DED002E0D60 /* HomeNewsItemCollectionViewCell.swift */; }; 902939441A8C22EB00868221 /* info-bloklocaties.html in Resources */ = {isa = PBXBuildFile; fileRef = 902939431A8C22EB00868221 /* info-bloklocaties.html */; }; + 9035BF121B6C1753008E7875 /* schamper.png in Resources */ = {isa = PBXBuildFile; fileRef = 9035BF111B6C1753008E7875 /* schamper.png */; }; + 9035BF141B6C1F5D008E7875 /* HomeSchamperCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9035BF131B6C1F5D008E7875 /* HomeSchamperCollectionViewCell.swift */; }; + 9035BF161B6C37FE008E7875 /* home-zeus.png in Resources */ = {isa = PBXBuildFile; fileRef = 9035BF151B6C37FE008E7875 /* home-zeus.png */; }; + 9035BF181B6CB52A008E7875 /* HomeActivityCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9035BF171B6CB52A008E7875 /* HomeActivityCollectionViewCell.swift */; }; + 9035BF1A1B6CF663008E7875 /* LocationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9035BF191B6CF663008E7875 /* LocationService.swift */; }; + 903A8AF61C04F369002DAF5B /* ActivityOverviewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 903A8AF51C04F369002DAF5B /* ActivityOverviewCell.swift */; }; + 903A8AF81C04FBF7002DAF5B /* ActivityOverviewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 903A8AF71C04FBF7002DAF5B /* ActivityOverviewCell.xib */; }; 90410B101848DF9100331F6C /* info-guide.png in Resources */ = {isa = PBXBuildFile; fileRef = 90410B0E1848DF9100331F6C /* info-guide.png */; }; 90410B111848DF9100331F6C /* info-guide@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 90410B0F1848DF9100331F6C /* info-guide@2x.png */; }; + 904166231B7A689500D231EF /* tabbar-urgent@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 904166211B7A689500D231EF /* tabbar-urgent@2x.png */; }; + 904166241B7A689500D231EF /* tabbar-urgent.png in Resources */ = {isa = PBXBuildFile; fileRef = 904166221B7A689500D231EF /* tabbar-urgent.png */; }; + 904166261B7A837C00D231EF /* tabbar-settings.png in Resources */ = {isa = PBXBuildFile; fileRef = 904166251B7A837C00D231EF /* tabbar-settings.png */; }; + 9041C3681B7E67B600E4A50C /* RestoMenuCollectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9041C3671B7E67B600E4A50C /* RestoMenuCollectionCell.swift */; }; + 9041C36D1B7E801B00E4A50C /* RestoMenuHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9041C36C1B7E801B00E4A50C /* RestoMenuHeader.swift */; }; 904BC46C17E2014A0000FED6 /* dot-question.png in Resources */ = {isa = PBXBuildFile; fileRef = 904BC46817E2014A0000FED6 /* dot-question.png */; }; 904BC46D17E2014A0000FED6 /* dot-question@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 904BC46917E2014A0000FED6 /* dot-question@2x.png */; }; + 905EB0131BC809C600F38679 /* tabbar-settings@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 905EB0121BC809C600F38679 /* tabbar-settings@2x.png */; }; + 905EB0151BC80A3800F38679 /* tabbar-news@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 905EB0141BC80A3800F38679 /* tabbar-news@2x.png */; }; + 905EB0171BC80B2100F38679 /* tabbar-activities@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 905EB0161BC80B2000F38679 /* tabbar-activities@2x.png */; }; + 9068BA781B7BB437005F79FA /* tabbar-resto@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 9068BA771B7BB437005F79FA /* tabbar-resto@2x.png */; }; + 9068BA7A1B7BB560005F79FA /* tabbar-home@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 9068BA791B7BB560005F79FA /* tabbar-home@2x.png */; }; + 9068BA7C1B7BB684005F79FA /* tabbar-info@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 9068BA7B1B7BB684005F79FA /* tabbar-info@2x.png */; }; 906C1D8917C8A4A600145CD3 /* MapViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 906C1D8817C8A4A600145CD3 /* MapViewController.m */; }; + 907CD3CA1B7E52B600DD7539 /* RestoMenuViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 907CD3C91B7E52B600DD7539 /* RestoMenuViewController.swift */; }; 908CA4DD1A8D439C00F8C31F /* info-mapmarker.png in Resources */ = {isa = PBXBuildFile; fileRef = 908CA4DB1A8D439C00F8C31F /* info-mapmarker.png */; }; 908CA4DE1A8D439C00F8C31F /* info-mapmarker@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 908CA4DC1A8D439C00F8C31F /* info-mapmarker@2x.png */; }; 908DFF03188E932D00D526DC /* Pods-acknowledgements.plist in Resources */ = {isa = PBXBuildFile; fileRef = 908DFF02188E932D00D526DC /* Pods-acknowledgements.plist */; }; 908E536519BE4F3900F1DA57 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 908E536419BE4F3900F1DA57 /* Images.xcassets */; }; + 90969AD21BFF5D8D006EDD9D /* AssociationStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90969AD11BFF5D8D006EDD9D /* AssociationStore.swift */; }; + 90969AD41BFF5F90006EDD9D /* RestoStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90969AD31BFF5F90006EDD9D /* RestoStore.swift */; }; + 90969AD61BFF6014006EDD9D /* SchamperStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90969AD51BFF6014006EDD9D /* SchamperStore.swift */; }; 90B1029717A85E9200669CCD /* kalender.css in Resources */ = {isa = PBXBuildFile; fileRef = 90B1029617A85E9200669CCD /* kalender.css */; }; + 90B1EF0C1B7A5DC300E733C1 /* tabbar-home.png in Resources */ = {isa = PBXBuildFile; fileRef = 90B1EF0B1B7A5DC300E733C1 /* tabbar-home.png */; }; + 90B1EF0E1B7A5EF000E733C1 /* tabbar-resto.png in Resources */ = {isa = PBXBuildFile; fileRef = 90B1EF0D1B7A5EF000E733C1 /* tabbar-resto.png */; }; + 90B1EF101B7A5FDB00E733C1 /* tabbar-info.png in Resources */ = {isa = PBXBuildFile; fileRef = 90B1EF0F1B7A5FDB00E733C1 /* tabbar-info.png */; }; + 90B1EF121B7A606E00E733C1 /* tabbar-activities.png in Resources */ = {isa = PBXBuildFile; fileRef = 90B1EF111B7A606E00E733C1 /* tabbar-activities.png */; }; + 90B1EF141B7A60B000E733C1 /* tabbar-news.png in Resources */ = {isa = PBXBuildFile; fileRef = 90B1EF131B7A60B000E733C1 /* tabbar-news.png */; }; + 90B1EF161B7A60FC00E733C1 /* tabbar-schamper@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 90B1EF151B7A60FC00E733C1 /* tabbar-schamper@2x.png */; }; + 90B1EF181B7A61DD00E733C1 /* tabbar-schamper.png in Resources */ = {isa = PBXBuildFile; fileRef = 90B1EF171B7A61DD00E733C1 /* tabbar-schamper.png */; }; 90BAB266162749BD0043BA92 /* SchamperDetailViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 90BAB265162749BC0043BA92 /* SchamperDetailViewController.m */; }; + 90BE1F891B6AB3390061888C /* HomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90BE1F881B6AB3390061888C /* HomeViewController.swift */; }; + 90BE1F8D1B6AB61D0061888C /* MainStoryboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 90BE1F8C1B6AB61D0061888C /* MainStoryboard.storyboard */; }; + 90BE1F901B6AB8780061888C /* home-header.png in Resources */ = {isa = PBXBuildFile; fileRef = 90BE1F8E1B6AB8780061888C /* home-header.png */; }; + 90BE1F911B6AB8780061888C /* home-background.png in Resources */ = {isa = PBXBuildFile; fileRef = 90BE1F8F1B6AB8780061888C /* home-background.png */; }; 90C532AB16A325A200857EB0 /* FacebookEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = 90C532AA16A325A200857EB0 /* FacebookEvent.m */; }; + 90C8CA2E1B6AE3E80073E026 /* HomeFeedService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90C8CA2D1B6AE3E80073E026 /* HomeFeedService.swift */; }; + 90D55B9B1B7A4CE800813179 /* HydraTabbarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90D55B9A1B7A4CE800813179 /* HydraTabbarController.swift */; }; + 90DDFB791B6BB3D300DDAFA0 /* HomeRestoCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90DDFB781B6BB3D300DDAFA0 /* HomeRestoCollectionViewCell.swift */; }; 90E814F31854C76C00075F96 /* ios7-Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 90E814F11854C76C00075F96 /* ios7-Default-568h@2x.png */; }; 90E814F41854C76C00075F96 /* ios7-Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 90E814F21854C76C00075F96 /* ios7-Default@2x.png */; }; - 90EB7BE31688BCC700A9582D /* RestoInfoView.m in Sources */ = {isa = PBXBuildFile; fileRef = 90EB7BE21688BCC700A9582D /* RestoInfoView.m */; }; + 90E8DDC11B6AD45C0059F71B /* home-button-bar.png in Resources */ = {isa = PBXBuildFile; fileRef = 90E8DDC01B6AD45C0059F71B /* home-button-bar.png */; }; 90EB7BEA1688ECE300A9582D /* RestoLegendItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 90EB7BE91688ECE300A9582D /* RestoLegendItem.m */; }; + 90EE4CD01BB7193900C2DD32 /* RestoMenuInfoCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90EE4CCF1BB7193900C2DD32 /* RestoMenuInfoCollectionViewCell.swift */; }; + 90EF4A591B6D8AE40034AD8F /* HomeUrgentCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90EF4A581B6D8AE40034AD8F /* HomeUrgentCollectionViewCell.swift */; }; 90F0525E168C663F004CAFFC /* RestoMapController.m in Sources */ = {isa = PBXBuildFile; fileRef = 90F0525C168C663F004CAFFC /* RestoMapController.m */; }; 90F05261168C7022004CAFFC /* CoreLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 90F05260168C7022004CAFFC /* CoreLocation.framework */; }; 90F05263168C7726004CAFFC /* MapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 90F05262168C7726004CAFFC /* MapKit.framework */; }; @@ -135,7 +174,6 @@ F59D170115BD693F00B6F892 /* info-doctors@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F59D16F115BD693F00B6F892 /* info-doctors@2x.png */; }; F59D170915BD7CE500B6F892 /* info-content.plist in Resources */ = {isa = PBXBuildFile; fileRef = F59D170715BD7CE500B6F892 /* info-content.plist */; }; F59D170A15BD7CE500B6F892 /* info-sport-openingsuren.html in Resources */ = {isa = PBXBuildFile; fileRef = F59D170815BD7CE500B6F892 /* info-sport-openingsuren.html */; }; - F59D170B15BD7E7E00B6F892 /* DashboardViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = F5EBF10F159DFD3B00EB5D26 /* DashboardViewController.xib */; }; F59D170E15BD811000B6F892 /* info-sport-aanbod.html in Resources */ = {isa = PBXBuildFile; fileRef = F59D170D15BD811000B6F892 /* info-sport-aanbod.html */; }; F59D171015BD813E00B6F892 /* webview.css in Resources */ = {isa = PBXBuildFile; fileRef = F59D170F15BD813E00B6F892 /* webview.css */; }; F5A81CBE15CF1BA700FE033B /* external-link.png in Resources */ = {isa = PBXBuildFile; fileRef = F5A81CBD15CF1BA700FE033B /* external-link.png */; }; @@ -168,7 +206,6 @@ F5BAEC8F1518868200F6A1B1 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F5BAEC8E1518868200F6A1B1 /* CoreGraphics.framework */; }; F5BAEC971518868200F6A1B1 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = F5BAEC961518868200F6A1B1 /* main.m */; }; F5BAEC9B1518868200F6A1B1 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = F5BAEC9A1518868200F6A1B1 /* AppDelegate.m */; }; - F5BAECA41518868200F6A1B1 /* DashboardViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F5BAECA31518868200F6A1B1 /* DashboardViewController.m */; }; F5BAECB01518868200F6A1B1 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F5BAEC8A1518868200F6A1B1 /* UIKit.framework */; }; F5BAECB11518868200F6A1B1 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F5BAEC8C1518868200F6A1B1 /* Foundation.framework */; }; F5BAECBC1518868200F6A1B1 /* HydraTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F5BAECBB1518868200F6A1B1 /* HydraTests.m */; }; @@ -211,12 +248,10 @@ F5E851D516C44BD7003DF993 /* hydra-logo@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F5E851D316C44BD7003DF993 /* hydra-logo@2x.png */; }; F5EBF0E0164BE583002C14BF /* SORelativeDateTransformer.bundle in Resources */ = {isa = PBXBuildFile; fileRef = F5EBF0DD164BE583002C14BF /* SORelativeDateTransformer.bundle */; }; F5EBF0E1164BE583002C14BF /* SORelativeDateTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = F5EBF0DF164BE583002C14BF /* SORelativeDateTransformer.m */; }; - F5EBF117159E040700EB5D26 /* RestoMenuController.m in Sources */ = {isa = PBXBuildFile; fileRef = F5EBF115159E040700EB5D26 /* RestoMenuController.m */; }; F5EBF11D159E092300EB5D26 /* SchamperViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F5EBF11B159E092300EB5D26 /* SchamperViewController.m */; }; F5EBF13D159E13E600EB5D26 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F5EBF13C159E13E600EB5D26 /* MobileCoreServices.framework */; }; F5EBF143159E13FD00EB5D26 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F5EBF142159E13FD00EB5D26 /* QuartzCore.framework */; }; F5EBF14C159E1E3600EB5D26 /* SchamperArticle.m in Sources */ = {isa = PBXBuildFile; fileRef = F5EBF14B159E1E3600EB5D26 /* SchamperArticle.m */; }; - F5F43C1415BAC8A7003C2AAE /* Associations.plist in Resources */ = {isa = PBXBuildFile; fileRef = F5F43C1315BAC8A7003C2AAE /* Associations.plist */; }; F5F43C3E15BAD246003C2AAE /* AssociationNewsItem.m in Sources */ = {isa = PBXBuildFile; fileRef = F5F43C3D15BAD246003C2AAE /* AssociationNewsItem.m */; }; F5F43C4115BAD2CC003C2AAE /* AssociationActivity.m in Sources */ = {isa = PBXBuildFile; fileRef = F5F43C4015BAD2CC003C2AAE /* AssociationActivity.m */; }; F5F43C4415BAD2EB003C2AAE /* AssociationStore.m in Sources */ = {isa = PBXBuildFile; fileRef = F5F43C4315BAD2EB003C2AAE /* AssociationStore.m */; }; @@ -268,41 +303,78 @@ 39B9A4055F5F374AA56058ED /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = ""; }; 3B436DF615B8915200984D3F /* InfoViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InfoViewController.h; sourceTree = ""; }; 3B436DF715B8915200984D3F /* InfoViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InfoViewController.m; sourceTree = ""; }; - 3B7AD3B715BC830F0026BB62 /* RestoMenuView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RestoMenuView.h; sourceTree = ""; }; - 3B7AD3B815BC830F0026BB62 /* RestoMenuView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RestoMenuView.m; sourceTree = ""; }; 3B93FB9E1630939200C962DC /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; }; 3B93FBA81630969900C962DC /* UrgentPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UrgentPlayer.h; sourceTree = ""; }; 3B93FBA91630969900C962DC /* UrgentPlayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UrgentPlayer.m; sourceTree = ""; }; 3B93FBAB16309E2100C962DC /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; }; - 3BAFFE7C15BB04B600921E7D /* BadgedButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BadgedButton.h; sourceTree = ""; }; - 3BAFFE7D15BB04B600921E7D /* BadgedButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BadgedButton.m; sourceTree = ""; }; 445C81F316C3DB0B0080819C /* AssociationPreferenceController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AssociationPreferenceController.h; sourceTree = ""; }; 445C81F416C3DB0B0080819C /* AssociationPreferenceController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AssociationPreferenceController.m; sourceTree = ""; }; 445C81FA16C45D7A0080819C /* button-settings.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "button-settings.png"; sourceTree = ""; }; 445C81FB16C45D7A0080819C /* button-settings@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "button-settings@2x.png"; sourceTree = ""; }; 44A29C57162DA88E001A573C /* EventKitUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = EventKitUI.framework; path = System/Library/Frameworks/EventKitUI.framework; sourceTree = SDKROOT; }; + 9004F58D1B7F2FB60041FAA6 /* resto-map-icon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "resto-map-icon@2x.png"; sourceTree = ""; }; + 9004F58E1B7F2FB60041FAA6 /* resto-map-icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "resto-map-icon.png"; sourceTree = ""; }; + 901603DB1B716DED002E0D60 /* HomeNewsItemCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeNewsItemCollectionViewCell.swift; sourceTree = ""; }; 902939431A8C22EB00868221 /* info-bloklocaties.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "info-bloklocaties.html"; sourceTree = ""; }; + 9035BF111B6C1753008E7875 /* schamper.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = schamper.png; sourceTree = ""; }; + 9035BF131B6C1F5D008E7875 /* HomeSchamperCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeSchamperCollectionViewCell.swift; sourceTree = ""; }; + 9035BF151B6C37FE008E7875 /* home-zeus.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "home-zeus.png"; sourceTree = ""; }; + 9035BF171B6CB52A008E7875 /* HomeActivityCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeActivityCollectionViewCell.swift; sourceTree = ""; }; + 9035BF191B6CF663008E7875 /* LocationService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocationService.swift; sourceTree = ""; }; + 903A8AF51C04F369002DAF5B /* ActivityOverviewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActivityOverviewCell.swift; sourceTree = ""; }; + 903A8AF71C04FBF7002DAF5B /* ActivityOverviewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ActivityOverviewCell.xib; sourceTree = ""; }; 90410B0E1848DF9100331F6C /* info-guide.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "info-guide.png"; sourceTree = ""; }; 90410B0F1848DF9100331F6C /* info-guide@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "info-guide@2x.png"; sourceTree = ""; }; + 904166211B7A689500D231EF /* tabbar-urgent@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "tabbar-urgent@2x.png"; sourceTree = ""; }; + 904166221B7A689500D231EF /* tabbar-urgent.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "tabbar-urgent.png"; sourceTree = ""; }; + 904166251B7A837C00D231EF /* tabbar-settings.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "tabbar-settings.png"; sourceTree = ""; }; + 9041C3671B7E67B600E4A50C /* RestoMenuCollectionCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RestoMenuCollectionCell.swift; sourceTree = ""; }; + 9041C36C1B7E801B00E4A50C /* RestoMenuHeader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RestoMenuHeader.swift; sourceTree = ""; }; 904BC46817E2014A0000FED6 /* dot-question.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "dot-question.png"; sourceTree = ""; }; 904BC46917E2014A0000FED6 /* dot-question@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "dot-question@2x.png"; sourceTree = ""; }; + 905EB0121BC809C600F38679 /* tabbar-settings@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "tabbar-settings@2x.png"; sourceTree = ""; }; + 905EB0141BC80A3800F38679 /* tabbar-news@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "tabbar-news@2x.png"; sourceTree = ""; }; + 905EB0161BC80B2000F38679 /* tabbar-activities@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "tabbar-activities@2x.png"; sourceTree = ""; }; + 9068BA771B7BB437005F79FA /* tabbar-resto@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "tabbar-resto@2x.png"; sourceTree = ""; }; + 9068BA791B7BB560005F79FA /* tabbar-home@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "tabbar-home@2x.png"; sourceTree = ""; }; + 9068BA7B1B7BB684005F79FA /* tabbar-info@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "tabbar-info@2x.png"; sourceTree = ""; }; 906C1D8717C8A4A600145CD3 /* MapViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MapViewController.h; sourceTree = ""; }; 906C1D8817C8A4A600145CD3 /* MapViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MapViewController.m; sourceTree = ""; }; + 907CD3C91B7E52B600DD7539 /* RestoMenuViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RestoMenuViewController.swift; sourceTree = ""; }; 908CA4DB1A8D439C00F8C31F /* info-mapmarker.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "info-mapmarker.png"; sourceTree = ""; }; 908CA4DC1A8D439C00F8C31F /* info-mapmarker@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "info-mapmarker@2x.png"; sourceTree = ""; }; 908DFF02188E932D00D526DC /* Pods-acknowledgements.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Pods-acknowledgements.plist"; sourceTree = ""; }; 908E536419BE4F3900F1DA57 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + 90969AD11BFF5D8D006EDD9D /* AssociationStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AssociationStore.swift; sourceTree = ""; }; + 90969AD31BFF5F90006EDD9D /* RestoStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RestoStore.swift; sourceTree = ""; }; + 90969AD51BFF6014006EDD9D /* SchamperStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchamperStore.swift; sourceTree = ""; }; 90B1029617A85E9200669CCD /* kalender.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; path = kalender.css; sourceTree = ""; }; + 90B1EF0B1B7A5DC300E733C1 /* tabbar-home.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "tabbar-home.png"; sourceTree = ""; }; + 90B1EF0D1B7A5EF000E733C1 /* tabbar-resto.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "tabbar-resto.png"; sourceTree = ""; }; + 90B1EF0F1B7A5FDB00E733C1 /* tabbar-info.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "tabbar-info.png"; sourceTree = ""; }; + 90B1EF111B7A606E00E733C1 /* tabbar-activities.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "tabbar-activities.png"; sourceTree = ""; }; + 90B1EF131B7A60B000E733C1 /* tabbar-news.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "tabbar-news.png"; sourceTree = ""; }; + 90B1EF151B7A60FC00E733C1 /* tabbar-schamper@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "tabbar-schamper@2x.png"; sourceTree = ""; }; + 90B1EF171B7A61DD00E733C1 /* tabbar-schamper.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "tabbar-schamper.png"; sourceTree = ""; }; 90BAB264162749BC0043BA92 /* SchamperDetailViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SchamperDetailViewController.h; sourceTree = ""; }; 90BAB265162749BC0043BA92 /* SchamperDetailViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SchamperDetailViewController.m; sourceTree = ""; }; + 90BE1F871B6AB3390061888C /* Hydra-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "Hydra-Bridging-Header.h"; path = "Hydra/Hydra-Bridging-Header.h"; sourceTree = ""; }; + 90BE1F881B6AB3390061888C /* HomeViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeViewController.swift; sourceTree = ""; }; + 90BE1F8C1B6AB61D0061888C /* MainStoryboard.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = MainStoryboard.storyboard; sourceTree = ""; }; + 90BE1F8E1B6AB8780061888C /* home-header.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "home-header.png"; sourceTree = ""; }; + 90BE1F8F1B6AB8780061888C /* home-background.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "home-background.png"; sourceTree = ""; }; 90C532A916A325A200857EB0 /* FacebookEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FacebookEvent.h; sourceTree = ""; }; 90C532AA16A325A200857EB0 /* FacebookEvent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FacebookEvent.m; sourceTree = ""; }; + 90C8CA2D1B6AE3E80073E026 /* HomeFeedService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeFeedService.swift; sourceTree = ""; }; + 90D55B9A1B7A4CE800813179 /* HydraTabbarController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HydraTabbarController.swift; sourceTree = ""; }; + 90DDFB781B6BB3D300DDAFA0 /* HomeRestoCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeRestoCollectionViewCell.swift; sourceTree = ""; }; 90E814F11854C76C00075F96 /* ios7-Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ios7-Default-568h@2x.png"; sourceTree = ""; }; 90E814F21854C76C00075F96 /* ios7-Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ios7-Default@2x.png"; sourceTree = ""; }; - 90EB7BE11688BCC700A9582D /* RestoInfoView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RestoInfoView.h; sourceTree = ""; }; - 90EB7BE21688BCC700A9582D /* RestoInfoView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RestoInfoView.m; sourceTree = ""; }; + 90E8DDC01B6AD45C0059F71B /* home-button-bar.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "home-button-bar.png"; sourceTree = ""; }; 90EB7BE81688ECE300A9582D /* RestoLegendItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RestoLegendItem.h; sourceTree = ""; }; 90EB7BE91688ECE300A9582D /* RestoLegendItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RestoLegendItem.m; sourceTree = ""; }; + 90EE4CCF1BB7193900C2DD32 /* RestoMenuInfoCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RestoMenuInfoCollectionViewCell.swift; sourceTree = ""; }; + 90EF4A581B6D8AE40034AD8F /* HomeUrgentCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeUrgentCollectionViewCell.swift; sourceTree = ""; }; 90F0525B168C663F004CAFFC /* RestoMapController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RestoMapController.h; sourceTree = ""; }; 90F0525C168C663F004CAFFC /* RestoMapController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RestoMapController.m; sourceTree = ""; }; 90F05260168C7022004CAFFC /* CoreLocation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreLocation.framework; path = System/Library/Frameworks/CoreLocation.framework; sourceTree = SDKROOT; }; @@ -456,8 +528,6 @@ F5BAEC961518868200F6A1B1 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; name = main.m; path = Hydra/main.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; F5BAEC991518868200F6A1B1 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = AppDelegate.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; F5BAEC9A1518868200F6A1B1 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; - F5BAECA21518868200F6A1B1 /* DashboardViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = DashboardViewController.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; - F5BAECA31518868200F6A1B1 /* DashboardViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DashboardViewController.m; sourceTree = ""; }; F5BAECAD1518868200F6A1B1 /* HydraTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = HydraTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; F5BAECB61518868200F6A1B1 /* HydraTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "HydraTests-Info.plist"; sourceTree = ""; }; F5BAECBA1518868200F6A1B1 /* HydraTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = HydraTests.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; @@ -506,16 +576,12 @@ F5EBF0DD164BE583002C14BF /* SORelativeDateTransformer.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = SORelativeDateTransformer.bundle; sourceTree = ""; }; F5EBF0DE164BE583002C14BF /* SORelativeDateTransformer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SORelativeDateTransformer.h; sourceTree = ""; }; F5EBF0DF164BE583002C14BF /* SORelativeDateTransformer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SORelativeDateTransformer.m; sourceTree = ""; }; - F5EBF112159DFDEC00EB5D26 /* nl */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = nl; path = nl.lproj/DashboardViewController.xib; sourceTree = ""; }; - F5EBF114159E040700EB5D26 /* RestoMenuController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RestoMenuController.h; sourceTree = ""; }; - F5EBF115159E040700EB5D26 /* RestoMenuController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RestoMenuController.m; sourceTree = ""; wrapsLines = 0; }; F5EBF11A159E092300EB5D26 /* SchamperViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SchamperViewController.h; sourceTree = ""; }; F5EBF11B159E092300EB5D26 /* SchamperViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SchamperViewController.m; sourceTree = ""; }; F5EBF13C159E13E600EB5D26 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; }; F5EBF142159E13FD00EB5D26 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; F5EBF14A159E1E3600EB5D26 /* SchamperArticle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SchamperArticle.h; sourceTree = ""; }; F5EBF14B159E1E3600EB5D26 /* SchamperArticle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SchamperArticle.m; sourceTree = ""; }; - F5F43C1315BAC8A7003C2AAE /* Associations.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Associations.plist; path = Resources/Associations.plist; sourceTree = ""; }; F5F43C3C15BAD246003C2AAE /* AssociationNewsItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AssociationNewsItem.h; sourceTree = ""; }; F5F43C3D15BAD246003C2AAE /* AssociationNewsItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AssociationNewsItem.m; sourceTree = ""; }; F5F43C3F15BAD2CC003C2AAE /* AssociationActivity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AssociationActivity.h; sourceTree = ""; }; @@ -583,10 +649,9 @@ 3B059BF215B99B4800B4C1DA /* Custom views */ = { isa = PBXGroup; children = ( - 3BAFFE7C15BB04B600921E7D /* BadgedButton.h */, - 3BAFFE7D15BB04B600921E7D /* BadgedButton.m */, F5ACC3AF16D2693F00626EAE /* CustomTableViewCell.h */, F5ACC3B016D2694000626EAE /* CustomTableViewCell.m */, + 90DDFB7B1B6BB40100DDAFA0 /* Home */, ); name = "Custom views"; sourceTree = ""; @@ -594,14 +659,12 @@ 3B7AD3B615BC82EB0026BB62 /* Resto */ = { isa = PBXGroup; children = ( - 90EB7BE11688BCC700A9582D /* RestoInfoView.h */, - 90EB7BE21688BCC700A9582D /* RestoInfoView.m */, 90F0525B168C663F004CAFFC /* RestoMapController.h */, 90F0525C168C663F004CAFFC /* RestoMapController.m */, - 3B7AD3B715BC830F0026BB62 /* RestoMenuView.h */, - 3B7AD3B815BC830F0026BB62 /* RestoMenuView.m */, - F5EBF114159E040700EB5D26 /* RestoMenuController.h */, - F5EBF115159E040700EB5D26 /* RestoMenuController.m */, + 9041C3671B7E67B600E4A50C /* RestoMenuCollectionCell.swift */, + 9041C36C1B7E801B00E4A50C /* RestoMenuHeader.swift */, + 90EE4CCF1BB7193900C2DD32 /* RestoMenuInfoCollectionViewCell.swift */, + 907CD3C91B7E52B600DD7539 /* RestoMenuViewController.swift */, ); name = Resto; sourceTree = ""; @@ -611,18 +674,43 @@ children = ( F5F43C4215BAD2EB003C2AAE /* AssociationStore.h */, F5F43C4315BAD2EB003C2AAE /* AssociationStore.m */, + 90969AD11BFF5D8D006EDD9D /* AssociationStore.swift */, + 90C8CA2D1B6AE3E80073E026 /* HomeFeedService.swift */, + 9035BF191B6CF663008E7875 /* LocationService.swift */, + F5C6093616D2DFCB0043BD44 /* PreferencesService.h */, + F5C6093716D2DFCB0043BD44 /* PreferencesService.m */, F55895C515B6100C0001B399 /* RestoStore.h */, F55895C615B610130001B399 /* RestoStore.m */, + 90969AD31BFF5F90006EDD9D /* RestoStore.swift */, F5D36BBF15B5BDA500B6E017 /* SchamperStore.h */, F5D36BC015B5BDA500B6E017 /* SchamperStore.m */, + 90969AD51BFF6014006EDD9D /* SchamperStore.swift */, 3B93FBA81630969900C962DC /* UrgentPlayer.h */, 3B93FBA91630969900C962DC /* UrgentPlayer.m */, - F5C6093616D2DFCB0043BD44 /* PreferencesService.h */, - F5C6093716D2DFCB0043BD44 /* PreferencesService.m */, ); name = Services; sourceTree = ""; }; + 90BE1F861B6AB30F0061888C /* Home */ = { + isa = PBXGroup; + children = ( + 90BE1F881B6AB3390061888C /* HomeViewController.swift */, + ); + name = Home; + sourceTree = ""; + }; + 90DDFB7B1B6BB40100DDAFA0 /* Home */ = { + isa = PBXGroup; + children = ( + 9035BF171B6CB52A008E7875 /* HomeActivityCollectionViewCell.swift */, + 90DDFB781B6BB3D300DDAFA0 /* HomeRestoCollectionViewCell.swift */, + 9035BF131B6C1F5D008E7875 /* HomeSchamperCollectionViewCell.swift */, + 90EF4A581B6D8AE40034AD8F /* HomeUrgentCollectionViewCell.swift */, + 901603DB1B716DED002E0D60 /* HomeNewsItemCollectionViewCell.swift */, + ); + name = Home; + sourceTree = ""; + }; B007FEAAB912E2F80EF4FE3C /* Pods */ = { isa = PBXGroup; children = ( @@ -709,6 +797,8 @@ F5016BA81627665800BBDB0A /* ActivityDetailController.m */, F52A8B7717CD366E00C3379C /* ActivityMapController.h */, F52A8B7817CD366E00C3379C /* ActivityMapController.m */, + 903A8AF51C04F369002DAF5B /* ActivityOverviewCell.swift */, + 903A8AF71C04FBF7002DAF5B /* ActivityOverviewCell.xib */, ); name = Activities; sourceTree = ""; @@ -802,6 +892,10 @@ F5A81CC115CF1CC500FE033B /* external-link@2x.png */, F53D2489159DB50800F408AB /* header-bg.png */, F5E7F06B15EF9407004024AA /* header-bg@2x.png */, + 90BE1F8F1B6AB8780061888C /* home-background.png */, + 90E8DDC01B6AD45C0059F71B /* home-button-bar.png */, + 90BE1F8E1B6AB8780061888C /* home-header.png */, + 9035BF151B6C37FE008E7875 /* home-zeus.png */, F5E851D216C44BD7003DF993 /* hydra-logo.png */, F5E851D316C44BD7003DF993 /* hydra-logo@2x.png */, F520A2DB166E933500EE340F /* icon-calendar.png */, @@ -820,6 +914,8 @@ F59D16E715BD693F00B6F892 /* info-bicycle@2x.png */, F59D16F015BD693F00B6F892 /* info-doctors.png */, F59D16F115BD693F00B6F892 /* info-doctors@2x.png */, + 90410B0E1848DF9100331F6C /* info-guide.png */, + 90410B0F1848DF9100331F6C /* info-guide@2x.png */, F59D16EA15BD693F00B6F892 /* info-library.png */, F59D16EB15BD693F00B6F892 /* info-library@2x.png */, 908CA4DB1A8D439C00F8C31F /* info-mapmarker.png */, @@ -830,8 +926,6 @@ F59D16ED15BD693F00B6F892 /* info-more@2x.png */, F59D16EE15BD693F00B6F892 /* info-sports.png */, F59D16EF15BD693F00B6F892 /* info-sports@2x.png */, - 90410B0E1848DF9100331F6C /* info-guide.png */, - 90410B0F1848DF9100331F6C /* info-guide@2x.png */, F5C4618B166E442000874D79 /* navigation-down.png */, F5C4618C166E442000874D79 /* navigation-down@2x.png */, F5C4618D166E442000874D79 /* navigation-up.png */, @@ -840,8 +934,27 @@ F5329E0115B9858900C4C5DF /* resto-closed@2x.jpg */, F54B160015B7FFE6000A4407 /* resto-logo.png */, F54B160115B7FFE6000A4407 /* resto-logo@2x.png */, + 9004F58E1B7F2FB60041FAA6 /* resto-map-icon.png */, + 9004F58D1B7F2FB60041FAA6 /* resto-map-icon@2x.png */, F533DABB163163E2001269A8 /* schamper-bg.png */, F557EA45163431FC00635CDD /* schamper-bg@2x.png */, + 9035BF111B6C1753008E7875 /* schamper.png */, + 90B1EF111B7A606E00E733C1 /* tabbar-activities.png */, + 905EB0161BC80B2000F38679 /* tabbar-activities@2x.png */, + 90B1EF0B1B7A5DC300E733C1 /* tabbar-home.png */, + 9068BA791B7BB560005F79FA /* tabbar-home@2x.png */, + 90B1EF0F1B7A5FDB00E733C1 /* tabbar-info.png */, + 9068BA7B1B7BB684005F79FA /* tabbar-info@2x.png */, + 90B1EF131B7A60B000E733C1 /* tabbar-news.png */, + 905EB0141BC80A3800F38679 /* tabbar-news@2x.png */, + 90B1EF0D1B7A5EF000E733C1 /* tabbar-resto.png */, + 9068BA771B7BB437005F79FA /* tabbar-resto@2x.png */, + 90B1EF171B7A61DD00E733C1 /* tabbar-schamper.png */, + 90B1EF151B7A60FC00E733C1 /* tabbar-schamper@2x.png */, + 904166251B7A837C00D231EF /* tabbar-settings.png */, + 905EB0121BC809C600F38679 /* tabbar-settings@2x.png */, + 904166221B7A689500D231EF /* tabbar-urgent.png */, + 904166211B7A689500D231EF /* tabbar-urgent@2x.png */, F5E851CE16C44B47003DF993 /* urgent-bg.jpg */, F5E851CF16C44B47003DF993 /* urgent-bg@2x.jpg */, F5B650AC16C135A900989ADB /* urgent-logo.png */, @@ -986,11 +1099,10 @@ F53A5E8316700B28009EA4CC /* ApplicationWithRemoteSupport.m */, F57CB9DB15B84C4E00D87CB3 /* Categories */, F5EBF119159E04B500EB5D26 /* Controllers */, - 3B059BF215B99B4800B4C1DA /* Custom views */, + 908E536419BE4F3900F1DA57 /* Images.xcassets */, F5EBF11F159E09C700EB5D26 /* Models */, 3B93FBA01630942500C962DC /* Services */, F59CEE9716A189290038FD24 /* Social */, - 908E536419BE4F3900F1DA57 /* Images.xcassets */, ); path = Hydra; sourceTree = ""; @@ -999,7 +1111,6 @@ isa = PBXGroup; children = ( F5BAEC961518868200F6A1B1 /* main.m */, - F5F43C1315BAC8A7003C2AAE /* Associations.plist */, F5BC3D8D162EBB5100E4A902 /* Default-568h@2x.png */, F5D36BB315B5BD2A00B6E017 /* Default.png */, F5D36BB415B5BD2A00B6E017 /* Default@2x.png */, @@ -1007,6 +1118,7 @@ 90E814F21854C76C00075F96 /* ios7-Default@2x.png */, F5BAEC921518868200F6A1B1 /* Hydra-Info.plist */, F5D36BD815B5CD1500B6E017 /* Hydra-Prefix.pch */, + 90BE1F871B6AB3390061888C /* Hydra-Bridging-Header.h */, F5CE522016C2BDA70033A52B /* Errors.strings */, F56D76701699ED4D002CF245 /* Icons */, F53D2488159DB4DB00F408AB /* Images */, @@ -1049,9 +1161,8 @@ isa = PBXGroup; children = ( F536359C16A69C86000A50C4 /* Activities */, - F5BAECA21518868200F6A1B1 /* DashboardViewController.h */, - F5BAECA31518868200F6A1B1 /* DashboardViewController.m */, - F5EBF10F159DFD3B00EB5D26 /* DashboardViewController.xib */, + 90BE1F861B6AB30F0061888C /* Home */, + 90D55B9A1B7A4CE800813179 /* HydraTabbarController.swift */, 3B436DF615B8915200984D3F /* InfoViewController.h */, 3B436DF715B8915200984D3F /* InfoViewController.m */, F536359E16A69C94000A50C4 /* News */, @@ -1062,6 +1173,8 @@ F53A5E7416700242009EA4CC /* UrgentViewController.h */, F53A5E7516700242009EA4CC /* UrgentViewController.m */, F56F139E169731FE003D4447 /* UrgentViewController.xib */, + 90BE1F8C1B6AB61D0061888C /* MainStoryboard.storyboard */, + 3B059BF215B99B4800B4C1DA /* Custom views */, ); name = Controllers; sourceTree = ""; @@ -1095,6 +1208,7 @@ buildConfigurationList = F49F79391A1671960040987F /* Build configuration list for PBXNativeTarget "RestoMenuToday" */; buildPhases = ( F49F79281A1671950040987F /* Sources */, + 90EE4CD11BB804F600C2DD32 /* ShellScript */, F49F79291A1671950040987F /* Frameworks */, F49F792A1A1671950040987F /* Resources */, ); @@ -1152,6 +1266,8 @@ F5BAEC7D1518868100F6A1B1 /* Project object */ = { isa = PBXProject; attributes = { + LastSwiftMigration = 0700; + LastSwiftUpdateCheck = 0700; LastUpgradeCheck = 0500; ORGANIZATIONNAME = "Zeus WPI"; TargetAttributes = { @@ -1199,32 +1315,36 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - F59D170B15BD7E7E00B6F892 /* DashboardViewController.xib in Resources */, F53D248A159DB50800F408AB /* header-bg.png in Resources */, F58408B7159DDA0E00EE7010 /* button-activities-active.png in Resources */, 908CA4DD1A8D439C00F8C31F /* info-mapmarker.png in Resources */, + 905EB0151BC80A3800F38679 /* tabbar-news@2x.png in Resources */, F58408B8159DDA0E00EE7010 /* button-activities.png in Resources */, F58408BB159DDA0E00EE7010 /* button-info-active.png in Resources */, F58408BC159DDA0E00EE7010 /* button-info.png in Resources */, F58408BD159DDA0E00EE7010 /* button-news-active.png in Resources */, + 905EB0131BC809C600F38679 /* tabbar-settings@2x.png in Resources */, F58408BE159DDA0E00EE7010 /* button-news.png in Resources */, F58408BF159DDA0E00EE7010 /* button-resto-active.png in Resources */, F58408C0159DDA0E00EE7010 /* button-resto.png in Resources */, F58408C1159DDA0E00EE7010 /* button-schamper-active.png in Resources */, + 90BE1F8D1B6AB61D0061888C /* MainStoryboard.storyboard in Resources */, F58408C2159DDA0E00EE7010 /* button-schamper.png in Resources */, F5D36BB715B5BD2A00B6E017 /* Default.png in Resources */, F5D36BB815B5BD2A00B6E017 /* Default@2x.png in Resources */, + 904166261B7A837C00D231EF /* tabbar-settings.png in Resources */, F54B160215B7FFE6000A4407 /* resto-logo.png in Resources */, F54B160315B7FFE6000A4407 /* resto-logo@2x.png in Resources */, F5B9F2F117CEAA040042C8A4 /* Icon-Small-iOS6@2x.png in Resources */, F57E7E0E15B837F100D2A59E /* icon-meal.png in Resources */, F57E7E0F15B837F100D2A59E /* icon-soup.png in Resources */, F57E7E1015B837F100D2A59E /* icon-vegetables.png in Resources */, + 903A8AF81C04FBF7002DAF5B /* ActivityOverviewCell.xib in Resources */, F5329E0215B9858900C4C5DF /* resto-closed.jpg in Resources */, F5329E0315B9858900C4C5DF /* resto-closed@2x.jpg in Resources */, 904BC46C17E2014A0000FED6 /* dot-question.png in Resources */, - F5F43C1415BAC8A7003C2AAE /* Associations.plist in Resources */, F59D16F415BD693F00B6F892 /* info-academiccalendar.png in Resources */, + 90B1EF141B7A60B000E733C1 /* tabbar-news.png in Resources */, F59D16F515BD693F00B6F892 /* info-academiccalendar@2x.png in Resources */, 904BC46D17E2014A0000FED6 /* dot-question@2x.png in Resources */, F59D16F615BD693F00B6F892 /* info-bicycle.png in Resources */, @@ -1251,9 +1371,12 @@ F5A81CBE15CF1BA700FE033B /* external-link.png in Resources */, F5A81CC215CF1CC500FE033B /* external-link-active.png in Resources */, F5A81CC315CF1CC500FE033B /* external-link-active@2x.png in Resources */, + 90BE1F901B6AB8780061888C /* home-header.png in Resources */, F5A81CC415CF1CC500FE033B /* external-link@2x.png in Resources */, F5E7F06C15EF9408004024AA /* header-bg@2x.png in Resources */, + 90B1EF101B7A5FDB00E733C1 /* tabbar-info.png in Resources */, F5E7F07215EFAA51004024AA /* button-activities-active@2x.png in Resources */, + 90B1EF0C1B7A5DC300E733C1 /* tabbar-home.png in Resources */, F5E7F07315EFAA51004024AA /* button-activities@2x.png in Resources */, F5D2B6B515EFE2DE003EA54C /* button-news@2x.png in Resources */, F5D2B6B715EFEAAF003EA54C /* button-news-active@2x.png in Resources */, @@ -1261,7 +1384,11 @@ F5FC117015F0211300B3F127 /* button-info@2x.png in Resources */, F5FC117115F0211300B3F127 /* button-resto-active@2x.png in Resources */, F5FC117215F0211300B3F127 /* button-resto@2x.png in Resources */, + 905EB0171BC80B2100F38679 /* tabbar-activities@2x.png in Resources */, + 9068BA7C1B7BB684005F79FA /* tabbar-info@2x.png in Resources */, + 90B1EF181B7A61DD00E733C1 /* tabbar-schamper.png in Resources */, F5FC117315F0211300B3F127 /* button-schamper-active@2x.png in Resources */, + 9068BA7A1B7BB560005F79FA /* tabbar-home@2x.png in Resources */, F5FC117415F0211300B3F127 /* button-schamper@2x.png in Resources */, F5BC3D8E162EBB5100E4A902 /* Default-568h@2x.png in Resources */, F5D3B612162EF02F00698C3E /* button-feedback.png in Resources */, @@ -1269,9 +1396,14 @@ 908E536519BE4F3900F1DA57 /* Images.xcassets in Resources */, F533DABA16315E87001269A8 /* schamper.css in Resources */, F533DABC163163E2001269A8 /* schamper-bg.png in Resources */, + 90B1EF121B7A606E00E733C1 /* tabbar-activities.png in Resources */, F557EA46163431FC00635CDD /* schamper-bg@2x.png in Resources */, + 90BE1F911B6AB8780061888C /* home-background.png in Resources */, 90E814F41854C76C00075F96 /* ios7-Default@2x.png in Resources */, + 9068BA781B7BB437005F79FA /* tabbar-resto@2x.png in Resources */, + 9035BF161B6C37FE008E7875 /* home-zeus.png in Resources */, F5EBF0E0164BE583002C14BF /* SORelativeDateTransformer.bundle in Resources */, + 9004F58F1B7F2FB60041FAA6 /* resto-map-icon@2x.png in Resources */, F5C4618F166E442000874D79 /* navigation-down.png in Resources */, F5C46190166E442000874D79 /* navigation-down@2x.png in Resources */, F5C46191166E442000874D79 /* navigation-up.png in Resources */, @@ -1301,7 +1433,9 @@ F5D51CF716B6D44800826B51 /* info-minerva@2x.png in Resources */, F567044416BD5D8100C6B00D /* icon-meal@2x.png in Resources */, F567044516BD5D8100C6B00D /* icon-soup@2x.png in Resources */, + 9004F5901B7F2FB60041FAA6 /* resto-map-icon.png in Resources */, F567044616BD5D8100C6B00D /* icon-vegetables@2x.png in Resources */, + 90E8DDC11B6AD45C0059F71B /* home-button-bar.png in Resources */, F567044716BD5D8100C6B00D /* info-minerva.png in Resources */, F5B6509C16C129B700989ADB /* btn-urgent-bg-highlighted.png in Resources */, F5B6509D16C129B700989ADB /* btn-urgent-bg-highlighted@2x.png in Resources */, @@ -1310,6 +1444,7 @@ F5B9F2F317CEAA040042C8A4 /* iTunesArtwork.png in Resources */, F5B650A416C12A9300989ADB /* btn-urgent-pause.png in Resources */, F5B650A516C12A9300989ADB /* btn-urgent-pause@2x.png in Resources */, + 9035BF121B6C1753008E7875 /* schamper.png in Resources */, F5B650A616C12A9300989ADB /* btn-urgent-play.png in Resources */, F5B650A716C12A9300989ADB /* btn-urgent-play@2x.png in Resources */, F5B650AE16C135A900989ADB /* urgent-logo.png in Resources */, @@ -1321,6 +1456,7 @@ 445C81FD16C45D7A0080819C /* button-settings@2x.png in Resources */, F5E851BE16C43174003DF993 /* btn-urgent-facebook.png in Resources */, F5E851BF16C43174003DF993 /* btn-urgent-facebook@2x.png in Resources */, + 904166231B7A689500D231EF /* tabbar-urgent@2x.png in Resources */, F5E851C016C43174003DF993 /* btn-urgent-home.png in Resources */, F5E851C116C43174003DF993 /* btn-urgent-home@2x.png in Resources */, F5E851C216C43174003DF993 /* btn-urgent-mail.png in Resources */, @@ -1329,15 +1465,18 @@ F5B9F2F417CEAA040042C8A4 /* iTunesArtwork@2x.png in Resources */, F5E851C516C43174003DF993 /* btn-urgent-soundcloud@2x.png in Resources */, F5E851C616C43174003DF993 /* btn-urgent-twitter.png in Resources */, + 90B1EF0E1B7A5EF000E733C1 /* tabbar-resto.png in Resources */, F5E851C716C43174003DF993 /* btn-urgent-twitter@2x.png in Resources */, F5B9F2E817CEA96F0042C8A4 /* Icon-iOS6.png in Resources */, F5E851CC16C447C4003DF993 /* urgent-nowplaying.jpg in Resources */, F5E851CD16C447C4003DF993 /* urgent-nowplaying@2x.jpg in Resources */, + 90B1EF161B7A60FC00E733C1 /* tabbar-schamper@2x.png in Resources */, F5E851D016C44B47003DF993 /* urgent-bg.jpg in Resources */, F5E851D116C44B47003DF993 /* urgent-bg@2x.jpg in Resources */, F5B9F2EA17CEA96F0042C8A4 /* Icon-iOS7@2x.png in Resources */, F5E851D416C44BD7003DF993 /* hydra-logo.png in Resources */, F5E851D516C44BD7003DF993 /* hydra-logo@2x.png in Resources */, + 904166241B7A689500D231EF /* tabbar-urgent.png in Resources */, 90B1029717A85E9200669CCD /* kalender.css in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1366,6 +1505,19 @@ shellPath = /bin/sh; shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n"; }; + 90EE4CD11BB804F600C2DD32 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/bash; + shellScript = "src_plist=${PROJECT_DIR}/${INFOPLIST_FILE}\nbuild_plist=${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}\n\nif [ $CONFIGURATION == Release ]; then\n # increment the build number (ie 1.0.115 to 1.0.116)\n echo \"Bumping build number...\"\n\n full_version=$(/usr/libexec/PlistBuddy -c \"Print :CFBundleVersion\" \"${src_plist}\")\n if [[ \"${full_version}\" == \"\" ]]; then\n echo \"No build number in ${src_plist}\"\n exit 2\n fi\n\n version=${full_version%.*}\n build=${full_version##*.}\n \n new_version=\"$version.$(($build+1))\"\n /usr/libexec/Plistbuddy -c \"Set :CFBundleVersion $new_version\" \"${src_plist}\"\n echo \"Bumped build number to $new_version\"\nfi"; + }; F5D3B621162EFBD400698C3E /* ShellScript */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -1400,12 +1552,12 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 3B0D77EC15BB0945002B23C1 /* BadgedButton.m in Sources */, F5BAEC971518868200F6A1B1 /* main.m in Sources */, + 907CD3CA1B7E52B600DD7539 /* RestoMenuViewController.swift in Sources */, + 90969AD41BFF5F90006EDD9D /* RestoStore.swift in Sources */, F5BAEC9B1518868200F6A1B1 /* AppDelegate.m in Sources */, - F5BAECA41518868200F6A1B1 /* DashboardViewController.m in Sources */, - F5EBF117159E040700EB5D26 /* RestoMenuController.m in Sources */, F5EBF11D159E092300EB5D26 /* SchamperViewController.m in Sources */, + 90EE4CD01BB7193900C2DD32 /* RestoMenuInfoCollectionViewCell.swift in Sources */, F5EBF14C159E1E3600EB5D26 /* SchamperArticle.m in Sources */, F5D36BC115B5BDA500B6E017 /* SchamperStore.m in Sources */, F50EE09715B5F587000F1992 /* WebViewController.m in Sources */, @@ -1418,26 +1570,37 @@ F5F43C4115BAD2CC003C2AAE /* AssociationActivity.m in Sources */, F5F43C4415BAD2EB003C2AAE /* AssociationStore.m in Sources */, F5F43C4715BAD42E003C2AAE /* Association.m in Sources */, - 3B7AD3B915BC830F0026BB62 /* RestoMenuView.m in Sources */, 395C117A16272D8000B99F9B /* NewsViewController.m in Sources */, 395C117D1627437000B99F9B /* NewsDetailViewController.m in Sources */, 90BAB266162749BD0043BA92 /* SchamperDetailViewController.m in Sources */, F5016BA6162756F300BBDB0A /* ActivitiesController.m in Sources */, + 90969AD21BFF5D8D006EDD9D /* AssociationStore.swift in Sources */, + 9041C3681B7E67B600E4A50C /* RestoMenuCollectionCell.swift in Sources */, F5016BA91627665800BBDB0A /* ActivityDetailController.m in Sources */, 3B93FBAA1630969900C962DC /* UrgentPlayer.m in Sources */, + 90C8CA2E1B6AE3E80073E026 /* HomeFeedService.swift in Sources */, F52A8B7917CD366E00C3379C /* ActivityMapController.m in Sources */, F5EBF0E1164BE583002C14BF /* SORelativeDateTransformer.m in Sources */, + 9035BF181B6CB52A008E7875 /* HomeActivityCollectionViewCell.swift in Sources */, F53A5E7716700242009EA4CC /* UrgentViewController.m in Sources */, F53A5E8416700B29009EA4CC /* ApplicationWithRemoteSupport.m in Sources */, + 90BE1F891B6AB3390061888C /* HomeViewController.swift in Sources */, F52B9B77168507BD000B71D6 /* NSDateFormatter+AppLocale.m in Sources */, - 90EB7BE31688BCC700A9582D /* RestoInfoView.m in Sources */, + 90EF4A591B6D8AE40034AD8F /* HomeUrgentCollectionViewCell.swift in Sources */, 90EB7BEA1688ECE300A9582D /* RestoLegendItem.m in Sources */, + 901603DC1B716DED002E0D60 /* HomeNewsItemCollectionViewCell.swift in Sources */, 90F0525E168C663F004CAFFC /* RestoMapController.m in Sources */, + 9035BF141B6C1F5D008E7875 /* HomeSchamperCollectionViewCell.swift in Sources */, + 90D55B9B1B7A4CE800813179 /* HydraTabbarController.swift in Sources */, + 90DDFB791B6BB3D300DDAFA0 /* HomeRestoCollectionViewCell.swift in Sources */, 90F05266168C7D19004CAFFC /* RestoLocation.m in Sources */, F59CEE8A16A188050038FD24 /* FacebookSession.m in Sources */, 90C532AB16A325A200857EB0 /* FacebookEvent.m in Sources */, F55E89EC16A4AF8E008595CB /* ShareKitConfigurator.m in Sources */, F569582716AA095800C45D00 /* UINavigationController+ReplaceController.m in Sources */, + 90969AD61BFF6014006EDD9D /* SchamperStore.swift in Sources */, + 9041C36D1B7E801B00E4A50C /* RestoMenuHeader.swift in Sources */, + 9035BF1A1B6CF663008E7875 /* LocationService.swift in Sources */, 445C81F516C3DB0B0080819C /* AssociationPreferenceController.m in Sources */, F5DF2FB916C41DDE003B05EC /* MarqueeLabel.m in Sources */, F521D11616C53A1200B686B2 /* PreferencesController.m in Sources */, @@ -1445,6 +1608,7 @@ F5ACC3B116D2694000626EAE /* CustomTableViewCell.m in Sources */, F5C6093816D2DFCB0043BD44 /* PreferencesService.m in Sources */, 906C1D8917C8A4A600145CD3 /* MapViewController.m in Sources */, + 903A8AF61C04F369002DAF5B /* ActivityOverviewCell.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1496,14 +1660,6 @@ name = Errors.strings; sourceTree = ""; }; - F5EBF10F159DFD3B00EB5D26 /* DashboardViewController.xib */ = { - isa = PBXVariantGroup; - children = ( - F5EBF112159DFDEC00EB5D26 /* nl */, - ); - name = DashboardViewController.xib; - sourceTree = ""; - }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ @@ -1543,8 +1699,10 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE = ""; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; @@ -1579,7 +1737,9 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE = ""; SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; @@ -1592,6 +1752,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; COPY_PHASE_STRIP = NO; CUSTOM_IDENTIFIER_SUFFIX = "-dev"; + ENABLE_BITCODE = NO; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", @@ -1619,6 +1780,7 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; COPY_PHASE_STRIP = YES; CUSTOM_IDENTIFIER_SUFFIX = ""; + ENABLE_BITCODE = NO; GCC_PREPROCESSOR_DEFINITIONS = "DEBUG=0"; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_SHADOW = YES; @@ -1640,6 +1802,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CUSTOM_BUNDLE_DISPLAY_NAME = "${PRODUCT_NAME} β"; @@ -1649,8 +1812,12 @@ GCC_PREFIX_HEADER = "Hydra/Hydra-Prefix.pch"; INFOPLIST_FILE = "Hydra-Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 7.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; + SWIFT_OBJC_BRIDGING_HEADER = "Hydra/Hydra-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TARGETED_DEVICE_FAMILY = "1,2"; WRAPPER_EXTENSION = app; }; name = Debug; @@ -1661,6 +1828,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CUSTOM_BUNDLE_DISPLAY_NAME = "${PRODUCT_NAME}"; @@ -1670,8 +1838,11 @@ GCC_PREFIX_HEADER = "Hydra/Hydra-Prefix.pch"; INFOPLIST_FILE = "Hydra-Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 7.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; + SWIFT_OBJC_BRIDGING_HEADER = "Hydra/Hydra-Bridging-Header.h"; + TARGETED_DEVICE_FAMILY = "1,2"; WRAPPER_EXTENSION = app; }; name = Release; diff --git a/iOS/Hydra/ActivitiesController.m b/iOS/Hydra/ActivitiesController.m index 4a5f57a3..b7cb3068 100644 --- a/iOS/Hydra/ActivitiesController.m +++ b/iOS/Hydra/ActivitiesController.m @@ -15,11 +15,9 @@ #import "NSDateFormatter+AppLocale.h" #import "PreferencesService.h" #import "RMPickerViewController.h" +#import "Hydra-Swift.h" #import -#define kCellTitleLabel 101 -#define kCellSubtitleLabel 102 - @interface ActivitiesController () @property (nonatomic, assign) BOOL activitiesUpdated; @@ -87,6 +85,9 @@ - (void)viewDidLoad [RMPickerViewController setLocalizedTitleForCancelButton:@"Sluit"]; [RMPickerViewController setLocalizedTitleForSelectButton:@"Gereed"]; + + UINib *nib = [UINib nibWithNibName:@"ActivityOverviewCell" bundle:nil]; + [self.tableView registerNib:nib forCellReuseIdentifier:@"ActivityOverviewCell"]; } - (void)viewWillAppear:(BOOL)animated @@ -209,88 +210,20 @@ - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInte - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { - return 46; + return 44; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - UILabel *titleLabel, *subtitleLabel; - NSDate *date = self.days[indexPath.section]; AssociationActivity *activity = self.data[date][indexPath.row]; - static NSString *NoHighlightCellIdentifier = @"ActivityCellNoHighlight"; - static NSString *HighlightCellIdentifier = @"ActivityCellHighlight"; - UITableViewCell *cell; - - if (!activity.highlighted) { - // request cell without the special star view - cell = [tableView dequeueReusableCellWithIdentifier:NoHighlightCellIdentifier]; - if (cell == nil) { - cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault - reuseIdentifier:NoHighlightCellIdentifier]; - [self setupCell:cell withRightMargin:10]; - } - } - else { - // request cell with special star view - cell = [tableView dequeueReusableCellWithIdentifier:HighlightCellIdentifier]; - if (cell == nil) { - cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault - reuseIdentifier:HighlightCellIdentifier]; - [self setupCell:cell withRightMargin:40]; - - UIImageView *star = [[UIImageView alloc] initWithImage: - [UIImage imageNamed:@"icon-star"]]; - star.frame = CGRectMake(286, 8, 27, 27); - [cell.contentView addSubview:star]; - } - } - titleLabel = (UILabel *)[cell viewWithTag:kCellTitleLabel]; - subtitleLabel = (UILabel *)[cell viewWithTag:kCellSubtitleLabel]; - - static NSDateFormatter *dateFormatter = nil; - if (!dateFormatter) { - dateFormatter = [NSDateFormatter H_dateFormatterWithAppLocale]; - dateFormatter.dateFormat = @"HH.mm"; - } - - cell.textLabel.text = [dateFormatter stringFromDate:activity.start]; - titleLabel.text = activity.title; - subtitleLabel.text = activity.association.displayName; + static NSString *CellIdentifier = @"ActivityOverviewCell"; + ActivityOverviewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; + cell.activity = activity; return cell; } -- (void)setupCell:(UITableViewCell *)cell withRightMargin:(int)rightMargin -{ - cell.textLabel.font = [UIFont boldSystemFontOfSize:15.0f]; - cell.textLabel.textColor = [UIColor colorWithWhite:0.5 alpha:1]; - cell.textLabel.highlightedTextColor = [UIColor colorWithWhite:0.94 alpha:1]; - - // iOS7 - if ([cell respondsToSelector:@selector(setSeparatorInset:)]) { - cell.separatorInset = UIEdgeInsetsZero; - } - - CGFloat offsetX = IOS_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0") ? 64 : 60; - CGFloat width = self.view.bounds.size.width - offsetX - rightMargin; - - CGRect titleFrame = CGRectMake(offsetX, 4, width, 20); - UILabel *titleLabel = [[UILabel alloc] initWithFrame:titleFrame]; - titleLabel.tag = kCellTitleLabel; - titleLabel.font = [UIFont boldSystemFontOfSize:17.0f]; - titleLabel.highlightedTextColor = [UIColor whiteColor]; - [cell.contentView addSubview:titleLabel]; - - CGRect subtitleFrame = CGRectMake(offsetX, 24, width, 16); - UILabel *subtitleLabel = [[UILabel alloc] initWithFrame:subtitleFrame]; - subtitleLabel.tag = kCellSubtitleLabel; - subtitleLabel.font = [UIFont systemFontOfSize:13.0f]; - subtitleLabel.textColor = [UIColor colorWithWhite:0.2 alpha:1]; - subtitleLabel.highlightedTextColor = [UIColor whiteColor]; - [cell.contentView addSubview:subtitleLabel]; -} - - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { NSDate *date = self.days[indexPath.section]; @@ -439,70 +372,13 @@ - (void)didSelectActivity:(AssociationActivity *)activity - (void)dateButtonTapped:(id)sender { - if (IOS_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0")) { - RMPickerViewController *pickerVC = [RMPickerViewController pickerController]; - pickerVC.delegate = self; - UIPickerView *picker = pickerVC.picker; - NSInteger row = ((NSIndexPath *)[self.tableView indexPathsForVisibleRows][0]).section; - [picker selectRow:row inComponent:0 animated:NO]; + RMPickerViewController *pickerVC = [RMPickerViewController pickerController]; + pickerVC.delegate = self; + UIPickerView *picker = pickerVC.picker; + NSInteger row = ((NSIndexPath *)[self.tableView indexPathsForVisibleRows][0]).section; + [picker selectRow:row inComponent:0 animated:NO]; - [pickerVC show]; - } else { - // TODO: this is abuse of UIActionSheet, and shouldn't be used like this - UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:nil - delegate:nil - cancelButtonTitle:nil - destructiveButtonTitle:nil - otherButtonTitles:nil]; - - BOOL iOS7 = IOS_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0"); - - // Create toolbar - UIBarButtonItem *flexSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace - target:nil action:nil]; - UIBarButtonItem *doneBtn = [[UIBarButtonItem alloc] initWithTitle:@"Gereed" style:UIBarButtonItemStyleBordered - target:self action:@selector(dismissActionSheet:)]; - UIToolbar *pickerToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, 320, 44)]; - pickerToolbar.tintColor = [UIColor hydraTintColor]; - pickerToolbar.items = @[flexSpace, doneBtn]; - - if (iOS7) { - // Add a gray border to the bottom of the toolbar - CALayer *border = [CALayer layer]; - border.borderColor = [UIColor lightGrayColor].CGColor; - border.borderWidth = 0.25; - border.frame = CGRectMake(0, pickerToolbar.frame.size.height, - pickerToolbar.frame.size.width, 0.25); - [pickerToolbar.layer addSublayer:border]; - } - - [actionSheet addSubview:pickerToolbar]; - - UILabel *title = [[UILabel alloc] initWithFrame:CGRectMake(0, 12, 290, 22)]; - title.font = [UIFont boldSystemFontOfSize:18]; - title.text = @"Selecteer een dag"; - title.textAlignment = NSTextAlignmentCenter; - title.backgroundColor = [UIColor clearColor]; - - if (!iOS7) { - title.textColor = [UIColor whiteColor]; - title.shadowColor = [UIColor darkTextColor]; - } - [actionSheet addSubview:title]; - - // Create datepicker - self.datePicker = [[UIPickerView alloc] initWithFrame:CGRectMake(0, iOS7 ? 34 : 44, 0, 0)]; - self.datePicker.showsSelectionIndicator = YES; - self.datePicker.dataSource = self; - self.datePicker.delegate = self; - [actionSheet addSubview:self.datePicker]; - - NSIndexPath *firstSection = [self.tableView indexPathsForVisibleRows][0]; - [self.datePicker selectRow:firstSection.section inComponent:0 animated:NO]; - - [actionSheet showInView:self.view]; - [actionSheet setBounds:CGRectMake(0, 0, 320, 500)]; - } + [pickerVC show]; } - (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView diff --git a/iOS/Hydra/ActivityDetailController.m b/iOS/Hydra/ActivityDetailController.m index 43a350c3..1575f0b6 100644 --- a/iOS/Hydra/ActivityDetailController.m +++ b/iOS/Hydra/ActivityDetailController.m @@ -274,12 +274,7 @@ - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPa // Different calculation for UITextView if (!self.descriptionView) { self.descriptionView = [self createDescriptionView]; - if (IOS_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0")) { - width = tableView.frame.size.width; - } else { - width = tableView.frame.size.width - 20; - self.descriptionView.frame = CGRectMake(0, 0, width, 0); - } + width = tableView.frame.size.width; } if (IOS_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0")) { UIEdgeInsets textContainerInsets = self.descriptionView.textContainerInset; @@ -502,11 +497,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView infoCellForRowAtIndex:(N cell.textLabel.text = @"Gasten"; cell.alignToTop = YES; - if (IOS_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0")){ - cell.accessoryType = UITableViewCellAccessoryDetailButton; - } else { - cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton; - } + cell.accessoryType = UITableViewCellAccessoryDetailButton; FacebookEvent *event = self.activity.facebookEvent; if (event.friendsAttending.count > 0) { @@ -683,16 +674,12 @@ - (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexP - (void)addEventToCalendar { EKEventStore *store = [[EKEventStore alloc] init]; - if ([store respondsToSelector:@selector(requestAccessToEntityType:completion:)]) { - [store requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError *error) { - if (!granted) return; - [self performSelectorOnMainThread:@selector(addEventWithCalendarStore:) - withObject:store waitUntilDone:NO]; - }]; - } - else { - [self addEventWithCalendarStore:store]; - } + + [store requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError *error) { + if (!granted) return; + [self performSelectorOnMainThread:@selector(addEventWithCalendarStore:) + withObject:store waitUntilDone:NO]; + }]; } - (void)addEventWithCalendarStore:(EKEventStore *)store diff --git a/iOS/Hydra/ActivityOverviewCell.swift b/iOS/Hydra/ActivityOverviewCell.swift new file mode 100644 index 00000000..bb62cf35 --- /dev/null +++ b/iOS/Hydra/ActivityOverviewCell.swift @@ -0,0 +1,30 @@ +// +// ActivityOverviewCell.swift +// Hydra +// +// Created by Feliciaan De Palmenaer on 24/11/2015. +// Copyright © 2015 Zeus WPI. All rights reserved. +// + +import UIKit + +@objc class ActivityOverviewCell: UITableViewCell { + + @IBOutlet weak var titleLabel: UILabel! + @IBOutlet weak var associationLabel: UILabel! + @IBOutlet weak var dateLabel: UILabel! + + var activity: AssociationActivity? { + didSet { + associationLabel.text = activity?.association.displayName + titleLabel.text = activity?.title + + let dateStartFormatter = NSDateFormatter.H_dateFormatterWithAppLocale() + dateStartFormatter.dateFormat = "H:mm"; + dateLabel.text = "\(dateStartFormatter.stringFromDate((self.activity?.start)!))" + + //TODO: do something if highlighted + } + } + +} \ No newline at end of file diff --git a/iOS/Hydra/ActivityOverviewCell.xib b/iOS/Hydra/ActivityOverviewCell.xib new file mode 100644 index 00000000..8585e85d --- /dev/null +++ b/iOS/Hydra/ActivityOverviewCell.xib @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iOS/Hydra/AppDelegate.m b/iOS/Hydra/AppDelegate.m index 6560f493..b398a55a 100644 --- a/iOS/Hydra/AppDelegate.m +++ b/iOS/Hydra/AppDelegate.m @@ -8,7 +8,6 @@ #import "AppDelegate.h" #import "UIColor+AppColors.h" -#import "DashboardViewController.h" #import "ShareKitConfigurator.h" #import "FacebookSession.h" #import "SchamperStore.h" @@ -65,18 +64,14 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( // Restore Facebook-session [[FacebookSession sharedSession] openWithAllowLoginUI:NO]; - // Create and setup controllers - DashboardViewController *dashboard = [[DashboardViewController alloc] init]; - self.navController = [[UINavigationController alloc] initWithRootViewController:dashboard]; - self.navController.navigationBar.tintColor = [UIColor hydraTintColor]; - - // iOS7 specific appearance - if (IOS_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0")) { - self.navController.view.backgroundColor = [UIColor hydraBackgroundColor]; - } - + // Start storyboard + UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:[NSBundle mainBundle]]; + UIViewController *rootvc = [storyboard instantiateInitialViewController]; + + // Set root view controller and make windows visible self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; - self.window.rootViewController = self.navController; + self.window.rootViewController = rootvc; + [self.window makeKeyAndVisible]; return YES; diff --git a/iOS/Hydra/Association.h b/iOS/Hydra/Association.h index 9095e8c9..169906aa 100644 --- a/iOS/Hydra/Association.h +++ b/iOS/Hydra/Association.h @@ -1,5 +1,5 @@ // -// Assocation.h +// Association.h // Hydra // // Created by Pieter De Baets on 21/07/12. @@ -20,9 +20,9 @@ // Check that the current association list is up-to-date with the one provided // in the application bundle -+ (NSDictionary *)updateAssociations:(NSDictionary *)associations; + (RKObjectMapping *)objectMapping; ++ (RKObjectMapping *)objectMappingActivities; - (BOOL)matches:(NSString *)query; -@end +@end \ No newline at end of file diff --git a/iOS/Hydra/Association.m b/iOS/Hydra/Association.m index 8b17c7d6..13ab0618 100644 --- a/iOS/Hydra/Association.m +++ b/iOS/Hydra/Association.m @@ -1,5 +1,5 @@ // -// Assocation.m +// Association.m // Hydra // // Created by Pieter De Baets on 21/07/12. @@ -10,56 +10,8 @@ #import "NSDate+Utilities.h" #import -NSString *const AssociationsLastUpdatedPref = @"AssociationsLastUpdated"; - @implementation Association -+ (NSDictionary *)updateAssociations:(NSDictionary *)associations -{ - NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; - NSDate *lastModified = [userDefaults valueForKey:AssociationsLastUpdatedPref]; - NSDate *currentVersion = [self currentVersion]; - - if (!associations || [currentVersion isLaterThanDate:lastModified]) { - associations = [self loadFromPlist]; - [userDefaults setObject:currentVersion forKey:AssociationsLastUpdatedPref]; - } - return associations; -} - -+ (NSString *)initializationPath -{ - return [[NSBundle mainBundle] pathForResource:@"Associations" ofType:@"plist"]; -} - -+ (NSDate *)currentVersion -{ - NSFileManager *manager = [NSFileManager defaultManager]; - NSString *filePath = [self initializationPath]; - - NSDictionary *attributes = [manager attributesOfItemAtPath:filePath error:nil]; - return attributes[NSFileModificationDate]; -} - -+ (NSDictionary *)loadFromPlist -{ - NSArray *bundled = [NSArray arrayWithContentsOfFile:[self initializationPath]]; - - NSMutableDictionary *associations = [NSMutableDictionary dictionaryWithCapacity:bundled.count]; - for (NSUInteger i = 0; i < bundled.count; i++) { - NSDictionary *props = bundled[i]; - - Association *assoc = [[Association alloc] init]; - assoc.displayName = props[@"displayName"]; - assoc.fullName = props[@"fullName"]; - assoc.internalName = props[@"internalName"]; - assoc.parentAssociation = props[@"parentAssociation"]; - - associations[assoc.internalName] = assoc; - } - return associations; -} - - (NSString *)displayedFullName { if (_fullName) { @@ -84,8 +36,8 @@ - (BOOL)matches:(NSString *)query { NSStringCompareOptions opts = NSCaseInsensitiveSearch | NSDiacriticInsensitiveSearch; return (_internalName && [_internalName rangeOfString:query options:opts].location != NSNotFound) || - (_displayName && [_displayName rangeOfString:query options:opts].location != NSNotFound) || - (_fullName && [_fullName rangeOfString:query options:opts].location != NSNotFound); + (_displayName && [_displayName rangeOfString:query options:opts].location != NSNotFound) || + (_fullName && [_fullName rangeOfString:query options:opts].location != NSNotFound); } - (NSString *)description @@ -93,14 +45,23 @@ - (NSString *)description return [NSString stringWithFormat:@"", self.displayName]; } -+ (RKObjectMapping *)objectMapping ++ (RKObjectMapping *)objectMappingActivities { RKObjectMapping *mapping = [RKObjectMapping mappingForClass:self]; [mapping addAttributeMappingsFromDictionary:@{ - @"internal_name": @"internalName", - @"full_name": @"fullName", - @"display_name": @"displayName" - }]; + @"internal_name": @"internalName", + @"full_name": @"fullName", + @"display_name": @"displayName" + }]; + return mapping; +} + ++ (RKObjectMapping *)objectMapping +{ + RKObjectMapping *mapping = [RKObjectMapping mappingForClass:self]; + [mapping addAttributeMappingsFromArray:@[ + @"displayName", @"fullName", @"internalName", @"parentAssociation" + ]]; return mapping; } @@ -142,4 +103,4 @@ - (id)copyWithZone:(NSZone *)zone return self; } -@end +@end \ No newline at end of file diff --git a/iOS/Hydra/AssociationActivity.m b/iOS/Hydra/AssociationActivity.m index cee438ce..0afbb539 100644 --- a/iOS/Hydra/AssociationActivity.m +++ b/iOS/Hydra/AssociationActivity.m @@ -30,7 +30,7 @@ + (RKObjectMapping *)objectMapping @"description": @"descriptionText" }]; [objectMapping addRelationshipMappingWithSourceKeyPath:@"association" - mapping:[Association objectMapping]]; + mapping:[Association objectMappingActivities]]; return objectMapping; } diff --git a/iOS/Hydra/AssociationNewsItem.m b/iOS/Hydra/AssociationNewsItem.m index b7f5a509..d584ccf2 100644 --- a/iOS/Hydra/AssociationNewsItem.m +++ b/iOS/Hydra/AssociationNewsItem.m @@ -15,7 +15,7 @@ @implementation AssociationNewsItem - (NSString *)description { - return [NSString stringWithFormat:@"", self.title]; + return [NSString stringWithFormat:@"", self.title]; } + (RKObjectMapping *)objectMapping @@ -23,7 +23,7 @@ + (RKObjectMapping *)objectMapping RKObjectMapping *objectMapping = [RKObjectMapping mappingForClass:self]; [objectMapping addAttributeMappingsFromArray:@[@"title", @"date", @"content", @"highlighted"]]; [objectMapping addAttributeMappingsFromDictionary:@{@"id": @"itemId"}]; - [objectMapping addRelationshipMappingWithSourceKeyPath:@"association" mapping:[Association objectMapping]]; + [objectMapping addRelationshipMappingWithSourceKeyPath:@"association" mapping:[Association objectMappingActivities]]; return objectMapping; } @@ -65,4 +65,4 @@ - (void)setRead:(BOOL)read } } -@end +@end \ No newline at end of file diff --git a/iOS/Hydra/AssociationPreferenceController.m b/iOS/Hydra/AssociationPreferenceController.m index 103acc4b..027322df 100644 --- a/iOS/Hydra/AssociationPreferenceController.m +++ b/iOS/Hydra/AssociationPreferenceController.m @@ -26,7 +26,7 @@ @implementation AssociationPreferenceController - (id)init { if (self = [super initWithStyle:UITableViewStylePlain]) { - [self loadAssocations]; + [self loadAssociations]; } return self; } @@ -34,11 +34,11 @@ - (id)init - (void)loadView { [super loadView]; - + UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 320, 44)]; searchBar.placeholder = @"Zoek een vereniging"; self.tableView.tableHeaderView = searchBar; - + // TODO: replace cancel button in search-mode by 'OK' self.searchController = [[UISearchDisplayController alloc] initWithSearchBar:searchBar contentsController:self]; @@ -64,16 +64,16 @@ - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interface return (interfaceOrientation == UIInterfaceOrientationPortrait); } -- (void)loadAssocations +- (void)loadAssociations { - NSArray *all = [[AssociationStore sharedStore] assocations]; - + NSArray *all = [[AssociationStore sharedStore] associations]; + // Get all unique parent organisations NSSet *convents = [NSSet setWithArray:[all valueForKey:@"parentAssociation"]]; NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:nil ascending:YES]; self.convents = [convents sortedArrayUsingDescriptors:@[sort]]; self.filteredConvents = [self.convents mutableCopy]; - + // Group by parentAssociation NSMutableDictionary *grouped = [[NSMutableDictionary alloc] init]; sort = [NSSortDescriptor sortDescriptorWithKey:@"fullName" ascending:YES]; @@ -97,7 +97,7 @@ - (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldRe return [evaluatedObject matches:query]; }]; self.filteredAssociations[convent] = [self.associations[convent] filteredArrayUsingPredicate:filter]; - + // Remove convent from list if it does not have any items if ([self.filteredAssociations[convent] count] == 0) { [self.filteredConvents removeObject:convent]; @@ -115,7 +115,7 @@ - (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldRe self.filteredAssociations = [self.associations mutableCopy]; self.filteredConvents = [self.convents mutableCopy]; } - + return YES; } @@ -135,7 +135,7 @@ - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInte else { internalName = self.filteredConvents[section]; } - + Association *association = [[AssociationStore sharedStore] associationWithName:internalName]; return association.displayName; } @@ -168,7 +168,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier]; } - + Association *association; if (tableView == self.tableView) { NSString *convent = self.convents[indexPath.section]; @@ -179,7 +179,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N association = self.filteredAssociations[convent][indexPath.row]; } cell.textLabel.text = association.fullName; - + NSArray *preferred = [PreferencesService sharedService].preferredAssociations; if ([preferred containsObject:association.internalName]){ cell.accessoryType = UITableViewCellAccessoryCheckmark; @@ -187,7 +187,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N else { cell.accessoryType = nil; } - + return cell; } @@ -202,7 +202,7 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath NSString *convent = self.filteredConvents[indexPath.section]; name = [self.filteredAssociations[convent][indexPath.row] internalName]; } - + PreferencesService *prefs = [PreferencesService sharedService]; NSMutableArray *preferred = [prefs.preferredAssociations mutableCopy]; if ([preferred containsObject:name]) { @@ -212,10 +212,10 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath [preferred addObject:name]; } prefs.preferredAssociations = preferred; - + [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; [tableView deselectRowAtIndexPath:indexPath animated:YES]; } -@end +@end \ No newline at end of file diff --git a/iOS/Hydra/AssociationStore.h b/iOS/Hydra/AssociationStore.h index a9f22d33..d3526689 100644 --- a/iOS/Hydra/AssociationStore.h +++ b/iOS/Hydra/AssociationStore.h @@ -15,7 +15,7 @@ extern NSString *const AssociationStoreDidUpdateActivitiesNotification; @interface AssociationStore : NSObject -@property (nonatomic, strong, readonly) NSArray *assocations; +@property (nonatomic, strong, readonly) NSArray *associations; @property (nonatomic, strong, readonly) NSArray *activities; @property (nonatomic, strong, readonly) NSArray *newsItems; @@ -28,4 +28,4 @@ extern NSString *const AssociationStoreDidUpdateActivitiesNotification; - (void)syncStorage; - (void)markStorageOutdated; -@end +@end \ No newline at end of file diff --git a/iOS/Hydra/AssociationStore.m b/iOS/Hydra/AssociationStore.m index e48ed76e..f899ed30 100644 --- a/iOS/Hydra/AssociationStore.m +++ b/iOS/Hydra/AssociationStore.m @@ -17,21 +17,24 @@ #define kBaseUrl @"http://student.ugent.be/hydra/api/1.0/" #define kActivitiesResource @"all_activities.json" #define kNewsResource @"all_news.json" +#define kAssociationResource @"associations.json" #define kUpdateInterval (15 * 60) NSString *const AssociationStoreDidUpdateNewsNotification = - @"AssociationStoreDidUpdateNewsNotification"; +@"AssociationStoreDidUpdateNewsNotification"; NSString *const AssociationStoreDidUpdateActivitiesNotification = - @"AssociationStoreDidUpdateActivitiesNotification"; +@"AssociationStoreDidUpdateActivitiesNotification"; @interface AssociationStore () @property (nonatomic, strong) NSDictionary *associationLookup; +@property (nonatomic, strong) NSArray *associations; @property (nonatomic, strong) NSArray *newsItems; @property (nonatomic, strong) NSArray *activities; @property (nonatomic, strong) NSDate *newsLastUpdated; @property (nonatomic, strong) NSDate *activitiesLastUpdated; +@property (nonatomic, strong) NSDate *associationsLastUpdated; @property (nonatomic, strong) RKObjectManager *objectManager; @property (nonatomic, strong) NSMutableArray *activeRequests; @@ -71,7 +74,6 @@ - (id)init - (void)sharedInit { - self.associationLookup = [Association updateAssociations:self.associationLookup]; self.activeRequests = [[NSMutableArray alloc] init]; self.objectManager = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:kBaseUrl]]; @@ -91,19 +93,21 @@ - (void)dealloc - (id)initWithCoder:(NSCoder *)decoder { if (self = [super init]) { - _associationLookup = [decoder decodeObjectForKey:@"associationLookup"]; - AssertClassOrNil(_associationLookup, NSDictionary); - + _associations = [decoder decodeObjectForKey:@"associations"]; + AssertClassOrNil(_associations, NSArray); + _associationsLastUpdated = [decoder decodeObjectForKey:@"associationsLastUpdated"]; + AssertClassOrNil(_associationsLastUpdated, NSDate); + _newsItems = [decoder decodeObjectForKey:@"newsItems"]; AssertClassOrNil(_newsItems, NSArray); _newsLastUpdated = [decoder decodeObjectForKey:@"newsLastUpdated"]; AssertClassOrNil(_newsLastUpdated, NSDate); - + _activities = [decoder decodeObjectForKey:@"activities"]; AssertClassOrNil(_activities, NSArray); _activitiesLastUpdated = [decoder decodeObjectForKey:@"activitiesLastUpdated"]; AssertClassOrNil(_activitiesLastUpdated, NSDate); - + [self sharedInit]; } return self; @@ -111,7 +115,8 @@ - (id)initWithCoder:(NSCoder *)decoder - (void)encodeWithCoder:(NSCoder *)coder { - [coder encodeObject:_associationLookup forKey:@"associationLookup"]; + [coder encodeObject:_associationsLastUpdated forKey:@"associationsLastUpdated"]; + [coder encodeObject:_associations forKey:@"associations"]; [coder encodeObject:_newsLastUpdated forKey:@"newsLastUpdated"]; [coder encodeObject:_newsItems forKey:@"newsItems"]; [coder encodeObject:_activitiesLastUpdated forKey:@"activitiesLastUpdated"]; @@ -122,9 +127,9 @@ + (NSString *)storeCachePath { // Get cache directory NSArray *cacheDirectories = - NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); + NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); NSString *cacheDirectory = cacheDirectories[0]; - + return [cacheDirectory stringByAppendingPathComponent:@"association.archive"]; } @@ -133,10 +138,10 @@ - (void)syncStorage if (!self.storageOutdated) { return; } - + // Immediately mark the cache as being updated, as this is an async operation self.storageOutdated = NO; - + dispatch_queue_t async = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(async, ^{ [NSKeyedArchiver archiveRootObject:self toFile:self.class.storeCachePath]; @@ -150,36 +155,41 @@ - (void)markStorageOutdated #pragma mark - Accessors -- (NSArray *)assocations -{ - return [self.associationLookup allValues]; -} - - (Association *)associationWithName:(NSString *)internalName { + if (self.associationLookup == nil) { + [self createAssociationsLookup]; + } Association *association = self.associationLookup[internalName]; - + // If the association is unknown, just give a fake record if (!association) { association = [[Association alloc] init]; association.internalName = internalName; association.displayName = internalName; } - + return association; } +- (NSArray *)associations +{ + [self _updateResource:kAssociationResource lastUpdated:self.associationsLastUpdated + objectMapping:[Association objectMapping]]; + return _associations; +} + - (NSArray *)activities { [self _updateResource:kActivitiesResource lastUpdated:self.activitiesLastUpdated - objectMapping:[AssociationActivity objectMapping]]; + objectMapping:[AssociationActivity objectMapping]]; return _activities; } - (NSArray *)newsItems { [self _updateResource:kNewsResource lastUpdated:self.newsLastUpdated - objectMapping:[AssociationNewsItem objectMapping]]; + objectMapping:[AssociationNewsItem objectMapping]]; return _newsItems; } @@ -188,14 +198,22 @@ - (void)reloadActivities // Force reload, remove cache-entry [[NSURLCache sharedURLCache] removeAllCachedResponses]; [self _updateResource:kActivitiesResource lastUpdated:nil - objectMapping:[AssociationActivity objectMapping]]; + objectMapping:[AssociationActivity objectMapping]]; + [self reloadAssociations]; } - (void)reloadNewsItems { [[NSURLCache sharedURLCache] removeAllCachedResponses]; [self _updateResource:kNewsResource lastUpdated:nil - objectMapping:[AssociationNewsItem objectMapping]]; + objectMapping:[AssociationNewsItem objectMapping]]; + [self reloadAssociations]; +} + +- (void)reloadAssociations +{ + [self _updateResource:kAssociationResource lastUpdated:nil + objectMapping:[Association objectMapping]]; } #pragma mark - RestKit Object loading @@ -203,32 +221,32 @@ - (void)reloadNewsItems - (void)_updateResource:(NSString *)resource lastUpdated:(NSDate *)lastUpdated objectMapping:(RKObjectMapping *)mapping { DLog(@"updateResource %@ (last: %@ => %f)", resource, lastUpdated, [lastUpdated timeIntervalSinceNow]); - + // Check if an update is required if (lastUpdated && [lastUpdated timeIntervalSinceNow] > -kUpdateInterval) { return; } - + // Already working on request if ([self.activeRequests containsObject:resource]) { - return; + return; } - + DLog(@"Updating %@", resource); [self.activeRequests addObject:resource]; [self.objectManager addResponseDescriptor: - [RKResponseDescriptor responseDescriptorWithMapping:mapping - method:RKRequestMethodGET - pathPattern:resource - keyPath:nil - statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]]; + [RKResponseDescriptor responseDescriptorWithMapping:mapping + method:RKRequestMethodGET + pathPattern:resource + keyPath:nil + statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]]; [self.objectManager getObjectsAtPath:resource parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) { - [self _processResult:mappingResult forResource:resource]; + [self _processResult:mappingResult forResource:resource]; } failure:^(RKObjectRequestOperation *operation, NSError *error) { - [self _processError:error forResource:resource]; + [self _processError:error forResource:resource]; }]; } @@ -236,52 +254,56 @@ - (void)_processResult:(RKMappingResult *)mappingResult forResource:(NSString *) { NSString *notification = nil; NSArray *objects = [mappingResult array]; - + // Received some NewsItems if ([resource isEqualToString:kNewsResource]) { - NSMutableSet *readItems = [NSMutableSet set]; - // Using direct access because accessors reload the data - for (AssociationNewsItem *item in _newsItems) { - if (item.read) { - [readItems addObject:@(item.itemId)]; + NSMutableSet *readItems = [NSMutableSet set]; + // Using direct access because accessors reload the data + for (AssociationNewsItem *item in _newsItems) { + if (item.read) { + [readItems addObject:@(item.itemId)]; + } } - } - for (AssociationNewsItem *item in objects) { - if ([readItems containsObject:@(item.itemId)]) { - item.read = YES; + for (AssociationNewsItem *item in objects) { + if ([readItems containsObject:@(item.itemId)]) { + item.read = YES; + } } - } - self.newsItems = objects; - self.newsLastUpdated = [NSDate date]; - notification = AssociationStoreDidUpdateNewsNotification; + self.newsItems = objects; + self.newsLastUpdated = [NSDate date]; + notification = AssociationStoreDidUpdateNewsNotification; } // Received Activities else if ([resource isEqualToString:kActivitiesResource]) { - NSMutableDictionary *availableEvents = [NSMutableDictionary dictionary]; - // Using direct access because accessors reload the data - for (AssociationActivity *activity in _activities) { - if ([activity hasFacebookEvent]) { - availableEvents[activity.facebookId] = activity; + NSMutableDictionary *availableEvents = [NSMutableDictionary dictionary]; + // Using direct access because accessors reload the data + for (AssociationActivity *activity in _activities) { + if ([activity hasFacebookEvent]) { + availableEvents[activity.facebookId] = activity; + } } - } - for (AssociationActivity *activity in objects) { - if ([availableEvents objectForKey:activity.facebookId]) { - AssociationActivity *oldActivity = availableEvents[activity.facebookId]; - activity.facebookEvent = oldActivity.facebookEvent; + for (AssociationActivity *activity in objects) { + if ([availableEvents objectForKey:activity.facebookId]) { + AssociationActivity *oldActivity = availableEvents[activity.facebookId]; + activity.facebookEvent = oldActivity.facebookEvent; + } } - } - self.activities = objects; - self.activitiesLastUpdated = [NSDate date]; - notification = AssociationStoreDidUpdateActivitiesNotification; + self.activities = objects; + self.activitiesLastUpdated = [NSDate date]; + notification = AssociationStoreDidUpdateActivitiesNotification; } - + else if ([resource isEqualToString:kAssociationResource]) { + self.associations = objects; + [self createAssociationsLookup]; + } + [self markStorageOutdated]; [self syncStorage]; - + // Send notification NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; [center postNotificationName:notification object:self userInfo:nil]; - + [self.activeRequests removeObject:resource]; } @@ -290,7 +312,7 @@ - (void)_processError:(NSError *)error forResource:(NSString *)resource NSLog(@"Updating resource %@ failed: %@", resource, error); AppDelegate *app = (AppDelegate *)[[UIApplication sharedApplication] delegate]; [app handleError:error]; - + NSString *notification = nil; if ([resource isEqualToString:kNewsResource]) { notification = AssociationStoreDidUpdateNewsNotification; @@ -300,21 +322,32 @@ - (void)_processError:(NSError *)error forResource:(NSString *)resource } NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; [center postNotificationName:notification object:self userInfo:nil]; - + // Only clear the request after 10 seconds, to prevent failed requests // restarting due to related succesful requests dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC); dispatch_after(popTime, dispatch_get_main_queue(), ^(void) { - [self.activeRequests removeObject:resource]; + [self.activeRequests removeObject:resource]; }); } +#pragma mark - Utility functions +- (void)createAssociationsLookup +{ + NSMutableDictionary *associationsLookup = [NSMutableDictionary dictionary]; + for (Association *association in self.associations) { + associationsLookup[association.internalName] = association; + } + + self.associationLookup = associationsLookup; +} + #pragma mark - Notifications - (void)facebookEventUpdated:(NSNotification *)notification { [self markStorageOutdated]; - + // Call method in 10 seconds so multiple changes are written at once [[self class] cancelPreviousPerformRequestsWithTarget:self selector:@selector(syncStorage) @@ -322,4 +355,4 @@ - (void)facebookEventUpdated:(NSNotification *)notification [self performSelector:@selector(syncStorage) withObject:nil afterDelay:10]; } -@end +@end \ No newline at end of file diff --git a/iOS/Hydra/AssociationStore.swift b/iOS/Hydra/AssociationStore.swift new file mode 100644 index 00000000..1819629d --- /dev/null +++ b/iOS/Hydra/AssociationStore.swift @@ -0,0 +1,65 @@ +// +// AssociationStore.swift +// Hydra +// +// Created by Feliciaan De Palmenaer on 20/11/2015. +// Copyright © 2015 Zeus WPI. All rights reserved. +// + +import Foundation + +extension AssociationStore: FeedItemProtocol { + func feedItems() -> [FeedItem] { + return getActivities() + getNewsItems() + } + + private func getActivities() -> [FeedItem] { + var feedItems = [FeedItem]() + let preferencesService = PreferencesService.sharedService() + if let activities = activities as? [AssociationActivity] { + var filter: ((AssociationActivity) -> (Bool)) + if preferencesService.filterAssociations { + let associations = preferencesService.preferredAssociations + filter = { activity in activity.highlighted || associations.contains { activity.association.internalName == ($0 as! String) } } + } else { + filter = { $0.highlighted } + feedItems.append(FeedItem(itemType: .SettingsItem, object: nil, priority: 850)) + } + + for activity in activities.filter(filter) { + // Force load facebookEvent + if let facebookEvent = activity.facebookEvent { + facebookEvent.update() + } + var priority = 999 //TODO: calculate priorities, with more options + priority -= activity.start.daysAfterDate(NSDate()) * 100 + if priority > 0 { + feedItems.append(FeedItem(itemType: .ActivityItem, object: activity, priority: priority)) + } + } + } + return feedItems + } + + private func getNewsItems() -> [FeedItem] { + var feedItems = [FeedItem]() + + if let newsItems = newsItems as? [AssociationNewsItem] { + for newsItem in newsItems { + var priority = 999 + let daysOld = newsItem.date.daysBeforeDate(NSDate()) + if newsItem.highlighted { + priority -= 25*daysOld + } else { + priority -= 90*daysOld + } + + if priority > 0 { + feedItems.append(FeedItem(itemType: .NewsItem, object: newsItem, priority: priority)) + } + } + } + + return feedItems + } +} \ No newline at end of file diff --git a/iOS/Hydra/BadgedButton.h b/iOS/Hydra/BadgedButton.h deleted file mode 100644 index d51e201f..00000000 --- a/iOS/Hydra/BadgedButton.h +++ /dev/null @@ -1,16 +0,0 @@ -// -// DashboardButton.h -// Hydra -// -// Created by Yasser Deceukelier on 20/07/12. -// Copyright (c) 2012 Zeus WPI. All rights reserved. -// - -#import - -@interface BadgedButton : UIButton - -- (id)initWithFrame:(CGRect)frame; -- (void)setBadgeNumber:(int)number; - -@end diff --git a/iOS/Hydra/BadgedButton.m b/iOS/Hydra/BadgedButton.m deleted file mode 100644 index 95f3be4c..00000000 --- a/iOS/Hydra/BadgedButton.m +++ /dev/null @@ -1,110 +0,0 @@ -// -// DashboardButton.m -// Hydra -// -// Created by Yasser Deceukelier on 20/07/12. -// Copyright (c) 2012 Zeus WPI. All rights reserved. -// - -#import "BadgedButton.h" -#import - -#define kBadgeFontSize 15 - -@implementation BadgedButton -{ - // two layer 1 text & 1 badge, so the text can be centerd in the badge - // no __weak, because not supported on iOS 4 - __unsafe_unretained CATextLayer *_textLayer; - __unsafe_unretained CALayer *_badgeLayer; - - UIFont *badgeFont; - CGFloat badgeHeight; -} - -#pragma mark - Badge properties - -- (void)setBadgeText:(NSString *)badgeText -{ - [_textLayer setString:badgeText]; - - //under development!!! - CGSize textSize = [badgeText sizeWithAttributes:@{NSFontAttributeName: badgeFont}]; - CGFloat textWidth = MAX(textSize.height, textSize.width); - CGRect textFrame = CGRectMake(0, badgeHeight -textSize.height, textWidth, textSize.height); - - CGFloat badgeWidth = ([_textLayer.string length] > 1 ? textWidth+badgeHeight/2 : badgeHeight); - textFrame.origin.x = (badgeWidth -textWidth)/2; - _textLayer.frame = textFrame; - - CGRect badgeFrame = CGRectMake(self.frame.size.width -2*badgeWidth/3, -badgeHeight/3, badgeWidth, badgeHeight); - _badgeLayer.frame = badgeFrame; - - UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRoundedRect:_badgeLayer.bounds cornerRadius:_badgeLayer.cornerRadius]; - _badgeLayer.shadowPath = shadowPath.CGPath; - - _badgeLayer.hidden = (badgeText ? NO : YES); -} - -- (void)setBadgeNumber:(int)number -{ - if(number != 0) { - [self setBadgeText:[NSString stringWithFormat:@"%d", number]]; - } - else { - [self setBadgeText:nil]; - } -} - -#pragma mark - Badge setup - -- (id)initWithFrame:(CGRect)frame -{ - self = [super initWithFrame:frame]; - if(self) { - [self setupBadgeLayers]; - } - return self; -} - -- (void)awakeFromNib -{ - [self setupBadgeLayers]; -} - -- (void)setupBadgeLayers -{ - // Somehow the red background is peeking through the border?? - // Is it also visible on an actual device or just in the simulator??? - - badgeFont = [UIFont boldSystemFontOfSize:kBadgeFontSize]; - CGFloat newlineSpace = [badgeFont lineHeight] - [badgeFont ascender]; - badgeHeight = [badgeFont lineHeight] + newlineSpace; - - CAGradientLayer *badgeLayer = [CAGradientLayer layer]; - badgeLayer.colors = @[(id)[UIColor colorWithRed:1 green:.5 blue:.5 alpha:1].CGColor, - (id)[UIColor colorWithRed:.8 green:0 blue:0 alpha:1].CGColor]; - badgeLayer.cornerRadius = badgeHeight/2; - badgeLayer.borderWidth = 2; - badgeLayer.borderColor = [UIColor whiteColor].CGColor; - badgeLayer.hidden = YES; - badgeLayer.shadowColor = [[UIColor blackColor] CGColor]; //Note: ca shadows may slow down scrolling on actual devices, no problem as long as scrolling (or rotation isn't enabled in the Dashboard - badgeLayer.shadowOpacity = 0.5; - badgeLayer.shadowOffset = CGSizeMake(0, 4.0); - [self.layer addSublayer:badgeLayer]; - _badgeLayer = badgeLayer; - - CATextLayer *textLayer = [CATextLayer layer]; - textLayer.alignmentMode = kCAAlignmentCenter; - textLayer.wrapped = YES; - textLayer.fontSize = kBadgeFontSize; - - CGFontRef cgFont = CGFontCreateWithFontName((__bridge CFStringRef)badgeFont.fontName); - textLayer.font = cgFont; - CGFontRelease(cgFont); - - [badgeLayer addSublayer:textLayer]; - _textLayer = textLayer; -} - -@end diff --git a/iOS/Hydra/DashboardViewController.h b/iOS/Hydra/DashboardViewController.h deleted file mode 100644 index c9a53aeb..00000000 --- a/iOS/Hydra/DashboardViewController.h +++ /dev/null @@ -1,32 +0,0 @@ -// -// MasterViewController.h -// Hydra -// -// Created by Pieter De Baets on 20/03/12. -// Copyright (c) 2012 Zeus WPI. All rights reserved. -// - -#import -#import "BadgedButton.h" - -@interface DashboardViewController : UIViewController - -@property (nonatomic, unsafe_unretained) IBOutlet BadgedButton *newsButton; -@property (nonatomic, unsafe_unretained) IBOutlet BadgedButton *activitiesButton; -@property (nonatomic, unsafe_unretained) IBOutlet BadgedButton *infoButton; -@property (nonatomic, unsafe_unretained) IBOutlet BadgedButton *restoButton; -@property (nonatomic, unsafe_unretained) IBOutlet BadgedButton *urgentButton; -@property (nonatomic, unsafe_unretained) IBOutlet BadgedButton *schamperButton; -@property (nonatomic, unsafe_unretained) IBOutlet UIButton *feedbackButton; -@property (nonatomic, unsafe_unretained) IBOutlet UIButton *preferencesButton; - -- (IBAction)showNews:(id)sender; -- (IBAction)showActivities:(id)sender; -- (IBAction)showInfo:(id)sender; -- (IBAction)showResto:(id)sender; -- (IBAction)showUrgent:(id)sender; -- (IBAction)showSchamper:(id)sender; -- (IBAction)showFeedbackView:(id)sender; -- (IBAction)showPreferences:(id)sender; - -@end diff --git a/iOS/Hydra/DashboardViewController.m b/iOS/Hydra/DashboardViewController.m deleted file mode 100644 index 3123faef..00000000 --- a/iOS/Hydra/DashboardViewController.m +++ /dev/null @@ -1,244 +0,0 @@ -// -// MasterViewController.m -// Hydra -// -// Created by Pieter De Baets on 20/03/12. -// Copyright (c) 2012 Zeus WPI. All rights reserved. -// - -#import "DashboardViewController.h" - -#import - -#import "ActivitiesController.h" -#import "InfoViewController.h" -#import "NewsViewController.h" -#import "PreferencesController.h" -#import "RestoMenuController.h" -#import "SchamperViewController.h" -#import "UrgentViewController.h" - -#define EasterEggEnabled 0 - -@interface DashboardViewController () - -@property (nonatomic, strong) UISwipeGestureRecognizer *gestureRecognizer; -@property (nonatomic, unsafe_unretained) UITextField *codeField; -@property (nonatomic, strong) NSArray *requiredMoves; -@property (nonatomic, assign) NSUInteger movesPerformed; - -@end - -@implementation DashboardViewController - -- (void)viewDidLoad -{ -#if BETA_RELEASE - self.feedbackButton.hidden = NO; -#endif - -#if EasterEggEnabled - self.requiredMoves = @[ - @(UISwipeGestureRecognizerDirectionUp), @(UISwipeGestureRecognizerDirectionUp), - @(UISwipeGestureRecognizerDirectionDown), @(UISwipeGestureRecognizerDirectionDown), - @(UISwipeGestureRecognizerDirectionLeft), @(UISwipeGestureRecognizerDirectionRight), - @(UISwipeGestureRecognizerDirectionLeft), @(UISwipeGestureRecognizerDirectionRight), - @"b", @"a" - ]; - self.codeField = nil; -#endif - -#ifdef __IPHONE_7_0 - // iOS 7 layout - if ([self respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)]) { - [self setNeedsStatusBarAppearanceUpdate]; - } - else { - // TODO: create IBOutlet for this - UIView *headerView = self.view.subviews[0]; - CGRect headerFrame = headerView.frame; - headerFrame.size.height -= 10; - headerView.frame = headerFrame; - } -#endif -} - -- (void)viewWillAppear:(BOOL)animated -{ - [super viewWillAppear:animated]; - [self.navigationController setNavigationBarHidden:YES animated:animated]; - -#ifdef __IPHONE_7_0 - if (IOS_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0")) { - [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent]; - } -#endif - -#if EasterEggEnabled - [self configureMoveDetectionForMove:0]; -#endif -} - -- (void)viewDidAppear:(BOOL)animated -{ - [super viewDidAppear:animated]; - GAI_Track(@"Home"); -} - -- (void)viewWillDisappear:(BOOL)animated -{ - [super viewWillDisappear:animated]; - [self.navigationController setNavigationBarHidden:NO animated:animated]; - [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault animated:YES]; -} - -- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation -{ - return (interfaceOrientation == UIInterfaceOrientationPortrait); -} - -#pragma mark - Button actions - -- (IBAction)showNews:(id)sender -{ - DLog(@"Dashboard switching to News"); - NewsViewController *c = [[NewsViewController alloc] init]; - [self.navigationController pushViewController:c animated:YES]; -} - -- (IBAction)showActivities:(id)sender -{ - DLog(@"Dashboard switching to Activities"); - ActivitiesController *c = [[ActivitiesController alloc] init]; - [self.navigationController pushViewController:c animated:YES]; -} - -- (IBAction)showInfo:(id)sender -{ - DLog(@"Dashboard switching to Info"); - InfoViewController *c = [[InfoViewController alloc] init]; - [self.navigationController pushViewController:c animated:YES]; -} - -- (IBAction)showResto:(id)sender -{ - DLog(@"Dashboard switching to Resto"); - UIViewController *c = [[RestoMenuController alloc] init]; - [self.navigationController pushViewController:c animated:YES]; -} - -- (IBAction)showUrgent:(id)sender -{ - DLog(@"Dashboard switching to Urgent"); - UIViewController *c = [[UrgentViewController alloc] init]; - [self.navigationController pushViewController:c animated:YES]; -} - -- (IBAction)showSchamper:(id)sender -{ - DLog(@"Dashboard switching to Schamper"); - UIViewController *c = [[SchamperViewController alloc] init]; - [self.navigationController pushViewController:c animated:YES]; -} - -- (IBAction)showFeedbackView:(id)sender -{ - MFMailComposeViewController *controller = [[MFMailComposeViewController alloc] init]; - [controller setMailComposeDelegate:self]; - [controller setToRecipients:@[@"hydra@zeus.ugent.be"]]; - [controller setSubject:@"Bericht via Hydra"]; - [self presentViewController:controller animated:YES completion:nil]; -} - -- (void)mailComposeController:(MFMailComposeViewController *)controller - didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error -{ - [controller dismissViewControllerAnimated:YES completion:nil]; -} - --(IBAction)showPreferences:(id)sender -{ - DLog(@"Dashboard switching to Preferences"); - UIViewController *c = [[PreferencesController alloc] init]; - [self.navigationController pushViewController:c animated:YES]; -} - -#pragma mark - Surprise feature - -#if EasterEggEnabled -- (void)configureMoveDetectionForMove:(NSUInteger)move -{ - if (move == [self.requiredMoves count]) { - UrgentPlayer *urgentPlayer = [UrgentPlayer sharedPlayer]; - [urgentPlayer start]; - - UILog(@"Congratulations, you won the game!"); - move = 0; - } - self.movesPerformed = move; - - id nextMove = (self.requiredMoves)[move]; - if ([nextMove isKindOfClass:[NSNumber class]]) { - if (!self.gestureRecognizer) { - self.gestureRecognizer = [[UISwipeGestureRecognizer alloc] init]; - [self.gestureRecognizer addTarget:self action:@selector(handleGesture:)]; - [self.view addGestureRecognizer:self.gestureRecognizer]; - - [self.codeField removeFromSuperview]; - [self.codeField resignFirstResponder]; - self.codeField = nil; - } - - self.gestureRecognizer.direction = [nextMove intValue]; - } - else if ([nextMove isKindOfClass:[NSString class]]) { - if (!self.codeField) { - self.codeField = [[UITextField alloc] init]; - self.codeField.hidden = YES; - self.codeField.delegate = self; - self.codeField.autocapitalizationType = UITextAutocapitalizationTypeNone; - self.codeField.returnKeyType = UIReturnKeyDone; - [self.view addSubview:self.codeField]; - - [self.view removeGestureRecognizer:self.gestureRecognizer]; - self.gestureRecognizer = nil; - } - - // Store the string to be matched in the textfield, for easy comparison - self.codeField.text = nextMove; - [self.codeField becomeFirstResponder]; - } -} - -- (void)handleGesture:(UIGestureRecognizer *)recognizer -{ - [self configureMoveDetectionForMove:(self.movesPerformed + 1)]; - DLog(@"Surprise progress: %d/%d", self.movesPerformed, [self.requiredMoves count]); -} - -- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event -{ - [self configureMoveDetectionForMove:0]; -} - -- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string -{ - if ([string caseInsensitiveCompare:textField.text] == NSOrderedSame) { - [self configureMoveDetectionForMove:(self.movesPerformed + 1)]; - DLog(@"Surprise progress: %d/%d", self.movesPerformed, [self.requiredMoves count]); - } - else { - [self configureMoveDetectionForMove:0]; - } - return NO; -} - -- (BOOL)textFieldShouldReturn:(UITextField *)textField -{ - [textField resignFirstResponder]; - [self configureMoveDetectionForMove:0]; - return NO; -} -#endif - -@end diff --git a/iOS/Hydra/HomeActivityCollectionViewCell.swift b/iOS/Hydra/HomeActivityCollectionViewCell.swift new file mode 100644 index 00000000..422574fe --- /dev/null +++ b/iOS/Hydra/HomeActivityCollectionViewCell.swift @@ -0,0 +1,66 @@ +// +// HomeActivityCollectionViewCell.swift +// Hydra +// +// Created by Feliciaan De Palmenaer on 01/08/15. +// Copyright © 2015 Zeus WPI. All rights reserved. +// + +import UIKit + +class HomeActivityCollectionViewCell: UICollectionViewCell { + @IBOutlet weak var titleLabel: UILabel! + @IBOutlet weak var associationLabel: UILabel! + @IBOutlet weak var dateLabel: UILabel! + @IBOutlet weak var descriptionLabel: UILabel! + @IBOutlet weak var locationLabel: UILabel! + @IBOutlet weak var imageView: UIImageView! + + var activity: AssociationActivity? { + didSet { + let longDateFormatter = NSDateFormatter.H_dateFormatterWithAppLocale() + longDateFormatter.timeStyle = .ShortStyle + longDateFormatter.dateStyle = .LongStyle + longDateFormatter.doesRelativeDateFormatting = true + + let shortDateFormatter = NSDateFormatter.H_dateFormatterWithAppLocale() + shortDateFormatter.timeStyle = .ShortStyle + shortDateFormatter.dateStyle = .NoStyle + + associationLabel.text = activity?.association.displayName + titleLabel.text = activity?.title + + if (self.activity!.end != nil) { + if self.activity!.start.dateByAddingDays(1).isLaterThanDate(self.activity!.end) { + dateLabel.text = "\(longDateFormatter.stringFromDate((self.activity?.start)!)) - \(shortDateFormatter.stringFromDate((self.activity?.end)!))" + } else { + dateLabel.text = "\(longDateFormatter.stringFromDate((self.activity?.start)!)) - \(longDateFormatter.stringFromDate((self.activity?.end)!))" + } + } else { + dateLabel.text = longDateFormatter.stringFromDate((self.activity?.start)!) + } + + descriptionLabel.text = activity?.descriptionText + var distance: Double? = nil + if (activity?.latitude != nil && activity?.longitude != nil) { + distance = LocationService.sharedService.calculateDistance(activity!.latitude, longitude: activity!.longitude) + } + + if let d = distance where d < 100*1000{ + if d < 1000 { + locationLabel.text = activity!.location + " (\(Int(d))m)" + } else { + locationLabel.text = activity!.location + " (\(Int(d/1000))km)" + } + } else { + locationLabel.text = activity?.location + } + + if let url = activity?.facebookEvent?.smallImageUrl { + imageView.sd_setImageWithURL(url, placeholderImage: imageView.image) + } else { + imageView.image = nil + } + } + } +} \ No newline at end of file diff --git a/iOS/Hydra/HomeFeedService.swift b/iOS/Hydra/HomeFeedService.swift new file mode 100644 index 00000000..3fb0955d --- /dev/null +++ b/iOS/Hydra/HomeFeedService.swift @@ -0,0 +1,107 @@ +// +// HomeFeedService.swift +// Hydra +// +// Created by Feliciaan De Palmenaer on 31/07/15. +// Copyright © 2015 Zeus WPI. All rights reserved. +// + +import Foundation + +let HomeFeedDidUpdateFeedNotification = "HomeFeedDidUpdateFeedNotification" +let UpdateInterval: Double = 30 * 60 // half an hour + +class HomeFeedService { + + static let sharedService = HomeFeedService() + + let associationStore = AssociationStore.sharedStore() + let restoStore = RestoStore.sharedStore() + let schamperStore = SchamperStore.sharedStore() + let preferencesService = PreferencesService.sharedService() + let locationService = LocationService.sharedService + + var previousRefresh = NSDate() + + private init() { + refreshStores() + locationService.startUpdating() + + let notifications = [RestoStoreDidReceiveMenuNotification, AssociationStoreDidUpdateActivitiesNotification, AssociationStoreDidUpdateNewsNotification, SchamperStoreDidUpdateArticlesNotification] + for notification in notifications { + NSNotificationCenter.defaultCenter().addObserver(self, selector: "storeUpdatedNotification:", name: notification, object: nil) + } + } + + + @objc func storeUpdatedNotification(notification: NSNotification) { + NSNotificationCenter.defaultCenter().postNotificationName(HomeFeedDidUpdateFeedNotification, object: nil) + } + + deinit { + NSNotificationCenter.defaultCenter().removeObserver(self) + } + + func refreshStoresIfNecessary() + { + if self.previousRefresh.timeIntervalSinceNow > -UpdateInterval { + self.refreshStores() + } else { + NSNotificationCenter.defaultCenter().postNotificationName(HomeFeedDidUpdateFeedNotification, object: nil) + } + } + + func refreshStores() { + previousRefresh = NSDate() + associationStore.reloadActivities() + associationStore.reloadNewsItems() + + restoStore.menuForDay(NSDate()) + restoStore.locations + + schamperStore.reloadArticles() + } + + func createFeed() -> [FeedItem] { + var list = [FeedItem]() + + let feedItemProviders: [FeedItemProtocol] = [associationStore, restoStore, schamperStore] + + for provider in feedItemProviders { + list.appendContentsOf(provider.feedItems()) + } + + // Urgent.fm + list.append(FeedItem(itemType: .UrgentItem, object: nil, priority: 825)) + + list.sortInPlace{ $0.priority > $1.priority } + + return list + } +} + +protocol FeedItemProtocol { + func feedItems() -> [FeedItem] +} + +struct FeedItem { + let itemType: FeedItemType + let object: AnyObject? + let priority: Int + + init(itemType: FeedItemType, object: AnyObject?, priority: Int) { + self.itemType = itemType + self.object = object + self.priority = priority + } +} + +enum FeedItemType { + case NewsItem + case ActivityItem + case InfoItem + case RestoItem + case UrgentItem + case SchamperNewsItem + case SettingsItem +} \ No newline at end of file diff --git a/iOS/Hydra/HomeNewsItemCollectionViewCell.swift b/iOS/Hydra/HomeNewsItemCollectionViewCell.swift new file mode 100644 index 00000000..afd84c80 --- /dev/null +++ b/iOS/Hydra/HomeNewsItemCollectionViewCell.swift @@ -0,0 +1,26 @@ +// +// HomeNewsItemCollectionViewCell.swift +// Hydra +// +// Created by Feliciaan De Palmenaer on 05/08/15. +// Copyright © 2015 Zeus WPI. All rights reserved. +// + +import UIKit + +class HomeNewsItemCollectionViewCell: UICollectionViewCell { + @IBOutlet weak var titleLabel: UILabel! + @IBOutlet weak var assocationLabel: UILabel! + @IBOutlet weak var dateLabel: UILabel! + @IBOutlet weak var highlightImage: UIImageView! + + var article: AssociationNewsItem? { + didSet { + titleLabel.text = article?.title + let dateTransformer = SORelativeDateTransformer() + dateLabel.text = dateTransformer.transformedValue(article?.date) as! String? + assocationLabel.text = article?.association.displayName + highlightImage.hidden = !article!.highlighted + } + } +} \ No newline at end of file diff --git a/iOS/Hydra/HomeRestoCollectionViewCell.swift b/iOS/Hydra/HomeRestoCollectionViewCell.swift new file mode 100644 index 00000000..ddde6f6e --- /dev/null +++ b/iOS/Hydra/HomeRestoCollectionViewCell.swift @@ -0,0 +1,73 @@ +// +// HomeRestoCollectionViewCell.swift +// Hydra +// +// Created by Feliciaan De Palmenaer on 31/07/15. +// Copyright © 2015 Zeus WPI. All rights reserved. +// + +import UIKit + +class HomeRestoCollectionViewCell: UICollectionViewCell, UITableViewDataSource, UITableViewDelegate { + @IBOutlet weak var tableView: UITableView! + @IBOutlet weak var dayLabel: UILabel! + @IBOutlet weak var closedLabel: UILabel! + + var restoMenu: RestoMenu? { + didSet { + if restoMenu != nil { + closedLabel.hidden = restoMenu!.open + if restoMenu!.day.isToday() { + dayLabel.text = "vandaag" + } else if restoMenu!.day.isTomorrow() { + dayLabel.text = "morgen" + } else { + let formatter = NSDateFormatter.H_dateFormatterWithAppLocale() + formatter.dateFormat = "EEEE d MMMM" + dayLabel.text = formatter.stringFromDate(restoMenu!.day) + } + } else { + dayLabel.text = "" + closedLabel.hidden = false + } + tableView.reloadData() + self.layoutSubviews() // call this to force an update after setting the new menu, so the tableview height changes. + } + } + + override func awakeFromNib() { + tableView.separatorColor = UIColor.clearColor() + } + + func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + if restoMenu!.open { + if let count = restoMenu?.meat.count where restoMenu!.open{ + return count + } + } + return 0 + } + + func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCellWithIdentifier("restoMenuTableViewCell") as? HomeRestoMenuItemTableViewCell + + cell!.menuItem = restoMenu?.meat[indexPath.row] as? RestoMenuItem + + return cell! + } +} + +class HomeRestoMenuItemTableViewCell: UITableViewCell { + @IBOutlet weak var nameLabel: UILabel! + @IBOutlet weak var priceLabel: UILabel! + + var menuItem: RestoMenuItem? { + didSet { + if let menuItem = menuItem { + nameLabel.text = menuItem.name + priceLabel.text = menuItem.price + self.contentView.layoutIfNeeded() // relayout when prices are added + } + } + } +} diff --git a/iOS/Hydra/HomeSchamperCollectionViewCell.swift b/iOS/Hydra/HomeSchamperCollectionViewCell.swift new file mode 100644 index 00000000..9fa19899 --- /dev/null +++ b/iOS/Hydra/HomeSchamperCollectionViewCell.swift @@ -0,0 +1,25 @@ +// +// HomeSchamperCollectionViewCell.swift +// Hydra +// +// Created by Feliciaan De Palmenaer on 31/07/15. +// Copyright © 2015 Zeus WPI. All rights reserved. +// + +import UIKit + +class HomeSchamperCollectionViewCell: UICollectionViewCell { + @IBOutlet weak var titleLabel: UILabel! + @IBOutlet weak var dateLabel: UILabel! + @IBOutlet weak var authorLabel: UILabel! + + var article: SchamperArticle? { + didSet { + titleLabel.text = article?.title + let dateTransformer = SORelativeDateTransformer() + dateLabel.text = dateTransformer.transformedValue(article?.date) as! String? + authorLabel.text = article?.author + } + } + +} \ No newline at end of file diff --git a/iOS/Hydra/HomeUrgentCollectionViewCell.swift b/iOS/Hydra/HomeUrgentCollectionViewCell.swift new file mode 100644 index 00000000..0f6b4952 --- /dev/null +++ b/iOS/Hydra/HomeUrgentCollectionViewCell.swift @@ -0,0 +1,37 @@ +// +// HomeUrgentCollectionViewCell.swift +// Hydra +// +// Created by Feliciaan De Palmenaer on 02/08/15. +// Copyright © 2015 Zeus WPI. All rights reserved. +// + +import UIKit + +class HomeUrgentCollectionViewCell: UICollectionViewCell { + @IBOutlet weak var button: UIButton! + + let notificationCenter = NSNotificationCenter.defaultCenter() + + override func awakeFromNib() { + notificationCenter.addObserver(self, selector: "playerStatusChanged:", name: UrgentPlayerDidChangeStateNotification, object: nil) + button.selected = UrgentPlayer.sharedPlayer().isPlaying() + } + + deinit { + notificationCenter.removeObserver(self) + } + + @IBAction func playButtonTapped(sender: UIButton) { + let player = UrgentPlayer.sharedPlayer() + if player.isPlaying() { + player.pause() + } else { + player.play() + } + } + + func playerStatusChanged(notification: NSNotification) { + button.selected = UrgentPlayer.sharedPlayer().isPlaying() + } +} \ No newline at end of file diff --git a/iOS/Hydra/HomeViewController.swift b/iOS/Hydra/HomeViewController.swift new file mode 100644 index 00000000..957513c7 --- /dev/null +++ b/iOS/Hydra/HomeViewController.swift @@ -0,0 +1,191 @@ +// +// HomeViewController.swift +// Hydra +// +// Created by Feliciaan De Palmenaer on 30/07/15. +// Copyright © 2015 Zeus WPI. All rights reserved. +// + +import UIKit + +class HomeViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout { + @IBOutlet weak var feedCollectionView: UICollectionView! + + let homeFeedService = HomeFeedService.sharedService + + var feedItems = HomeFeedService.sharedService.createFeed() + let refreshControl = UIRefreshControl() + var lastUpdated = NSDate() + + override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) { + super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) + sharedInit() + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + sharedInit() + } + + private func sharedInit() { + NSNotificationCenter.defaultCenter().addObserver(self, selector: "homeFeedUpdatedNotification:", name: HomeFeedDidUpdateFeedNotification, object: nil) + } + + deinit { + NSNotificationCenter.defaultCenter().removeObserver(self) + } + + func homeFeedUpdatedNotification(notification: NSNotification) { + self.feedItems = HomeFeedService.sharedService.createFeed() + self.feedCollectionView?.reloadData() + + self.refreshControl.endRefreshing() + } + + // MARK - View initialization + override func viewDidLoad() { + super.viewDidLoad() + + refreshControl.tintColor = .whiteColor() + refreshControl.addTarget(self, action: "startRefresh", forControlEvents: .ValueChanged) + feedCollectionView.addSubview(refreshControl) + + // REMOVE ME IF THE BUG IS FIXED, THIS IS FUCKING UGLY + NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: Selector("refreshDataTimer"), userInfo: nil, repeats: false) + } + + func refreshDataTimer(){ // REMOVE ME WHEN THE BUG IS FIXED + self.feedCollectionView?.reloadData() + } + + override func viewWillAppear(animated: Bool) { + super.viewWillAppear(animated) + + self.navigationController?.navigationBarHidden = true + UIApplication.sharedApplication().setStatusBarStyle(.LightContent, animated: false) + + HomeFeedService.sharedService.refreshStoresIfNecessary() + } + + override func viewWillDisappear(animated: Bool) { + super.viewWillDisappear(animated) + + self.navigationController?.navigationBarHidden = false + UIApplication.sharedApplication().setStatusBarStyle(.Default, animated: false) + } + + override func viewDidAppear(animated: Bool) { + UIApplication.sharedApplication().setStatusBarStyle(.LightContent, animated: animated) + } + + override func didRotateFromInterfaceOrientation(fromInterfaceOrientation: UIInterfaceOrientation) { + // this is called when changing layout :) + self.feedCollectionView.collectionViewLayout.invalidateLayout() + } + + func startRefresh() { + self.homeFeedService.refreshStores() + } + + // MARK: - UICollectionViewDataSource and Delegate methods + func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return feedItems.count; + } + + func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { + let feedItem = feedItems[indexPath.row] + + switch feedItem.itemType { + case .RestoItem: + let cell = collectionView.dequeueReusableCellWithReuseIdentifier("restoCell", forIndexPath: indexPath) as? HomeRestoCollectionViewCell + cell?.restoMenu = feedItem.object as? RestoMenu + cell?.layoutIfNeeded() // iOS 9 bug + return cell! + case .SchamperNewsItem: + let cell = collectionView.dequeueReusableCellWithReuseIdentifier("schamperCell", forIndexPath: indexPath) as? HomeSchamperCollectionViewCell + cell!.article = feedItem.object as? SchamperArticle + cell?.layoutIfNeeded() // iOS 9 bug + return cell! + case .ActivityItem: + let cell = collectionView.dequeueReusableCellWithReuseIdentifier("activityCell", forIndexPath: indexPath) as? HomeActivityCollectionViewCell + cell?.activity = feedItem.object as? AssociationActivity + cell?.layoutIfNeeded() // iOS 9 bug + return cell! + case .NewsItem: + let cell = collectionView.dequeueReusableCellWithReuseIdentifier("newsItemCell", forIndexPath: indexPath) as? HomeNewsItemCollectionViewCell + cell?.article = feedItem.object as? AssociationNewsItem + return cell! + case .UrgentItem: + return collectionView.dequeueReusableCellWithReuseIdentifier("urgentfmCell", forIndexPath: indexPath) + case .SettingsItem: + return collectionView.dequeueReusableCellWithReuseIdentifier("settingsCell", forIndexPath: indexPath) + default: + return collectionView.dequeueReusableCellWithReuseIdentifier("testCell", forIndexPath: indexPath) + } + } + + func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView { + return collectionView.dequeueReusableSupplementaryViewOfKind(kind, withReuseIdentifier: "homeHeader", forIndexPath: indexPath) + } + + func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize { + let feedItem = feedItems[indexPath.row] + + switch feedItem.itemType { + case .RestoItem: + let restoMenu = feedItem.object as? RestoMenu + var count = 1 + if (restoMenu != nil && restoMenu!.open) { + count = restoMenu!.meat.count + } + + return CGSizeMake(self.view.frame.size.width, CGFloat(90+count*15)) + case .ActivityItem: + let activity = feedItem.object as? AssociationActivity + //TODO: guess height of cell + let activity_height = activity!.descriptionText.isEmpty ? 60 : 0 + + return CGSizeMake(self.view.frame.size.width, CGFloat(180 - activity_height)) + case .SettingsItem: + return CGSizeMake(self.view.frame.size.width, 80) + case .NewsItem: + return CGSizeMake(self.view.frame.size.width, 100) + default: + return CGSizeMake(self.view.frame.size.width, 135) //TODO: per type + } + } + + func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAtIndex section: Int) -> UIEdgeInsets { + return UIEdgeInsetsMake(10, 0, 0, 0) + } + + func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) { + let feedItem = feedItems[indexPath.row] + + switch feedItem.itemType { + case .RestoItem: + let index = self.tabBarController?.viewControllers?.indexOf({$0.tabBarItem.tag == 221}) // using hardcoded tag of Resto Menu viewcontroller + self.tabBarController?.selectedIndex = index! + let navigationController = self.tabBarController?.viewControllers![index!] as? UINavigationController + if let menuController = navigationController?.visibleViewController as? RestoMenuViewController { + let menu = feedItem.object as! RestoMenu + menuController.scrollToDate(menu.day) + } + case .ActivityItem: + self.navigationController?.pushViewController(ActivityDetailController(activity: feedItem.object as! AssociationActivity, delegate: nil), animated: true) + case .SchamperNewsItem: + let article = feedItem.object as! SchamperArticle + if !article.read { + article.read = true + SchamperStore.sharedStore().syncStorage() + } + + self.navigationController?.pushViewController(SchamperDetailViewController(article: article), animated: true) + case .NewsItem: + self.navigationController?.pushViewController(NewsDetailViewController(newsItem: feedItem.object as! AssociationNewsItem), animated: true) + case .SettingsItem: + self.navigationController?.pushViewController(PreferencesController(), animated: true) + default: break + } + } +} \ No newline at end of file diff --git a/iOS/Hydra/Hydra-Bridging-Header.h b/iOS/Hydra/Hydra-Bridging-Header.h new file mode 100644 index 00000000..4b2d02e0 --- /dev/null +++ b/iOS/Hydra/Hydra-Bridging-Header.h @@ -0,0 +1,42 @@ +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// + +// Services +#import "AssociationStore.h" +#import "PreferencesService.h" +#import "RestoStore.h" +#import "SchamperStore.h" +#import "UrgentPlayer.h" + +// Models +#import "Association.h" +#import "AssociationActivity.h" +#import "AssociationNewsItem.h" +#import "NewsDetailViewController.h" +#import "RestoLegendItem.h" +#import "RestoMenu.h" +#import "SchamperArticle.h" +#import "FacebookEvent.h" + +// Controllers +#import "NewsViewController.h" +#import "ActivitiesController.h" +#import "ActivityDetailController.h" +#import "InfoViewController.h" +#import "PreferencesController.h" +#import "RestoMapController.h" +#import "SchamperViewController.h" +#import "SchamperDetailViewController.h" +#import "UrgentViewController.h" + +// Categories and extenions +#import "NSDateFormatter+AppLocale.h" + +// Third party classes +#import "NSDate+Utilities.h" +#import "SORelativeDateTransformer.h" + + +// Remove from bridiging header when removing iOS 7 support, so we can use the iOS >= 8 frameworks in Cocoapods +#import "UIImageView+WebCache.h" diff --git a/iOS/Hydra/Hydra-Prefix.pch b/iOS/Hydra/Hydra-Prefix.pch index ba315fb0..794c6486 100644 --- a/iOS/Hydra/Hydra-Prefix.pch +++ b/iOS/Hydra/Hydra-Prefix.pch @@ -4,7 +4,7 @@ #import -#ifndef __IPHONE_5_0 +#ifndef __IPHONE_7_0 #warning "This project uses features only available in iOS SDK 5.0 and later." #endif diff --git a/iOS/Hydra/HydraTabbarController.swift b/iOS/Hydra/HydraTabbarController.swift new file mode 100644 index 00000000..abbc8a54 --- /dev/null +++ b/iOS/Hydra/HydraTabbarController.swift @@ -0,0 +1,90 @@ +// +// HydraTabbarController.swift +// Hydra +// +// Created by Feliciaan De Palmenaer on 11/08/15. +// Copyright © 2015 Zeus WPI. All rights reserved. +// + +import UIKit + +class HydraTabBarController: UITabBarController, UITabBarControllerDelegate { + + override func viewDidLoad() { + super.viewDidLoad() + self.delegate = self + + let newsViewController = UINavigationController(rootViewController: NewsViewController()) + let activityController = UINavigationController(rootViewController: ActivitiesController()) + let infoController = UINavigationController(rootViewController: InfoViewController()) + let schamperController = UINavigationController(rootViewController: SchamperViewController()) + let prefsController = UINavigationController(rootViewController: PreferencesController()) + let urgentController = UrgentViewController() + + infoController.tabBarItem.configure(nil, image: "info", tag: 231) + activityController.tabBarItem.configure(nil, image: "activities", tag: 232) + schamperController.tabBarItem.configure(nil, image: "schamper", tag: 233) + newsViewController.tabBarItem.configure(nil, image: "news", tag: 234) + urgentController.tabBarItem.configure("Urgent.fm", image: "urgent", tag: 235) + prefsController.tabBarItem.configure("Voorkeuren", image: "settings", tag: 236) + + var viewControllers = self.viewControllers! + viewControllers.appendContentsOf([infoController, activityController, newsViewController, schamperController, urgentController, prefsController]) + + self.viewControllers = orderViewControllers(viewControllers) + + // Fix gray tabbars + self.tabBar.translucent = false + } + + func orderViewControllers(viewControllers: [UIViewController]) -> [UIViewController]{ + let tagsOrder = PreferencesService.sharedService().hydraTabBarOrder as! [Int] + if tagsOrder.count == 0 { + return viewControllers + } + + var orderedViewControllers = [UIViewController]() + var oldViewControllers = viewControllers + + for tag in tagsOrder { + let controller_index: Int? = oldViewControllers.indexOf({ (el) -> Bool in + el.tabBarItem.tag == tag + }) + if let index = controller_index { + orderedViewControllers.append(oldViewControllers.removeAtIndex(index)) + } + } + + // Add all other viewcontrollers, it's possible new ones are added + orderedViewControllers.appendContentsOf(oldViewControllers) + return orderedViewControllers + } + + // MARK: UITabBarControllerDelegate + func tabBarController(tabBarController: UITabBarController, didEndCustomizingViewControllers viewControllers: [UIViewController], changed: Bool) { + debugPrint("didEndCustomizingViewControllers called") + if !changed { + return + } + + var tagsOrder = [Int]() + for controller in viewControllers { + tagsOrder.append(controller.tabBarItem.tag) + } + + PreferencesService.sharedService().hydraTabBarOrder = tagsOrder + } +} + +// MARK: UITabBarItem functions +extension UITabBarItem { + + // Configure UITabBarItem with string, image and tag + func configure(title: String?, image: String, tag: Int) { + if let title = title { + self.title = title + } + self.image = UIImage(named: "tabbar-" + image + ".png") + self.tag = tag + } +} \ No newline at end of file diff --git a/iOS/Hydra/Images.xcassets/AppIcon-2.appiconset/Contents.json b/iOS/Hydra/Images.xcassets/AppIcon-2.appiconset/Contents.json index a93eae40..5705af91 100644 --- a/iOS/Hydra/Images.xcassets/AppIcon-2.appiconset/Contents.json +++ b/iOS/Hydra/Images.xcassets/AppIcon-2.appiconset/Contents.json @@ -6,12 +6,22 @@ "filename" : "Icon-Small-iOS6@2x.png", "scale" : "2x" }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, { "size" : "40x40", "idiom" : "iphone", "filename" : "Icon-Small-iOS7@2x.png", "scale" : "2x" }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, { "size" : "60x60", "idiom" : "iphone", diff --git a/iOS/Hydra/InfoViewController.m b/iOS/Hydra/InfoViewController.m index 466c5198..753f8884 100644 --- a/iOS/Hydra/InfoViewController.m +++ b/iOS/Hydra/InfoViewController.m @@ -6,6 +6,8 @@ // Copyright (c) 2012 Zeus WPI. All rights reserved. // +@import SafariServices; + #import "InfoViewController.h" #import "WebViewController.h" @@ -76,10 +78,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N if(!cell) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; - // iOS7 - if ([cell respondsToSelector:@selector(setSeparatorInset:)]) { - cell.separatorInset = UIEdgeInsetsZero; - } + cell.separatorInset = UIEdgeInsetsZero; } cell.contentView.backgroundColor = [UIColor whiteColor]; @@ -141,8 +140,12 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath NSURL *url = nil; if (item[@"url-ios"]) url = [NSURL URLWithString:item[@"url-ios"]]; else url = [NSURL URLWithString:item[@"url"]]; - - [[UIApplication sharedApplication] openURL:url]; + if (IOS_VERSION_GREATER_THAN_OR_EQUAL_TO(@"9")) { + SFSafariViewController *svc = [[SFSafariViewController alloc] initWithURL:url]; + [self.navigationController presentViewController:svc animated:YES completion:nil]; + } else { + [[UIApplication sharedApplication] openURL:url]; + } [tableView deselectRowAtIndexPath:indexPath animated:YES]; } else { diff --git a/iOS/Hydra/LocationService.swift b/iOS/Hydra/LocationService.swift new file mode 100644 index 00000000..a2b7e1e9 --- /dev/null +++ b/iOS/Hydra/LocationService.swift @@ -0,0 +1,65 @@ +// +// LocationService.swift +// Hydra +// +// Created by Feliciaan De Palmenaer on 01/08/15. +// Copyright © 2015 Zeus WPI. All rights reserved. +// + +import Foundation +import CoreLocation + +public class LocationService: NSObject, CLLocationManagerDelegate { + + static let sharedService = LocationService() + + public var allowedLocation: Bool = false + + private var locationManager: CLLocationManager = CLLocationManager() + private var location: CLLocation? + + private override init() { + super.init() + self.locationManager.delegate = self + self.locationManager.pausesLocationUpdatesAutomatically = true + self.locationManager.distanceFilter = 100.0 + self.locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters + } + + public func startUpdating() { + let status = CLLocationManager.authorizationStatus() + if status == CLAuthorizationStatus.Restricted || status == CLAuthorizationStatus.Denied { + allowedLocation = false + return + } else if status == .NotDetermined { + if #available(iOS 8.0, *) { + locationManager.requestWhenInUseAuthorization() + } + } + allowedLocation = true + + self.locationManager.startUpdatingLocation() + } + + public func pauseUpdating() { //TODO: call this + self.locationManager.stopUpdatingLocation() + } + + public func calculateDistance(latitude: Double, longitude: Double) -> CLLocationDistance? { + if !allowedLocation || location == nil{ + return nil + } + return location?.distanceFromLocation(CLLocation(latitude: latitude, longitude: longitude)) + } + + //MARK: - Implement core location delegate methods + @objc public func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { + debugPrint("Locations updated") + + location = locations[0] + } + + @objc public func locationManagerDidResumeLocationUpdates(manager: CLLocationManager) { + debugPrint("Resumed location updates") + } +} \ No newline at end of file diff --git a/iOS/Hydra/MainStoryboard.storyboard b/iOS/Hydra/MainStoryboard.storyboard new file mode 100644 index 00000000..cdb15719 --- /dev/null +++ b/iOS/Hydra/MainStoryboard.storyboard @@ -0,0 +1,1444 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iOS/Hydra/MapViewController.m b/iOS/Hydra/MapViewController.m index 32e11d09..204b4e28 100644 --- a/iOS/Hydra/MapViewController.m +++ b/iOS/Hydra/MapViewController.m @@ -25,11 +25,7 @@ @implementation MapViewController - (void)loadView { -#ifdef __IPHONE_7_0 - if ([self respondsToSelector:@selector(setEdgesForExtendedLayout:)]) { - self.edgesForExtendedLayout = UIRectEdgeNone; - } -#endif + self.edgesForExtendedLayout = UIRectEdgeNone; CGRect bounds = [UIScreen mainScreen].bounds; self.view = [[UIView alloc] initWithFrame:bounds]; @@ -210,27 +206,18 @@ - (void)routeButtonTapped:(UIButton *)sender // Check for iOS 6 Class mapItemClass = [MKMapItem class]; - if (mapItemClass && [mapItemClass respondsToSelector:@selector(openMapsWithItems:launchOptions:)]) { - // Create an MKMapItem to pass to the Maps app - MKPlacemark *placemark = [[MKPlacemark alloc] initWithCoordinate:coordinates - addressDictionary:nil]; - MKMapItem *mapItem = [[MKMapItem alloc] initWithPlacemark:placemark]; - [mapItem setName:annotation.title]; - - // Route between the current location and the mapitem - MKMapItem *currentLocationMapItem = [MKMapItem mapItemForCurrentLocation]; - [MKMapItem openMapsWithItems:@[currentLocationMapItem, mapItem] - launchOptions:@{ - MKLaunchOptionsDirectionsModeKey : MKLaunchOptionsDirectionsModeWalking - }]; - } - // iOS < 6 use maps.apple.com - else { - CLLocationCoordinate2D user = self.mapView.userLocation.coordinate; - NSString *url = [NSString stringWithFormat:@"http://maps.apple.com/maps?saddr=%f,%f&daddr=%f,%f&dirflg=w", - user.latitude, user.longitude, coordinates.latitude, coordinates.longitude]; - [[UIApplication sharedApplication] openURL:[NSURL URLWithString:url]]; - } + // Create an MKMapItem to pass to the Maps app + MKPlacemark *placemark = [[MKPlacemark alloc] initWithCoordinate:coordinates + addressDictionary:nil]; + MKMapItem *mapItem = [[MKMapItem alloc] initWithPlacemark:placemark]; + [mapItem setName:annotation.title]; + + // Route between the current location and the mapitem + MKMapItem *currentLocationMapItem = [MKMapItem mapItemForCurrentLocation]; + [MKMapItem openMapsWithItems:@[currentLocationMapItem, mapItem] + launchOptions:@{ + MKLaunchOptionsDirectionsModeKey : MKLaunchOptionsDirectionsModeWalking + }]; } #pragma mark - User tracking diff --git a/iOS/Hydra/NewsDetailViewController.m b/iOS/Hydra/NewsDetailViewController.m index b1d9ce14..009f0c57 100644 --- a/iOS/Hydra/NewsDetailViewController.m +++ b/iOS/Hydra/NewsDetailViewController.m @@ -29,11 +29,8 @@ - (id)initWithNewsItem:(AssociationNewsItem *)newsItem - (void)viewDidLoad { -#ifdef __IPHONE_7_0 - if ([self respondsToSelector:@selector(setEdgesForExtendedLayout:)]) { - self.edgesForExtendedLayout = UIRectEdgeNone; - } -#endif + self.edgesForExtendedLayout = UIRectEdgeNone; + CGSize viewSize = self.view.bounds.size; CGSize contentSize = CGSizeMake(viewSize.width - 20, CGFLOAT_MAX); self.view.backgroundColor = [UIColor whiteColor]; diff --git a/iOS/Hydra/NewsViewController.m b/iOS/Hydra/NewsViewController.m index 8782ff7b..2fbb6391 100644 --- a/iOS/Hydra/NewsViewController.m +++ b/iOS/Hydra/NewsViewController.m @@ -105,10 +105,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier]; cell.detailTextLabel.textColor = [UIColor colorWithWhite:0.3 alpha:1]; - // iOS7 - if ([cell respondsToSelector:@selector(setSeparatorInset:)]) { - cell.separatorInset = UIEdgeInsetsZero; - } + cell.separatorInset = UIEdgeInsetsZero; } AssociationNewsItem *newsItem = self.newsItems[indexPath.row]; diff --git a/iOS/Hydra/PreferencesService.h b/iOS/Hydra/PreferencesService.h index 93d1a0f9..690ac6ca 100644 --- a/iOS/Hydra/PreferencesService.h +++ b/iOS/Hydra/PreferencesService.h @@ -14,6 +14,7 @@ @property (nonatomic, assign) BOOL filterAssociations; @property (nonatomic, strong) NSArray *preferredAssociations; +@property (nonatomic, strong) NSArray *hydraTabBarOrder; @property (nonatomic, assign) BOOL shownFacebookPrompt; @end diff --git a/iOS/Hydra/PreferencesService.m b/iOS/Hydra/PreferencesService.m index addeff21..34a67b07 100644 --- a/iOS/Hydra/PreferencesService.m +++ b/iOS/Hydra/PreferencesService.m @@ -10,9 +10,9 @@ #define kFilterAssociationsKey @"useAssociationFilter" #define kPreferredAssociationsKey @"preferredAssociations" +#define kHydraTabBarOrder @"hydraTabBarOrder" #define kShownFacebookPrompt @"shownFacebookPrompt" - @interface PreferencesService () @property (nonatomic, strong) NSUserDefaults *settings; @@ -79,4 +79,21 @@ - (void)setPreferredAssociations:(NSArray *)preferredAssociations [self didChangeValueForKey:@"preferredAssociations"]; } + +- (NSArray *)hydraTabBarOrder +{ + NSArray *list = [self.settings objectForKey:kHydraTabBarOrder]; + AssertClassOrNil(list, NSArray); + if (list == nil) { + list = [[NSArray alloc] init]; + } + return list; +} + +- (void)setHydraTabBarOrder:(NSArray *)hydraTabBarOrder +{ + [self willChangeValueForKey:kHydraTabBarOrder]; + [self.settings setObject:hydraTabBarOrder forKey:kHydraTabBarOrder]; + [self didChangeValueForKey:kHydraTabBarOrder]; +} @end diff --git a/iOS/Hydra/RestoInfoView.h b/iOS/Hydra/RestoInfoView.h deleted file mode 100644 index 30c6c723..00000000 --- a/iOS/Hydra/RestoInfoView.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// RestoInfoView.h -// Hydra -// -// Created by Feliciaan De Palmenaer on 24/12/12. -// Copyright (c) 2012 Zeus WPI. All rights reserved. -// - -#import - -@interface RestoInfoView : UIView - -@property (nonatomic, strong) NSArray *legend; - -- (id)initWithFrame:(CGRect)frame; - -@end diff --git a/iOS/Hydra/RestoInfoView.m b/iOS/Hydra/RestoInfoView.m deleted file mode 100644 index ca9e19ae..00000000 --- a/iOS/Hydra/RestoInfoView.m +++ /dev/null @@ -1,180 +0,0 @@ -// -// RestoInfoView.m -// Hydra -// -// Created by Feliciaan De Palmenaer on 24/12/12. -// Copyright (c) 2012 Zeus WPI. All rights reserved. -// - -#import "RestoInfoView.h" -#import "RestoLegendItem.h" -#import "RestoMapController.h" -#import "RestoStore.h" -#import "AppDelegate.h" - -#define kCellKeyLabel 101 -#define kCellValueLabel 102 - -@interface RestoInfoView () - -@property (nonatomic, unsafe_unretained) UITableView *tableView; - -@end -@implementation RestoInfoView - -#pragma mark - Properties and init - -- (id)initWithFrame:(CGRect)frame -{ - if (self = [super initWithFrame:frame]) { - [self createView]; - } - return self; -} - -- (void)createView -{ - // background - UIImage *background = [UIImage imageNamed:@"header-bg.png"]; - UIImageView *backgroundView = [[UIImageView alloc] initWithFrame:self.bounds]; - backgroundView.image = background; - backgroundView.contentMode = UIViewContentModeScaleToFill; - backgroundView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - [self addSubview:backgroundView]; - - // Header view - UIView *headerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, 208)]; - - // logo - UIImage *logo = [UIImage imageNamed:@"resto-logo.png"]; - UIImageView *imageView = [[UIImageView alloc] initWithImage:logo]; - [imageView setFrame:CGRectMake(self.frame.size.width/2-50, 0, 100, 100)]; - [headerView addSubview:imageView]; - - // resto info - UILabel *infoLabel = [[UILabel alloc] initWithFrame:CGRectMake(30, CGRectGetMaxY(imageView.frame), - self.frame.size.width - 60, 80)]; - infoLabel.text = @"De resto's van de UGent zijn elke weekdag open van 11u15 tot 14u. 's Avonds kan je ook terecht in resto De Brug van 17u30 tot 21u."; - infoLabel.backgroundColor = [UIColor clearColor]; - infoLabel.textColor = [UIColor whiteColor]; - infoLabel.font = [UIFont systemFontOfSize:14]; - infoLabel.textAlignment = NSTextAlignmentCenter; - infoLabel.numberOfLines = 4; - [headerView addSubview:infoLabel]; - - // title - CGRect titleFrame = CGRectMake(0, CGRectGetMaxY(infoLabel.frame) + 5, self.frame.size.width, 20); - UILabel *headerTitle = [[UILabel alloc] initWithFrame:titleFrame]; - headerTitle.text = @"Legende"; - headerTitle.textAlignment = NSTextAlignmentCenter; - headerTitle.font = [UIFont boldSystemFontOfSize:13]; - headerTitle.textColor = [UIColor whiteColor]; - headerTitle.backgroundColor = [UIColor clearColor]; - [headerView addSubview:headerTitle]; - - // Tableview - UITableView *tableView = [[UITableView alloc] initWithFrame:self.bounds - style:UITableViewStylePlain]; - tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - tableView.delegate = self; - tableView.dataSource = self; - tableView.bounces = NO; - tableView.separatorColor = [UIColor clearColor]; - tableView.backgroundColor = [UIColor clearColor]; - tableView.allowsSelection = NO; - tableView.tableHeaderView = headerView; - tableView.contentInset = UIEdgeInsetsMake(10, 0, 10, 0); - tableView.scrollIndicatorInsets = UIEdgeInsetsMake(5, 0, 5, 0); - [self addSubview:tableView]; - self.tableView = tableView; -} - -- (void)setLegend:(NSArray *)legend -{ - _legend = legend; - [self.tableView reloadData]; -} - -#pragma mark - Table view datasource - -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section -{ - return self.legend.count; -} - -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath -{ - RestoLegendItem *legend = self.legend[indexPath.row]; - - UILabel *keyLabel, *valueLabel; - - static NSString *cellIdentifier = @"RestoLegendViewCell"; - UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; - if (cell == nil) { - cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault - reuseIdentifier:cellIdentifier]; - cell.textLabel.font = [UIFont boldSystemFontOfSize:15]; - cell.textLabel.textColor = [UIColor colorWithWhite:0.5 alpha:1]; - cell.backgroundColor = [UIColor clearColor]; - - keyLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 0, 40, 19)]; - keyLabel.tag = kCellKeyLabel; - keyLabel.font = [UIFont boldSystemFontOfSize:13]; - keyLabel.backgroundColor = [UIColor clearColor]; - keyLabel.textColor = [UIColor whiteColor]; - keyLabel.textAlignment = NSTextAlignmentRight; - [cell.contentView addSubview:keyLabel]; - - CGFloat valueX = CGRectGetMaxX(keyLabel.frame) + 10; - CGRect valueFrame = CGRectMake(valueX, 0, cell.frame.size.width - valueX - 20, - cell.frame.size.height); - valueLabel = [[UILabel alloc] initWithFrame:valueFrame]; - valueLabel.autoresizingMask = UIViewAutoresizingFlexibleHeight - | UIViewAutoresizingFlexibleWidth; - valueLabel.numberOfLines = 0; - valueLabel.lineBreakMode = NSLineBreakByWordWrapping; - valueLabel.tag = kCellValueLabel; - valueLabel.font = [UIFont systemFontOfSize:13]; - valueLabel.backgroundColor = [UIColor clearColor]; - valueLabel.textColor = [UIColor whiteColor]; - [cell.contentView addSubview:valueLabel]; - } - else { - keyLabel = (UILabel *)[cell viewWithTag:kCellKeyLabel]; - valueLabel = (UILabel *)[cell viewWithTag:kCellValueLabel]; - } - - if ([legend.style isEqual:@"bold"]) { - keyLabel.font = [UIFont boldSystemFontOfSize:13]; - } - else { - keyLabel.font = [UIFont systemFontOfSize:13]; - } - - valueLabel.text = legend.value; - keyLabel.text = legend.key; - - return cell; - -} - -- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath -{ - RestoLegendItem *legend = self.legend[indexPath.row]; - - CGSize constraintSize = CGSizeMake(200, CGFLOAT_MAX); - - NSMutableParagraphStyle * paragraphStyle = [[NSMutableParagraphStyle alloc] init]; - paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping; - paragraphStyle.alignment = NSTextAlignmentLeft; - - NSDictionary *attributes = @{ NSFontAttributeName: [UIFont systemFontOfSize:13], NSParagraphStyleAttributeName: paragraphStyle}; - - CGSize labelSize = [legend.value boundingRectWithSize:constraintSize - options:NSStringDrawingUsesLineFragmentOrigin - attributes:attributes - context:nil].size; - return labelSize.height + 5; -} - -@end diff --git a/iOS/Hydra/RestoMapController.m b/iOS/Hydra/RestoMapController.m index f0372b9f..0075526c 100644 --- a/iOS/Hydra/RestoMapController.m +++ b/iOS/Hydra/RestoMapController.m @@ -7,7 +7,6 @@ // #import "RestoMapController.h" -#import "RestoMenuController.h" #import "RestoLocation.h" #import "RestoStore.h" #import "UINavigationController+ReplaceController.h" @@ -67,12 +66,6 @@ - (void)viewDidLoad { [super viewDidLoad]; self.title = @"Resto Map"; - - // Add button to navigation bar - UIBarButtonItem *menuButton = [[UIBarButtonItem alloc] initWithTitle:@"Menu" - style:UIBarButtonItemStylePlain - target:self action:@selector(menuButtonTapped:)]; - self.navigationItem.rightBarButtonItem = menuButton; } - (void)viewDidAppear:(BOOL)animated @@ -86,13 +79,6 @@ - (void)dealloc [[NSNotificationCenter defaultCenter] removeObserver:self]; } -- (void)menuButtonTapped:(id)sender -{ - RestoMenuController *menuController = [[RestoMenuController alloc] init]; - [self.navigationController H_replaceViewControllerWith:menuController - options:UIViewAnimationOptionTransitionFlipFromLeft]; -} - - (void)mapLocationUpdated { if (self.searchController.isActive) { @@ -243,10 +229,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N cell.textLabel.backgroundColor = [UIColor clearColor]; cell.detailTextLabel.backgroundColor = [UIColor clearColor]; - // iOS7 - if ([cell respondsToSelector:@selector(setSeparatorInset:)]) { - cell.separatorInset = UIEdgeInsetsZero; - } + cell.separatorInset = UIEdgeInsetsZero; } RestoLocation *resto = self.filteredMapItems[indexPath.row]; diff --git a/iOS/Hydra/RestoMenuCollectionCell.swift b/iOS/Hydra/RestoMenuCollectionCell.swift new file mode 100644 index 00000000..b7da92f9 --- /dev/null +++ b/iOS/Hydra/RestoMenuCollectionCell.swift @@ -0,0 +1,132 @@ +// +// RestoMenuCollectionCell.swift +// Hydra +// +// Created by Feliciaan De Palmenaer on 14/08/15. +// Copyright © 2015 Zeus WPI. All rights reserved. +// + +import UIKit + +class RestoMenuCollectionCell: UICollectionViewCell, UITableViewDataSource, UITableViewDelegate { + @IBOutlet weak var tableView: UITableView! + + var restoMenu: RestoMenu? { + didSet { + tableView.reloadData() + } + } + + override func awakeFromNib() { + tableView.separatorColor = UIColor.clearColor() + } + + func numberOfSectionsInTableView(tableView: UITableView) -> Int { + return 4; //TODO: add maaltijdsoep + } + + func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + if let menu = restoMenu where menu.open { + let restoMenuSection = RestoMenuSection(rawValue: section) + switch restoMenuSection! { + case .Soup: + return restoMenu?.soup != nil ? 1 : 0 + case .Meat: + return (restoMenu?.meat.count)! + case .Vegetable: + return (restoMenu?.vegetables.count)! + default: + return 0 + } + + } + return 0 + } + + func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCellWithIdentifier("menuItemCell") as? RestoMenuItemTableViewCell + + cell?.backgroundColor = UIColor.clearColor() // for iPads, for some strange the cells lose their color + + let restoMenuSection = RestoMenuSection(rawValue: indexPath.section) + switch restoMenuSection! { + case .Soup: + cell!.menuItem = restoMenu?.soup + case .Meat: + cell!.menuItem = restoMenu?.meat[indexPath.row] as? RestoMenuItem + case .Vegetable: + cell!.vegetable = restoMenu?.vegetables[indexPath.row] as? String + default: break + } + + return cell! + } + + // Using footers of the previous section instead of headers so they scroll + func tableView(tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { + // Zero height for last section footer + return section < 3 ? 40 : 0 + } + + func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView?{ + // Return nil if last footer + if section == 3 { + return nil + } + let frame = CGRectMake(0, 0, self.bounds.width, 40) + let header = UIView(frame: frame) + + let label = UILabel(frame: frame) + label.textAlignment = .Center + if #available(iOS 8.2, *) { + label.font = UIFont.systemFontOfSize(20, weight: UIFontWeightLight) + } else { + // Fallback on earlier versions + label.font = UIFont.systemFontOfSize(20) + } + label.baselineAdjustment = .AlignCenters + label.textColor = UIColor.whiteColor() + let restoMenuSection = RestoMenuSection(rawValue: section+1) + switch restoMenuSection! { + case .Soup: + label.text = "SOEP" + case .Meat: + label.text = "VLEES & VEGGIE" + case .Vegetable: + label.text = "GROENTEN" + default: + return header + } + + header.addSubview(label) + return header + } +} + +class RestoMenuItemTableViewCell: UITableViewCell { + @IBOutlet weak var nameLabel: UILabel! + @IBOutlet weak var priceLabel: UILabel! + + var menuItem: RestoMenuItem? { + didSet { + if let menuItem = menuItem { + nameLabel.text = menuItem.name + priceLabel.text = menuItem.price + self.contentView.layoutIfNeeded() // relayout when prices are added + } + } + } + + var vegetable: String? { + didSet { + if let vegetable = vegetable { + nameLabel.text = vegetable + priceLabel.text = "" + } + } + } +} + +enum RestoMenuSection: Int { + case Soup = 1, Meat = 3, Vegetable = 2, Empty = 0 +} \ No newline at end of file diff --git a/iOS/Hydra/RestoMenuController.h b/iOS/Hydra/RestoMenuController.h deleted file mode 100644 index bae0016a..00000000 --- a/iOS/Hydra/RestoMenuController.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// RestoMenuController.h -// Hydra -// -// Created by Pieter De Baets on 29/06/12. -// Copyright (c) 2012 Zeus WPI. All rights reserved. -// - -#import - -@interface RestoMenuController : UIViewController - -@end diff --git a/iOS/Hydra/RestoMenuController.m b/iOS/Hydra/RestoMenuController.m deleted file mode 100644 index 026f651c..00000000 --- a/iOS/Hydra/RestoMenuController.m +++ /dev/null @@ -1,319 +0,0 @@ -// -// RestoMenuController.m -// Hydra -// -// Created by Pieter De Baets on 29/06/12. -// Copyright (c) 2012 Zeus WPI. All rights reserved. -// -#import -#import "SMPageControl.h" -#import "NSDate+Utilities.h" -#import "RestoInfoView.h" -#import "RestoMapController.h" -#import "RestoMenu.h" -#import "RestoMenuController.h" -#import "RestoMenuView.h" -#import "RestoStore.h" -#import "UIColor+AppColors.h" -#import "UINavigationController+ReplaceController.h" - -#define kRestoDaysShown 5 - -@interface RestoMenuController () - -@property (nonatomic, unsafe_unretained) UIScrollView *scrollView; -@property (nonatomic, unsafe_unretained) SMPageControl *pageControl; -@property (nonatomic, unsafe_unretained) RestoInfoView *infoSheet; -@property (nonatomic, unsafe_unretained) RestoMenuView *menuSheetA; -@property (nonatomic, unsafe_unretained) RestoMenuView *menuSheetB; - -@property (nonatomic, strong) NSArray *days; -@property (nonatomic, strong) NSMutableArray *menus; - -@property (nonatomic, assign) NSUInteger pageControlUsed; - -@end - -@implementation RestoMenuController - -#pragma mark Setting up the view & viewcontroller - -- (id)init -{ - if (self = [super init]) { - // Check for updates - NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; - [center addObserver:self selector:@selector(reloadMenu) - name:RestoStoreDidReceiveMenuNotification object:nil]; - [center addObserver:self selector:@selector(reloadInfo) - name:RestoStoreDidUpdateInfoNotification object:nil]; - [center addObserver:self selector:@selector(applicationDidBecomeActive:) - name:UIApplicationDidBecomeActiveNotification object:nil]; - } - return self; -} - -- (void)loadView -{ -#ifdef __IPHONE_7_0 - if ([self respondsToSelector:@selector(setEdgesForExtendedLayout:)]) { - self.edgesForExtendedLayout = UIRectEdgeNone; - } -#endif - - CGRect bounds = [UIScreen mainScreen].bounds; - self.view = [[UIView alloc] initWithFrame:bounds]; - self.view.backgroundColor = [UIColor hydraBackgroundColor]; - self.title = @"Resto Menu"; - - UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:bounds]; - scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - scrollView.pagingEnabled = YES; - scrollView.delegate = self; - scrollView.showsHorizontalScrollIndicator = NO; - [self.view addSubview:scrollView]; - self.scrollView = scrollView; - - CGRect pageControlFrame = CGRectMake(0, bounds.size.height - 36, bounds.size.width, 36); - SMPageControl *pageControl = [[SMPageControl alloc] initWithFrame:pageControlFrame]; - pageControl.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleWidth; - [pageControl addTarget:self action:@selector(pageChanged:) - forControlEvents:UIControlEventValueChanged]; - [self.view addSubview:pageControl]; - self.pageControl = pageControl; - - // Sheets - CGSize viewSize = self.scrollView.frame.size; - CGRect sheetFrame = CGRectMake(20, 20, viewSize.width - 40, viewSize.height - 60); - - self.menuSheetA = [self addSheet:[[RestoMenuView alloc] initWithFrame:sheetFrame]]; - self.menuSheetB = [self addSheet:[[RestoMenuView alloc] initWithFrame:sheetFrame]]; - self.infoSheet = [self addSheet:[[RestoInfoView alloc] initWithFrame:sheetFrame]]; - - // Add button to navigation bar - UIBarButtonItem *mapButton = [[UIBarButtonItem alloc] initWithTitle:@"Kaart" - style:UIBarButtonItemStylePlain - target:self action:@selector(mapButtonTapped:)]; - [self.navigationItem setRightBarButtonItem:mapButton]; -} - -- (void)viewDidLoad -{ - [super viewDidLoad]; - - // Update views - [self reloadMenu]; - [self reloadInfo]; - - // Setup scrollview - [self updateView:self.menuSheetA toIndex:0]; - [self updateView:self.menuSheetB toIndex:1]; - - CGSize viewSize = self.scrollView.frame.size; - self.scrollView.contentSize = CGSizeMake(viewSize.width * (self.days.count + 1), 1); - self.scrollView.contentOffset = CGPointMake(viewSize.width, 0); - - // Setup pageControl - self.pageControlUsed = 0; - self.pageControl.numberOfPages = self.days.count + 1; - [self.pageControl setImageMask:[UIImage imageNamed:@"dot-question"] forPage:0]; - self.pageControl.currentPage = 1; -} - -- (void)viewDidAppear:(BOOL)animated -{ - [super viewDidAppear:animated]; - GAI_Track(@"Resto Menu"); -} - -- (void)applicationDidBecomeActive:(NSNotification *)notification -{ - // Update days - NSDate *firstDay = self.days[0]; - [self calculateDays]; - - // Check if day changed - if (![firstDay isEqualToDateIgnoringTime:self.days[0]]) { - [self reloadMenu]; - [self scrollViewDidScroll:self.scrollView]; - } -} - -- (void)viewDidLayoutSubviews -{ - // Restyle the sheets to keep the shadow the right size - [self setupSheetStyle:self.menuSheetA]; - [self setupSheetStyle:self.menuSheetB]; - [self setupSheetStyle:self.infoSheet]; -} - -- (void)dealloc -{ - [[NSNotificationCenter defaultCenter] removeObserver:self]; -} - -- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation -{ - return (interfaceOrientation == UIInterfaceOrientationPortrait); -} - -#pragma mark - Sheets -#define kPageCornerRadius 10 - -- (id)addSheet:(UIView *)contentView -{ - // Use the outer view for shadows, the contentView uses maskToBounds for rounded corners - UIView *holderView = [[UIView alloc] initWithFrame:contentView.frame]; - holderView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - [self.scrollView addSubview:holderView]; - - contentView.frame = holderView.bounds; - contentView.autoresizingMask = holderView.autoresizingMask; - [holderView addSubview:contentView]; - - return contentView; -} - -- (void)setupSheetStyle:(UIView *)contentView -{ - CALayer *layer = contentView.superview.layer; - layer.cornerRadius = kPageCornerRadius; - - UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRoundedRect:layer.bounds - cornerRadius:kPageCornerRadius]; - layer.shadowPath = shadowPath.CGPath; - layer.shadowColor = [UIColor blackColor].CGColor; - layer.shadowOpacity = 0.3; - layer.shadowOffset = CGSizeMake(1.5, 3.0); - - contentView.layer.cornerRadius = kPageCornerRadius; - contentView.layer.masksToBounds = YES; -} - -#pragma mark - Buttons - -- (void)mapButtonTapped:(id)sender -{ - RestoMapController *mapController = [[RestoMapController alloc] init]; - [self.navigationController H_replaceViewControllerWith:mapController - options:UIViewAnimationOptionTransitionFlipFromLeft]; -} - -#pragma mark - Loading days & menus - -- (void)calculateDays -{ - NSDate *day = [NSDate date]; - NSMutableArray *days = [NSMutableArray arrayWithCapacity:kRestoDaysShown]; - - // Find the next 5 days to display - while (days.count < kRestoDaysShown) { - if ([day isTypicallyWorkday]) { - [days addObject:day]; - } - day = [day dateByAddingDays:1]; - } - self.days = days; -} - -- (void)reloadMenu -{ - if (!self.days.count) [self calculateDays]; - - RestoStore *store = [RestoStore sharedStore]; - NSMutableArray *menus = [[NSMutableArray alloc] init]; - for (NSUInteger i = 0; i < self.days.count; i++) { - id menu = [store menuForDay:self.days[i]]; - if (!menu) menu = [NSNull null]; - menus[i] = menu; - } - self.menus = menus; -} - -- (void)setMenus:(NSMutableArray *)menus -{ - _menus = menus; - - NSUInteger currentIndex = [self.days indexOfObject:self.menuSheetA.day]; - if (currentIndex != NSNotFound) { - [self.menuSheetA configureWithDay:self.days[currentIndex] - menu:self.menus[currentIndex]]; - } - - NSUInteger nextIndex = [self.days indexOfObject:self.menuSheetB.day]; - if (nextIndex != NSNotFound) { - [self.menuSheetB configureWithDay:self.days[nextIndex] - menu:self.menus[nextIndex]]; - } -} - -- (void)reloadInfo -{ - self.infoSheet.legend = [RestoStore sharedStore].legend; -} - -#pragma mark - View scrolling and page changing - -- (void)scrollViewDidScroll:(UIScrollView *)scrollView -{ - CGFloat pageWidth = scrollView.frame.size.width; - float fractionalPage = scrollView.contentOffset.x / pageWidth; - - if (self.pageControlUsed == 0) { - self.pageControl.currentPage = round(fractionalPage); - } - - // Nothing needs to change for the InfoView - if (fractionalPage < 1) return; - - NSInteger lowerNumber = floor(fractionalPage) - 1; - NSInteger upperNumber = lowerNumber + 1; - - NSDate *lowerDate = self.days[lowerNumber]; - NSDate *upperDate = nil; - if (upperNumber < kRestoDaysShown) { - upperDate = self.days[upperNumber]; - } - - // Goal: apply lower and upper date to menuSheetA and menuSheetB - // with the least amount of changes possible - if (self.menuSheetA.day != lowerDate && self.menuSheetA.day != upperDate) { - NSUInteger newIndex = self.menuSheetB.day == upperDate ? lowerNumber : upperNumber; - [self updateView:self.menuSheetA toIndex:newIndex]; - } - if (self.menuSheetB.day != lowerDate && self.menuSheetB.day != upperDate) { - NSUInteger newIndex = self.menuSheetA.day == upperDate ? lowerNumber : upperNumber; - [self updateView:self.menuSheetB toIndex:newIndex]; - } -} - -- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)sender -{ - self.pageControlUsed--; -} - -- (void)pageChanged:(UIPageControl *)sender -{ - DLog(@"UIPageControl requesting page %ld", (long)self.pageControl.currentPage); - - CGRect newPage = self.scrollView.bounds; - newPage.origin.x = newPage.size.width * self.pageControl.currentPage; - [self.scrollView scrollRectToVisible:newPage animated:YES]; - - // Keep track of active UIPageControl animations - self.pageControlUsed++; -} - -- (void)updateView:(RestoMenuView *)view toIndex:(NSInteger)index -{ - if (index >= kRestoDaysShown || view.day == self.days[index]) return; - - CGSize viewSize = self.view.bounds.size; - CGRect frame = CGRectMake(viewSize.width * (index + 1) + 20, 20, - viewSize.width - 40, viewSize.height - 60); - view.superview.frame = frame; - - [view configureWithDay:self.days[index] menu:self.menus[index]]; -} - -@end diff --git a/iOS/Hydra/RestoMenuHeader.swift b/iOS/Hydra/RestoMenuHeader.swift new file mode 100644 index 00000000..26d4b9e2 --- /dev/null +++ b/iOS/Hydra/RestoMenuHeader.swift @@ -0,0 +1,66 @@ +// +// RestoMenuHeader.swift +// Hydra +// +// Created by Feliciaan De Palmenaer on 14/08/15. +// Copyright © 2015 Zeus WPI. All rights reserved. +// + +import UIKit + +class RestoMenuHeaderView: UIView { + @IBOutlet weak var controller: RestoMenuViewController? + @IBOutlet weak var infoView: UIView? + @IBOutlet weak var day1View: UIView? + @IBOutlet weak var day2View: UIView? + @IBOutlet weak var day3View: UIView? + @IBOutlet weak var day4View: UIView? + @IBOutlet weak var day5View: UIView? + @IBOutlet weak var mapView: UIView? + + @IBAction func infoViewPressed(gestureRecognizer: UITapGestureRecognizer) { + controller?.scrollToIndex(0) + } + + @IBAction func viewPressed(gestureRecognizer: UITapGestureRecognizer) { + controller?.scrollToIndex((gestureRecognizer.view?.tag)!) + } + + func updateDays() { + for (index, day) in (controller?.days.enumerate())! { + updateView(day, onIndex: index) + } + } + + func updateView(date: NSDate, onIndex index: Int) { + // Index only days so + 1 + let view = headerViews()[index+1] + let dayLabel = view?.viewWithTag(998) as! UILabel + let numberLabel = view?.viewWithTag(999) as! UILabel + + let formatter = NSDateFormatter.H_dateFormatterWithAppLocale() + formatter.dateFormat = "EE" + dayLabel.text = formatter.stringFromDate(date).uppercaseString + formatter.dateFormat = "d" + numberLabel.text = formatter.stringFromDate(date) + } + + func selectedIndex(index: Int) { + // modify background of label + for view in headerViews() { + let numberLabel = view?.viewWithTag(999) as! UILabel + numberLabel.backgroundColor = UIColor.clearColor() + numberLabel.layer.borderColor = UIColor.clearColor().CGColor + } + let view = headerViews()[index] + let numberLabel = view?.viewWithTag(999) as! UILabel + numberLabel.layer.masksToBounds = true + numberLabel.layer.borderColor = UIColor.whiteColor().CGColor + numberLabel.layer.borderWidth = 2 + numberLabel.layer.cornerRadius = 15 + } + + func headerViews() -> [UIView?] { + return [infoView, day1View, day2View, day3View, day4View, day5View] + } +} \ No newline at end of file diff --git a/iOS/Hydra/RestoMenuInfoCollectionViewCell.swift b/iOS/Hydra/RestoMenuInfoCollectionViewCell.swift new file mode 100644 index 00000000..528fc724 --- /dev/null +++ b/iOS/Hydra/RestoMenuInfoCollectionViewCell.swift @@ -0,0 +1,72 @@ +// +// RestoMenuInfoCollectionViewCell.swift +// Hydra +// +// Created by Feliciaan De Palmenaer on 26/09/15. +// Copyright © 2015 Zeus WPI. All rights reserved. +// + +import UIKit + +class RestoMenuInfoCollectionViewCell: UICollectionViewCell, UITableViewDataSource, UITableViewDelegate { + @IBOutlet weak var tableView: UITableView! + + var legend: [RestoLegendItem]? { + didSet { + tableView.reloadData() + } + } + + func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + if let legend = self.legend { + return legend.count + } + return 0 + } + + func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCellWithIdentifier("infoItemCell") as? RestoLegendItemTableViewCell + + cell?.item = legend?[indexPath.item] + + return cell! + } + + func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { + let item = legend?[indexPath.item] + + let size = CGSizeMake(tableView.frame.width - 40, CGFloat.max) + let paragraphStyle = NSParagraphStyle() + //paragraphStyle.lineBreakMode = NSLineBreakMode.ByWordWrapping + //paragraphStyle.alignment = NSTextAlignment.Left + + let attributes = [ + NSFontAttributeName: UIFont.systemFontOfSize(15), + NSParagraphStyleAttributeName: paragraphStyle + ] + + let mutableText = NSMutableAttributedString(string: (item?.value)!, attributes: attributes) + + let options = unsafeBitCast(NSStringDrawingOptions.UsesLineFragmentOrigin.rawValue | + NSStringDrawingOptions.UsesFontLeading.rawValue, + NSStringDrawingOptions.self) + + let labelSize: CGSize = mutableText.boundingRectWithSize(size, options: options, context: nil).size + + return labelSize.height + 5 + } +} + +class RestoLegendItemTableViewCell: UITableViewCell { + @IBOutlet weak var keyLabel: UILabel! + @IBOutlet weak var valueLabel: UILabel! + + var item: RestoLegendItem? { + didSet { + if let item = item { + keyLabel.text = item.key + valueLabel.text = item.value + } + } + } +} \ No newline at end of file diff --git a/iOS/Hydra/RestoMenuView.h b/iOS/Hydra/RestoMenuView.h deleted file mode 100644 index 312bfed8..00000000 --- a/iOS/Hydra/RestoMenuView.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// RestoMenuView.h -// Hydra -// -// Created by Yasser Deceukelier on 22/07/12. -// Copyright (c) 2012 Zeus WPI. All rights reserved. -// - -#import -#import "RestoMenu.h" - -@interface RestoMenuView : UIView - -@property (nonatomic, strong, readonly) NSDate *day; - -- (id)initWithFrame:(CGRect)frame; -- (void)configureWithDay:(NSDate *)day menu:(RestoMenu *)menu; - -@end diff --git a/iOS/Hydra/RestoMenuView.m b/iOS/Hydra/RestoMenuView.m deleted file mode 100644 index d9dd0831..00000000 --- a/iOS/Hydra/RestoMenuView.m +++ /dev/null @@ -1,259 +0,0 @@ -// -// RestoMenuView.m -// Hydra -// -// Created by Yasser Deceukelier on 22/07/12. -// Copyright (c) 2012 Zeus WPI. All rights reserved. -// - -#import "RestoMenuView.h" -#import "NSDate+Utilities.h" -#import "NSDateFormatter+AppLocale.h" - -@interface RestoMenuView () - -@property (nonatomic, strong) NSDate *day; -@property (nonatomic, strong) RestoMenu *menu; - -@property (nonatomic, unsafe_unretained) UIView *contentView; -@property (nonatomic, unsafe_unretained) UILabel *dateHeader; -@property (nonatomic, unsafe_unretained) UITableView *tableView; -@property (nonatomic, unsafe_unretained) UIImageView *closedView; -@property (nonatomic, unsafe_unretained) UIActivityIndicatorView *spinner; - -@property (nonatomic, strong) UIView *soupHeader; -@property (nonatomic, strong) UIView *meatHeader; -@property (nonatomic, strong) UIView *vegetableHeader; - -@end - -@implementation RestoMenuView - -#pragma mark - Constants - -#define kDateHeaderHeight 45 -#define kSectionHeaderHeight 48 -#define kRowHeight 22 -#define kCellLabelTag 101 - -#pragma mark - Properties and init - -- (id)initWithFrame:(CGRect)frame -{ - if (self = [super initWithFrame:frame]) { - [self createView]; - } - return self; -} - -- (void)configureWithDay:(NSDate *)day menu:(id)menu -{ - if (![self.day isEqual:day] || ![self.menu isEqual:menu]) { - self.day = day; - self.menu = (menu != [NSNull null]) ? menu : nil; - [self reloadData]; - } -} - -- (void)createView -{ - self.backgroundColor = [UIColor whiteColor]; - - CGRect headerFrame = CGRectMake(0, 0, self.frame.size.width, kDateHeaderHeight); - UIImageView *header = [[UIImageView alloc] initWithFrame:headerFrame]; - header.contentMode = UIViewContentModeScaleToFill; - header.image = [UIImage imageNamed:@"header-bg"]; - [self addSubview:header]; - - CGRect dateHeaderFrame = CGRectMake(0, 3, self.frame.size.width, kDateHeaderHeight - 3); - UILabel *dateHeader = [[UILabel alloc] initWithFrame:dateHeaderFrame]; - dateHeader.font = [UIFont boldSystemFontOfSize:19]; - dateHeader.textAlignment = NSTextAlignmentCenter; - dateHeader.textColor = [UIColor whiteColor]; - dateHeader.backgroundColor = [UIColor clearColor]; - dateHeader.shadowColor = [UIColor blackColor]; - dateHeader.shadowOffset = CGSizeMake(0, 2); - [header addSubview:dateHeader]; - self.dateHeader = dateHeader; - - CGRect tableFrame = CGRectMake(0, headerFrame.size.height, self.frame.size.width, - self.bounds.size.height - headerFrame.size.height - 3); - UITableView *tableView = [[UITableView alloc] initWithFrame:tableFrame style:UITableViewStylePlain]; - tableView.delegate = self; - tableView.dataSource = self; - tableView.bounces = NO; - tableView.rowHeight = kRowHeight; - tableView.separatorColor = [UIColor clearColor]; - tableView.allowsSelection = NO; - tableView.contentInset = UIEdgeInsetsMake(0, 0, 5, 0); - tableView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; - [self addSubview:tableView]; - self.tableView = tableView; - - UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; - spinner.center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds)); - spinner.autoresizingMask = UIViewAutoresizingFlexibleTopMargin - | UIViewAutoresizingFlexibleBottomMargin; - [self addSubview:spinner]; - self.spinner = spinner; - - CGRect closedFrame = CGRectMake(0, kDateHeaderHeight, self.frame.size.width, - self.frame.size.height - 2*kDateHeaderHeight); - UIImageView *closedView = [[UIImageView alloc] initWithFrame:closedFrame]; - closedView.image = [UIImage imageNamed:@"resto-closed.jpg"]; - closedView.contentMode = UIViewContentModeCenter; - closedView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin - | UIViewAutoresizingFlexibleBottomMargin; - [self addSubview:closedView]; - self.closedView = closedView; -} - -- (void)reloadData -{ - NSString *dateString; - if ([self.day isToday]) dateString = @"Vandaag"; - else if ([self.day isTomorrow]) dateString = @"Morgen"; - else { - // Create capitalized, formatted string - NSDateFormatter *formatter = [NSDateFormatter H_dateFormatterWithAppLocale]; - [formatter setDateFormat:@"EEEE d MMMM"]; - dateString = [formatter stringFromDate:self.day]; - dateString = [dateString stringByReplacingCharactersInRange:NSMakeRange(0, 1) - withString:[[dateString substringToIndex:1] capitalizedString]]; - } - self.dateHeader.text = dateString; - - self.spinner.hidden = (self.menu != nil); - if (!self.spinner.hidden) [self.spinner startAnimating]; - self.closedView.hidden = (self.menu == nil || self.menu.open); - - [self.tableView reloadData]; -} - -#pragma mark - Table view datasource - -- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView -{ - return (self.menu.open ? 4 : 0); -} - -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section -{ - switch (section) { - case 1: return 1; - case 2: return self.menu.meat.count; - case 3: return self.menu.vegetables.count; - default: return 0; - } -} - -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath -{ - UILabel *textLabel; - - static NSString *cellIdentifier = @"RestoMenuViewCell"; - UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; - if(!cell) { - cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 - reuseIdentifier:cellIdentifier]; - CGRect labelFrame = CGRectMake(10, 0, self.tableView.frame.size.width - 70, kRowHeight - 1); - textLabel = [[UILabel alloc] initWithFrame:labelFrame]; - textLabel.tag = kCellLabelTag; - textLabel.lineBreakMode = NSLineBreakByTruncatingMiddle; - [cell.contentView addSubview:textLabel]; - - cell.detailTextLabel.textColor = textLabel.textColor; - } - else { - textLabel = (UILabel *)[cell viewWithTag:kCellLabelTag]; - } - - textLabel.font = [UIFont systemFontOfSize:15]; - cell.detailTextLabel.font = textLabel.font; - - if(indexPath.section == 1) { - textLabel.text = self.menu.soup.name; - cell.detailTextLabel.text = self.menu.soup.price; - } - else if (indexPath.section == 2) { - RestoMenuItem *item = self.menu.meat[indexPath.row]; - - if(item.recommended) { - textLabel.font = [UIFont boldSystemFontOfSize:15]; - cell.detailTextLabel.font = textLabel.font; - } - - textLabel.text = item.name; - cell.detailTextLabel.text = item.price; - } - else { // section == 3 - textLabel.text = (self.menu.vegetables)[indexPath.row]; - } - - return cell; -} - -#pragma mark - Table view delegate - -// Using footers of the previous section instead of headers so they scroll -- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section { - return (section < 3) ? kSectionHeaderHeight : 0; -} - -- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section -{ - if(section == 0) { - if(!self.soupHeader) { - UIImage *soupImage = [UIImage imageNamed:@"icon-soup"]; - self.soupHeader = [self headerWithImage:soupImage andTitle:@"Soep"]; - } - return self.soupHeader; - } - else if(section == 1) { - if(!self.meatHeader) { - UIImage *meatImage = [UIImage imageNamed:@"icon-meal"]; - self.meatHeader = [self headerWithImage:meatImage andTitle:@"Vlees en veggie"]; - } - return self.meatHeader; - } - else if(section == 2) { - if(!self.vegetableHeader) { - UIImage *vegetableImage = [UIImage imageNamed:@"icon-vegetables"]; - self.vegetableHeader = [self headerWithImage:vegetableImage andTitle:@"Groenten"]; - } - return self.vegetableHeader; - } - else { - return nil; - } -} - -#pragma mark - Utility methods - -- (UIView *)headerWithImage:(UIImage *)image andTitle:(NSString *)title -{ - CGRect headerFrame = CGRectMake(0, 0, self.bounds.size.width, kSectionHeaderHeight); - UIView *header = [[UIView alloc] initWithFrame:headerFrame]; - header.backgroundColor = [UIColor whiteColor]; - - UIFont *font = [UIFont fontWithName:@"Futura-Medium" size:18]; - CGSize textSize = [title sizeWithAttributes:@{NSFontAttributeName: font}]; - CGFloat offsetX = roundf((self.bounds.size.width - textSize.width - 34) / 2); - - CGRect iconFrame = CGRectMake(offsetX - 20, 13, 30, 30); - UIImageView *iconView = [[UIImageView alloc] initWithFrame:iconFrame]; - iconView.image = image; - [header addSubview:iconView]; - - CGRect titleFrame = CGRectMake(offsetX + 17, 19, textSize.width, 22); - UILabel *titleLabel = [[UILabel alloc] initWithFrame:titleFrame]; - titleLabel.textAlignment = NSTextAlignmentCenter; - titleLabel.baselineAdjustment = UIBaselineAdjustmentAlignCenters; - titleLabel.font = font; - titleLabel.text = title; - [header addSubview:titleLabel]; - - return header; -} - -@end diff --git a/iOS/Hydra/RestoMenuViewController.swift b/iOS/Hydra/RestoMenuViewController.swift new file mode 100644 index 00000000..3d0ea174 --- /dev/null +++ b/iOS/Hydra/RestoMenuViewController.swift @@ -0,0 +1,231 @@ +// +// RestoMenuViewController.swift +// Hydra +// +// Created by Feliciaan De Palmenaer on 14/08/15. +// Copyright © 2015 Zeus WPI. All rights reserved. +// + +import UIKit + +class RestoMenuViewController: UIViewController { + @IBOutlet weak var collectionView: UICollectionView? + @IBOutlet weak var restoMenuHeader: RestoMenuHeaderView? + + var days: [NSDate] = [] + var menus: [RestoMenu?] = [] + var legend: [RestoLegendItem] = [] + + var currentIndex: Int = 1 + + override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) { + super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) + initialize() + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + initialize() + } + + func initialize() { + let center = NSNotificationCenter.defaultCenter() + center.addObserver(self, selector: "reloadMenu", name: RestoStoreDidReceiveMenuNotification, object: nil) + center.addObserver(self, selector: "reloadInfo", name: RestoStoreDidUpdateInfoNotification, object: nil) + center.addObserver(self, selector: "applicationDidBecomeActive:", name: UIApplicationDidBecomeActiveNotification, object: nil) + + days = calculateDays() + } + + deinit { + NSNotificationCenter.defaultCenter().removeObserver(self) + } + + override func viewDidLoad() { + super.viewDidLoad() + self.loadMenu() + self.legend = (RestoStore.sharedStore().legend as? [RestoLegendItem])! + + // update days and reloadData + self.restoMenuHeader?.updateDays() + //self.collectionView?.reloadData() // Uncomment when bug is fixed + //self.scrollToIndex(self.currentIndex, animated: false) + + // REMOVE ME IF THE BUG IS FIXED, THIS IS UGLY + NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: Selector("refreshDataTimer:"), userInfo: nil, repeats: false) + } + + override func viewDidAppear(animated: Bool) { + UIApplication.sharedApplication().setStatusBarStyle(.LightContent, animated: animated) + } + + func refreshDataTimer(timer: NSTimer){ // REMOVE ME WHEN THE BUG IS FIXED + self.collectionView?.reloadData() + self.scrollToIndex(self.currentIndex, animated: false) + } + + override func viewWillAppear(animated: Bool) { + super.viewWillAppear(animated) + + self.days = calculateDays() + self.restoMenuHeader?.updateDays() + //do not hide if in moreController + if self.parentViewController != self.tabBarController?.moreNavigationController { + if UIApplication.sharedApplication().statusBarStyle != .LightContent { + UIApplication.sharedApplication().setStatusBarStyle(.LightContent, animated: false) + } + self.navigationController?.navigationBarHidden = true + } + // scroll to today + self.scrollToIndex(currentIndex, animated: false) + } + + override func viewWillDisappear(animated: Bool) { + super.viewWillDisappear(animated) + + self.navigationController?.navigationBarHidden = false + UIApplication.sharedApplication().setStatusBarStyle(.Default, animated: false) + } + + override func didRotateFromInterfaceOrientation(fromInterfaceOrientation: UIInterfaceOrientation) { + // this is called when changing layout :) + self.collectionView?.collectionViewLayout.invalidateLayout() + self.scrollToIndex(currentIndex, animated: true) + } + + func loadMenu() { + // New menus are available + let store = RestoStore.sharedStore() + var menus = [RestoMenu?]() + for day in days { + let menu = store.menuForDay(day) as RestoMenu? + menus.append(menu) + } + self.menus = menus + } + + func reloadMenu() { + debugPrint("Reloading menu") + self.loadMenu() + self.collectionView?.reloadData() + } + + func reloadInfo() { + // New info is available + debugPrint("Reloading info") + self.legend = (RestoStore.sharedStore().legend as? [RestoLegendItem])! + self.collectionView?.reloadData() + } + + func applicationDidBecomeActive(notification: NSNotification) { + let firstDay = self.days[0] + self.days = self.calculateDays() + + if !firstDay.isEqualToDateIgnoringTime(self.days[0]) { + self.reloadMenu() + } + } + + func calculateDays() -> [NSDate] { + // Find the next x days to display + var day = NSDate() + var days = [NSDate]() + while (days.count < 5) { + if day.isTypicallyWorkday() { + days.append(day) + } + day = day.dateByAddingDays(1) + } + return days + } + + // MARK: - Headerview actions + + @IBAction func mapViewPressed(gestureRecognizer: UITapGestureRecognizer) { + debugPrint("Map view pressed!") + if let navigationController = self.navigationController { + navigationController.pushViewController(RestoMapController(), animated: true) + } else { + fatalError("An navigationcontroller should be present") + } + } +} + +// MARK: - Collection view data source & delegate +extension RestoMenuViewController: UICollectionViewDataSource, UICollectionViewDelegate { + func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return self.days.count + 1 + } + + func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { + + switch indexPath.row { + case 0: // info cell + let cell = collectionView.dequeueReusableCellWithReuseIdentifier("infoCell", forIndexPath: indexPath) as! RestoMenuInfoCollectionViewCell + + cell.legend = self.legend + return cell + case 1...self.days.count: + let menu = self.menus[indexPath.row-1] + if menu!.open { + let cell = collectionView.dequeueReusableCellWithReuseIdentifier("restoMenuOpenCell", forIndexPath: indexPath) as! RestoMenuCollectionCell + + cell.restoMenu = menu + return cell + } + + return collectionView.dequeueReusableCellWithReuseIdentifier("restoMenuClosedCell", forIndexPath: indexPath) + default: + debugPrint("Shouldn't be here") + return collectionView.dequeueReusableCellWithReuseIdentifier("infoCell", forIndexPath: indexPath) + } + } + + func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize { + return CGSizeMake(collectionView.frame.size.width, collectionView.frame.size.height) // cells always fill the whole screen + } +} + +extension RestoMenuViewController: UIScrollViewDelegate { + func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer) { + // Stop if velocity is 0 + if velocity.x == 0{ + return + } + + let pageWidth = Float(self.collectionView!.frame.size.width) + let currentOffset = Float(scrollView.contentOffset.x) + let targetOffset = Float(targetContentOffset.memory.x) + pageWidth/2 + + let index = max(min(Int(round(targetOffset / pageWidth))-1, (self.collectionView?.numberOfItemsInSection(0))!-1),0) + + targetContentOffset.memory = CGPointMake(CGFloat(currentOffset), 0) + + self.scrollToIndex(index, animated: true) + } + + func scrollViewDidEndDragging(scrollView: UIScrollView, willDecelerate decelerate: Bool) { + if decelerate { + let index = Int((scrollView.contentOffset.x + self.collectionView!.frame.size.width/2) / self.collectionView!.frame.size.width) + scrollToIndex(index) + } + } +} + +// MARK: - Header view actions +extension RestoMenuViewController { + func scrollToIndex(index: Int, animated: Bool = true) { + self.collectionView?.scrollToItemAtIndexPath(NSIndexPath(forRow: index, inSection: 0), atScrollPosition: UICollectionViewScrollPosition.CenteredHorizontally, animated: animated) + self.restoMenuHeader?.selectedIndex(index) + currentIndex = index + } + + func scrollToDate(date: NSDate) { + for (index, day) in days.enumerate() { + if day.dateAtStartOfDay().isEqualToDate(date.dateAtStartOfDay()) { + self.scrollToIndex(index+1) + return + } + } + } +} \ No newline at end of file diff --git a/iOS/Hydra/RestoStore.swift b/iOS/Hydra/RestoStore.swift new file mode 100644 index 00000000..d0837544 --- /dev/null +++ b/iOS/Hydra/RestoStore.swift @@ -0,0 +1,37 @@ +// +// RestoStore.swift +// Hydra +// +// Created by Feliciaan De Palmenaer on 20/11/2015. +// Copyright © 2015 Zeus WPI. All rights reserved. +// + +import Foundation + +extension RestoStore: FeedItemProtocol { + func feedItems() -> [FeedItem] { + var day = NSDate() + if day.hour > 20 { + day = day.dateByAddingDays(1) + } + var feedItems = [FeedItem]() + + // Find the next x days to display + while (feedItems.count < 5) { //TODO: replace with var + if day.isTypicallyWorkday() { + var menu = menuForDay(day) + + if (menu == nil) { + menu = RestoMenu() + menu.open = false + menu.day = day + } + + feedItems.append(FeedItem(itemType: .RestoItem, object: menu, priority: 1000 - 100*feedItems.count)) + } + day = day.dateByAddingDays(1) + } + + return feedItems + } +} \ No newline at end of file diff --git a/iOS/Hydra/SchamperDetailViewController.m b/iOS/Hydra/SchamperDetailViewController.m index 6cbd7403..6d764f5a 100644 --- a/iOS/Hydra/SchamperDetailViewController.m +++ b/iOS/Hydra/SchamperDetailViewController.m @@ -35,11 +35,7 @@ - (void)viewDidLoad { [super viewDidLoad]; -#ifdef __IPHONE_7_0 - if ([self respondsToSelector:@selector(setAutomaticallyAdjustsScrollViewInsets:)]) { - self.automaticallyAdjustsScrollViewInsets = NO; - } -#endif + self.automaticallyAdjustsScrollViewInsets = NO; // Set tracked name self.trackedViewName = [@"Schamper > " stringByAppendingString:self.article.title]; @@ -63,7 +59,7 @@ - (void)viewDidLoad UIScrollView *scrollView = self.webView.scrollView; scrollView.delegate = self; - CGFloat scrollOffset = IOS_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0") ? 64 : 44; + CGFloat scrollOffset = 64; scrollView.contentInset = UIEdgeInsetsMake(scrollOffset, 0, 0, 0); scrollView.scrollIndicatorInsets = scrollView.contentInset; @@ -160,13 +156,8 @@ - (void)setNavigationBarHidden:(BOOL)hide // This will cause a recursive call in this method [self.navigationController setNavigationBarHidden:hide animated:YES]; - if (IOS_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0")) { - [[UIApplication sharedApplication] setStatusBarHidden:hide withAnimation:UIStatusBarAnimationSlide]; - scrollView.contentInset = UIEdgeInsetsMake(hide ? 0 : 64, 0, 0, 0); - } - else { - scrollView.contentInset = UIEdgeInsetsMake(hide ? 0 : 44, 0, 0, 0); - } + [[UIApplication sharedApplication] setStatusBarHidden:hide withAnimation:UIStatusBarAnimationSlide]; + scrollView.contentInset = UIEdgeInsetsMake(hide ? 0 : 64, 0, 0, 0); scrollView.scrollIndicatorInsets = scrollView.contentInset; self.animationActive = false; diff --git a/iOS/Hydra/SchamperStore.swift b/iOS/Hydra/SchamperStore.swift new file mode 100644 index 00000000..1ac703b9 --- /dev/null +++ b/iOS/Hydra/SchamperStore.swift @@ -0,0 +1,30 @@ +// +// SchamperStore.swift +// Hydra +// +// Created by Feliciaan De Palmenaer on 20/11/2015. +// Copyright © 2015 Zeus WPI. All rights reserved. +// + +import Foundation + +extension SchamperStore: FeedItemProtocol { + func feedItems() -> [FeedItem] { + var feedItems = [FeedItem]() + if let articles = articles as? [SchamperArticle] { + for article in articles { //TODO: test articles and sort them + let daysOld = article.date.daysBeforeDate(NSDate()) + var priority = 999 + if !article.read { + priority = priority - daysOld*40 + } else { + priority = priority - daysOld*150 + } + if priority > 0 { + feedItems.append(FeedItem(itemType: .SchamperNewsItem, object: article, priority: priority)) + } + } + } + return feedItems + } +} \ No newline at end of file diff --git a/iOS/Hydra/SchamperViewController.m b/iOS/Hydra/SchamperViewController.m index afbbae8e..c967a661 100644 --- a/iOS/Hydra/SchamperViewController.m +++ b/iOS/Hydra/SchamperViewController.m @@ -77,6 +77,14 @@ - (void)viewDidAppear:(BOOL)animated } } +- (void)viewWillAppear:(BOOL)animated +{ + [super viewWillAppear:animated]; + + // Reload data to show just read articles + [self.tableView reloadData]; +} + - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; @@ -110,10 +118,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N reuseIdentifier:CellIdentifier]; cell.detailTextLabel.textColor = [UIColor colorWithWhite:0.3 alpha:1]; - // iOS7 - if ([cell respondsToSelector:@selector(setSeparatorInset:)]) { - cell.separatorInset = UIEdgeInsetsZero; - } + cell.separatorInset = UIEdgeInsetsZero; } SchamperArticle *article = self.articles[indexPath.row]; diff --git a/iOS/Hydra/UrgentPlayer.m b/iOS/Hydra/UrgentPlayer.m index 7416ba63..209e86aa 100644 --- a/iOS/Hydra/UrgentPlayer.m +++ b/iOS/Hydra/UrgentPlayer.m @@ -20,6 +20,8 @@ #define kShowResourcePath @"http://urgent.fm/nowplaying/program.php" #define kStreamResourcePath @"http://urgent.stream.flumotion.com/urgent/high.mp3.m3u" +#define UrgentNowPlayingEnabled 0 + NSString *const UrgentPlayerDidUpdateSongNotification = @"UrgentPlayerDidUpdateSongNotification"; NSString *const UrgentPlayerDidUpdateShowNotification = @@ -163,6 +165,7 @@ - (void)audioRouteChanged:(NSNotification *)notification - (void)playerStateChanged { +#if UrgentNowPlayingEnabled // Update timers if ([self isPlaying]) { // The state of updateSongTimer and updateShowTimer should always be equal @@ -176,10 +179,11 @@ - (void)playerStateChanged self.currentSong = nil; self.previousSong = nil; } - [self updateNowPlaying]; +#endif } +#if UrgentNowPlayingEnabled - (void)updateNowPlaying { MPNowPlayingInfoCenter *center = [MPNowPlayingInfoCenter defaultCenter]; @@ -293,6 +297,7 @@ - (void)showUpdateTimerFired:(NSTimer *)sender } }]; } +#endif - (void)fetchString:(NSString *)resource withCompletion:(void (^)(NSString *result))completion { diff --git a/iOS/Hydra/UrgentViewController.m b/iOS/Hydra/UrgentViewController.m index 8c425a76..4b1f22e6 100644 --- a/iOS/Hydra/UrgentViewController.m +++ b/iOS/Hydra/UrgentViewController.m @@ -46,11 +46,7 @@ - (void)viewDidLoad { [super viewDidLoad]; -#ifdef __IPHONE_7_0 - if ([self respondsToSelector:@selector(setEdgesForExtendedLayout:)]) { - self.edgesForExtendedLayout = UIRectEdgeNone; - } -#endif + self.edgesForExtendedLayout = UIRectEdgeNone; // Set state for highlighted|selected UIImage *selectedImage = [self.playButton imageForState:UIControlStateSelected]; @@ -82,6 +78,9 @@ - (void)viewDidLoad - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; + if (self.parentViewController != self.tabBarController.moreNavigationController) { + [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent]; + } GAI_Track(@"Urgent"); } @@ -96,6 +95,11 @@ - (void)viewWillAppear:(BOOL)animated [MarqueeLabel controllerViewAppearing:self]; } +- (void)viewWillDisappear:(BOOL)animated +{ + [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault]; +} + #pragma mark - Buttons - (void)playButtonTapped:(id)sender diff --git a/iOS/Hydra/nl.lproj/DashboardViewController.xib b/iOS/Hydra/nl.lproj/DashboardViewController.xib deleted file mode 100644 index a2cf4f44..00000000 --- a/iOS/Hydra/nl.lproj/DashboardViewController.xib +++ /dev/null @@ -1,684 +0,0 @@ - - - - 1280 - 12F37 - 4510 - 1187.39 - 626.00 - - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - 3742 - - - IBProxyObject - IBUIButton - IBUIImageView - IBUIView - - - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - - PluginDependencyRecalculationVersion - - - - - IBFilesOwner - IBCocoaTouchFramework - - - IBFirstResponder - IBCocoaTouchFramework - - - - 274 - - - - 290 - - - - 274 - {320, 114} - - - _NS:9 - NO - IBCocoaTouchFramework - - NSImage - header-bg.png - - - - - 269 - {{93, 37}, {134, 50}} - - - _NS:9 - 1 - NO - IBCocoaTouchFramework - - NSImage - hydra-logo.png - - - - - -2147483380 - {{20, 71}, {34, 34}} - - - _NS:9 - NO - - Feedback - - IBCocoaTouchFramework - 0 - 0 - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - - - 3 - MQA - - - 3 - MC41AA - - - NSImage - button-feedback.png - - - 2 - 15 - - - HelveticaNeue-Bold - 15 - 16 - - - - - 265 - {{266, 71}, {34, 34}} - - - _NS:9 - NO - IBCocoaTouchFramework - 0 - 0 - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - - - - - NSImage - button-settings.png - - - - - - {320, 114} - - - _NS:9 - - 3 - MQA - - 2 - - - IBCocoaTouchFramework - - - - 300 - {{17, 156}, {137, 100}} - - - _NS:9 - NO - 1 - - Nieuws - - - IBCocoaTouchFramework - 0 - 0 - - 1 - MC4xOTYwNzg0MzE0IDAuMzA5ODAzOTIxNiAwLjUyMTU2ODYyNzUAA - - - - - NSImage - button-news-active.png - - - NSImage - button-news.png - - - 2 - 2 - - - HelveticaNeue-Bold - 18 - 16 - - - - - 297 - {{166, 156}, {137, 100}} - - - _NS:9 - NO - 2 - - Activiteiten - - - IBCocoaTouchFramework - 0 - 0 - - 1 - MC4xOTYwNzg0MzE0IDAuMzA5ODAzOTIxNiAwLjUyMTU2ODYyNzUAA - - - - - NSImage - button-activities-active.png - - - NSImage - button-activities.png - - - - - - - 300 - {{17, 298}, {137, 100}} - - - _NS:9 - NO - 3 - - Info - - - IBCocoaTouchFramework - 0 - 0 - - 1 - MC4xOTYwNzg0MzE0IDAuMzA5ODAzOTIxNiAwLjUyMTU2ODYyNzUAA - - - - - NSImage - button-info-active.png - - - NSImage - button-info.png - - - - - - - 297 - {{166, 298}, {137, 100}} - - - _NS:9 - NO - 4 - - Resto Menu - - - IBCocoaTouchFramework - 0 - 0 - - 1 - MC4xOTYwNzg0MzE0IDAuMzA5ODAzOTIxNiAwLjUyMTU2ODYyNzUAA - - - - - NSImage - button-resto-active.png - - - NSImage - button-resto.png - - - - - - - 300 - {{17, 440}, {137, 100}} - - - _NS:9 - NO - 5 - - Urgent.fm - - - IBCocoaTouchFramework - 0 - 0 - - 1 - MC4xOTYwNzg0MzE0IDAuMzA5ODAzOTIxNiAwLjUyMTU2ODYyNzUAA - - - - - NSImage - button-urgent-active.png - - - NSImage - button-urgent.png - - - - - - - 297 - {{166, 440}, {137, 100}} - - _NS:9 - NO - 6 - - Schamper - - - IBCocoaTouchFramework - 0 - 0 - - 1 - MC4xOTYwNzg0MzE0IDAuMzA5ODAzOTIxNiAwLjUyMTU2ODYyNzUAA - - - - - NSImage - button-schamper-active.png - - - NSImage - button-schamper.png - - - - - - {320, 568} - - - 1 - MC44MDY3NjU5NzM2IDAuODM5OTE4OTExNSAwLjg3NzkxOTkxMjMAA - - IBCocoaTouchFramework - - - - NO - - - - view - - - - 3 - - - - activitiesButton - - - - 44 - - - - infoButton - - - - 46 - - - - newsButton - - - - 47 - - - - restoButton - - - - 48 - - - - schamperButton - - - - 49 - - - - urgentButton - - - - 56 - - - - feedbackButton - - - - 57 - - - - preferencesButton - - - - 60 - - - - showNews: - - - 7 - - 19 - - - - showActivities: - - - 7 - - 32 - - - - showInfo: - - - 7 - - 33 - - - - showUrgent: - - - 7 - - 54 - - - - showResto: - - - 7 - - 34 - - - - showSchamper: - - - 7 - - 37 - - - - showFeedbackView: - - - 7 - - 53 - - - - showPreferences: - - - 7 - - 61 - - - - - - 0 - - - - - - 1 - - - - - - - - - - - - - - -1 - - - File's Owner - - - -2 - - - - - 18 - - - Button - Nieuws - - - 22 - - - Button - Activiteiten - - - 24 - - - Button - Info - - - 26 - - - Button - GSR - - - 28 - - - Button - Resto - - - 30 - - - Button - Schamper - - - pbl-6n-bXm - - - - - - - - - - - 5 - - - - - 6 - - - - - 52 - - - - - 58 - - - - - - - DashboardViewController - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - UIResponder - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - BadgedButton - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - BadgedButton - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - BadgedButton - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - BadgedButton - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - BadgedButton - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - BadgedButton - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - - - - - - - 0 - IBCocoaTouchFramework - YES - - com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS - - - - com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 - - - YES - 3 - - {137, 100} - {137, 100} - {34, 34} - {137, 100} - {137, 100} - {137, 100} - {137, 100} - {137, 100} - {137, 100} - {137, 100} - {137, 100} - {34, 34} - {137, 100} - {137, 100} - {1, 94} - {134, 50} - - 3742 - - diff --git a/iOS/Hydra/nl.lproj/UrgentViewController.xib b/iOS/Hydra/nl.lproj/UrgentViewController.xib index 4f7df0c7..f392622e 100644 --- a/iOS/Hydra/nl.lproj/UrgentViewController.xib +++ b/iOS/Hydra/nl.lproj/UrgentViewController.xib @@ -1,9 +1,9 @@ - + - + @@ -18,17 +18,19 @@ - + + - + +