Commit 5c1b463a authored by 刘俊宏's avatar 刘俊宏

添加JXPhotoBrowser(图片浏览器)

parent 31ddb37d
...@@ -302,6 +302,20 @@ ...@@ -302,6 +302,20 @@
F950FDE624518B9500C40530 /* FSCalendarAppearance.m in Sources */ = {isa = PBXBuildFile; fileRef = F950FDD624518B9500C40530 /* FSCalendarAppearance.m */; }; F950FDE624518B9500C40530 /* FSCalendarAppearance.m in Sources */ = {isa = PBXBuildFile; fileRef = F950FDD624518B9500C40530 /* FSCalendarAppearance.m */; };
F950FDE824518CD800C40530 /* TimeSelectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F950FDE724518CD800C40530 /* TimeSelectView.swift */; }; F950FDE824518CD800C40530 /* TimeSelectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F950FDE724518CD800C40530 /* TimeSelectView.swift */; };
F950FDEA2451920200C40530 /* DIYCalendarCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F950FDE92451920200C40530 /* DIYCalendarCell.swift */; }; F950FDEA2451920200C40530 /* DIYCalendarCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F950FDE92451920200C40530 /* DIYCalendarCell.swift */; };
F9531F392465108800724BEE /* JXPhotoBrowserPageIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9531F2B2465108800724BEE /* JXPhotoBrowserPageIndicator.swift */; };
F9531F3A2465108800724BEE /* JXPhotoBrowserNoneAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9531F2C2465108800724BEE /* JXPhotoBrowserNoneAnimator.swift */; };
F9531F3B2465108800724BEE /* JXPhotoBrowserZoomSupportedCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9531F2D2465108800724BEE /* JXPhotoBrowserZoomSupportedCell.swift */; };
F9531F3C2465108800724BEE /* JXPhotoBrowserSmoothZoomAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9531F2E2465108800724BEE /* JXPhotoBrowserSmoothZoomAnimator.swift */; };
F9531F3D2465108800724BEE /* JXPhotoBrowserAnimatedTransitioning.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9531F2F2465108800724BEE /* JXPhotoBrowserAnimatedTransitioning.swift */; };
F9531F3E2465108800724BEE /* JXPhotoBrowserCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9531F302465108800724BEE /* JXPhotoBrowserCell.swift */; };
F9531F3F2465108800724BEE /* JXPhotoBrowserNumberPageIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9531F312465108800724BEE /* JXPhotoBrowserNumberPageIndicator.swift */; };
F9531F402465108800724BEE /* JXPhotoBrowserZoomAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9531F322465108800724BEE /* JXPhotoBrowserZoomAnimator.swift */; };
F9531F412465108800724BEE /* JXPhotoBrowserView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9531F332465108800724BEE /* JXPhotoBrowserView.swift */; };
F9531F422465108800724BEE /* JXPhotoBrowserDefaultPageIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9531F342465108800724BEE /* JXPhotoBrowserDefaultPageIndicator.swift */; };
F9531F432465108800724BEE /* JXPhotoBrowserImageCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9531F352465108800724BEE /* JXPhotoBrowserImageCell.swift */; };
F9531F442465108800724BEE /* JXPhotoBrowserFadeAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9531F362465108800724BEE /* JXPhotoBrowserFadeAnimator.swift */; };
F9531F452465108800724BEE /* JXPhotoBrowserLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9531F372465108800724BEE /* JXPhotoBrowserLog.swift */; };
F9531F462465108800724BEE /* JXPhotoBrowser.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9531F382465108800724BEE /* JXPhotoBrowser.swift */; };
F95567A72453DA86009674AB /* WarehoseMangementListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = F95567A52453DA86009674AB /* WarehoseMangementListVC.swift */; }; F95567A72453DA86009674AB /* WarehoseMangementListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = F95567A52453DA86009674AB /* WarehoseMangementListVC.swift */; };
F95567A82453DA86009674AB /* WarehoseMangementListVC.xib in Resources */ = {isa = PBXBuildFile; fileRef = F95567A62453DA86009674AB /* WarehoseMangementListVC.xib */; }; F95567A82453DA86009674AB /* WarehoseMangementListVC.xib in Resources */ = {isa = PBXBuildFile; fileRef = F95567A62453DA86009674AB /* WarehoseMangementListVC.xib */; };
F95567AB2453E40B009674AB /* OrderListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F95567A92453E40B009674AB /* OrderListViewController.swift */; }; F95567AB2453E40B009674AB /* OrderListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F95567A92453E40B009674AB /* OrderListViewController.swift */; };
...@@ -689,6 +703,20 @@ ...@@ -689,6 +703,20 @@
F950FDD624518B9500C40530 /* FSCalendarAppearance.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FSCalendarAppearance.m; sourceTree = "<group>"; }; F950FDD624518B9500C40530 /* FSCalendarAppearance.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FSCalendarAppearance.m; sourceTree = "<group>"; };
F950FDE724518CD800C40530 /* TimeSelectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeSelectView.swift; sourceTree = "<group>"; }; F950FDE724518CD800C40530 /* TimeSelectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeSelectView.swift; sourceTree = "<group>"; };
F950FDE92451920200C40530 /* DIYCalendarCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DIYCalendarCell.swift; sourceTree = "<group>"; }; F950FDE92451920200C40530 /* DIYCalendarCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DIYCalendarCell.swift; sourceTree = "<group>"; };
F9531F2B2465108800724BEE /* JXPhotoBrowserPageIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JXPhotoBrowserPageIndicator.swift; sourceTree = "<group>"; };
F9531F2C2465108800724BEE /* JXPhotoBrowserNoneAnimator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JXPhotoBrowserNoneAnimator.swift; sourceTree = "<group>"; };
F9531F2D2465108800724BEE /* JXPhotoBrowserZoomSupportedCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JXPhotoBrowserZoomSupportedCell.swift; sourceTree = "<group>"; };
F9531F2E2465108800724BEE /* JXPhotoBrowserSmoothZoomAnimator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JXPhotoBrowserSmoothZoomAnimator.swift; sourceTree = "<group>"; };
F9531F2F2465108800724BEE /* JXPhotoBrowserAnimatedTransitioning.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JXPhotoBrowserAnimatedTransitioning.swift; sourceTree = "<group>"; };
F9531F302465108800724BEE /* JXPhotoBrowserCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JXPhotoBrowserCell.swift; sourceTree = "<group>"; };
F9531F312465108800724BEE /* JXPhotoBrowserNumberPageIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JXPhotoBrowserNumberPageIndicator.swift; sourceTree = "<group>"; };
F9531F322465108800724BEE /* JXPhotoBrowserZoomAnimator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JXPhotoBrowserZoomAnimator.swift; sourceTree = "<group>"; };
F9531F332465108800724BEE /* JXPhotoBrowserView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JXPhotoBrowserView.swift; sourceTree = "<group>"; };
F9531F342465108800724BEE /* JXPhotoBrowserDefaultPageIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JXPhotoBrowserDefaultPageIndicator.swift; sourceTree = "<group>"; };
F9531F352465108800724BEE /* JXPhotoBrowserImageCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JXPhotoBrowserImageCell.swift; sourceTree = "<group>"; };
F9531F362465108800724BEE /* JXPhotoBrowserFadeAnimator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JXPhotoBrowserFadeAnimator.swift; sourceTree = "<group>"; };
F9531F372465108800724BEE /* JXPhotoBrowserLog.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JXPhotoBrowserLog.swift; sourceTree = "<group>"; };
F9531F382465108800724BEE /* JXPhotoBrowser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JXPhotoBrowser.swift; sourceTree = "<group>"; };
F95567A52453DA86009674AB /* WarehoseMangementListVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WarehoseMangementListVC.swift; sourceTree = "<group>"; }; F95567A52453DA86009674AB /* WarehoseMangementListVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WarehoseMangementListVC.swift; sourceTree = "<group>"; };
F95567A62453DA86009674AB /* WarehoseMangementListVC.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = WarehoseMangementListVC.xib; sourceTree = "<group>"; }; F95567A62453DA86009674AB /* WarehoseMangementListVC.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = WarehoseMangementListVC.xib; sourceTree = "<group>"; };
F95567A92453E40B009674AB /* OrderListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrderListViewController.swift; sourceTree = "<group>"; }; F95567A92453E40B009674AB /* OrderListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrderListViewController.swift; sourceTree = "<group>"; };
...@@ -1083,6 +1111,7 @@ ...@@ -1083,6 +1111,7 @@
E088CC882446FFA1000DAA8A /* Other */ = { E088CC882446FFA1000DAA8A /* Other */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
F9531F2A2465108800724BEE /* JXPhotoBrowser */,
F9DDBE932457D3CB00A32CB2 /* Web */, F9DDBE932457D3CB00A32CB2 /* Web */,
F95A9ED324551DDC00867849 /* Parchment */, F95A9ED324551DDC00867849 /* Parchment */,
F933F6D02452DAC300189561 /* TabBarVc */, F933F6D02452DAC300189561 /* TabBarVc */,
...@@ -1661,6 +1690,27 @@ ...@@ -1661,6 +1690,27 @@
path = FSCalendar; path = FSCalendar;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
F9531F2A2465108800724BEE /* JXPhotoBrowser */ = {
isa = PBXGroup;
children = (
F9531F2B2465108800724BEE /* JXPhotoBrowserPageIndicator.swift */,
F9531F2C2465108800724BEE /* JXPhotoBrowserNoneAnimator.swift */,
F9531F2D2465108800724BEE /* JXPhotoBrowserZoomSupportedCell.swift */,
F9531F2E2465108800724BEE /* JXPhotoBrowserSmoothZoomAnimator.swift */,
F9531F2F2465108800724BEE /* JXPhotoBrowserAnimatedTransitioning.swift */,
F9531F302465108800724BEE /* JXPhotoBrowserCell.swift */,
F9531F312465108800724BEE /* JXPhotoBrowserNumberPageIndicator.swift */,
F9531F322465108800724BEE /* JXPhotoBrowserZoomAnimator.swift */,
F9531F332465108800724BEE /* JXPhotoBrowserView.swift */,
F9531F342465108800724BEE /* JXPhotoBrowserDefaultPageIndicator.swift */,
F9531F352465108800724BEE /* JXPhotoBrowserImageCell.swift */,
F9531F362465108800724BEE /* JXPhotoBrowserFadeAnimator.swift */,
F9531F372465108800724BEE /* JXPhotoBrowserLog.swift */,
F9531F382465108800724BEE /* JXPhotoBrowser.swift */,
);
path = JXPhotoBrowser;
sourceTree = "<group>";
};
F95A9ED324551DDC00867849 /* Parchment */ = { F95A9ED324551DDC00867849 /* Parchment */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
...@@ -2099,13 +2149,16 @@ ...@@ -2099,13 +2149,16 @@
files = ( files = (
E0A409C32463F31600CFF109 /* ShouZhiTongJiViewController.swift in Sources */, E0A409C32463F31600CFF109 /* ShouZhiTongJiViewController.swift in Sources */,
F950FDE624518B9500C40530 /* FSCalendarAppearance.m in Sources */, F950FDE624518B9500C40530 /* FSCalendarAppearance.m in Sources */,
F9531F392465108800724BEE /* JXPhotoBrowserPageIndicator.swift in Sources */,
F9023F6D2449A9B100DD5A63 /* TitleAndTFCell.swift in Sources */, F9023F6D2449A9B100DD5A63 /* TitleAndTFCell.swift in Sources */,
F933F6D22452DAE100189561 /* TabBarContentClassView.swift in Sources */, F933F6D22452DAE100189561 /* TabBarContentClassView.swift in Sources */,
F933F6C82452C68B00189561 /* PKHUDTextView.swift in Sources */, F933F6C82452C68B00189561 /* PKHUDTextView.swift in Sources */,
E0A409C82464010D00CFF109 /* FirstCell.swift in Sources */, E0A409C82464010D00CFF109 /* FirstCell.swift in Sources */,
F9531F422465108800724BEE /* JXPhotoBrowserDefaultPageIndicator.swift in Sources */,
E0B6036E244D376900A56B38 /* KuWeiCell.swift in Sources */, E0B6036E244D376900A56B38 /* KuWeiCell.swift in Sources */,
E0D197BA2448431C002B080C /* BabyDefine.m in Sources */, E0D197BA2448431C002B080C /* BabyDefine.m in Sources */,
F94A314924624FA400FEC38E /* ShopAdministratorInfoModel.swift in Sources */, F94A314924624FA400FEC38E /* ShopAdministratorInfoModel.swift in Sources */,
F9531F3E2465108800724BEE /* JXPhotoBrowserCell.swift in Sources */,
F961D264244FD744007FBE70 /* SearchBarView.swift in Sources */, F961D264244FD744007FBE70 /* SearchBarView.swift in Sources */,
F950FDE124518B9500C40530 /* FSCalendarConstants.m in Sources */, F950FDE124518B9500C40530 /* FSCalendarConstants.m in Sources */,
F9023F712449ACA800DD5A63 /* TitleAndSwitchCell.swift in Sources */, F9023F712449ACA800DD5A63 /* TitleAndSwitchCell.swift in Sources */,
...@@ -2124,6 +2177,7 @@ ...@@ -2124,6 +2177,7 @@
E0B94623245671E100FBEC9A /* DaiKeXiaDanGouWuViewController.swift in Sources */, E0B94623245671E100FBEC9A /* DaiKeXiaDanGouWuViewController.swift in Sources */,
E0A320C52456D01300AF321C /* DaiKeXiaDanFooterView.swift in Sources */, E0A320C52456D01300AF321C /* DaiKeXiaDanFooterView.swift in Sources */,
E0336DD5244EC40000380BE9 /* CreatNewSpecsViewController.swift in Sources */, E0336DD5244EC40000380BE9 /* CreatNewSpecsViewController.swift in Sources */,
F9531F3F2465108800724BEE /* JXPhotoBrowserNumberPageIndicator.swift in Sources */,
E09F4CA22452D18800BE033D /* ZengZhiServicesViewController.swift in Sources */, E09F4CA22452D18800BE033D /* ZengZhiServicesViewController.swift in Sources */,
F94A315B24628C7E00FEC38E /* WorkItemTableViewCell.swift in Sources */, F94A315B24628C7E00FEC38E /* WorkItemTableViewCell.swift in Sources */,
E0DDCB18244AE2AC00DF54C7 /* XiuGaiYuShouJiaViewController.swift in Sources */, E0DDCB18244AE2AC00DF54C7 /* XiuGaiYuShouJiaViewController.swift in Sources */,
...@@ -2151,6 +2205,8 @@ ...@@ -2151,6 +2205,8 @@
E0A2E82B24555D8600D6DB34 /* KuCunXiangQingCell.swift in Sources */, E0A2E82B24555D8600D6DB34 /* KuCunXiangQingCell.swift in Sources */,
F933F6C22452C68B00189561 /* PKHUDAnimating.swift in Sources */, F933F6C22452C68B00189561 /* PKHUDAnimating.swift in Sources */,
E00E48C62457C99900F16545 /* KuCunShangPinXQViewController.swift in Sources */, E00E48C62457C99900F16545 /* KuCunShangPinXQViewController.swift in Sources */,
F9531F3C2465108800724BEE /* JXPhotoBrowserSmoothZoomAnimator.swift in Sources */,
F9531F402465108800724BEE /* JXPhotoBrowserZoomAnimator.swift in Sources */,
E0BE8A9C245A9E5900E80134 /* YingFuZhangKuanXQCell.swift in Sources */, E0BE8A9C245A9E5900E80134 /* YingFuZhangKuanXQCell.swift in Sources */,
E02C882A2456B4BA00925766 /* WLGZCell.swift in Sources */, E02C882A2456B4BA00925766 /* WLGZCell.swift in Sources */,
F950FDE824518CD800C40530 /* TimeSelectView.swift in Sources */, F950FDE824518CD800C40530 /* TimeSelectView.swift in Sources */,
...@@ -2158,6 +2214,7 @@ ...@@ -2158,6 +2214,7 @@
F94A314B246250DB00FEC38E /* GoodsDetailModel.swift in Sources */, F94A314B246250DB00FEC38E /* GoodsDetailModel.swift in Sources */,
E02DDD85244FDC6A00ABB707 /* TitleAndSwitchHeaderView.swift in Sources */, E02DDD85244FDC6A00ABB707 /* TitleAndSwitchHeaderView.swift in Sources */,
E0F3C2CB24590C86006F0D1F /* XinZengAndBianJiKeHuViewController.swift in Sources */, E0F3C2CB24590C86006F0D1F /* XinZengAndBianJiKeHuViewController.swift in Sources */,
F9531F412465108800724BEE /* JXPhotoBrowserView.swift in Sources */,
E0D197C02448431C002B080C /* BabyCentralManager.m in Sources */, E0D197C02448431C002B080C /* BabyCentralManager.m in Sources */,
F95D9CA424493E840080D6E3 /* ShopInfoViewController.swift in Sources */, F95D9CA424493E840080D6E3 /* ShopInfoViewController.swift in Sources */,
F9171EFC245128B200359C2B /* Interface.swift in Sources */, F9171EFC245128B200359C2B /* Interface.swift in Sources */,
...@@ -2213,6 +2270,7 @@ ...@@ -2213,6 +2270,7 @@
F933F6C02452C68B00189561 /* PKHUDSystemActivityIndicatorView.swift in Sources */, F933F6C02452C68B00189561 /* PKHUDSystemActivityIndicatorView.swift in Sources */,
E08B0674244A98B2001D2E01 /* WareHouseSpCell.swift in Sources */, E08B0674244A98B2001D2E01 /* WareHouseSpCell.swift in Sources */,
E0A2E81E24552F0A00D6DB34 /* TilteAndTextViewCell.swift in Sources */, E0A2E81E24552F0A00D6DB34 /* TilteAndTextViewCell.swift in Sources */,
F9531F3D2465108800724BEE /* JXPhotoBrowserAnimatedTransitioning.swift in Sources */,
E0D197CA24484D90002B080C /* PersonCenterCell.swift in Sources */, E0D197CA24484D90002B080C /* PersonCenterCell.swift in Sources */,
E0C4F4B5244EE9EE00CC7E2F /* YuShouCell.swift in Sources */, E0C4F4B5244EE9EE00CC7E2F /* YuShouCell.swift in Sources */,
F933F6C52452C68B00189561 /* PKHUDSuccessView.swift in Sources */, F933F6C52452C68B00189561 /* PKHUDSuccessView.swift in Sources */,
...@@ -2222,8 +2280,10 @@ ...@@ -2222,8 +2280,10 @@
E042F74024528CD500CDFA12 /* TitleAndOnlyBtnCell.swift in Sources */, E042F74024528CD500CDFA12 /* TitleAndOnlyBtnCell.swift in Sources */,
F969BF07244EC38000C79953 /* GoodsCollectionViewCell.swift in Sources */, F969BF07244EC38000C79953 /* GoodsCollectionViewCell.swift in Sources */,
E02FD9F12455705F00E10E27 /* ChuRuKuJiLuCell.swift in Sources */, E02FD9F12455705F00E10E27 /* ChuRuKuJiLuCell.swift in Sources */,
F9531F462465108800724BEE /* JXPhotoBrowser.swift in Sources */,
E0A2E82724555B4400D6DB34 /* KuCunShangPinCell.swift in Sources */, E0A2E82724555B4400D6DB34 /* KuCunShangPinCell.swift in Sources */,
F950FDA72451494500C40530 /* GeliPayInfoModel.swift in Sources */, F950FDA72451494500C40530 /* GeliPayInfoModel.swift in Sources */,
F9531F432465108800724BEE /* JXPhotoBrowserImageCell.swift in Sources */,
E07B1E6B245926330055BA3C /* GYSGLCell.swift in Sources */, E07B1E6B245926330055BA3C /* GYSGLCell.swift in Sources */,
E09B03C02445B96500211A51 /* FgPassViewController.swift in Sources */, E09B03C02445B96500211A51 /* FgPassViewController.swift in Sources */,
F950FDAB2451634B00C40530 /* LocationGeliPayModel.swift in Sources */, F950FDAB2451634B00C40530 /* LocationGeliPayModel.swift in Sources */,
...@@ -2244,6 +2304,7 @@ ...@@ -2244,6 +2304,7 @@
E06524F524580BB600CA22E2 /* BaoZhiQiCell.swift in Sources */, E06524F524580BB600CA22E2 /* BaoZhiQiCell.swift in Sources */,
E0BE8AA4245AA31400E80134 /* YingFuZhangKuanXQViewController.swift in Sources */, E0BE8AA4245AA31400E80134 /* YingFuZhangKuanXQViewController.swift in Sources */,
F94A315324626D7800FEC38E /* WorkCViewController.swift in Sources */, F94A315324626D7800FEC38E /* WorkCViewController.swift in Sources */,
F9531F3A2465108800724BEE /* JXPhotoBrowserNoneAnimator.swift in Sources */,
E07B1E62245921A60055BA3C /* KeHuXQCell.swift in Sources */, E07B1E62245921A60055BA3C /* KeHuXQCell.swift in Sources */,
E0DDCB1D244AE81800DF54C7 /* GuanLianKeHuCell.swift in Sources */, E0DDCB1D244AE81800DF54C7 /* GuanLianKeHuCell.swift in Sources */,
E0D197BF2448431C002B080C /* BabyBluetooth.m in Sources */, E0D197BF2448431C002B080C /* BabyBluetooth.m in Sources */,
...@@ -2265,7 +2326,9 @@ ...@@ -2265,7 +2326,9 @@
E0516F772448003D00E373CE /* PayViewController.swift in Sources */, E0516F772448003D00E373CE /* PayViewController.swift in Sources */,
E02DDD952450106C00ABB707 /* PuTongJieTiCell.swift in Sources */, E02DDD952450106C00ABB707 /* PuTongJieTiCell.swift in Sources */,
E0BE8A8B245A7D3300E80134 /* BtmBtnCell.swift in Sources */, E0BE8A8B245A7D3300E80134 /* BtmBtnCell.swift in Sources */,
F9531F452465108800724BEE /* JXPhotoBrowserLog.swift in Sources */,
F9956B1E2463D94E001A617D /* WorkSelectTimeView.swift in Sources */, F9956B1E2463D94E001A617D /* WorkSelectTimeView.swift in Sources */,
F9531F3B2465108800724BEE /* JXPhotoBrowserZoomSupportedCell.swift in Sources */,
F950FDE024518B9500C40530 /* FSCalendarExtensions.m in Sources */, F950FDE024518B9500C40530 /* FSCalendarExtensions.m in Sources */,
E09B03A32445AF9500211A51 /* NetworkRequest.swift in Sources */, E09B03A32445AF9500211A51 /* NetworkRequest.swift in Sources */,
E0D46C5624503EC50087B564 /* TitleAndSelectCell.swift in Sources */, E0D46C5624503EC50087B564 /* TitleAndSelectCell.swift in Sources */,
...@@ -2280,6 +2343,7 @@ ...@@ -2280,6 +2343,7 @@
E0B9461E24566B3100FBEC9A /* TitleAndTFAndBtnCell.swift in Sources */, E0B9461E24566B3100FBEC9A /* TitleAndTFAndBtnCell.swift in Sources */,
E042F74324529B7B00CDFA12 /* JiTuoWuXinXiView.swift in Sources */, E042F74324529B7B00CDFA12 /* JiTuoWuXinXiView.swift in Sources */,
F933F6CC2452C68B00189561 /* WindowRootViewController.swift in Sources */, F933F6CC2452C68B00189561 /* WindowRootViewController.swift in Sources */,
F9531F442465108800724BEE /* JXPhotoBrowserFadeAnimator.swift in Sources */,
E088CC8D24470BF0000DAA8A /* CodeView.swift in Sources */, E088CC8D24470BF0000DAA8A /* CodeView.swift in Sources */,
E01EA1A4245A60E000F019D5 /* XiaDanChengGongViewController.swift in Sources */, E01EA1A4245A60E000F019D5 /* XiaDanChengGongViewController.swift in Sources */,
F94A314D2462582F00FEC38E /* GeneralInfoModel.swift in Sources */, F94A314D2462582F00FEC38E /* GeneralInfoModel.swift in Sources */,
......
//
// JXPhotoBrowser.swift
// JXPhotoBrowser
//
// Created by JiongXing on 2019/11/11.
// Copyright © 2019 JiongXing. All rights reserved.
//
import UIKit
/// 图片浏览器
/// - 不支持复用,每次使用请新建实例
open class JXPhotoBrowser: UIViewController, UIViewControllerTransitioningDelegate, UINavigationControllerDelegate {
/// 通过本回调,把图片浏览器嵌套在导航控制器里
public typealias PresentEmbedClosure = (JXPhotoBrowser) -> UINavigationController
/// 打开方式类型
public enum ShowMethod {
case push(inNC: UINavigationController?)
case present(fromVC: UIViewController?, embed: PresentEmbedClosure?)
}
/// 滑动方向类型
public enum ScrollDirection {
case horizontal
case vertical
}
/// 自实现转场动画
open lazy var transitionAnimator: JXPhotoBrowserAnimatedTransitioning = JXPhotoBrowserFadeAnimator()
/// 滑动方向
open var scrollDirection: JXPhotoBrowser.ScrollDirection {
set { browserView.scrollDirection = newValue }
get { return browserView.scrollDirection }
}
/// 项间距
open var itemSpacing: CGFloat {
set { browserView.itemSpacing = newValue }
get { return browserView.itemSpacing }
}
/// 当前页码
open var pageIndex: Int {
set { browserView.pageIndex = newValue }
get { return browserView.pageIndex }
}
/// 浏览过程中实时获取数据总量
open var numberOfItems: () -> Int {
set { browserView.numberOfItems = newValue }
get { return browserView.numberOfItems }
}
/// 返回可复用的Cell类。用户可根据index返回不同的类。本闭包将在每次复用Cell时实时调用。
open var cellClassAtIndex: (_ index: Int) -> JXPhotoBrowserCell.Type {
set { browserView.cellClassAtIndex = newValue }
get { return browserView.cellClassAtIndex }
}
/// Cell刷新时用的上下文。index: 刷新的Cell对应的index;currentIndex: 当前显示的页
public typealias ReloadCellContext = (cell: JXPhotoBrowserCell, index: Int, currentIndex: Int)
/// 刷新Cell数据。本闭包将在Cell完成位置布局后调用。
open var reloadCellAtIndex: (ReloadCellContext) -> Void {
set { browserView.reloadCellAtIndex = newValue }
get { return browserView.reloadCellAtIndex }
}
/// 自然滑动引起的页码改变时回调
open lazy var didChangedPageIndex: (_ index: Int) -> Void = { _ in }
/// Cell将显示
open var cellWillAppear: (JXPhotoBrowserCell, Int) -> Void {
set { browserView.cellWillAppear = newValue }
get { return browserView.cellWillAppear }
}
/// Cell将不显示
open var cellWillDisappear: (JXPhotoBrowserCell, Int) -> Void {
set { browserView.cellWillDisappear = newValue }
get { return browserView.cellWillDisappear }
}
/// Cell已显示
open var cellDidAppear: (JXPhotoBrowserCell, Int) -> Void {
set { browserView.cellDidAppear = newValue }
get { return browserView.cellDidAppear }
}
/// 主视图
open lazy var browserView = JXPhotoBrowserView()
/// 页码指示
open var pageIndicator: JXPhotoBrowserPageIndicator?
/// 背景蒙版
open lazy var maskView: UIView = {
let view = UIView()
view.backgroundColor = .black
return view
}()
open weak var previousNavigationControllerDelegate: UINavigationControllerDelegate?
public init() {
super.init(nibName: nil, bundle: nil)
}
required public init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
/// 显示图片浏览器
open func show(method: ShowMethod = .present(fromVC: nil, embed: nil)) {
switch method {
case .push(let inNC):
let nav = inNC ?? JXPhotoBrowser.topMost?.navigationController
previousNavigationControllerDelegate = nav?.delegate
nav?.delegate = self
nav?.pushViewController(self, animated: true)
case .present(let fromVC, let embed):
let toVC = embed?(self) ?? self
toVC.modalPresentationStyle = .custom
toVC.modalPresentationCapturesStatusBarAppearance = true
toVC.transitioningDelegate = self
let from = fromVC ?? JXPhotoBrowser.topMost
from?.present(toVC, animated: true, completion: nil)
}
}
/// 刷新
open func reloadData() {
browserView.reloadData()
pageIndicator?.reloadData(numberOfItems: numberOfItems(), pageIndex: pageIndex)
}
open override func viewDidLoad() {
super.viewDidLoad()
automaticallyAdjustsScrollViewInsets = false
hideNavigationBar(true)
browserView.photoBrowser = self
transitionAnimator.photoBrowser = self
view.backgroundColor = .clear
view.addSubview(maskView)
view.addSubview(browserView)
browserView.didChangedPageIndex = { [weak self] index in
guard let `self` = self else { return }
self.pageIndicator?.didChanged(pageIndex: index)
self.didChangedPageIndex(index)
}
view.setNeedsLayout()
view.layoutIfNeeded()
}
open override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
maskView.frame = view.bounds
browserView.frame = view.bounds
pageIndicator?.reloadData(numberOfItems: numberOfItems(), pageIndex: pageIndex)
}
open override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
hideNavigationBar(true)
}
open override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
navigationController?.delegate = previousNavigationControllerDelegate
if let indicator = pageIndicator {
view.addSubview(indicator)
indicator.setup(with: self)
}
}
open override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
hideNavigationBar(false)
}
open override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
browserView.isRotating = true
}
//
// MARK: - Navigation Bar
//
/// 在PhotoBrowser打开之前,导航栏是否隐藏
open var isPreviousNavigationBarHidden: Bool?
private func hideNavigationBar(_ hide: Bool) {
if hide {
if isPreviousNavigationBarHidden == nil {
isPreviousNavigationBarHidden = navigationController?.isNavigationBarHidden
}
navigationController?.setNavigationBarHidden(true, animated: false)
} else {
if let barHidden = isPreviousNavigationBarHidden {
navigationController?.setNavigationBarHidden(barHidden, animated: false)
}
}
}
//
// MARK: - Status Bar
//
private lazy var isPreviousStatusBarHidden: Bool = {
var previousVC: UIViewController?
if let vc = self.presentingViewController {
previousVC = vc
} else {
if let navVCs = self.navigationController?.viewControllers, navVCs.count >= 2 {
previousVC = navVCs[navVCs.count - 2]
}
}
return previousVC?.prefersStatusBarHidden ?? false
}()
private lazy var isStatusBarHidden = self.isPreviousStatusBarHidden
open override var prefersStatusBarHidden: Bool {
return isStatusBarHidden
}
open func setStatusBar(hidden: Bool) {
if hidden {
isStatusBarHidden = true
} else {
isStatusBarHidden = isPreviousStatusBarHidden
}
setNeedsStatusBarAppearanceUpdate()
}
//
// MARK: - 转场
//
public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
transitionAnimator.isForShow = true
transitionAnimator.photoBrowser = self
return transitionAnimator
}
public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
transitionAnimator.isForShow = false
transitionAnimator.photoBrowser = self
return transitionAnimator
}
public func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationController.Operation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
transitionAnimator.isForShow = (operation == .push)
transitionAnimator.photoBrowser = self
transitionAnimator.isNavigationAnimation = true
return transitionAnimator
}
/// 关闭PhotoBrowser
open func dismiss() {
setStatusBar(hidden: false)
pageIndicator?.removeFromSuperview()
if presentingViewController != nil {
dismiss(animated: true, completion: nil)
} else {
navigationController?.delegate = self
navigationController?.popViewController(animated: true)
}
}
deinit {
JXPhotoBrowserLog.low("deinit - \(self.classForCoder)")
navigationController?.delegate = previousNavigationControllerDelegate
}
//
// MARK: - 取顶层控制器
//
/// 取最顶层的ViewController
open class var topMost: UIViewController? {
return topMost(of: UIApplication.shared.keyWindow?.rootViewController)
}
open class func topMost(of viewController: UIViewController?) -> UIViewController? {
// presented view controller
if let presentedViewController = viewController?.presentedViewController {
return self.topMost(of: presentedViewController)
}
// UITabBarController
if let tabBarController = viewController as? UITabBarController,
let selectedViewController = tabBarController.selectedViewController {
return self.topMost(of: selectedViewController)
}
// UINavigationController
if let navigationController = viewController as? UINavigationController,
let visibleViewController = navigationController.visibleViewController {
return self.topMost(of: visibleViewController)
}
// UIPageController
if let pageViewController = viewController as? UIPageViewController,
pageViewController.viewControllers?.count == 1 {
return self.topMost(of: pageViewController.viewControllers?.first)
}
// child view controller
for subview in viewController?.view?.subviews ?? [] {
if let childViewController = subview.next as? UIViewController {
return self.topMost(of: childViewController)
}
}
return viewController
}
}
//
// JXPhotoBrowserAnimatedTransitioning.swift
// JXPhotoBrowser
//
// Created by JiongXing on 2019/11/25.
// Copyright © 2019 JiongXing. All rights reserved.
//
import UIKit
public protocol JXPhotoBrowserAnimatedTransitioning: UIViewControllerAnimatedTransitioning {
var isForShow: Bool { get set }
var photoBrowser: JXPhotoBrowser? { get set }
var isNavigationAnimation: Bool { get set }
}
private var isForShowKey = "isForShowKey"
private var photoBrowserKey = "photoBrowserKey"
extension JXPhotoBrowserAnimatedTransitioning {
public var isForShow: Bool {
get {
if let value = objc_getAssociatedObject(self, &isForShowKey) as? Bool {
return value
}
return true
}
set {
objc_setAssociatedObject(self, &isForShowKey, newValue, .OBJC_ASSOCIATION_ASSIGN)
}
}
public weak var photoBrowser: JXPhotoBrowser? {
get {
return objc_getAssociatedObject(self, &photoBrowserKey) as? JXPhotoBrowser
}
set {
objc_setAssociatedObject(self, &photoBrowserKey, newValue, .OBJC_ASSOCIATION_ASSIGN)
}
}
public var isNavigationAnimation: Bool {
get { return false }
set { }
}
public func fastSnapshot(with view: UIView) -> UIView? {
UIGraphicsBeginImageContextWithOptions(view.bounds.size, false, UIScreen.main.scale)
view.drawHierarchy(in: view.bounds, afterScreenUpdates: false)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return UIImageView(image: image)
}
public func snapshot(with view: UIView) -> UIView? {
UIGraphicsBeginImageContextWithOptions(view.bounds.size, false, UIScreen.main.scale)
guard let context = UIGraphicsGetCurrentContext() else { return nil }
view.layer.render(in: context)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return UIImageView(image: image)
}
}
//
// JXPhotoBrowserCell.swift
// JXPhotoBrowser
//
// Created by JiongXing on 2019/11/26.
// Copyright © 2019 JiongXing. All rights reserved.
//
import UIKit
public protocol JXPhotoBrowserCell: UIView {
static func generate(with browser: JXPhotoBrowser) -> Self
}
//
// JXPhotoBrowserDefaultPageIndicator.swift
// JXPhotoBrowser
//
// Created by JiongXing on 2019/11/25.
// Copyright © 2019 JiongXing. All rights reserved.
//
import UIKit
open class JXPhotoBrowserDefaultPageIndicator: UIPageControl, JXPhotoBrowserPageIndicator {
/// 页码与底部的距离
open lazy var bottomPadding: CGFloat = {
if #available(iOS 11.0, *),
let window = UIApplication.shared.keyWindow,
window.safeAreaInsets.bottom > 0 {
return 20
}
return 15
}()
open func setup(with browser: JXPhotoBrowser) {
isEnabled = false
}
open func reloadData(numberOfItems: Int, pageIndex: Int) {
numberOfPages = numberOfItems
currentPage = min(pageIndex, numberOfPages - 1)
sizeToFit()
isHidden = numberOfPages <= 1
if let view = superview {
center.x = view.bounds.width / 2
frame.origin.y = view.bounds.maxY - bottomPadding - bounds.height
}
}
open func didChanged(pageIndex: Int) {
currentPage = pageIndex
}
}
//
// JXPhotoBrowserFadeAnimator.swift
// JXPhotoBrowser
//
// Created by JiongXing on 2019/11/25.
// Copyright © 2019 JiongXing. All rights reserved.
//
import UIKit
open class JXPhotoBrowserFadeAnimator: NSObject, JXPhotoBrowserAnimatedTransitioning {
open var showDuration: TimeInterval = 0.25
open var dismissDuration: TimeInterval = 0.25
public var isNavigationAnimation: Bool = false
public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return isForShow ? showDuration : dismissDuration
}
public func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let browser = photoBrowser else {
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
return
}
if isNavigationAnimation, isForShow,
let fromView = transitionContext.view(forKey: .from),
let fromViewSnapshot = snapshot(with: fromView),
let toView = transitionContext.view(forKey: .to) {
toView.insertSubview(fromViewSnapshot, at: 0)
}
if isForShow {
browser.maskView.alpha = 0
browser.browserView.alpha = 0
if let toView = transitionContext.view(forKey: .to) {
transitionContext.containerView.addSubview(toView)
}
} else {
if isNavigationAnimation,
let fromView = transitionContext.view(forKey: .from), let toView = transitionContext.view(forKey: .to) {
transitionContext.containerView.insertSubview(toView, belowSubview: fromView)
}
}
browser.browserView.isHidden = true
UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: {
browser.browserView.isHidden = false
browser.maskView.alpha = self.isForShow ? 1.0 : 0
browser.browserView.alpha = self.isForShow ? 1.0 : 0
}) { _ in
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
}
}
}
//
// JXPhotoBrowserImageCell.swift
// JXPhotoBrowser
//
// Created by JiongXing on 2019/11/12.
// Copyright © 2019 JiongXing. All rights reserved.
//
import UIKit
open class JXPhotoBrowserImageCell: UIView, UIScrollViewDelegate, UIGestureRecognizerDelegate, JXPhotoBrowserCell, JXPhotoBrowserZoomSupportedCell {
/// 弱引用PhotoBrowser
open weak var photoBrowser: JXPhotoBrowser?
open var index: Int = 0
open var scrollDirection: JXPhotoBrowser.ScrollDirection = .horizontal {
didSet {
if scrollDirection == .horizontal {
addPanGesture()
} else if let existed = existedPan {
scrollView.removeGestureRecognizer(existed)
}
}
}
open var imageView: UIImageView = {
let view = UIImageView()
view.clipsToBounds = true
return view
}()
open var scrollView: UIScrollView = {
let view = UIScrollView()
view.maximumZoomScale = 2.0
view.showsVerticalScrollIndicator = false
view.showsHorizontalScrollIndicator = false
if #available(iOS 11.0, *) {
view.contentInsetAdjustmentBehavior = .never
}
return view
}()
deinit {
JXPhotoBrowserLog.low("deinit - \(self.classForCoder)")
}
public required override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
public required init?(coder: NSCoder) {
super.init(coder: coder)
setup()
}
/// 生成实例
public static func generate(with browser: JXPhotoBrowser) -> Self {
let cell = Self.init(frame: .zero)
cell.photoBrowser = browser
cell.scrollDirection = browser.scrollDirection
return cell
}
open func setup() {
backgroundColor = .clear
scrollView.delegate = self
addSubview(scrollView)
scrollView.addSubview(imageView)
/// 拖动手势
addPanGesture()
// 双击手势
let doubleTap = UITapGestureRecognizer(target: self, action: #selector(onDoubleTap(_:)))
doubleTap.numberOfTapsRequired = 2
addGestureRecognizer(doubleTap)
// 单击手势
let singleTap = UITapGestureRecognizer(target: self, action: #selector(onSingleTap(_:)))
singleTap.require(toFail: doubleTap)
addGestureRecognizer(singleTap)
}
// 长按事件
public typealias LongPressAction = (JXPhotoBrowserImageCell, UILongPressGestureRecognizer) -> Void
/// 长按时回调。赋值时自动添加手势,赋值为nil时移除手势
open var longPressedAction: LongPressAction? {
didSet {
if oldValue != nil && longPressedAction == nil {
removeGestureRecognizer(longPress)
} else if oldValue == nil && longPressedAction != nil {
addGestureRecognizer(longPress)
}
}
}
/// 已添加的长按手势
private lazy var longPress: UILongPressGestureRecognizer = {
return UILongPressGestureRecognizer(target: self, action: #selector(onLongPress(_:)))
}()
private weak var existedPan: UIPanGestureRecognizer?
/// 添加拖动手势
open func addPanGesture() {
guard existedPan == nil else {
return
}
let pan = UIPanGestureRecognizer(target: self, action: #selector(onPan(_:)))
pan.delegate = self
// 必须加在图片容器上,否则长图下拉不能触发
scrollView.addGestureRecognizer(pan)
existedPan = pan
}
open override func layoutSubviews() {
super.layoutSubviews()
scrollView.frame = bounds
scrollView.setZoomScale(1.0, animated: false)
let size = computeImageLayoutSize(for: imageView.image, in: scrollView)
let origin = computeImageLayoutOrigin(for: size, in: scrollView)
imageView.frame = CGRect(origin: origin, size: size)
scrollView.setZoomScale(1.0, animated: false)
}
open func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return imageView
}
open func scrollViewDidZoom(_ scrollView: UIScrollView) {
imageView.center = computeImageLayoutCenter(in: scrollView)
}
open func computeImageLayoutSize(for image: UIImage?, in scrollView: UIScrollView) -> CGSize {
guard let imageSize = image?.size, imageSize.width > 0 && imageSize.height > 0 else {
return .zero
}
var width: CGFloat
var height: CGFloat
let containerSize = scrollView.bounds.size
if scrollDirection == .horizontal {
// 横竖屏判断
if containerSize.width < containerSize.height {
width = containerSize.width
height = imageSize.height / imageSize.width * width
} else {
height = containerSize.height
width = imageSize.width / imageSize.height * height
if width > containerSize.width {
width = containerSize.width
height = imageSize.height / imageSize.width * width
}
}
} else {
width = containerSize.width
height = imageSize.height / imageSize.width * width
if height > containerSize.height {
height = containerSize.height
width = imageSize.width / imageSize.height * height
}
}
return CGSize(width: width, height: height)
}
open func computeImageLayoutOrigin(for imageSize: CGSize, in scrollView: UIScrollView) -> CGPoint {
let containerSize = scrollView.bounds.size
var y = (containerSize.height - imageSize.height) * 0.5
y = max(0, y)
var x = (containerSize.width - imageSize.width) * 0.5
x = max(0, x)
return CGPoint(x: x, y: y)
}
open func computeImageLayoutCenter(in scrollView: UIScrollView) -> CGPoint {
var x = scrollView.contentSize.width * 0.5
var y = scrollView.contentSize.height * 0.5
let offsetX = (bounds.width - scrollView.contentSize.width) * 0.5
if offsetX > 0 {
x += offsetX
}
let offsetY = (bounds.height - scrollView.contentSize.height) * 0.5
if offsetY > 0 {
y += offsetY
}
return CGPoint(x: x, y: y)
}
/// 单击
@objc open func onSingleTap(_ tap: UITapGestureRecognizer) {
photoBrowser?.dismiss()
}
/// 双击
@objc open func onDoubleTap(_ tap: UITapGestureRecognizer) {
// 如果当前没有任何缩放,则放大到目标比例,否则重置到原比例
if scrollView.zoomScale < 1.1 {
// 以点击的位置为中心,放大
let pointInView = tap.location(in: imageView)
let width = scrollView.bounds.size.width / scrollView.maximumZoomScale
let height = scrollView.bounds.size.height / scrollView.maximumZoomScale
let x = pointInView.x - (width / 2.0)
let y = pointInView.y - (height / 2.0)
scrollView.zoom(to: CGRect(x: x, y: y, width: width, height: height), animated: true)
} else {
scrollView.setZoomScale(1.0, animated: true)
}
}
/// 长按
@objc open func onLongPress(_ press: UILongPressGestureRecognizer) {
if press.state == .began {
longPressedAction?(self, press)
}
}
/// 记录pan手势开始时imageView的位置
private var beganFrame = CGRect.zero
/// 记录pan手势开始时,手势位置
private var beganTouch = CGPoint.zero
/// 响应拖动
@objc open func onPan(_ pan: UIPanGestureRecognizer) {
guard imageView.image != nil else {
return
}
switch pan.state {
case .began:
beganFrame = imageView.frame
beganTouch = pan.location(in: scrollView)
case .changed:
let result = panResult(pan)
imageView.frame = result.frame
photoBrowser?.maskView.alpha = result.scale * result.scale
photoBrowser?.setStatusBar(hidden: result.scale > 0.99)
photoBrowser?.pageIndicator?.isHidden = result.scale < 0.99
case .ended, .cancelled:
imageView.frame = panResult(pan).frame
let isDown = pan.velocity(in: self).y > 0
if isDown {
photoBrowser?.dismiss()
} else {
photoBrowser?.maskView.alpha = 1.0
photoBrowser?.setStatusBar(hidden: true)
photoBrowser?.pageIndicator?.isHidden = false
resetImageViewPosition()
}
default:
resetImageViewPosition()
}
}
/// 计算拖动时图片应调整的frame和scale值
private func panResult(_ pan: UIPanGestureRecognizer) -> (frame: CGRect, scale: CGFloat) {
// 拖动偏移量
let translation = pan.translation(in: scrollView)
let currentTouch = pan.location(in: scrollView)
// 由下拉的偏移值决定缩放比例,越往下偏移,缩得越小。scale值区间[0.3, 1.0]
let scale = min(1.0, max(0.3, 1 - translation.y / bounds.height))
let width = beganFrame.size.width * scale
let height = beganFrame.size.height * scale
// 计算x和y。保持手指在图片上的相对位置不变。
// 即如果手势开始时,手指在图片X轴三分之一处,那么在移动图片时,保持手指始终位于图片X轴的三分之一处
let xRate = (beganTouch.x - beganFrame.origin.x) / beganFrame.size.width
let currentTouchDeltaX = xRate * width
let x = currentTouch.x - currentTouchDeltaX
let yRate = (beganTouch.y - beganFrame.origin.y) / beganFrame.size.height
let currentTouchDeltaY = yRate * height
let y = currentTouch.y - currentTouchDeltaY
return (CGRect(x: x.isNaN ? 0 : x, y: y.isNaN ? 0 : y, width: width, height: height), scale)
}
/// 复位ImageView
private func resetImageViewPosition() {
// 如果图片当前显示的size小于原size,则重置为原size
let size = computeImageLayoutSize(for: imageView.image, in: scrollView)
let needResetSize = imageView.bounds.size.width < size.width || imageView.bounds.size.height < size.height
UIView.animate(withDuration: 0.25) {
self.imageView.center = self.computeImageLayoutCenter(in: self.scrollView)
if needResetSize {
self.imageView.bounds.size = size
}
}
}
open override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
// 只处理pan手势
guard let pan = gestureRecognizer as? UIPanGestureRecognizer else {
return true
}
let velocity = pan.velocity(in: self)
// 向上滑动时,不响应手势
if velocity.y < 0 {
return false
}
// 横向滑动时,不响应pan手势
if abs(Int(velocity.x)) > Int(velocity.y) {
return false
}
// 向下滑动,如果图片顶部超出可视区域,不响应手势
if scrollView.contentOffset.y > 0 {
return false
}
// 响应允许范围内的下滑手势
return true
}
open var showContentView: UIView {
return imageView
}
}
//
// JXPhotoBrowserLog.swift
// JXPhotoBrowser
//
// Created by JiongXing on 2019/11/12.
// Copyright © 2019 JiongXing. All rights reserved.
//
import Foundation
public struct JXPhotoBrowserLog {
/// 日志重要程度等级
public enum Level: Int {
case low = 0
case middle
case high
case forbidden
}
/// 允许输出日志的最低等级。`forbidden`为禁止所有日志
public static var level: Level = .forbidden
public static func low(_ item: @autoclosure () -> Any) {
if level.rawValue <= Level.low.rawValue {
print("[JXPhotoBrowser] [low]", item())
}
}
public static func middle(_ item: @autoclosure () -> Any) {
if level.rawValue <= Level.middle.rawValue {
print("[JXPhotoBrowser] [middle]", item())
}
}
public static func high(_ item: @autoclosure () -> Any) {
if level.rawValue <= Level.high.rawValue {
print("[JXPhotoBrowser] [high]", item())
}
}
}
//
// JXPhotoBrowserNoneAnimator.swift
// JXPhotoBrowser
//
// Created by JiongXing on 2019/11/26.
// Copyright © 2019 JiongXing. All rights reserved.
//
import UIKit
/// 使用本类以实现不出现转场动画的需求
open class JXPhotoBrowserNoneAnimator: JXPhotoBrowserFadeAnimator {
public override init() {
super.init()
showDuration = 0
dismissDuration = 0
}
}
//
// JXPhotoBrowserNumberPageIndicator.swift
// JXPhotoBrowser
//
// Created by JiongXing on 2019/11/25.
// Copyright © 2019 JiongXing. All rights reserved.
//
import UIKit
open class JXPhotoBrowserNumberPageIndicator: UILabel, JXPhotoBrowserPageIndicator {
/// 页码与顶部的距离
open lazy var topPadding: CGFloat = {
if #available(iOS 11.0, *),
let window = UIApplication.shared.keyWindow {
return window.safeAreaInsets.top
}
return 20
}()
public convenience init() {
self.init(frame: .zero)
}
public override init(frame: CGRect) {
super.init(frame: frame)
config()
}
required public init?(coder: NSCoder) {
super.init(coder: coder)
config()
}
private func config() {
font = UIFont.systemFont(ofSize: 17)
textAlignment = .center
textColor = UIColor.white
backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.4)
layer.masksToBounds = true
}
public func setup(with browser: JXPhotoBrowser) {
}
private var total: Int = 0
public func reloadData(numberOfItems: Int, pageIndex: Int) {
total = numberOfItems
text = "\(pageIndex + 1) / \(total)"
sizeToFit()
frame.size.width += frame.height
layer.cornerRadius = frame.height / 2
if let view = superview {
center.x = view.bounds.width / 2
frame.origin.y = topPadding
}
isHidden = numberOfItems <= 1
}
public func didChanged(pageIndex: Int) {
text = "\(pageIndex + 1) / \(total)"
}
}
//
// JXPhotoBrowserPageIndicator.swift
// JXPhotoBrowser
//
// Created by JiongXing on 2019/11/25.
// Copyright © 2019 JiongXing. All rights reserved.
//
import UIKit
public protocol JXPhotoBrowserPageIndicator: UIView {
func setup(with browser: JXPhotoBrowser)
func reloadData(numberOfItems: Int, pageIndex: Int)
func didChanged(pageIndex: Int)
}
//
// JXPhotoBrowserSmoothZoomAnimator.swift
// JXPhotoBrowser
//
// Created by JiongXing on 2019/11/26.
// Copyright © 2019 JiongXing. All rights reserved.
//
import UIKit
/// 更丝滑的Zoom动画
open class JXPhotoBrowserSmoothZoomAnimator: NSObject, JXPhotoBrowserAnimatedTransitioning {
open var showDuration: TimeInterval = 0.25
open var dismissDuration: TimeInterval = 0.25
open var isNavigationAnimation = false
public typealias TransitionViewAndFrame = (transitionView: UIView, thumbnailFrame: CGRect)
public typealias TransitionViewAndFrameProvider = (_ index: Int, _ destinationView: UIView) -> TransitionViewAndFrame?
/// 获取转场缩放的视图与前置Frame
open var transitionViewAndFrameProvider: TransitionViewAndFrameProvider = { _, _ in return nil }
/// 替补的动画方案
open lazy var substituteAnimator: JXPhotoBrowserAnimatedTransitioning = JXPhotoBrowserFadeAnimator()
public init(transitionViewAndFrame: @escaping TransitionViewAndFrameProvider) {
transitionViewAndFrameProvider = transitionViewAndFrame
}
public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return isForShow ? showDuration : dismissDuration
}
public func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
if isForShow {
playShowAnimation(context: transitionContext)
} else {
playDismissAnimation(context: transitionContext)
}
}
private func playShowAnimation(context: UIViewControllerContextTransitioning) {
guard let browser = photoBrowser else {
context.completeTransition(!context.transitionWasCancelled)
return
}
if isNavigationAnimation,
let fromView = context.view(forKey: .from),
let fromViewSnapshot = snapshot(with: fromView),
let toView = context.view(forKey: .to) {
toView.insertSubview(fromViewSnapshot, at: 0)
}
context.containerView.addSubview(browser.view)
guard let (transitionView, thumbnailFrame, destinationFrame) = transitionViewAndFrames(with: browser) else {
// 转为执行替补动画
substituteAnimator.isForShow = isForShow
substituteAnimator.photoBrowser = photoBrowser
substituteAnimator.isNavigationAnimation = isNavigationAnimation
substituteAnimator.animateTransition(using: context)
return
}
browser.maskView.alpha = 0
browser.browserView.isHidden = true
transitionView.frame = thumbnailFrame
context.containerView.addSubview(transitionView)
UIView.animate(withDuration: showDuration, animations: {
browser.maskView.alpha = 1.0
transitionView.frame = destinationFrame
}) { _ in
browser.browserView.isHidden = false
browser.view.insertSubview(browser.maskView, belowSubview: browser.browserView)
transitionView.removeFromSuperview()
context.completeTransition(!context.transitionWasCancelled)
}
}
private func playDismissAnimation(context: UIViewControllerContextTransitioning) {
guard let browser = photoBrowser else {
return
}
guard let (transitionView, thumbnailFrame, destinationFrame) = transitionViewAndFrames(with: browser) else {
// 转为执行替补动画
substituteAnimator.isForShow = isForShow
substituteAnimator.photoBrowser = photoBrowser
substituteAnimator.isNavigationAnimation = isNavigationAnimation
substituteAnimator.animateTransition(using: context)
return
}
browser.browserView.isHidden = true
transitionView.frame = destinationFrame
context.containerView.addSubview(transitionView)
UIView.animate(withDuration: showDuration, animations: {
browser.maskView.alpha = 0
transitionView.frame = thumbnailFrame
}) { _ in
if let toView = context.view(forKey: .to) {
context.containerView.addSubview(toView)
}
transitionView.removeFromSuperview()
context.completeTransition(!context.transitionWasCancelled)
}
}
private func transitionViewAndFrames(with browser: JXPhotoBrowser) -> (UIView, CGRect, CGRect)? {
let browserView = browser.browserView
let destinationView = browser.view!
guard let transitionContext = transitionViewAndFrameProvider(browser.pageIndex, destinationView) else {
return nil
}
guard let cell = browserView.visibleCells[browserView.pageIndex] as? JXPhotoBrowserZoomSupportedCell else {
return nil
}
let showContentView = cell.showContentView
let destinationFrame = showContentView.convert(showContentView.bounds, to: destinationView)
return (transitionContext.transitionView, transitionContext.thumbnailFrame, destinationFrame)
}
}
//
// JXPhotoBrowserView.swift
// JXPhotoBrowser
//
// Created by JiongXing on 2019/11/14.
// Copyright © 2019 JiongXing. All rights reserved.
//
import UIKit
open class JXPhotoBrowserView: UIView, UIScrollViewDelegate {
/// 弱引用PhotoBrowser
open weak var photoBrowser: JXPhotoBrowser?
/// 询问当前数据总量
open lazy var numberOfItems: () -> Int = { 0 }
/// 返回可复用的Cell类。用户可根据index返回不同的类。本闭包将在每次复用Cell时实时调用。
open lazy var cellClassAtIndex: (_ index: Int) -> JXPhotoBrowserCell.Type = { _ in
JXPhotoBrowserImageCell.self
}
/// 刷新Cell数据。本闭包将在Cell完成位置布局后调用。
open lazy var reloadCellAtIndex: (JXPhotoBrowser.ReloadCellContext) -> Void = { _ in }
/// 自然滑动引起的页码改变时回调
open lazy var didChangedPageIndex: (_ index: Int) -> Void = { _ in }
/// Cell将显示
open lazy var cellWillAppear: (JXPhotoBrowserCell, Int) -> Void = { _, _ in }
/// Cell将不显示
open lazy var cellWillDisappear: (JXPhotoBrowserCell, Int) -> Void = { _, _ in }
/// Cell已显示
open lazy var cellDidAppear: (JXPhotoBrowserCell, Int) -> Void = { _, _ in }
/// 滑动方向
open var scrollDirection: JXPhotoBrowser.ScrollDirection = .horizontal
/// 项间距
open var itemSpacing: CGFloat = 30
/// 当前页码。给本属性赋值不会触发`didChangedPageIndex`闭包。
open var pageIndex = 0 {
didSet {
if pageIndex != oldValue {
isPageIndexChanged = true
}
}
}
/// 页码是否已改变
public var isPageIndexChanged = true
/// 容器
open lazy var scrollView: UIScrollView = {
let sv = UIScrollView()
sv.backgroundColor = .clear
sv.showsVerticalScrollIndicator = false
sv.showsHorizontalScrollIndicator = false
sv.isPagingEnabled = true
sv.isScrollEnabled = true
sv.delegate = self
if #available(iOS 11.0, *) {
sv.contentInsetAdjustmentBehavior = .never
}
return sv
}()
var isRotating = false
deinit {
JXPhotoBrowserLog.low("deinit - \(self.classForCoder)")
}
public convenience init() {
self.init(frame: .zero)
}
public override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
public required init?(coder: NSCoder) {
super.init(coder: coder)
setup()
}
open func setup() {
backgroundColor = .clear
addSubview(scrollView)
}
open override func layoutSubviews() {
super.layoutSubviews()
if scrollDirection == .horizontal {
scrollView.frame = CGRect(x: 0, y: 0, width: bounds.width + itemSpacing, height: bounds.height)
} else {
scrollView.frame = CGRect(x: 0, y: 0, width: bounds.width, height: bounds.height + itemSpacing)
}
reloadData()
}
open func resetContentSize() {
let maxIndex = CGFloat(numberOfItems())
if scrollDirection == .horizontal {
scrollView.contentSize = CGSize(width: scrollView.frame.width * maxIndex,
height: scrollView.frame.height)
} else {
scrollView.contentSize = CGSize(width: scrollView.frame.width,
height: scrollView.frame.height * maxIndex)
}
}
/// 刷新数据,同时刷新Cell布局
open func reloadData() {
// 修正pageIndex,同步数据源的变更
pageIndex = max(0, pageIndex)
pageIndex = min(pageIndex, numberOfItems())
resetContentSize()
resetCells()
layoutCells()
reloadItems()
refreshContentOffset()
}
/// 根据页码更新滑动位置
open func refreshContentOffset() {
if scrollDirection == .horizontal {
scrollView.contentOffset = CGPoint(x: CGFloat(pageIndex) * scrollView.bounds.width, y: 0)
} else {
scrollView.contentOffset = CGPoint(x: 0, y: CGFloat(pageIndex) * scrollView.bounds.height)
}
}
open func scrollViewDidScroll(_ scrollView: UIScrollView) {
// 屏幕旋转时会触发本方法。此时不可更改pageIndex
if isRotating {
isRotating = false
return
}
if scrollDirection == .horizontal && scrollView.bounds.width > 0 {
pageIndex = Int(round(scrollView.contentOffset.x / (scrollView.bounds.width)))
} else if scrollDirection == .vertical && scrollView.bounds.height > 0 {
pageIndex = Int(round(scrollView.contentOffset.y / (scrollView.bounds.height)))
}
if isPageIndexChanged {
isPageIndexChanged = false
resetCells()
layoutCells()
reloadItems()
didChangedPageIndex(pageIndex)
}
}
public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
if let cell = visibleCells[pageIndex] {
cellDidAppear(cell, pageIndex)
}
}
//
// MARK: - 复用Cell
//
/// 显示中的Cell
open var visibleCells = [Int: JXPhotoBrowserCell]()
/// 缓存中的Cell
open var reusableCells = [String: [JXPhotoBrowserCell]]()
/// 入队
private func enqueue(cell: JXPhotoBrowserCell) {
let name = String(describing: cell.classForCoder)
if var array = reusableCells[name] {
array.append(cell)
reusableCells[name] = array
} else {
reusableCells[name] = [cell]
}
}
/// 出队,没缓存则新建
private func dequeue(cellType: JXPhotoBrowserCell.Type, browser: JXPhotoBrowser) -> JXPhotoBrowserCell {
var cell: JXPhotoBrowserCell
let name = String(describing: cellType.classForCoder())
if var array = reusableCells[name], array.count > 0 {
JXPhotoBrowserLog.middle("命中缓存!\(name)")
cell = array.removeFirst()
reusableCells[name] = array
} else {
JXPhotoBrowserLog.middle("新建Cell! \(name)")
cell = cellType.generate(with: browser)
}
return cell
}
/// 重置所有Cell的位置。更新 visibleCells 和 reusableCells
open func resetCells() {
guard let browser = photoBrowser else {
return
}
var removeFromVisibles = [Int]()
for (index, cell) in visibleCells {
if index == pageIndex {
continue
}
cellWillDisappear(cell, index)
cell.removeFromSuperview()
enqueue(cell: cell)
removeFromVisibles.append(index)
}
removeFromVisibles.forEach { visibleCells.removeValue(forKey: $0) }
// 添加要显示的cell
let itemsTotalCount = numberOfItems()
for index in (pageIndex - 1)...(pageIndex + 1) {
if index < 0 || index > itemsTotalCount - 1 {
continue
}
if index == pageIndex && visibleCells[index] != nil {
continue
}
let clazz = cellClassAtIndex(index)
JXPhotoBrowserLog.middle("Required class name: \(String(describing: clazz))")
JXPhotoBrowserLog.middle("index:\(index) 出队!")
let cell = dequeue(cellType: clazz, browser: browser)
visibleCells[index] = cell
scrollView.addSubview(cell)
}
}
/// 刷新所有显示中的Cell位置
open func layoutCells() {
let cellWidth = bounds.width
let cellHeight = bounds.height
for (index, cell) in visibleCells {
if scrollDirection == .horizontal {
cell.frame = CGRect(x: CGFloat(index) * (cellWidth + itemSpacing), y: 0, width: cellWidth, height: cellHeight)
} else {
cell.frame = CGRect(x: 0, y: CGFloat(index) * (cellHeight + itemSpacing), width: cellWidth, height: cellHeight)
}
}
}
/// 刷新所有Cell的数据
open func reloadItems() {
visibleCells.forEach { [weak self] index, cell in
guard let `self` = self else { return }
self.reloadCellAtIndex((cell, index, self.pageIndex))
cell.setNeedsLayout()
}
if let cell = visibleCells[pageIndex] {
cellWillAppear(cell, pageIndex)
}
}
}
//
// JXPhotoBrowserZoomAnimator.swift
// JXPhotoBrowser
//
// Created by JiongXing on 2019/11/26.
// Copyright © 2019 JiongXing. All rights reserved.
//
import UIKit
/// Zoom动画
open class JXPhotoBrowserZoomAnimator: NSObject, JXPhotoBrowserAnimatedTransitioning {
open var showDuration: TimeInterval = 0.25
open var dismissDuration: TimeInterval = 0.25
open var isNavigationAnimation = false
public typealias PreviousViewAtIndexClosure = (_ index: Int) -> UIView?
/// 转场动画的前向视图
open var previousViewProvider: PreviousViewAtIndexClosure = { _ in return nil }
/// 替补的动画方案
open lazy var substituteAnimator: JXPhotoBrowserAnimatedTransitioning = JXPhotoBrowserFadeAnimator()
public init(previousView: @escaping PreviousViewAtIndexClosure) {
previousViewProvider = previousView
}
public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return isForShow ? showDuration : dismissDuration
}
public func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
if isForShow {
playShowAnimation(context: transitionContext)
} else {
playDismissAnimation(context: transitionContext)
}
}
private func playShowAnimation(context: UIViewControllerContextTransitioning) {
guard let browser = photoBrowser else {
context.completeTransition(!context.transitionWasCancelled)
return
}
if isNavigationAnimation,
let fromView = context.view(forKey: .from),
let fromViewSnapshot = snapshot(with: fromView),
let toView = context.view(forKey: .to) {
toView.insertSubview(fromViewSnapshot, at: 0)
}
context.containerView.addSubview(browser.view)
guard let (snap1, snap2, thumbnailFrame, destinationFrame) = snapshotsAndFrames(browser: browser) else {
// 转为执行替补动画
substituteAnimator.isForShow = isForShow
substituteAnimator.photoBrowser = photoBrowser
substituteAnimator.isNavigationAnimation = isNavigationAnimation
substituteAnimator.animateTransition(using: context)
return
}
snap1.frame = thumbnailFrame
snap2.frame = thumbnailFrame
snap2.alpha = 0
browser.maskView.alpha = 0
browser.browserView.isHidden = true
context.containerView.addSubview(snap1)
context.containerView.addSubview(snap2)
UIView.animate(withDuration: showDuration, animations: {
browser.maskView.alpha = 1.0
snap1.frame = destinationFrame
snap1.alpha = 0
snap2.frame = destinationFrame
snap2.alpha = 1.0
}) { _ in
browser.browserView.isHidden = false
browser.view.insertSubview(browser.maskView, belowSubview: browser.browserView)
snap1.removeFromSuperview()
snap2.removeFromSuperview()
context.completeTransition(!context.transitionWasCancelled)
}
}
private func playDismissAnimation(context: UIViewControllerContextTransitioning) {
guard let browser = photoBrowser else {
return
}
guard let (snap1, snap2, thumbnailFrame, destinationFrame) = snapshotsAndFrames(browser: browser) else {
// 转为执行替补动画
substituteAnimator.isForShow = isForShow
substituteAnimator.photoBrowser = photoBrowser
substituteAnimator.isNavigationAnimation = isNavigationAnimation
substituteAnimator.animateTransition(using: context)
return
}
snap1.frame = destinationFrame
snap1.alpha = 0
snap2.frame = destinationFrame
context.containerView.addSubview(snap1)
context.containerView.addSubview(snap2)
browser.browserView.isHidden = true
UIView.animate(withDuration: showDuration, animations: {
browser.maskView.alpha = 0
snap1.frame = thumbnailFrame
snap1.alpha = 0
snap2.frame = thumbnailFrame
snap2.alpha = 1.0
}) { _ in
if let toView = context.view(forKey: .to) {
context.containerView.addSubview(toView)
}
snap1.removeFromSuperview()
snap2.removeFromSuperview()
context.completeTransition(!context.transitionWasCancelled)
}
}
private func snapshotsAndFrames(browser: JXPhotoBrowser) -> (UIView, UIView, CGRect, CGRect)? {
let browserView = browser.browserView
let view = browser.view
let closure = previousViewProvider
guard let previousView = closure(browserView.pageIndex) else {
return nil
}
guard let cell = browserView.visibleCells[browserView.pageIndex] as? JXPhotoBrowserZoomSupportedCell else {
return nil
}
let thumbnailFrame = previousView.convert(previousView.bounds, to: view)
let showContentView = cell.showContentView
// 两Rect求交集,得出显示中的区域
let destinationFrame = cell.convert(cell.bounds.intersection(showContentView.frame), to: view)
guard let snap1 = fastSnapshot(with: previousView) else {
JXPhotoBrowserLog.high("取不到前截图!")
return nil
}
guard let snap2 = snapshot(with: cell.showContentView) else {
JXPhotoBrowserLog.high("取不到后截图!")
return nil
}
return (snap1, snap2, thumbnailFrame, destinationFrame)
}
}
//
// JXPhotoBrowserZoomSupportedCell.swift
// JXPhotoBrowser
//
// Created by JiongXing on 2019/11/22.
// Copyright © 2019 JiongXing. All rights reserved.
//
import UIKit
/// 在Zoom转场时使用
protocol JXPhotoBrowserZoomSupportedCell: UIView {
/// 内容视图
var showContentView: UIView { get }
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment