diff --git a/WOKA.xcodeproj/project.pbxproj b/WOKA.xcodeproj/project.pbxproj index f7c431f..9a51737 100644 --- a/WOKA.xcodeproj/project.pbxproj +++ b/WOKA.xcodeproj/project.pbxproj @@ -150,6 +150,10 @@ 9C56E8462BDBEE6400E4CA14 /* EmailVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C56E8452BDBEE6400E4CA14 /* EmailVC.swift */; }; 9C56E8482BDBEFAB00E4CA14 /* AssetColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C56E8472BDBEFAB00E4CA14 /* AssetColor.swift */; }; 9C56E84B2BDBF03F00E4CA14 /* AuthenticationSB.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9C56E84A2BDBF03F00E4CA14 /* AuthenticationSB.storyboard */; }; + 9C7939132C0EFCAE00F5D6E6 /* FaqVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C7939122C0EFCAE00F5D6E6 /* FaqVM.swift */; }; + 9C7939152C0F23AA00F5D6E6 /* NsNotificationExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C7939142C0F23AA00F5D6E6 /* NsNotificationExtension.swift */; }; + 9C7939172C0F23E900F5D6E6 /* LinkTypeEnum.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C7939162C0F23E900F5D6E6 /* LinkTypeEnum.swift */; }; + 9C7939192C0F345000F5D6E6 /* ContactSupportVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C7939182C0F345000F5D6E6 /* ContactSupportVC.swift */; }; 9C9BEEC72BEE1BBF004ECC2F /* CollectionViewCenteredFlowLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C9BEEC62BEE1BBF004ECC2F /* CollectionViewCenteredFlowLayout.swift */; }; 9CBCB29B2BE4D614007D7934 /* LoginVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CBCB29A2BE4D614007D7934 /* LoginVC.swift */; }; 9CBCB29D2BE4D6BB007D7934 /* LoginVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CBCB29C2BE4D6BB007D7934 /* LoginVM.swift */; }; @@ -159,6 +163,13 @@ 9CBCB2A52BE50D49007D7934 /* NewPasswordVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CBCB2A42BE50D49007D7934 /* NewPasswordVC.swift */; }; 9CBCB2A82BE5105A007D7934 /* Home.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9CBCB2A72BE5105A007D7934 /* Home.storyboard */; }; 9CBCB2AA2BE51A52007D7934 /* ThemeOneVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CBCB2A92BE51A52007D7934 /* ThemeOneVC.swift */; }; + 9CBE1B3F2C0F37B300CA6E61 /* DPDConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CBE1B322C0F37B200CA6E61 /* DPDConstants.swift */; }; + 9CBE1B402C0F37B300CA6E61 /* DPDKeyboardListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CBE1B332C0F37B200CA6E61 /* DPDKeyboardListener.swift */; }; + 9CBE1B412C0F37B300CA6E61 /* DPDUIView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CBE1B342C0F37B200CA6E61 /* DPDUIView+Extension.swift */; }; + 9CBE1B422C0F37B300CA6E61 /* DropDownCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9CBE1B362C0F37B200CA6E61 /* DropDownCell.xib */; }; + 9CBE1B432C0F37B300CA6E61 /* DropDown.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CBE1B382C0F37B200CA6E61 /* DropDown.swift */; }; + 9CBE1B442C0F37B300CA6E61 /* DropDown+Appearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CBE1B392C0F37B200CA6E61 /* DropDown+Appearance.swift */; }; + 9CBE1B452C0F37B300CA6E61 /* DropDownCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CBE1B3A2C0F37B200CA6E61 /* DropDownCell.swift */; }; 9CDC343C2BDBBC6B00093089 /* SelectAgeVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CDC343B2BDBBC6B00093089 /* SelectAgeVC.swift */; }; 9CDCE1452BDB9B9A003FEF11 /* OnBoardMainSound.m4a in Resources */ = {isa = PBXBuildFile; fileRef = 9CDCE1442BDB9B9A003FEF11 /* OnBoardMainSound.m4a */; }; /* End PBXBuildFile section */ @@ -333,6 +344,10 @@ 9C56E8452BDBEE6400E4CA14 /* EmailVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmailVC.swift; sourceTree = ""; }; 9C56E8472BDBEFAB00E4CA14 /* AssetColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssetColor.swift; sourceTree = ""; }; 9C56E8492BDBF03F00E4CA14 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/AuthenticationSB.storyboard; sourceTree = ""; }; + 9C7939122C0EFCAE00F5D6E6 /* FaqVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FaqVM.swift; sourceTree = ""; }; + 9C7939142C0F23AA00F5D6E6 /* NsNotificationExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NsNotificationExtension.swift; sourceTree = ""; }; + 9C7939162C0F23E900F5D6E6 /* LinkTypeEnum.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkTypeEnum.swift; sourceTree = ""; }; + 9C7939182C0F345000F5D6E6 /* ContactSupportVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactSupportVC.swift; sourceTree = ""; }; 9C9BEEC62BEE1BBF004ECC2F /* CollectionViewCenteredFlowLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionViewCenteredFlowLayout.swift; sourceTree = ""; }; 9CBCB29A2BE4D614007D7934 /* LoginVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginVC.swift; sourceTree = ""; }; 9CBCB29C2BE4D6BB007D7934 /* LoginVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginVM.swift; sourceTree = ""; }; @@ -342,6 +357,14 @@ 9CBCB2A42BE50D49007D7934 /* NewPasswordVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewPasswordVC.swift; sourceTree = ""; }; 9CBCB2A72BE5105A007D7934 /* Home.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Home.storyboard; sourceTree = ""; }; 9CBCB2A92BE51A52007D7934 /* ThemeOneVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeOneVC.swift; sourceTree = ""; }; + 9CBE1B322C0F37B200CA6E61 /* DPDConstants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DPDConstants.swift; sourceTree = ""; }; + 9CBE1B332C0F37B200CA6E61 /* DPDKeyboardListener.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DPDKeyboardListener.swift; sourceTree = ""; }; + 9CBE1B342C0F37B200CA6E61 /* DPDUIView+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DPDUIView+Extension.swift"; sourceTree = ""; }; + 9CBE1B362C0F37B200CA6E61 /* DropDownCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = DropDownCell.xib; sourceTree = ""; }; + 9CBE1B382C0F37B200CA6E61 /* DropDown.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DropDown.swift; sourceTree = ""; }; + 9CBE1B392C0F37B200CA6E61 /* DropDown+Appearance.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DropDown+Appearance.swift"; sourceTree = ""; }; + 9CBE1B3A2C0F37B200CA6E61 /* DropDownCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DropDownCell.swift; sourceTree = ""; }; + 9CBE1B3C2C0F37B200CA6E61 /* DropDown.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DropDown.h; sourceTree = ""; }; 9CDC343B2BDBBC6B00093089 /* SelectAgeVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectAgeVC.swift; sourceTree = ""; }; 9CDCE1412BDB94BA003FEF11 /* hi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hi; path = hi.lproj/Main.strings; sourceTree = ""; }; 9CDCE1422BDB94BD003FEF11 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Main.strings; sourceTree = ""; }; @@ -426,9 +449,9 @@ 522A93142C0DE8770098FE49 /* SideBarNav */ = { isa = PBXGroup; children = ( - 522A93182C0DE8A50098FE49 /* View */, 522A93172C0DE8A20098FE49 /* Model */, - 522A93162C0DE89F0098FE49 /* ViewModel */, + 522A93182C0DE8A50098FE49 /* View */, + 9C7939112C0EFCA000F5D6E6 /* ViewModel */, 522A93152C0DE8960098FE49 /* Controller */, 522A93192C0DE8CC0098FE49 /* SideBarNav.storyboard */, ); @@ -440,17 +463,11 @@ children = ( 522A931B2C0DE9150098FE49 /* AboutUsVc.swift */, 52BC3BE12C0E02EE002FACA6 /* FaqVC.swift */, + 9C7939182C0F345000F5D6E6 /* ContactSupportVC.swift */, ); path = Controller; sourceTree = ""; }; - 522A93162C0DE89F0098FE49 /* ViewModel */ = { - isa = PBXGroup; - children = ( - ); - path = ViewModel; - sourceTree = ""; - }; 522A93172C0DE8A20098FE49 /* Model */ = { isa = PBXGroup; children = ( @@ -493,11 +510,9 @@ 523ED25C2BDA2BC700CFED02 /* WOKA */ = { isa = PBXGroup; children = ( - 522A93142C0DE8770098FE49 /* SideBarNav */, 523ED26B2BDA2BC900CFED02 /* Info.plist */, 9C9BEEC62BEE1BBF004ECC2F /* CollectionViewCenteredFlowLayout.swift */, 5259541E2BE8E93500191286 /* Config.xcconfig */, - 523ED2682BDA2BC900CFED02 /* LaunchScreen.storyboard */, 5259542C2BEA392A00191286 /* Alerts */, 523ED2932BDA3D0100CFED02 /* Assets */, 9C56E83E2BDBE4FB00E4CA14 /* Authentication */, @@ -505,10 +520,12 @@ 52C6E01F2BE3ADD800E22D59 /* Default Enum */, 52C8B0512BDA4B51003B51D0 /* Helpers */, 9CBCB2A62BE5104F007D7934 /* Home */, + 523ED2682BDA2BC900CFED02 /* LaunchScreen.storyboard */, 9C27E15E2BDB6E4F00EC1DA9 /* Localized Module */, 9C27E1612BDB6F0F00EC1DA9 /* Main */, 525954152BE8CAC900191286 /* Network Adapter */, 523ED28E2BDA372C00CFED02 /* OnBoarding Module */, + 522A93142C0DE8770098FE49 /* SideBarNav */, 525327D72BFCC30400F64283 /* TabBar & SideMenu */, 9C535DC82C00C34000DA6DCD /* Theme */, ); @@ -671,6 +688,7 @@ 524C422D2C048C620016A11C /* ViewModel */, 524C422C2C048C5E0016A11C /* Controller */, 522242582BFC73E40085C632 /* SideMenu */, + 9C7939162C0F23E900F5D6E6 /* LinkTypeEnum.swift */, ); path = "TabBar & SideMenu"; sourceTree = ""; @@ -739,6 +757,7 @@ 52C8B0512BDA4B51003B51D0 /* Helpers */ = { isa = PBXGroup; children = ( + 9CBE1B3E2C0F37B200CA6E61 /* DropDown */, 9C535DB62C0089A700DA6DCD /* Animation */, 525953D22BE8B2CD00191286 /* UIApplication */, 525953CD2BE8B28100191286 /* ActivityToast&Indicator */, @@ -755,6 +774,7 @@ 5259545D2BEBBA1A00191286 /* LoadingIndicatorImageView.swift */, 52A3F6AC2BECC0340000BB0B /* TypeAlias.swift */, 5219C2C12C086D9B00A1DF4D /* DataTypeConversion.swift */, + 9C7939142C0F23AA00F5D6E6 /* NsNotificationExtension.swift */, ); path = Helpers; sourceTree = ""; @@ -971,6 +991,14 @@ path = Controller; sourceTree = ""; }; + 9C7939112C0EFCA000F5D6E6 /* ViewModel */ = { + isa = PBXGroup; + children = ( + 9C7939122C0EFCAE00F5D6E6 /* FaqVM.swift */, + ); + path = ViewModel; + sourceTree = ""; + }; 9CBCB2A62BE5104F007D7934 /* Home */ = { isa = PBXGroup; children = ( @@ -983,6 +1011,45 @@ path = Home; sourceTree = ""; }; + 9CBE1B352C0F37B200CA6E61 /* helpers */ = { + isa = PBXGroup; + children = ( + 9CBE1B322C0F37B200CA6E61 /* DPDConstants.swift */, + 9CBE1B332C0F37B200CA6E61 /* DPDKeyboardListener.swift */, + 9CBE1B342C0F37B200CA6E61 /* DPDUIView+Extension.swift */, + ); + path = helpers; + sourceTree = ""; + }; + 9CBE1B372C0F37B200CA6E61 /* resources */ = { + isa = PBXGroup; + children = ( + 9CBE1B362C0F37B200CA6E61 /* DropDownCell.xib */, + ); + path = resources; + sourceTree = ""; + }; + 9CBE1B3B2C0F37B200CA6E61 /* src */ = { + isa = PBXGroup; + children = ( + 9CBE1B382C0F37B200CA6E61 /* DropDown.swift */, + 9CBE1B392C0F37B200CA6E61 /* DropDown+Appearance.swift */, + 9CBE1B3A2C0F37B200CA6E61 /* DropDownCell.swift */, + ); + path = src; + sourceTree = ""; + }; + 9CBE1B3E2C0F37B200CA6E61 /* DropDown */ = { + isa = PBXGroup; + children = ( + 9CBE1B352C0F37B200CA6E61 /* helpers */, + 9CBE1B372C0F37B200CA6E61 /* resources */, + 9CBE1B3B2C0F37B200CA6E61 /* src */, + 9CBE1B3C2C0F37B200CA6E61 /* DropDown.h */, + ); + path = DropDown; + sourceTree = ""; + }; 9CDCE1432BDB9B64003FEF11 /* Sounds */ = { isa = PBXGroup; children = ( @@ -1125,6 +1192,7 @@ 525954392BEB4B3B00191286 /* Exo2-ExtraBold.ttf in Resources */, 5259543A2BEB4B3B00191286 /* Exo2-SemiBold.ttf in Resources */, 9C535DC52C00BF2400DA6DCD /* HomeExploreCell.xib in Resources */, + 9CBE1B422C0F37B300CA6E61 /* DropDownCell.xib in Resources */, 52A3F6A92BECBF2A0000BB0B /* LinkedChildCell.xib in Resources */, 523ED2652BDA2BC700CFED02 /* Base in Resources */, 52BC3BE62C0E0326002FACA6 /* FaqCell.xib in Resources */, @@ -1202,6 +1270,7 @@ 5272FCE32BDFDB05000ECB1D /* UserDetailsRegisterVC.swift in Sources */, 525954102BE8B72900191286 /* FontCustom.swift in Sources */, 5202AAFE2BDF90590043B7BD /* TextFieldImage.swift in Sources */, + 9C7939152C0F23AA00F5D6E6 /* NsNotificationExtension.swift in Sources */, 52FDDAB52BF34DC300E037C1 /* YesNoAlertVC.swift in Sources */, 52C6E0232BE3B3E300E22D59 /* SelectAvatarVC.swift in Sources */, 529B0DD62C070C0F00CFC54B /* GuestDataDM.swift in Sources */, @@ -1220,6 +1289,7 @@ 5259541D2BE8D94400191286 /* QueueHelper.swift in Sources */, 525954232BE8F00400191286 /* BaseResponseModel.swift in Sources */, 9C27E1692BDB76F200EC1DA9 /* OnBoardVM.swift in Sources */, + 9C7939172C0F23E900F5D6E6 /* LinkTypeEnum.swift in Sources */, 523ED2622BDA2BC700CFED02 /* SplashVC.swift in Sources */, 9CDC343C2BDBBC6B00093089 /* SelectAgeVC.swift in Sources */, 525327D02BFCBC4A00F64283 /* ExploreWokaVC.swift in Sources */, @@ -1233,6 +1303,7 @@ 525954192BE8CC3400191286 /* ConstantString.swift in Sources */, 52D774EB2BDFC0BF001D87DE /* OTPVC.swift in Sources */, 9C27E16F2BDB866500EC1DA9 /* CellIdentifier.swift in Sources */, + 9CBE1B412C0F37B300CA6E61 /* DPDUIView+Extension.swift in Sources */, 9C27E1632BDB6F1900EC1DA9 /* AuthFunc.swift in Sources */, 9C0A85412BEE35670093783D /* ResetPassUserNameVM.swift in Sources */, 52C6E0292BE3B52500E22D59 /* SelectAvatarVM.swift in Sources */, @@ -1242,11 +1313,13 @@ 525954142BE8C87300191286 /* ExtensionVCToastAlert.swift in Sources */, 52B8D4D92C04A25E00ED65F3 /* UIViewController+Container.swift in Sources */, 523ED25E2BDA2BC700CFED02 /* AppDelegate.swift in Sources */, + 9C7939132C0EFCAE00F5D6E6 /* FaqVM.swift in Sources */, 52D774ED2BDFC13F001D87DE /* OTPVM.swift in Sources */, 525327D62BFCC23600F64283 /* SideMenuVM.swift in Sources */, 9CBCB2A32BE50C95007D7934 /* ResetPassUserNameVC.swift in Sources */, 52A3F6A52BECBA8D0000BB0B /* LinkedChildDM.swift in Sources */, 52B8D4DE2C04A25E00ED65F3 /* SideMenuController.swift in Sources */, + 9CBE1B402C0F37B300CA6E61 /* DPDKeyboardListener.swift in Sources */, 52A3F6AB2BECBF550000BB0B /* LinkedChildVC.swift in Sources */, 52FDBA7B2BFF2712009D7AC7 /* AuthFuncTimeHandling.swift in Sources */, 9CBCB2A52BE50D49007D7934 /* NewPasswordVC.swift in Sources */, @@ -1258,6 +1331,7 @@ 52B8D4E22C04A25E00ED65F3 /* Segue.swift in Sources */, 5259542E2BEA393700191286 /* AlertCustomVC.swift in Sources */, 52CA28FA2BE119F500708B49 /* UserIntrestVC.swift in Sources */, + 9CBE1B442C0F37B300CA6E61 /* DropDown+Appearance.swift in Sources */, 9C27E16B2BDB774D00EC1DA9 /* CarouselData.swift in Sources */, 525954212BE8EB7900191286 /* APIEndPoints.swift in Sources */, 5259545A2BEB67D200191286 /* DateFormatterLib.swift in Sources */, @@ -1277,6 +1351,8 @@ 52A3F6A82BECBF2A0000BB0B /* LinkedChildCell.swift in Sources */, 52C6E01E2BE3847F00E22D59 /* BorderView.swift in Sources */, 52FDBA7D2BFF481A009D7AC7 /* ThemeOneVM.swift in Sources */, + 9C7939192C0F345000F5D6E6 /* ContactSupportVC.swift in Sources */, + 9CBE1B432C0F37B300CA6E61 /* DropDown.swift in Sources */, 52C8B0742BDA7626003B51D0 /* OnBoardVC.swift in Sources */, 5219C2C22C086D9C00A1DF4D /* DataTypeConversion.swift in Sources */, 525953CF2BE8B28F00191286 /* Utilities.swift in Sources */, @@ -1304,6 +1380,7 @@ 52663FF72BDFACF60001D8CE /* ShadowView.swift in Sources */, 9C535DC22C00B36900DA6DCD /* ThemeTwoVC.swift in Sources */, 52D774F12BDFC53B001D87DE /* StringSubScript.swift in Sources */, + 9CBE1B3F2C0F37B300CA6E61 /* DPDConstants.swift in Sources */, 52B8D4DA2C04A25E00ED65F3 /* Preferences.swift in Sources */, 52FB2D8F2BDF898F0009B0C7 /* TextFieldPadding.swift in Sources */, 5257B2652BDFB6F50086D79B /* CheckPhoneHomeBtnOrNotch.swift in Sources */, @@ -1312,6 +1389,7 @@ 525954342BEA620800191286 /* IntrestTopicDM.swift in Sources */, 52BC3BE52C0E0326002FACA6 /* FaqCell.swift in Sources */, 52663FF52BDFAB830001D8CE /* TextFieldErrorView.swift in Sources */, + 9CBE1B452C0F37B300CA6E61 /* DropDownCell.swift in Sources */, 9C27E16D2BDB852F00EC1DA9 /* GVar.swift in Sources */, 52B8D4E02C04A25E00ED65F3 /* UIView+Container.swift in Sources */, 9C56E8462BDBEE6400E4CA14 /* EmailVC.swift in Sources */, diff --git a/WOKA/Alerts/AlertCustomVC.swift b/WOKA/Alerts/AlertCustomVC.swift index 0206310..58beb88 100644 --- a/WOKA/Alerts/AlertCustomVC.swift +++ b/WOKA/Alerts/AlertCustomVC.swift @@ -54,7 +54,7 @@ class AlertCustomVC: UIViewController { @IBAction func doneBtnTapped(_ sender: UIButton) { self.dismiss() // Execute completion block if provided - // self.onDoneBlock?(true) + self.onDoneBlock?(true) } // Action when cancel button is tapped diff --git a/WOKA/Assets/Assets.xcassets/Menu/SideMenu/CollapseFaq.imageset/Contents.json b/WOKA/Assets/Assets.xcassets/Menu/SideMenu/CollapseFaq.imageset/Contents.json new file mode 100644 index 0000000..c30479c --- /dev/null +++ b/WOKA/Assets/Assets.xcassets/Menu/SideMenu/CollapseFaq.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "next-svgrepo-com (1) 1.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "next-svgrepo-com (1) 1@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "next-svgrepo-com (1) 1@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/WOKA/Assets/Assets.xcassets/Menu/SideMenu/CollapseFaq.imageset/next-svgrepo-com (1) 1.png b/WOKA/Assets/Assets.xcassets/Menu/SideMenu/CollapseFaq.imageset/next-svgrepo-com (1) 1.png new file mode 100644 index 0000000..fd7ccab Binary files /dev/null and b/WOKA/Assets/Assets.xcassets/Menu/SideMenu/CollapseFaq.imageset/next-svgrepo-com (1) 1.png differ diff --git a/WOKA/Assets/Assets.xcassets/Menu/SideMenu/CollapseFaq.imageset/next-svgrepo-com (1) 1@2x.png b/WOKA/Assets/Assets.xcassets/Menu/SideMenu/CollapseFaq.imageset/next-svgrepo-com (1) 1@2x.png new file mode 100644 index 0000000..084fb4a Binary files /dev/null and b/WOKA/Assets/Assets.xcassets/Menu/SideMenu/CollapseFaq.imageset/next-svgrepo-com (1) 1@2x.png differ diff --git a/WOKA/Assets/Assets.xcassets/Menu/SideMenu/CollapseFaq.imageset/next-svgrepo-com (1) 1@3x.png b/WOKA/Assets/Assets.xcassets/Menu/SideMenu/CollapseFaq.imageset/next-svgrepo-com (1) 1@3x.png new file mode 100644 index 0000000..0e326ed Binary files /dev/null and b/WOKA/Assets/Assets.xcassets/Menu/SideMenu/CollapseFaq.imageset/next-svgrepo-com (1) 1@3x.png differ diff --git a/WOKA/Assets/Assets.xcassets/Menu/SideMenu/ExpandFaq.imageset/Contents.json b/WOKA/Assets/Assets.xcassets/Menu/SideMenu/ExpandFaq.imageset/Contents.json new file mode 100644 index 0000000..a592cf3 --- /dev/null +++ b/WOKA/Assets/Assets.xcassets/Menu/SideMenu/ExpandFaq.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "next-svgrepo-com (1) 2.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "next-svgrepo-com (1) 2@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "next-svgrepo-com (1) 2@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/WOKA/Assets/Assets.xcassets/Menu/SideMenu/ExpandFaq.imageset/next-svgrepo-com (1) 2.png b/WOKA/Assets/Assets.xcassets/Menu/SideMenu/ExpandFaq.imageset/next-svgrepo-com (1) 2.png new file mode 100644 index 0000000..53b5b30 Binary files /dev/null and b/WOKA/Assets/Assets.xcassets/Menu/SideMenu/ExpandFaq.imageset/next-svgrepo-com (1) 2.png differ diff --git a/WOKA/Assets/Assets.xcassets/Menu/SideMenu/ExpandFaq.imageset/next-svgrepo-com (1) 2@2x.png b/WOKA/Assets/Assets.xcassets/Menu/SideMenu/ExpandFaq.imageset/next-svgrepo-com (1) 2@2x.png new file mode 100644 index 0000000..56cc9e7 Binary files /dev/null and b/WOKA/Assets/Assets.xcassets/Menu/SideMenu/ExpandFaq.imageset/next-svgrepo-com (1) 2@2x.png differ diff --git a/WOKA/Assets/Assets.xcassets/Menu/SideMenu/ExpandFaq.imageset/next-svgrepo-com (1) 2@3x.png b/WOKA/Assets/Assets.xcassets/Menu/SideMenu/ExpandFaq.imageset/next-svgrepo-com (1) 2@3x.png new file mode 100644 index 0000000..2cde34f Binary files /dev/null and b/WOKA/Assets/Assets.xcassets/Menu/SideMenu/ExpandFaq.imageset/next-svgrepo-com (1) 2@3x.png differ diff --git a/WOKA/Assets/Assets.xcassets/Menu/SideMenu/SupportBottomArrow.imageset/Contents.json b/WOKA/Assets/Assets.xcassets/Menu/SideMenu/SupportBottomArrow.imageset/Contents.json new file mode 100644 index 0000000..a49365a --- /dev/null +++ b/WOKA/Assets/Assets.xcassets/Menu/SideMenu/SupportBottomArrow.imageset/Contents.json @@ -0,0 +1,26 @@ +{ + "images" : [ + { + "filename" : "downarrow.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "downarrow@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "downarrow@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/WOKA/Assets/Assets.xcassets/Menu/SideMenu/SupportBottomArrow.imageset/downarrow.png b/WOKA/Assets/Assets.xcassets/Menu/SideMenu/SupportBottomArrow.imageset/downarrow.png new file mode 100644 index 0000000..24d9122 Binary files /dev/null and b/WOKA/Assets/Assets.xcassets/Menu/SideMenu/SupportBottomArrow.imageset/downarrow.png differ diff --git a/WOKA/Assets/Assets.xcassets/Menu/SideMenu/SupportBottomArrow.imageset/downarrow@2x.png b/WOKA/Assets/Assets.xcassets/Menu/SideMenu/SupportBottomArrow.imageset/downarrow@2x.png new file mode 100644 index 0000000..57fdf21 Binary files /dev/null and b/WOKA/Assets/Assets.xcassets/Menu/SideMenu/SupportBottomArrow.imageset/downarrow@2x.png differ diff --git a/WOKA/Assets/Assets.xcassets/Menu/SideMenu/SupportBottomArrow.imageset/downarrow@3x.png b/WOKA/Assets/Assets.xcassets/Menu/SideMenu/SupportBottomArrow.imageset/downarrow@3x.png new file mode 100644 index 0000000..a773822 Binary files /dev/null and b/WOKA/Assets/Assets.xcassets/Menu/SideMenu/SupportBottomArrow.imageset/downarrow@3x.png differ diff --git a/WOKA/Assets/Assets.xcassets/Menu/SideMenu/SupportGirlImage.imageset/Contents.json b/WOKA/Assets/Assets.xcassets/Menu/SideMenu/SupportGirlImage.imageset/Contents.json new file mode 100644 index 0000000..0766e89 --- /dev/null +++ b/WOKA/Assets/Assets.xcassets/Menu/SideMenu/SupportGirlImage.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Ellipse 20.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/WOKA/Assets/Assets.xcassets/Menu/SideMenu/SupportGirlImage.imageset/Ellipse 20.png b/WOKA/Assets/Assets.xcassets/Menu/SideMenu/SupportGirlImage.imageset/Ellipse 20.png new file mode 100644 index 0000000..9477a18 Binary files /dev/null and b/WOKA/Assets/Assets.xcassets/Menu/SideMenu/SupportGirlImage.imageset/Ellipse 20.png differ diff --git a/WOKA/Assets/Assets.xcassets/Menu/SideMenu/SupportUpArrow.imageset/Contents.json b/WOKA/Assets/Assets.xcassets/Menu/SideMenu/SupportUpArrow.imageset/Contents.json new file mode 100644 index 0000000..165ea0b --- /dev/null +++ b/WOKA/Assets/Assets.xcassets/Menu/SideMenu/SupportUpArrow.imageset/Contents.json @@ -0,0 +1,26 @@ +{ + "images" : [ + { + "filename" : "uparrow.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "uparrow@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "uparrow@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/WOKA/Assets/Assets.xcassets/Menu/SideMenu/SupportUpArrow.imageset/uparrow.png b/WOKA/Assets/Assets.xcassets/Menu/SideMenu/SupportUpArrow.imageset/uparrow.png new file mode 100644 index 0000000..fd7ccab Binary files /dev/null and b/WOKA/Assets/Assets.xcassets/Menu/SideMenu/SupportUpArrow.imageset/uparrow.png differ diff --git a/WOKA/Assets/Assets.xcassets/Menu/SideMenu/SupportUpArrow.imageset/uparrow@2x.png b/WOKA/Assets/Assets.xcassets/Menu/SideMenu/SupportUpArrow.imageset/uparrow@2x.png new file mode 100644 index 0000000..084fb4a Binary files /dev/null and b/WOKA/Assets/Assets.xcassets/Menu/SideMenu/SupportUpArrow.imageset/uparrow@2x.png differ diff --git a/WOKA/Assets/Assets.xcassets/Menu/SideMenu/SupportUpArrow.imageset/uparrow@3x.png b/WOKA/Assets/Assets.xcassets/Menu/SideMenu/SupportUpArrow.imageset/uparrow@3x.png new file mode 100644 index 0000000..0e326ed Binary files /dev/null and b/WOKA/Assets/Assets.xcassets/Menu/SideMenu/SupportUpArrow.imageset/uparrow@3x.png differ diff --git a/WOKA/Constants K/StoryBoardID.swift b/WOKA/Constants K/StoryBoardID.swift index df4d438..287acfc 100644 --- a/WOKA/Constants K/StoryBoardID.swift +++ b/WOKA/Constants K/StoryBoardID.swift @@ -46,6 +46,7 @@ extension K{ struct SideBarNav{ static let aboutUsVc = "AboutUsVc" static let faqVC = "FaqVC" + static let contactSupportVC = "ContactSupportVC" } } } diff --git a/WOKA/Helpers/ActivityToast&Indicator/Utilities.swift b/WOKA/Helpers/ActivityToast&Indicator/Utilities.swift index 86bc57a..ee0e2eb 100644 --- a/WOKA/Helpers/ActivityToast&Indicator/Utilities.swift +++ b/WOKA/Helpers/ActivityToast&Indicator/Utilities.swift @@ -44,7 +44,7 @@ class Utilities{ static func alertWithBtn(title : String , msgBody : String, okBtnStr : String?,vc : UIViewController){ let alert = UIAlertController(title: title, message: msgBody, preferredStyle: .alert) - let titleAttrString = NSMutableAttributedString(string: title != "" ? (title + "\n") : "", attributes: [NSAttributedString.Key.font: UIFont(name: "Nunito-Medium", size: 20)! as Any]) + let titleAttrString = NSMutableAttributedString(string: title != "" ? (title + "\n") : "", attributes: [NSAttributedString.Key.font: FontCustom.shareInstance.customFont(fontName: .Exo2_Regular, size: 16) as Any]) alert.setValue(titleAttrString, forKey: "attributedTitle") diff --git a/WOKA/Helpers/DropDown/DropDown.h b/WOKA/Helpers/DropDown/DropDown.h new file mode 100644 index 0000000..f2674b3 --- /dev/null +++ b/WOKA/Helpers/DropDown/DropDown.h @@ -0,0 +1,19 @@ +// +// DropDown.h +// DropDown +// +// Created by Kevin Hirsch on 13/06/16. +// Copyright © 2016 Kevin Hirsch. All rights reserved. +// + +#import + +//! Project version number for DropDown. +FOUNDATION_EXPORT double DropDownVersionNumber; + +//! Project version string for DropDown. +FOUNDATION_EXPORT const unsigned char DropDownVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/WOKA/Helpers/DropDown/helpers/DPDConstants.swift b/WOKA/Helpers/DropDown/helpers/DPDConstants.swift new file mode 100644 index 0000000..52047be --- /dev/null +++ b/WOKA/Helpers/DropDown/helpers/DPDConstants.swift @@ -0,0 +1,61 @@ +// +// Constants.swift +// DropDown +// +// Created by Kevin Hirsch on 28/07/15. +// Copyright (c) 2015 Kevin Hirsch. All rights reserved. +// + +#if os(iOS) + +import UIKit + +internal struct DPDConstant { + + internal struct KeyPath { + + static let Frame = "frame" + + } + + internal struct ReusableIdentifier { + + static let DropDownCell = "DropDownCell" + + } + + internal struct UI { + + static let TextColor = UIColor.black + static let SelectedTextColor = UIColor.black + static let TextFont = UIFont.systemFont(ofSize: 15) + static let BackgroundColor = UIColor(white: 0.94, alpha: 1) + static let SelectionBackgroundColor = UIColor(white: 0.89, alpha: 1) + static let SeparatorColor = UIColor.clear + static let CornerRadius: CGFloat = 2 + static let RowHeight: CGFloat = 44 + static let HeightPadding: CGFloat = 20 + + struct Shadow { + + static let Color = UIColor.darkGray + static let Offset = CGSize.zero + static let Opacity: Float = 0.4 + static let Radius: CGFloat = 8 + + } + + } + + internal struct Animation { + + static let Duration = 0.15 + static let EntranceOptions: UIView.AnimationOptions = [.allowUserInteraction, .curveEaseOut] + static let ExitOptions: UIView.AnimationOptions = [.allowUserInteraction, .curveEaseIn] + static let DownScaleTransform = CGAffineTransform(scaleX: 0.9, y: 0.9) + + } + +} + +#endif diff --git a/WOKA/Helpers/DropDown/helpers/DPDKeyboardListener.swift b/WOKA/Helpers/DropDown/helpers/DPDKeyboardListener.swift new file mode 100644 index 0000000..d89c41c --- /dev/null +++ b/WOKA/Helpers/DropDown/helpers/DPDKeyboardListener.swift @@ -0,0 +1,72 @@ +// +// KeyboardListener.swift +// DropDown +// +// Created by Kevin Hirsch on 30/07/15. +// Copyright (c) 2015 Kevin Hirsch. All rights reserved. +// + +#if os(iOS) + +import UIKit + +internal final class KeyboardListener { + + static let sharedInstance = KeyboardListener() + + fileprivate(set) var isVisible = false + fileprivate(set) var keyboardFrame = CGRect.zero + fileprivate var isListening = false + + deinit { + stopListeningToKeyboard() + } + +} + +//MARK: - Notifications + +extension KeyboardListener { + + func startListeningToKeyboard() { + if isListening { + return + } + + isListening = true + + NotificationCenter.default.addObserver( + self, + selector: #selector(keyboardWillShow(_:)), + name: UIResponder.keyboardWillShowNotification, + object: nil) + NotificationCenter.default.addObserver( + self, + selector: #selector(keyboardWillHide(_:)), + name: UIResponder.keyboardWillHideNotification, + object: nil) + } + + func stopListeningToKeyboard() { + NotificationCenter.default.removeObserver(self) + } + + @objc + fileprivate func keyboardWillShow(_ notification: Notification) { + isVisible = true + keyboardFrame = keyboardFrame(fromNotification: notification) + } + + @objc + fileprivate func keyboardWillHide(_ notification: Notification) { + isVisible = false + keyboardFrame = keyboardFrame(fromNotification: notification) + } + + fileprivate func keyboardFrame(fromNotification notification: Notification) -> CGRect { + return ((notification as NSNotification).userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue ?? CGRect.zero + } + +} + +#endif diff --git a/WOKA/Helpers/DropDown/helpers/DPDUIView+Extension.swift b/WOKA/Helpers/DropDown/helpers/DPDUIView+Extension.swift new file mode 100644 index 0000000..f29705d --- /dev/null +++ b/WOKA/Helpers/DropDown/helpers/DPDUIView+Extension.swift @@ -0,0 +1,61 @@ +// +// UIView+Constraints.swift +// DropDown +// +// Created by Kevin Hirsch on 28/07/15. +// Copyright (c) 2015 Kevin Hirsch. All rights reserved. +// + +#if os(iOS) + +import UIKit + +//MARK: - Constraints + +internal extension UIView { + + func addConstraints(format: String, options: NSLayoutConstraint.FormatOptions = [], metrics: [String: AnyObject]? = nil, views: [String: UIView]) { + addConstraints(NSLayoutConstraint.constraints(withVisualFormat: format, options: options, metrics: metrics, views: views)) + } + + func addUniversalConstraints(format: String, options: NSLayoutConstraint.FormatOptions = [], metrics: [String: AnyObject]? = nil, views: [String: UIView]) { + addConstraints(format: "H:\(format)", options: options, metrics: metrics, views: views) + addConstraints(format: "V:\(format)", options: options, metrics: metrics, views: views) + } + +} + + + +//MARK: - Bounds + +internal extension UIView { + + var windowFrame: CGRect? { + return superview?.convert(frame, to: nil) + } + +} + +internal extension UIWindow { + + static func visibleWindow() -> UIWindow? { + var currentWindow = UIApplication.shared.keyWindow + + if currentWindow == nil { + let frontToBackWindows = Array(UIApplication.shared.windows.reversed()) + + for window in frontToBackWindows { + if window.windowLevel == UIWindow.Level.normal { + currentWindow = window + break + } + } + } + + return currentWindow + } + +} + +#endif diff --git a/WOKA/Helpers/DropDown/resources/DropDownCell.xib b/WOKA/Helpers/DropDown/resources/DropDownCell.xib new file mode 100644 index 0000000..c9148cf --- /dev/null +++ b/WOKA/Helpers/DropDown/resources/DropDownCell.xib @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/WOKA/Helpers/DropDown/src/DropDown+Appearance.swift b/WOKA/Helpers/DropDown/src/DropDown+Appearance.swift new file mode 100644 index 0000000..7ac057d --- /dev/null +++ b/WOKA/Helpers/DropDown/src/DropDown+Appearance.swift @@ -0,0 +1,35 @@ +// +// DropDown+Appearance.swift +// DropDown +// +// Created by Kevin Hirsch on 13/06/16. +// Copyright © 2016 Kevin Hirsch. All rights reserved. +// + +#if os(iOS) + +import UIKit + +extension DropDown { + + public class func setupDefaultAppearance() { + let appearance = DropDown.appearance() + + appearance.cellHeight = DPDConstant.UI.RowHeight + appearance.backgroundColor = DPDConstant.UI.BackgroundColor + appearance.selectionBackgroundColor = DPDConstant.UI.SelectionBackgroundColor + appearance.separatorColor = DPDConstant.UI.SeparatorColor + appearance.cornerRadius = DPDConstant.UI.CornerRadius + appearance.shadowColor = DPDConstant.UI.Shadow.Color + appearance.shadowOffset = DPDConstant.UI.Shadow.Offset + appearance.shadowOpacity = DPDConstant.UI.Shadow.Opacity + appearance.shadowRadius = DPDConstant.UI.Shadow.Radius + appearance.animationduration = DPDConstant.Animation.Duration + appearance.textColor = DPDConstant.UI.TextColor + appearance.selectedTextColor = DPDConstant.UI.SelectedTextColor + appearance.textFont = DPDConstant.UI.TextFont + } + +} + +#endif diff --git a/WOKA/Helpers/DropDown/src/DropDown.swift b/WOKA/Helpers/DropDown/src/DropDown.swift new file mode 100644 index 0000000..9139e63 --- /dev/null +++ b/WOKA/Helpers/DropDown/src/DropDown.swift @@ -0,0 +1,1209 @@ +// +// DropDown.swift +// DropDown +// +// Created by Kevin Hirsch on 28/07/15. +// Copyright (c) 2015 Kevin Hirsch. All rights reserved. +// + +#if os(iOS) + +import UIKit + +public typealias Index = Int +public typealias Closure = () -> Void +public typealias SelectionClosure = (Index, String) -> Void +public typealias MultiSelectionClosure = ([Index], [String]) -> Void +public typealias ConfigurationClosure = (Index, String) -> String +public typealias CellConfigurationClosure = (Index, String, DropDownCell) -> Void +private typealias ComputeLayoutTuple = (x: CGFloat, y: CGFloat, width: CGFloat, offscreenHeight: CGFloat) + +/// Can be `UIView` or `UIBarButtonItem`. +@objc +public protocol AnchorView: AnyObject { + + var plainView: UIView { get } + +} + +extension UIView: AnchorView { + + public var plainView: UIView { + return self + } + +} + +extension UIBarButtonItem: AnchorView { + + public var plainView: UIView { + return value(forKey: "view") as! UIView + } + +} + +/// A Material Design drop down in replacement for `UIPickerView`. +public final class DropDown: UIView { + + //TODO: handle iOS 7 landscape mode + + /// The dismiss mode for a drop down. + public enum DismissMode { + + /// A tap outside the drop down is required to dismiss. + case onTap + + /// No tap is required to dismiss, it will dimiss when interacting with anything else. + case automatic + + /// Not dismissable by the user. + case manual + + } + + /// The direction where the drop down will show from the `anchorView`. + public enum Direction { + + /// The drop down will show below the anchor view when possible, otherwise above if there is more place than below. + case any + + /// The drop down will show above the anchor view or will not be showed if not enough space. + case top + + /// The drop down will show below or will not be showed if not enough space. + case bottom + + } + + //MARK: - Properties + + /// The current visible drop down. There can be only one visible drop down at a time. + public static weak var VisibleDropDown: DropDown? + + //MARK: UI + fileprivate let dismissableView = UIView() + fileprivate let tableViewContainer = UIView() + fileprivate let tableView = UITableView() + fileprivate var templateCell: DropDownCell! + fileprivate lazy var arrowIndication: UIImageView = { + UIGraphicsBeginImageContextWithOptions(CGSize(width: 20, height: 10), false, 0) + let path = UIBezierPath() + path.move(to: CGPoint(x: 0, y: 10)) + path.addLine(to: CGPoint(x: 20, y: 10)) + path.addLine(to: CGPoint(x: 10, y: 0)) + path.addLine(to: CGPoint(x: 0, y: 10)) + UIColor.black.setFill() + path.fill() + let img = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + let tintImg = img?.withRenderingMode(.alwaysTemplate) + let imgv = UIImageView(image: tintImg) + imgv.frame = CGRect(x: 0, y: -10, width: 15, height: 10) + return imgv + }() + + + /// The view to which the drop down will displayed onto. + public weak var anchorView: AnchorView? { + didSet { setNeedsUpdateConstraints() } + } + + /** + The possible directions where the drop down will be showed. + + See `Direction` enum for more info. + */ + public var direction = Direction.any + + /** + The offset point relative to `anchorView` when the drop down is shown above the anchor view. + + By default, the drop down is showed onto the `anchorView` with the top + left corner for its origin, so an offset equal to (0, 0). + You can change here the default drop down origin. + */ + public var topOffset: CGPoint = .zero { + didSet { setNeedsUpdateConstraints() } + } + + /** + The offset point relative to `anchorView` when the drop down is shown below the anchor view. + + By default, the drop down is showed onto the `anchorView` with the top + left corner for its origin, so an offset equal to (0, 0). + You can change here the default drop down origin. + */ + public var bottomOffset: CGPoint = .zero { + didSet { setNeedsUpdateConstraints() } + } + + /** + The offset from the bottom of the window when the drop down is shown below the anchor view. + DropDown applies this offset only if keyboard is hidden. + */ + public var offsetFromWindowBottom = CGFloat(0) { + didSet { setNeedsUpdateConstraints() } + } + + /** + The width of the drop down. + + Defaults to `anchorView.bounds.width - offset.x`. + */ + public var width: CGFloat? { + didSet { setNeedsUpdateConstraints() } + } + + /** + arrowIndication.x + + arrowIndication will be add to tableViewContainer when configured + */ + public var arrowIndicationX: CGFloat? { + didSet { + if let arrowIndicationX = arrowIndicationX { + tableViewContainer.addSubview(arrowIndication) + arrowIndication.tintColor = tableViewBackgroundColor + arrowIndication.frame.origin.x = arrowIndicationX + } else { + arrowIndication.removeFromSuperview() + } + } + } + + //MARK: Constraints + fileprivate var heightConstraint: NSLayoutConstraint! + fileprivate var widthConstraint: NSLayoutConstraint! + fileprivate var xConstraint: NSLayoutConstraint! + fileprivate var yConstraint: NSLayoutConstraint! + + //MARK: Appearance + @objc public dynamic var cellHeight = DPDConstant.UI.RowHeight { + willSet { tableView.rowHeight = newValue } + didSet { reloadAllComponents() } + } + + @objc fileprivate dynamic var tableViewBackgroundColor = DPDConstant.UI.BackgroundColor { + willSet { + tableView.backgroundColor = newValue + if arrowIndicationX != nil { arrowIndication.tintColor = newValue } + } + } + + public override var backgroundColor: UIColor? { + get { return tableViewBackgroundColor } + set { tableViewBackgroundColor = newValue! } + } + + /** + The color of the dimmed background (behind the drop down, covering the entire screen). + */ + public var dimmedBackgroundColor = UIColor.clear { + willSet { super.backgroundColor = newValue } + } + + /** + The background color of the selected cell in the drop down. + + Changing the background color automatically reloads the drop down. + */ + @objc public dynamic var selectionBackgroundColor = DPDConstant.UI.SelectionBackgroundColor + + /** + The separator color between cells. + + Changing the separator color automatically reloads the drop down. + */ + @objc public dynamic var separatorColor = DPDConstant.UI.SeparatorColor { + willSet { tableView.separatorColor = newValue } + didSet { reloadAllComponents() } + } + + /** + The corner radius of DropDown. + + Changing the corner radius automatically reloads the drop down. + */ + @objc public dynamic var cornerRadius = DPDConstant.UI.CornerRadius { + willSet { + tableViewContainer.layer.cornerRadius = newValue + tableView.layer.cornerRadius = newValue + } + didSet { reloadAllComponents() } + } + + /** + Alias method for `cornerRadius` variable to avoid ambiguity. + */ + @objc public dynamic func setupCornerRadius(_ radius: CGFloat) { + tableViewContainer.layer.cornerRadius = radius + tableView.layer.cornerRadius = radius + reloadAllComponents() + } + + /** + The masked corners of DropDown. + + Changing the masked corners automatically reloads the drop down. + */ + @available(iOS 11.0, *) + @objc public dynamic func setupMaskedCorners(_ cornerMask: CACornerMask) { + tableViewContainer.layer.maskedCorners = cornerMask + tableView.layer.maskedCorners = cornerMask + reloadAllComponents() + } + + /** + The color of the shadow. + + Changing the shadow color automatically reloads the drop down. + */ + @objc public dynamic var shadowColor = DPDConstant.UI.Shadow.Color { + willSet { tableViewContainer.layer.shadowColor = newValue.cgColor } + didSet { reloadAllComponents() } + } + + /** + The offset of the shadow. + + Changing the shadow color automatically reloads the drop down. + */ + @objc public dynamic var shadowOffset = DPDConstant.UI.Shadow.Offset { + willSet { tableViewContainer.layer.shadowOffset = newValue } + didSet { reloadAllComponents() } + } + + /** + The opacity of the shadow. + + Changing the shadow opacity automatically reloads the drop down. + */ + @objc public dynamic var shadowOpacity = DPDConstant.UI.Shadow.Opacity { + willSet { tableViewContainer.layer.shadowOpacity = newValue } + didSet { reloadAllComponents() } + } + + /** + The radius of the shadow. + + Changing the shadow radius automatically reloads the drop down. + */ + @objc public dynamic var shadowRadius = DPDConstant.UI.Shadow.Radius { + willSet { tableViewContainer.layer.shadowRadius = newValue } + didSet { reloadAllComponents() } + } + + /** + The duration of the show/hide animation. + */ + @objc public dynamic var animationduration = DPDConstant.Animation.Duration + + /** + The option of the show animation. Global change. + */ + public static var animationEntranceOptions = DPDConstant.Animation.EntranceOptions + + /** + The option of the hide animation. Global change. + */ + public static var animationExitOptions = DPDConstant.Animation.ExitOptions + + /** + The option of the show animation. Only change the caller. To change all drop down's use the static var. + */ + public var animationEntranceOptions: UIView.AnimationOptions = DropDown.animationEntranceOptions + + /** + The option of the hide animation. Only change the caller. To change all drop down's use the static var. + */ + public var animationExitOptions: UIView.AnimationOptions = DropDown.animationExitOptions + + /** + The downScale transformation of the tableview when the DropDown is appearing + */ + public var downScaleTransform = DPDConstant.Animation.DownScaleTransform { + willSet { tableViewContainer.transform = newValue } + } + + /** + The color of the text for each cells of the drop down. + + Changing the text color automatically reloads the drop down. + */ + @objc public dynamic var textColor = DPDConstant.UI.TextColor { + didSet { reloadAllComponents() } + } + + /** + The color of the text for selected cells of the drop down. + + Changing the text color automatically reloads the drop down. + */ + @objc public dynamic var selectedTextColor = DPDConstant.UI.SelectedTextColor { + didSet { reloadAllComponents() } + } + + /** + The font of the text for each cells of the drop down. + + Changing the text font automatically reloads the drop down. + */ + @objc public dynamic var textFont = DPDConstant.UI.TextFont { + didSet { reloadAllComponents() } + } + + /** + The NIB to use for DropDownCells + + Changing the cell nib automatically reloads the drop down. + */ + public var cellNib = UINib(nibName: "DropDownCell", bundle: bundle) { + didSet { + tableView.register(cellNib, forCellReuseIdentifier: DPDConstant.ReusableIdentifier.DropDownCell) + templateCell = nil + reloadAllComponents() + } + } + + /// Correctly specify Bundle for Swift Packages + fileprivate static var bundle: Bundle { + #if SWIFT_PACKAGE + return Bundle.module + #else + return Bundle(for: DropDownCell.self) + #endif + } + + //MARK: Content + + /** + The data source for the drop down. + + Changing the data source automatically reloads the drop down. + */ + public var dataSource = [String]() { + didSet { + deselectRows(at: selectedRowIndices) + reloadAllComponents() + } + } + + /** + The localization keys for the data source for the drop down. + + Changing this value automatically reloads the drop down. + This has uses for setting accibility identifiers on the drop down cells (same ones as the localization keys). + */ + public var localizationKeysDataSource = [String]() { + didSet { + dataSource = localizationKeysDataSource.map { NSLocalizedString($0, comment: "") } + } + } + + /// The indicies that have been selected + fileprivate var selectedRowIndices = Set() + + /** + The format for the cells' text. + + By default, the cell's text takes the plain `dataSource` value. + Changing `cellConfiguration` automatically reloads the drop down. + */ + public var cellConfiguration: ConfigurationClosure? { + didSet { reloadAllComponents() } + } + + /** + A advanced formatter for the cells. Allows customization when custom cells are used + + Changing `customCellConfiguration` automatically reloads the drop down. + */ + public var customCellConfiguration: CellConfigurationClosure? { + didSet { reloadAllComponents() } + } + + /// The action to execute when the user selects a cell. + public var selectionAction: SelectionClosure? + + /** + The action to execute when the user selects multiple cells. + + Providing an action will turn on multiselection mode. + The single selection action will still be called if provided. + */ + public var multiSelectionAction: MultiSelectionClosure? + + /// The action to execute when the drop down will show. + public var willShowAction: Closure? + + /// The action to execute when the user cancels/hides the drop down. + public var cancelAction: Closure? + + /// The dismiss mode of the drop down. Default is `OnTap`. + public var dismissMode = DismissMode.onTap { + willSet { + if newValue == .onTap { + let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissableViewTapped)) + dismissableView.addGestureRecognizer(gestureRecognizer) + } else if let gestureRecognizer = dismissableView.gestureRecognizers?.first { + dismissableView.removeGestureRecognizer(gestureRecognizer) + } + } + } + + fileprivate var minHeight: CGFloat { + return tableView.rowHeight + } + + fileprivate var didSetupConstraints = false + + //MARK: - Init's + + deinit { + stopListeningToNotifications() + } + + /** + Creates a new instance of a drop down. + Don't forget to setup the `dataSource`, + the `anchorView` and the `selectionAction` + at least before calling `show()`. + */ + public convenience init() { + self.init(frame: .zero) + } + + /** + Creates a new instance of a drop down. + + - parameter anchorView: The view to which the drop down will displayed onto. + - parameter selectionAction: The action to execute when the user selects a cell. + - parameter dataSource: The data source for the drop down. + - parameter topOffset: The offset point relative to `anchorView` used when drop down is displayed on above the anchor view. + - parameter bottomOffset: The offset point relative to `anchorView` used when drop down is displayed on below the anchor view. + - parameter cellConfiguration: The format for the cells' text. + - parameter cancelAction: The action to execute when the user cancels/hides the drop down. + + - returns: A new instance of a drop down customized with the above parameters. + */ + public convenience init(anchorView: AnchorView, selectionAction: SelectionClosure? = nil, dataSource: [String] = [], topOffset: CGPoint? = nil, bottomOffset: CGPoint? = nil, cellConfiguration: ConfigurationClosure? = nil, cancelAction: Closure? = nil) { + self.init(frame: .zero) + + self.anchorView = anchorView + self.selectionAction = selectionAction + self.dataSource = dataSource + self.topOffset = topOffset ?? .zero + self.bottomOffset = bottomOffset ?? .zero + self.cellConfiguration = cellConfiguration + self.cancelAction = cancelAction + } + + override public init(frame: CGRect) { + super.init(frame: frame) + setup() + } + + public required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + setup() + } + +} + +//MARK: - Setup + +private extension DropDown { + + func setup() { + tableView.register(cellNib, forCellReuseIdentifier: DPDConstant.ReusableIdentifier.DropDownCell) + + DispatchQueue.main.async { + //HACK: If not done in dispatch_async on main queue `setupUI` will have no effect + self.updateConstraintsIfNeeded() + self.setupUI() + } + + tableView.rowHeight = cellHeight + setHiddentState() + isHidden = true + + dismissMode = .onTap + + tableView.delegate = self + tableView.dataSource = self + + startListeningToKeyboard() + + accessibilityIdentifier = "drop_down" + } + + func setupUI() { + super.backgroundColor = dimmedBackgroundColor + + tableViewContainer.layer.masksToBounds = false + tableViewContainer.layer.cornerRadius = cornerRadius + tableViewContainer.layer.shadowColor = shadowColor.cgColor + tableViewContainer.layer.shadowOffset = shadowOffset + tableViewContainer.layer.shadowOpacity = shadowOpacity + tableViewContainer.layer.shadowRadius = shadowRadius + + tableView.backgroundColor = tableViewBackgroundColor + tableView.separatorColor = separatorColor + tableView.layer.cornerRadius = cornerRadius + tableView.layer.masksToBounds = true + } + +} + +//MARK: - UI + +extension DropDown { + + public override func updateConstraints() { + if !didSetupConstraints { + setupConstraints() + } + + didSetupConstraints = true + + let layout = computeLayout() + + if !layout.canBeDisplayed { + super.updateConstraints() + hide() + + return + } + + xConstraint.constant = layout.x + yConstraint.constant = layout.y + widthConstraint.constant = layout.width + heightConstraint.constant = layout.visibleHeight + + tableView.isScrollEnabled = layout.offscreenHeight > 0 + + DispatchQueue.main.async { [weak self] in + self?.tableView.flashScrollIndicators() + } + + super.updateConstraints() + } + + fileprivate func setupConstraints() { + translatesAutoresizingMaskIntoConstraints = false + + // Dismissable view + addSubview(dismissableView) + dismissableView.translatesAutoresizingMaskIntoConstraints = false + + addUniversalConstraints(format: "|[dismissableView]|", views: ["dismissableView": dismissableView]) + + + // Table view container + addSubview(tableViewContainer) + tableViewContainer.translatesAutoresizingMaskIntoConstraints = false + + xConstraint = NSLayoutConstraint( + item: tableViewContainer, + attribute: .leading, + relatedBy: .equal, + toItem: self, + attribute: .leading, + multiplier: 1, + constant: 0) + addConstraint(xConstraint) + + yConstraint = NSLayoutConstraint( + item: tableViewContainer, + attribute: .top, + relatedBy: .equal, + toItem: self, + attribute: .top, + multiplier: 1, + constant: 0) + addConstraint(yConstraint) + + widthConstraint = NSLayoutConstraint( + item: tableViewContainer, + attribute: .width, + relatedBy: .equal, + toItem: nil, + attribute: .notAnAttribute, + multiplier: 1, + constant: 0) + tableViewContainer.addConstraint(widthConstraint) + + heightConstraint = NSLayoutConstraint( + item: tableViewContainer, + attribute: .height, + relatedBy: .equal, + toItem: nil, + attribute: .notAnAttribute, + multiplier: 1, + constant: 0) + tableViewContainer.addConstraint(heightConstraint) + + // Table view + tableViewContainer.addSubview(tableView) + tableView.translatesAutoresizingMaskIntoConstraints = false + + tableViewContainer.addUniversalConstraints(format: "|[tableView]|", views: ["tableView": tableView]) + } + + public override func layoutSubviews() { + super.layoutSubviews() + + // When orientation changes, layoutSubviews is called + // We update the constraint to update the position + setNeedsUpdateConstraints() + + let shadowPath = UIBezierPath(roundedRect: tableViewContainer.bounds, cornerRadius: cornerRadius) + tableViewContainer.layer.shadowPath = shadowPath.cgPath + } + + fileprivate func computeLayout() -> (x: CGFloat, y: CGFloat, width: CGFloat, offscreenHeight: CGFloat, visibleHeight: CGFloat, canBeDisplayed: Bool, Direction: Direction) { + var layout: ComputeLayoutTuple = (0, 0, 0, 0) + var direction = self.direction + + guard let window = UIWindow.visibleWindow() else { return (0, 0, 0, 0, 0, false, direction) } + + barButtonItemCondition: if let anchorView = anchorView as? UIBarButtonItem { + let isRightBarButtonItem = anchorView.plainView.frame.minX > window.frame.midX + + guard isRightBarButtonItem else { break barButtonItemCondition } + + let width = self.width ?? fittingWidth() + let anchorViewWidth = anchorView.plainView.frame.width + let x = -(width - anchorViewWidth) + + bottomOffset = CGPoint(x: x, y: 0) + } + + if anchorView == nil { + layout = computeLayoutBottomDisplay(window: window) + direction = .any + } else { + switch direction { + case .any: + layout = computeLayoutBottomDisplay(window: window) + direction = .bottom + + if layout.offscreenHeight > 0 { + let topLayout = computeLayoutForTopDisplay(window: window) + + if topLayout.offscreenHeight < layout.offscreenHeight { + layout = topLayout + direction = .top + } + } + case .bottom: + layout = computeLayoutBottomDisplay(window: window) + direction = .bottom + case .top: + layout = computeLayoutForTopDisplay(window: window) + direction = .top + } + } + + constraintWidthToFittingSizeIfNecessary(layout: &layout) + constraintWidthToBoundsIfNecessary(layout: &layout, in: window) + + let visibleHeight = tableHeight - layout.offscreenHeight + let canBeDisplayed = visibleHeight >= minHeight + + return (layout.x, layout.y, layout.width, layout.offscreenHeight, visibleHeight, canBeDisplayed, direction) + } + + fileprivate func computeLayoutBottomDisplay(window: UIWindow) -> ComputeLayoutTuple { + var offscreenHeight: CGFloat = 0 + + let width = self.width ?? (anchorView?.plainView.bounds.width ?? fittingWidth()) - bottomOffset.x + + let anchorViewX = anchorView?.plainView.windowFrame?.minX ?? window.frame.midX - (width / 2) + let anchorViewY = anchorView?.plainView.windowFrame?.minY ?? window.frame.midY - (tableHeight / 2) + + let x = anchorViewX + bottomOffset.x + let y = anchorViewY + bottomOffset.y + + let maxY = y + tableHeight + let windowMaxY = window.bounds.maxY - DPDConstant.UI.HeightPadding - offsetFromWindowBottom + + let keyboardListener = KeyboardListener.sharedInstance + let keyboardMinY = keyboardListener.keyboardFrame.minY - DPDConstant.UI.HeightPadding + + if keyboardListener.isVisible && maxY > keyboardMinY { + offscreenHeight = abs(maxY - keyboardMinY) + } else if maxY > windowMaxY { + offscreenHeight = abs(maxY - windowMaxY) + } + + return (x, y, width, offscreenHeight) + } + + fileprivate func computeLayoutForTopDisplay(window: UIWindow) -> ComputeLayoutTuple { + var offscreenHeight: CGFloat = 0 + + let anchorViewX = anchorView?.plainView.windowFrame?.minX ?? 0 + let anchorViewMaxY = anchorView?.plainView.windowFrame?.maxY ?? 0 + + let x = anchorViewX + topOffset.x + var y = (anchorViewMaxY + topOffset.y) - tableHeight + + let windowY = window.bounds.minY + DPDConstant.UI.HeightPadding + + if y < windowY { + offscreenHeight = abs(y - windowY) + y = windowY + } + + let width = self.width ?? (anchorView?.plainView.bounds.width ?? fittingWidth()) - topOffset.x + + return (x, y, width, offscreenHeight) + } + + fileprivate func fittingWidth() -> CGFloat { + if templateCell == nil { + templateCell = (cellNib.instantiate(withOwner: nil, options: nil)[0] as! DropDownCell) + } + + var maxWidth: CGFloat = 0 + + for index in 0.. maxWidth { + maxWidth = width + } + } + + return maxWidth + } + + fileprivate func constraintWidthToBoundsIfNecessary(layout: inout ComputeLayoutTuple, in window: UIWindow) { + let windowMaxX = window.bounds.maxX + let maxX = layout.x + layout.width + + if maxX > windowMaxX { + let delta = maxX - windowMaxX + let newOrigin = layout.x - delta + + if newOrigin > 0 { + layout.x = newOrigin + } else { + layout.x = 0 + layout.width += newOrigin // newOrigin is negative, so this operation is a substraction + } + } + } + + fileprivate func constraintWidthToFittingSizeIfNecessary(layout: inout ComputeLayoutTuple) { + guard width == nil else { return } + + if layout.width < fittingWidth() { + layout.width = fittingWidth() + } + } + +} + +//MARK: - Actions + +extension DropDown { + + /** + An Objective-C alias for the show() method which converts the returned tuple into an NSDictionary. + + - returns: An NSDictionary with a value for the "canBeDisplayed" Bool, and possibly for the "offScreenHeight" Optional(CGFloat). + */ + @objc(show) + public func objc_show() -> NSDictionary { + let (canBeDisplayed, offScreenHeight) = show() + + var info = [AnyHashable: Any]() + info["canBeDisplayed"] = canBeDisplayed + if let offScreenHeight = offScreenHeight { + info["offScreenHeight"] = offScreenHeight + } + + return NSDictionary(dictionary: info) + } + + /** + Shows the drop down if enough height. + + - returns: Wether it succeed and how much height is needed to display all cells at once. + */ + @discardableResult + public func show(onTopOf window: UIWindow? = nil, beforeTransform transform: CGAffineTransform? = nil, anchorPoint: CGPoint? = nil) -> (canBeDisplayed: Bool, offscreenHeight: CGFloat?) { + if self == DropDown.VisibleDropDown && DropDown.VisibleDropDown?.isHidden == false { // added condition - DropDown.VisibleDropDown?.isHidden == false -> to resolve forever hiding dropdown issue when continuous taping on button - Kartik Patel - 2016-12-29 + return (true, 0) + } + + if let visibleDropDown = DropDown.VisibleDropDown { + visibleDropDown.cancel() + } + + willShowAction?() + + DropDown.VisibleDropDown = self + + setNeedsUpdateConstraints() + + let visibleWindow = window != nil ? window : UIWindow.visibleWindow() + visibleWindow?.addSubview(self) + visibleWindow?.bringSubviewToFront(self) + + self.translatesAutoresizingMaskIntoConstraints = false + visibleWindow?.addUniversalConstraints(format: "|[dropDown]|", views: ["dropDown": self]) + + let layout = computeLayout() + + if !layout.canBeDisplayed { + hide() + return (layout.canBeDisplayed, layout.offscreenHeight) + } + + isHidden = false + + if anchorPoint != nil { + tableViewContainer.layer.anchorPoint = anchorPoint! + } + + if transform != nil { + tableViewContainer.transform = transform! + } else { + tableViewContainer.transform = downScaleTransform + } + + layoutIfNeeded() + + UIView.animate( + withDuration: animationduration, + delay: 0, + options: animationEntranceOptions, + animations: { [weak self] in + self?.setShowedState() + }, + completion: nil) + + accessibilityViewIsModal = true + UIAccessibility.post(notification: .screenChanged, argument: self) + + //deselectRows(at: selectedRowIndices) + selectRows(at: selectedRowIndices) + + return (layout.canBeDisplayed, layout.offscreenHeight) + } + + public override func accessibilityPerformEscape() -> Bool { + switch dismissMode { + case .automatic, .onTap: + cancel() + return true + case .manual: + return false + } + } + + /// Hides the drop down. + public func hide() { + if self == DropDown.VisibleDropDown { + /* + If one drop down is showed and another one is not + but we call `hide()` on the hidden one: + we don't want it to set the `VisibleDropDown` to nil. + */ + DropDown.VisibleDropDown = nil + } + + if isHidden { + return + } + + UIView.animate( + withDuration: animationduration, + delay: 0, + options: animationExitOptions, + animations: { [weak self] in + self?.setHiddentState() + }, + completion: { [weak self] finished in + guard let `self` = self else { return } + + self.isHidden = true + self.removeFromSuperview() + UIAccessibility.post(notification: .screenChanged, argument: nil) + }) + } + + fileprivate func cancel() { + hide() + cancelAction?() + } + + fileprivate func setHiddentState() { + alpha = 0 + } + + fileprivate func setShowedState() { + alpha = 1 + tableViewContainer.transform = CGAffineTransform.identity + } + +} + +//MARK: - UITableView + +extension DropDown { + + /** + Reloads all the cells. + + It should not be necessary in most cases because each change to + `dataSource`, `textColor`, `textFont`, `selectionBackgroundColor` + and `cellConfiguration` implicitly calls `reloadAllComponents()`. + */ + public func reloadAllComponents() { + DispatchQueue.executeOnMainThread { + self.tableView.reloadData() + self.setNeedsUpdateConstraints() + } + } + + /// (Pre)selects a row at a certain index. + public func selectRow(at index: Index?, scrollPosition: UITableView.ScrollPosition = .none) { + if let index = index { + tableView.selectRow( + at: IndexPath(row: index, section: 0), animated: true, scrollPosition: scrollPosition + ) + selectedRowIndices.insert(index) + } else { + deselectRows(at: selectedRowIndices) + selectedRowIndices.removeAll() + } + } + + public func selectRows(at indices: Set?) { + indices?.forEach { + selectRow(at: $0) + } + + // if we are in multi selection mode then reload data so that all selections are shown + if multiSelectionAction != nil { + tableView.reloadData() + } + } + + public func deselectRow(at index: Index?) { + guard let index = index + , index >= 0 + else { return } + + // remove from indices + if let selectedRowIndex = selectedRowIndices.firstIndex(where: { $0 == index }) { + selectedRowIndices.remove(at: selectedRowIndex) + } + + tableView.deselectRow(at: IndexPath(row: index, section: 0), animated: true) + } + + // de-selects the rows at the indices provided + public func deselectRows(at indices: Set?) { + indices?.forEach { + deselectRow(at: $0) + } + } + + /// Returns the index of the selected row. + public var indexForSelectedRow: Index? { + return (tableView.indexPathForSelectedRow as NSIndexPath?)?.row + } + + /// Returns the selected item. + public var selectedItem: String? { + guard let row = (tableView.indexPathForSelectedRow as NSIndexPath?)?.row else { return nil } + + return dataSource[row] + } + + /// Returns the height needed to display all cells. + fileprivate var tableHeight: CGFloat { + return tableView.rowHeight * CGFloat(dataSource.count) + } + + //MARK: Objective-C methods for converting the Swift type Index + @objc public func selectRow(_ index: Int, scrollPosition: UITableView.ScrollPosition = .none) { + self.selectRow(at:Index(index), scrollPosition: scrollPosition) + } + + @objc public func clearSelection() { + self.selectRow(at:nil) + } + + @objc public func deselectRow(_ index: Int) { + tableView.deselectRow(at: IndexPath(row: Index(index), section: 0), animated: true) + } + + @objc public var indexPathForSelectedRow: NSIndexPath? { + return tableView.indexPathForSelectedRow as NSIndexPath? + } +} + +//MARK: - UITableViewDataSource - UITableViewDelegate + +extension DropDown: UITableViewDataSource, UITableViewDelegate { + + public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return dataSource.count + } + + public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: DPDConstant.ReusableIdentifier.DropDownCell, for: indexPath) as! DropDownCell + let index = (indexPath as NSIndexPath).row + + configureCell(cell, at: index) + + return cell + } + + fileprivate func configureCell(_ cell: DropDownCell, at index: Int) { + if index >= 0 && index < localizationKeysDataSource.count { + cell.accessibilityIdentifier = localizationKeysDataSource[index] + } + + cell.optionLabel.textColor = textColor + cell.optionLabel.font = textFont + cell.selectedBackgroundColor = selectionBackgroundColor + cell.highlightTextColor = selectedTextColor + cell.normalTextColor = textColor + + if let cellConfiguration = cellConfiguration { + cell.optionLabel.text = cellConfiguration(index, dataSource[index]) + } else { + cell.optionLabel.text = dataSource[index] + } + + customCellConfiguration?(index, dataSource[index], cell) + } + + public func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { + cell.isSelected = selectedRowIndices.first{ $0 == (indexPath as NSIndexPath).row } != nil + } + + public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let selectedRowIndex = (indexPath as NSIndexPath).row + + + // are we in multi-selection mode? + if let multiSelectionCallback = multiSelectionAction { + // if already selected then deselect + if selectedRowIndices.first(where: { $0 == selectedRowIndex}) != nil { + deselectRow(at: selectedRowIndex) + + let selectedRowIndicesArray = Array(selectedRowIndices) + let selectedRows = selectedRowIndicesArray.map { dataSource[$0] } + multiSelectionCallback(selectedRowIndicesArray, selectedRows) + return + } + else { + selectedRowIndices.insert(selectedRowIndex) + + let selectedRowIndicesArray = Array(selectedRowIndices) + let selectedRows = selectedRowIndicesArray.map { dataSource[$0] } + + selectionAction?(selectedRowIndex, dataSource[selectedRowIndex]) + multiSelectionCallback(selectedRowIndicesArray, selectedRows) + tableView.reloadData() + return + } + } + + // Perform single selection logic + selectedRowIndices.removeAll() + selectedRowIndices.insert(selectedRowIndex) + selectionAction?(selectedRowIndex, dataSource[selectedRowIndex]) + + if let _ = anchorView as? UIBarButtonItem { + // DropDown's from UIBarButtonItem are menus so we deselect the selected menu right after selection + deselectRow(at: selectedRowIndex) + } + + hide() + + } + +} + +//MARK: - Auto dismiss + +extension DropDown { + + public override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + let view = super.hitTest(point, with: event) + + if dismissMode == .automatic && view === dismissableView { + cancel() + return nil + } else { + return view + } + } + + @objc + fileprivate func dismissableViewTapped() { + cancel() + } + +} + +//MARK: - Keyboard events + +extension DropDown { + + /** + Starts listening to keyboard events. + Allows the drop down to display correctly when keyboard is showed. + */ + @objc public static func startListeningToKeyboard() { + KeyboardListener.sharedInstance.startListeningToKeyboard() + } + + fileprivate func startListeningToKeyboard() { + KeyboardListener.sharedInstance.startListeningToKeyboard() + + NotificationCenter.default.addObserver( + self, + selector: #selector(keyboardUpdate), + name: UIResponder.keyboardWillShowNotification, + object: nil) + NotificationCenter.default.addObserver( + self, + selector: #selector(keyboardUpdate), + name: UIResponder.keyboardWillHideNotification, + object: nil) + } + + fileprivate func stopListeningToNotifications() { + NotificationCenter.default.removeObserver(self) + } + + @objc + fileprivate func keyboardUpdate() { + self.setNeedsUpdateConstraints() + } + +} + +private extension DispatchQueue { + static func executeOnMainThread(_ closure: @escaping Closure) { + if Thread.isMainThread { + closure() + } else { + main.async(execute: closure) + } + } +} + +#endif diff --git a/WOKA/Helpers/DropDown/src/DropDownCell.swift b/WOKA/Helpers/DropDown/src/DropDownCell.swift new file mode 100644 index 0000000..bf49947 --- /dev/null +++ b/WOKA/Helpers/DropDown/src/DropDownCell.swift @@ -0,0 +1,78 @@ +// +// DropDownCellTableViewCell.swift +// DropDown +// +// Created by Kevin Hirsch on 28/07/15. +// Copyright (c) 2015 Kevin Hirsch. All rights reserved. +// + +#if os(iOS) + +import UIKit + +open class DropDownCell: UITableViewCell { + + //UI + @IBOutlet open weak var optionLabel: UILabel! + + var selectedBackgroundColor: UIColor? + var highlightTextColor: UIColor? + var normalTextColor: UIColor? + +} + +//MARK: - UI + +extension DropDownCell { + + override open func awakeFromNib() { + super.awakeFromNib() + + backgroundColor = .clear + } + + override open var isSelected: Bool { + willSet { + setSelected(newValue, animated: false) + } + } + + override open var isHighlighted: Bool { + willSet { + setSelected(newValue, animated: false) + } + } + + override open func setHighlighted(_ highlighted: Bool, animated: Bool) { + setSelected(highlighted, animated: animated) + } + + override open func setSelected(_ selected: Bool, animated: Bool) { + let executeSelection: () -> Void = { [weak self] in + guard let `self` = self else { return } + + if let selectedBackgroundColor = self.selectedBackgroundColor { + if selected { + self.backgroundColor = selectedBackgroundColor + self.optionLabel.textColor = self.highlightTextColor + } else { + self.backgroundColor = .clear + self.optionLabel.textColor = self.normalTextColor + } + } + } + + if animated { + UIView.animate(withDuration: 0.3, animations: { + executeSelection() + }) + } else { + executeSelection() + } + + accessibilityTraits = selected ? .selected : .none + } + +} + +#endif diff --git a/WOKA/Helpers/NsNotificationExtension.swift b/WOKA/Helpers/NsNotificationExtension.swift new file mode 100644 index 0000000..cf1173e --- /dev/null +++ b/WOKA/Helpers/NsNotificationExtension.swift @@ -0,0 +1,13 @@ +// +// NsNotificationExtension.swift +// WOKA +// +// Created by Bilal on 04/06/2024. +// + +import Foundation + +extension Notification.Name { + static let languageDidChange = Notification.Name("languageDidChange") + static let linkPush = Notification.Name("linkPush") +} diff --git a/WOKA/Helpers/UIElements Helper/ApplyGradrient.swift b/WOKA/Helpers/UIElements Helper/ApplyGradrient.swift index a01207b..9541d58 100644 --- a/WOKA/Helpers/UIElements Helper/ApplyGradrient.swift +++ b/WOKA/Helpers/UIElements Helper/ApplyGradrient.swift @@ -48,4 +48,50 @@ extension UIView { // Insert the gradient layer as the bottom layer of the view's layer hierarchy layer.insertSublayer(gradientLayer, at: 0) } + + func applyMultiGradient(colors: [UIColor], startPoint: CGPoint, endPoint: CGPoint) { + // Check if the view already has a gradient layer + if let sublayers = layer.sublayers { + for sublayer in sublayers { + if let gradientLayer = sublayer as? CAGradientLayer { + // Update the existing gradient layer + gradientLayer.colors = colors.map { $0.cgColor } + gradientLayer.startPoint = startPoint + gradientLayer.endPoint = endPoint + gradientLayer.frame = bounds + return + } + } + } + + // Create a new CAGradientLayer instance + let gradientLayer = CAGradientLayer() + + // Set the frame of the gradient layer to match the bounds of the view + gradientLayer.frame = bounds + + // Ensure that there are at least two colors + guard colors.count >= 2 else { + fatalError("At least two colors are required for the gradient.") + } + + // Calculate the color stops + let colorStops = (0.. - + - - + @@ -355,6 +354,9 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/WOKA/SideBarNav/View/FaqCell.swift b/WOKA/SideBarNav/View/FaqCell.swift index 91f1e32..1e4f152 100644 --- a/WOKA/SideBarNav/View/FaqCell.swift +++ b/WOKA/SideBarNav/View/FaqCell.swift @@ -11,6 +11,8 @@ class FaqCell: UITableViewCell { @IBOutlet weak var question: UILabel! @IBOutlet weak var answer: UILabel! + @IBOutlet weak var expandedimage: UIImageView! + @IBOutlet weak var lineSeperator: UIView! override func awakeFromNib() { super.awakeFromNib() @@ -23,4 +25,20 @@ class FaqCell: UITableViewCell { // Configure the view for the selected state } + func setData(data : FaqListDM.ResultData){ + self.question.text = data.englishQuestion + self.answer.text = data.englishAnswer + self.answer.isHidden = !data.isExpanded! + + if let isExpaned = data.isExpanded , isExpaned{ + self.answer.isHidden = false + self.expandedimage.image = UIImage(named: "CollapseFaq") + lineSeperator.isHidden = false + }else{ + self.answer.isHidden = true + self.expandedimage.image = UIImage(named: "ExpandFaq") + lineSeperator.isHidden = true + } + } + } diff --git a/WOKA/SideBarNav/View/FaqCell.xib b/WOKA/SideBarNav/View/FaqCell.xib index e3f0557..a2d5d66 100644 --- a/WOKA/SideBarNav/View/FaqCell.xib +++ b/WOKA/SideBarNav/View/FaqCell.xib @@ -25,28 +25,60 @@ - + - - + - + @@ -72,9 +104,14 @@ + + + + + diff --git a/WOKA/SideBarNav/ViewModel/FaqVM.swift b/WOKA/SideBarNav/ViewModel/FaqVM.swift new file mode 100644 index 0000000..04a2630 --- /dev/null +++ b/WOKA/SideBarNav/ViewModel/FaqVM.swift @@ -0,0 +1,64 @@ +// +// FaqVM.swift +// WOKA +// +// Created by Bilal on 04/06/2024. +// + +import UIKit +import Alamofire + +class FaqVM{ + + weak var vc : FaqVC! + var faqData = [FaqListDM.ResultData]() + var lastIndex : Int? + + func initView(){ + getFaqs() + } + + // MARK: - Get Faq's + + func getFaqs(){ + Utilities.startProgressHUD() + let headers : HTTPHeaders = ["Accept-Language" : AuthFunc.shareInstance.languageSelected == .english ? "English" : "Hindi", + "access-token": AuthFunc.shareInstance.getAccessToken()] + let params : Parameters = ["faq_category_key" : "watch"] + NetworkManager.shareInstance.apiRequest(url: APIEndPoints.SideBarNav.faq_listing, method: .post, parameters: params,headers : headers) {(result : Result, NetworkManager.APIError>) in + switch result{ + case .success(let data): + switch data.success{ + case 0: + Utilities.dismissProgressHUD() + self.handlError(msg: data.message ?? "Unrecognised error") + return + case 1: + Utilities.dismissProgressHUD() + guard let data = data.data?.result else{return} + self.faqData = data + self.vc.tableView.reloadData() + default: + break + } + case .failure(let error): + Utilities.dismissProgressHUD() + self.handlError(msg: error.localizedDescription) + } + } + } + + private func handlError(msg : String){ + let sb = UIStoryboard(name: K.StoryBoard.customAlerts, bundle: nil) + let vcPush = sb.instantiateViewController(withIdentifier: K.StoryBoardID.CustomAlerts.alertCustomVC) as! AlertCustomVC + vcPush.contentLabel = msg + vcPush.mainTitleText = "Error" + vcPush.yesBtnText = "Retry?" + vcPush.onDoneBlock = { isDone in + self.getFaqs() + } + vcPush.modalPresentationStyle = .overCurrentContext + vcPush.modalTransitionStyle = .crossDissolve + self.vc.present(vcPush, animated: true) + } +} diff --git a/WOKA/TabBar & SideMenu/Controller/SideMenuVC.swift b/WOKA/TabBar & SideMenu/Controller/SideMenuVC.swift index 5f43242..f8451e3 100644 --- a/WOKA/TabBar & SideMenu/Controller/SideMenuVC.swift +++ b/WOKA/TabBar & SideMenu/Controller/SideMenuVC.swift @@ -53,10 +53,14 @@ class SideMenuVC: UIViewController { switch sender{ case aboutBtn: self.sideMenuController?.hideMenu() - NotificationCenter.default.post(name: .pushView, object: nil, userInfo: nil) + NotificationCenter.default.post(name: .linkPush, object: nil, userInfo: ["type": LinkTypeEnum.about]) case faqBtn: self.sideMenuController?.hideMenu() - NotificationCenter.default.post(name: .pushView, object: nil, userInfo: nil) + NotificationCenter.default.post(name: .linkPush, object: nil, userInfo: ["type": LinkTypeEnum.faq]) + case wokaSupportBtn: + self.sideMenuController?.hideMenu() + NotificationCenter.default.post(name: .linkPush, object: nil, userInfo: ["type": LinkTypeEnum.support]) + default: break } diff --git a/WOKA/TabBar & SideMenu/Controller/TabBarVC.swift b/WOKA/TabBar & SideMenu/Controller/TabBarVC.swift index 1429bb1..8c9e837 100644 --- a/WOKA/TabBar & SideMenu/Controller/TabBarVC.swift +++ b/WOKA/TabBar & SideMenu/Controller/TabBarVC.swift @@ -22,23 +22,36 @@ class TabBarVC: UITabBarController { self.setupTabBarUI() self.addCustomTabBarView() NotificationCenter.default.addObserver(self, selector: #selector(languageDidChange), name: .languageDidChange, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(pushView), name: .pushView, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(linkPush), name: .linkPush, object: nil) + customizeTabBarItemIconSize() } deinit{ NotificationCenter.default.removeObserver(self, name: .languageDidChange, object: nil) - NotificationCenter.default.removeObserver(self, name: .pushView, object: nil) + NotificationCenter.default.removeObserver(self, name: .linkPush, object: nil) } - @objc func pushView(){ - Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false) { _ in - let sb = UIStoryboard(name: K.StoryBoard.sideBarNav, bundle: nil) - let vcPush = sb.instantiateViewController(withIdentifier: K.StoryBoardID.SideBarNav.faqVC) as! FaqVC - self.navigationController?.pushViewController(vcPush, animated: true) -// let sb = UIStoryboard(name: K.StoryBoard.sideBarNav, bundle: nil) -// let vcPush = sb.instantiateViewController(withIdentifier: K.StoryBoardID.SideBarNav.aboutUsVc) as! AboutUsVc -// self.navigationController?.pushViewController(vcPush, animated: true) + @objc func linkPush(_ notification: NSNotification){ + if let type = notification.userInfo?["type"] as? LinkTypeEnum { + Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false) { _ in + switch type{ + case .about: + let sb = UIStoryboard(name: K.StoryBoard.sideBarNav, bundle: nil) + let vcPush = sb.instantiateViewController(withIdentifier: K.StoryBoardID.SideBarNav.aboutUsVc) as! AboutUsVc + self.navigationController?.pushViewController(vcPush, animated: true) + case .faq: + let sb = UIStoryboard(name: K.StoryBoard.sideBarNav, bundle: nil) + let vcPush = sb.instantiateViewController(withIdentifier: K.StoryBoardID.SideBarNav.faqVC) as! FaqVC + self.navigationController?.pushViewController(vcPush, animated: true) + case .support: + let sb = UIStoryboard(name: K.StoryBoard.sideBarNav, bundle: nil) + let vcPush = sb.instantiateViewController(withIdentifier: K.StoryBoardID.SideBarNav.contactSupportVC) as! ContactSupportVC + self.navigationController?.pushViewController(vcPush, animated: true) + default: + break + } + } } } diff --git a/WOKA/TabBar & SideMenu/LinkTypeEnum.swift b/WOKA/TabBar & SideMenu/LinkTypeEnum.swift new file mode 100644 index 0000000..bdd65d2 --- /dev/null +++ b/WOKA/TabBar & SideMenu/LinkTypeEnum.swift @@ -0,0 +1,16 @@ +// +// LinkTypeEnum.swift +// WOKA +// +// Created by Bilal on 04/06/2024. +// + +import Foundation + +enum LinkTypeEnum { + case about + case faq + case support + case profile + case addChild +}