├── Pods ├── Headers │ ├── Private │ │ └── VTMagic │ │ │ ├── VTMagic.h │ │ │ ├── VTEnumType.h │ │ │ ├── VTMenuBar.h │ │ │ ├── VTMagicView.h │ │ │ ├── UIColor+VTMagic.h │ │ │ ├── VTContentView.h │ │ │ ├── VTMagicMacros.h │ │ │ ├── VTMagicProtocol.h │ │ │ ├── UIButton+VTMagic.h │ │ │ ├── VTMagicController.h │ │ │ ├── UIScrollView+VTMagic.h │ │ │ └── UIViewController+VTMagic.h │ └── Public │ │ └── VTMagic │ │ ├── VTMagic.h │ │ ├── VTEnumType.h │ │ ├── VTMenuBar.h │ │ ├── VTContentView.h │ │ ├── VTMagicMacros.h │ │ ├── VTMagicView.h │ │ ├── UIColor+VTMagic.h │ │ ├── VTMagicProtocol.h │ │ ├── UIButton+VTMagic.h │ │ ├── VTMagicController.h │ │ ├── UIScrollView+VTMagic.h │ │ └── UIViewController+VTMagic.h ├── Target Support Files │ ├── VTMagic │ │ ├── VTMagic-prefix.pch │ │ ├── VTMagic-dummy.m │ │ └── VTMagic.xcconfig │ ├── Pods-VTMagic │ │ ├── Pods-VTMagic-dummy.m │ │ ├── Pods-VTMagic.debug.xcconfig │ │ ├── Pods-VTMagic.release.xcconfig │ │ ├── Pods-VTMagic-acknowledgements.markdown │ │ ├── Pods-VTMagic-acknowledgements.plist │ │ ├── Pods-VTMagic-frameworks.sh │ │ └── Pods-VTMagic-resources.sh │ └── Pods-VTMagicTests │ │ ├── Pods-VTMagicTests-dummy.m │ │ ├── Pods-VTMagicTests-acknowledgements.markdown │ │ ├── Pods-VTMagicTests.debug.xcconfig │ │ ├── Pods-VTMagicTests.release.xcconfig │ │ ├── Pods-VTMagicTests-acknowledgements.plist │ │ ├── Pods-VTMagicTests-frameworks.sh │ │ └── Pods-VTMagicTests-resources.sh ├── Manifest.lock ├── Local Podspecs │ └── VTMagic.podspec.json └── Pods.xcodeproj │ └── xcuserdata │ └── tianzhuo.xcuserdatad │ └── xcschemes │ ├── xcschememanagement.plist │ ├── VTMagic.xcscheme │ ├── Pods-VTMagic.xcscheme │ └── Pods-VTMagicTests.xcscheme ├── Gif └── magic.gif ├── VTMagicDemo ├── Images.xcassets │ ├── Contents.json │ ├── Home │ │ ├── Contents.json │ │ ├── image_0.imageset │ │ │ ├── image_0.png │ │ │ └── Contents.json │ │ ├── image_1.imageset │ │ │ ├── image_1.png │ │ │ └── Contents.json │ │ ├── image_2.imageset │ │ │ ├── image_2.png │ │ │ └── Contents.json │ │ ├── image_3.imageset │ │ │ ├── image_3.png │ │ │ └── Contents.json │ │ ├── image_4.imageset │ │ │ ├── image_4.png │ │ │ └── Contents.json │ │ ├── image_5.imageset │ │ │ ├── image_5.png │ │ │ └── Contents.json │ │ ├── image_6.imageset │ │ │ ├── image_6.png │ │ │ └── Contents.json │ │ ├── image_7.imageset │ │ │ ├── image_7.png │ │ │ └── Contents.json │ │ ├── image_8.imageset │ │ │ ├── image_8.png │ │ │ └── Contents.json │ │ ├── image_9.imageset │ │ │ ├── image_9.png │ │ │ └── Contents.json │ │ ├── image_10.imageset │ │ │ ├── image_10.png │ │ │ └── Contents.json │ │ ├── image_11.imageset │ │ │ ├── image_11.png │ │ │ └── Contents.json │ │ └── image_12.imageset │ │ │ ├── image_12.png │ │ │ └── Contents.json │ ├── Center │ │ ├── Contents.json │ │ ├── magic_arrow.imageset │ │ │ ├── magic_arrow.png │ │ │ └── Contents.json │ │ └── magic_search.imageset │ │ │ ├── magic_search@2x.png │ │ │ └── Contents.json │ └── AppIcon.appiconset │ │ └── Contents.json ├── VTTabBarController.h ├── Classes │ ├── Data │ │ ├── VTWebViewController.h │ │ ├── VTDataViewController.h │ │ ├── VTWebViewController.m │ │ └── VTDataViewController.m │ ├── Center │ │ ├── VTCenterViewController.h │ │ └── VTCenterViewController.m │ ├── Detail │ │ ├── VTDetailViewController.h │ │ ├── View │ │ │ ├── VTMenuItem.h │ │ │ └── VTMenuItem.m │ │ ├── VTRelateViewController.h │ │ ├── VTChatViewController.h │ │ ├── VTChatViewController.m │ │ ├── VTRelateViewController.m │ │ └── VTDetailViewController.m │ ├── Bubble │ │ ├── VTBubbleViewController.h │ │ └── VTBubbleViewController.m │ ├── Divide │ │ ├── VTDivideViewController.h │ │ └── VTDivideViewController.m │ ├── Home │ │ ├── VTHomeViewController.h │ │ ├── VTGridViewController.h │ │ ├── VTRecomViewController.h │ │ ├── View │ │ │ ├── VTRecomCell.h │ │ │ ├── VTGridViewCell.h │ │ │ ├── VTGridViewCell.m │ │ │ └── VTRecomCell.m │ │ ├── VTRecomViewController.m │ │ ├── VTGridViewController.m │ │ └── VTHomeViewController.m │ └── Models │ │ ├── MenuInfo.m │ │ ├── MenuInfo.h │ │ ├── DataManager.h │ │ └── DataManager.m ├── AppDelegate.h ├── VTMagic-Prefix.pch ├── main.m ├── Info.plist ├── AppDelegate.m ├── VTTabBarController.m └── Base.lproj │ └── LaunchScreen.xib ├── VTMagic.xcodeproj ├── project.xcworkspace │ ├── xcuserdata │ │ ├── zhuo.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ │ └── tianzhuo.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ └── contents.xcworkspacedata └── xcuserdata │ ├── tianzhuo.xcuserdatad │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ │ ├── xcschememanagement.plist │ │ └── VTMagic.xcscheme │ └── zhuo.xcuserdatad │ └── xcschemes │ ├── xcschememanagement.plist │ └── VTMagicView.xcscheme ├── VTMagic ├── UIButton+VTMagic.m ├── UIButton+VTMagic.h ├── VTMagic.h ├── UIScrollView+VTMagic.h ├── UIViewController+VTMagic.h ├── UIViewController+VTMagic.m ├── VTMagicProtocol.h ├── UIScrollView+VTMagic.m ├── VTMagicController.h ├── UIColor+VTMagic.h ├── VTEnumType.h ├── VTMagicMacros.h ├── UIColor+VTMagic.m ├── VTContentView.h ├── VTMagicController.m ├── VTMenuBar.h └── VTContentView.m ├── VTMagic.xcworkspace ├── contents.xcworkspacedata └── xcuserdata │ └── tianzhuo.xcuserdatad │ └── xcdebugger │ └── Breakpoints_v2.xcbkptlist ├── Podfile.lock ├── Podfile ├── VTMagicTests ├── Info.plist └── VTMagicTests.m ├── LICENSE ├── VTMagic.podspec ├── CHANGELOG_CN.md ├── CHANGELOG_EN.md └── README.md /Pods/Headers/Private/VTMagic/VTMagic.h: -------------------------------------------------------------------------------- 1 | ../../../../VTMagic/VTMagic.h -------------------------------------------------------------------------------- /Pods/Headers/Public/VTMagic/VTMagic.h: -------------------------------------------------------------------------------- 1 | ../../../../VTMagic/VTMagic.h -------------------------------------------------------------------------------- /Pods/Headers/Private/VTMagic/VTEnumType.h: -------------------------------------------------------------------------------- 1 | ../../../../VTMagic/VTEnumType.h -------------------------------------------------------------------------------- /Pods/Headers/Private/VTMagic/VTMenuBar.h: -------------------------------------------------------------------------------- 1 | ../../../../VTMagic/VTMenuBar.h -------------------------------------------------------------------------------- /Pods/Headers/Public/VTMagic/VTEnumType.h: -------------------------------------------------------------------------------- 1 | ../../../../VTMagic/VTEnumType.h -------------------------------------------------------------------------------- /Pods/Headers/Public/VTMagic/VTMenuBar.h: -------------------------------------------------------------------------------- 1 | ../../../../VTMagic/VTMenuBar.h -------------------------------------------------------------------------------- /Pods/Headers/Private/VTMagic/VTMagicView.h: -------------------------------------------------------------------------------- 1 | ../../../../VTMagic/VTMagicView.h -------------------------------------------------------------------------------- /Pods/Headers/Public/VTMagic/VTContentView.h: -------------------------------------------------------------------------------- 1 | ../../../../VTMagic/VTContentView.h -------------------------------------------------------------------------------- /Pods/Headers/Public/VTMagic/VTMagicMacros.h: -------------------------------------------------------------------------------- 1 | ../../../../VTMagic/VTMagicMacros.h -------------------------------------------------------------------------------- /Pods/Headers/Public/VTMagic/VTMagicView.h: -------------------------------------------------------------------------------- 1 | ../../../../VTMagic/VTMagicView.h -------------------------------------------------------------------------------- /Gif/magic.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianzhuo112/VTMagic/HEAD/Gif/magic.gif -------------------------------------------------------------------------------- /Pods/Headers/Private/VTMagic/UIColor+VTMagic.h: -------------------------------------------------------------------------------- 1 | ../../../../VTMagic/UIColor+VTMagic.h -------------------------------------------------------------------------------- /Pods/Headers/Private/VTMagic/VTContentView.h: -------------------------------------------------------------------------------- 1 | ../../../../VTMagic/VTContentView.h -------------------------------------------------------------------------------- /Pods/Headers/Private/VTMagic/VTMagicMacros.h: -------------------------------------------------------------------------------- 1 | ../../../../VTMagic/VTMagicMacros.h -------------------------------------------------------------------------------- /Pods/Headers/Private/VTMagic/VTMagicProtocol.h: -------------------------------------------------------------------------------- 1 | ../../../../VTMagic/VTMagicProtocol.h -------------------------------------------------------------------------------- /Pods/Headers/Public/VTMagic/UIColor+VTMagic.h: -------------------------------------------------------------------------------- 1 | ../../../../VTMagic/UIColor+VTMagic.h -------------------------------------------------------------------------------- /Pods/Headers/Public/VTMagic/VTMagicProtocol.h: -------------------------------------------------------------------------------- 1 | ../../../../VTMagic/VTMagicProtocol.h -------------------------------------------------------------------------------- /Pods/Headers/Private/VTMagic/UIButton+VTMagic.h: -------------------------------------------------------------------------------- 1 | ../../../../VTMagic/UIButton+VTMagic.h -------------------------------------------------------------------------------- /Pods/Headers/Private/VTMagic/VTMagicController.h: -------------------------------------------------------------------------------- 1 | ../../../../VTMagic/VTMagicController.h -------------------------------------------------------------------------------- /Pods/Headers/Public/VTMagic/UIButton+VTMagic.h: -------------------------------------------------------------------------------- 1 | ../../../../VTMagic/UIButton+VTMagic.h -------------------------------------------------------------------------------- /Pods/Headers/Public/VTMagic/VTMagicController.h: -------------------------------------------------------------------------------- 1 | ../../../../VTMagic/VTMagicController.h -------------------------------------------------------------------------------- /Pods/Headers/Private/VTMagic/UIScrollView+VTMagic.h: -------------------------------------------------------------------------------- 1 | ../../../../VTMagic/UIScrollView+VTMagic.h -------------------------------------------------------------------------------- /Pods/Headers/Public/VTMagic/UIScrollView+VTMagic.h: -------------------------------------------------------------------------------- 1 | ../../../../VTMagic/UIScrollView+VTMagic.h -------------------------------------------------------------------------------- /Pods/Headers/Private/VTMagic/UIViewController+VTMagic.h: -------------------------------------------------------------------------------- 1 | ../../../../VTMagic/UIViewController+VTMagic.h -------------------------------------------------------------------------------- /Pods/Headers/Public/VTMagic/UIViewController+VTMagic.h: -------------------------------------------------------------------------------- 1 | ../../../../VTMagic/UIViewController+VTMagic.h -------------------------------------------------------------------------------- /Pods/Target Support Files/VTMagic/VTMagic-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #endif 4 | 5 | -------------------------------------------------------------------------------- /VTMagicDemo/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /VTMagicDemo/Images.xcassets/Home/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /VTMagicDemo/Images.xcassets/Center/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /VTMagicDemo/Images.xcassets/Home/image_0.imageset/image_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianzhuo112/VTMagic/HEAD/VTMagicDemo/Images.xcassets/Home/image_0.imageset/image_0.png -------------------------------------------------------------------------------- /VTMagicDemo/Images.xcassets/Home/image_1.imageset/image_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianzhuo112/VTMagic/HEAD/VTMagicDemo/Images.xcassets/Home/image_1.imageset/image_1.png -------------------------------------------------------------------------------- /VTMagicDemo/Images.xcassets/Home/image_2.imageset/image_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianzhuo112/VTMagic/HEAD/VTMagicDemo/Images.xcassets/Home/image_2.imageset/image_2.png -------------------------------------------------------------------------------- /VTMagicDemo/Images.xcassets/Home/image_3.imageset/image_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianzhuo112/VTMagic/HEAD/VTMagicDemo/Images.xcassets/Home/image_3.imageset/image_3.png -------------------------------------------------------------------------------- /VTMagicDemo/Images.xcassets/Home/image_4.imageset/image_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianzhuo112/VTMagic/HEAD/VTMagicDemo/Images.xcassets/Home/image_4.imageset/image_4.png -------------------------------------------------------------------------------- /VTMagicDemo/Images.xcassets/Home/image_5.imageset/image_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianzhuo112/VTMagic/HEAD/VTMagicDemo/Images.xcassets/Home/image_5.imageset/image_5.png -------------------------------------------------------------------------------- /VTMagicDemo/Images.xcassets/Home/image_6.imageset/image_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianzhuo112/VTMagic/HEAD/VTMagicDemo/Images.xcassets/Home/image_6.imageset/image_6.png -------------------------------------------------------------------------------- /VTMagicDemo/Images.xcassets/Home/image_7.imageset/image_7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianzhuo112/VTMagic/HEAD/VTMagicDemo/Images.xcassets/Home/image_7.imageset/image_7.png -------------------------------------------------------------------------------- /VTMagicDemo/Images.xcassets/Home/image_8.imageset/image_8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianzhuo112/VTMagic/HEAD/VTMagicDemo/Images.xcassets/Home/image_8.imageset/image_8.png -------------------------------------------------------------------------------- /VTMagicDemo/Images.xcassets/Home/image_9.imageset/image_9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianzhuo112/VTMagic/HEAD/VTMagicDemo/Images.xcassets/Home/image_9.imageset/image_9.png -------------------------------------------------------------------------------- /VTMagicDemo/Images.xcassets/Home/image_10.imageset/image_10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianzhuo112/VTMagic/HEAD/VTMagicDemo/Images.xcassets/Home/image_10.imageset/image_10.png -------------------------------------------------------------------------------- /VTMagicDemo/Images.xcassets/Home/image_11.imageset/image_11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianzhuo112/VTMagic/HEAD/VTMagicDemo/Images.xcassets/Home/image_11.imageset/image_11.png -------------------------------------------------------------------------------- /VTMagicDemo/Images.xcassets/Home/image_12.imageset/image_12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianzhuo112/VTMagic/HEAD/VTMagicDemo/Images.xcassets/Home/image_12.imageset/image_12.png -------------------------------------------------------------------------------- /Pods/Target Support Files/VTMagic/VTMagic-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_VTMagic : NSObject 3 | @end 4 | @implementation PodsDummy_VTMagic 5 | @end 6 | -------------------------------------------------------------------------------- /VTMagicDemo/Images.xcassets/Center/magic_arrow.imageset/magic_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianzhuo112/VTMagic/HEAD/VTMagicDemo/Images.xcassets/Center/magic_arrow.imageset/magic_arrow.png -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-VTMagic/Pods-VTMagic-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_VTMagic : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_VTMagic 5 | @end 6 | -------------------------------------------------------------------------------- /VTMagicDemo/Images.xcassets/Center/magic_search.imageset/magic_search@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianzhuo112/VTMagic/HEAD/VTMagicDemo/Images.xcassets/Center/magic_search.imageset/magic_search@2x.png -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-VTMagicTests/Pods-VTMagicTests-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_VTMagicTests : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_VTMagicTests 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-VTMagicTests/Pods-VTMagicTests-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | Generated by CocoaPods - https://cocoapods.org 4 | -------------------------------------------------------------------------------- /VTMagic.xcodeproj/project.xcworkspace/xcuserdata/zhuo.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianzhuo112/VTMagic/HEAD/VTMagic.xcodeproj/project.xcworkspace/xcuserdata/zhuo.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /VTMagic/UIButton+VTMagic.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIButton+VTMagic.m 3 | // Pods 4 | // 5 | // Created by tianzhuo on 8/18/16. 6 | // 7 | // 8 | 9 | #import "UIButton+VTMagic.h" 10 | 11 | @implementation UIButton (VTMagic) 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /VTMagic.xcodeproj/project.xcworkspace/xcuserdata/tianzhuo.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianzhuo112/VTMagic/HEAD/VTMagic.xcodeproj/project.xcworkspace/xcuserdata/tianzhuo.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /VTMagic.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /VTMagic/UIButton+VTMagic.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIButton+VTMagic.h 3 | // Pods 4 | // 5 | // Created by tianzhuo on 8/18/16. 6 | // 7 | // 8 | 9 | #import 10 | #import "VTMagicProtocol.h" 11 | 12 | @interface UIButton (VTMagic) 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /VTMagic/VTMagic.h: -------------------------------------------------------------------------------- 1 | // 2 | // VTMagic.h 3 | // VTMagic 4 | // 5 | // Created by tianzhuo on 15/10/17. 6 | // Copyright © 2015年 tianzhuo. All rights reserved. 7 | // 8 | 9 | #ifndef VTMagic_h 10 | #define VTMagic_h 11 | 12 | #import "VTMagicController.h" 13 | 14 | #endif /* VTMagic_h */ 15 | -------------------------------------------------------------------------------- /VTMagicDemo/VTTabBarController.h: -------------------------------------------------------------------------------- 1 | // 2 | // VTTabBarController.h 3 | // VTMagic 4 | // 5 | // Created by tianzhuo on 6/1/16. 6 | // Copyright © 2016 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface VTTabBarController : UITabBarController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /VTMagic.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /VTMagicDemo/Classes/Data/VTWebViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // VTWebViewController.h 3 | // VTMagic 4 | // 5 | // Created by tianzhuo on 6/1/16. 6 | // Copyright © 2016 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface VTWebViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /VTMagicDemo/Classes/Data/VTDataViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // VTDataViewController.h 3 | // VTMagic 4 | // 5 | // Created by tianzhuo on 6/1/16. 6 | // Copyright © 2016 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface VTDataViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /VTMagicDemo/Classes/Center/VTCenterViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // VTCenterViewController.h 3 | // VTMagic 4 | // 5 | // Created by tianzhuo on 6/1/16. 6 | // Copyright © 2016 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface VTCenterViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /VTMagicDemo/Classes/Detail/VTDetailViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // VTDetailViewController.h 3 | // VTMagic 4 | // 5 | // Created by tianzhuo on 7/7/16. 6 | // Copyright © 2016 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface VTDetailViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /VTMagicDemo/Classes/Bubble/VTBubbleViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // VTBubbleViewController.h 3 | // VTMagic 4 | // 5 | // Created by tianzhuo on 6/4/16. 6 | // Copyright © 2016 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface VTBubbleViewController : VTMagicController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /VTMagicDemo/Classes/Divide/VTDivideViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // VTDivideViewController.h 3 | // VTMagic 4 | // 5 | // Created by tianzhuo on 6/1/16. 6 | // Copyright © 2016 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface VTDivideViewController : VTMagicController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /VTMagicDemo/Classes/Home/VTHomeViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // VTMagicView 4 | // 5 | // Created by tianzhuo on 14-11-11. 6 | // Copyright (c) 2014年 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface VTHomeViewController : VTMagicController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /VTMagicDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // VTMagicView 4 | // 5 | // Created by tianzhuo on 14-11-11. 6 | // Copyright (c) 2014年 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /VTMagicDemo/VTMagic-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // VTMagicView-Prefix.pch 3 | // VTMagicView 4 | // 5 | // Created by tianzhuo on 15/1/6. 6 | // Copyright (c) 2015年 tianzhuo. All rights reserved. 7 | // 8 | 9 | #ifndef VTMagicView_VTMagicView_Prefix_pch 10 | #define VTMagicView_VTMagicView_Prefix_pch 11 | 12 | #import 13 | #import "MenuInfo.h" 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /VTMagicDemo/Classes/Detail/View/VTMenuItem.h: -------------------------------------------------------------------------------- 1 | // 2 | // VTMenuItem.h 3 | // VTMagic 4 | // 5 | // Created by tianzhuo on 7/8/16. 6 | // Copyright © 2016 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface VTMenuItem : UIButton 12 | 13 | 14 | /** 15 | * 是否需要隐藏小圆点,默认YES 16 | */ 17 | @property (nonatomic, assign) BOOL dotHidden; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - VTMagic (1.2.4): 3 | - VTMagic/Core (= 1.2.4) 4 | - VTMagic/Core (1.2.4) 5 | 6 | DEPENDENCIES: 7 | - VTMagic (from `./`) 8 | 9 | EXTERNAL SOURCES: 10 | VTMagic: 11 | :path: ./ 12 | 13 | SPEC CHECKSUMS: 14 | VTMagic: b49e5f456dbcbfd9a3588ba92417233a105bc193 15 | 16 | PODFILE CHECKSUM: 9904a30729e9a4e8ceff7f85b5976ad299be7ee4 17 | 18 | COCOAPODS: 1.0.1 19 | -------------------------------------------------------------------------------- /Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - VTMagic (1.2.4): 3 | - VTMagic/Core (= 1.2.4) 4 | - VTMagic/Core (1.2.4) 5 | 6 | DEPENDENCIES: 7 | - VTMagic (from `./`) 8 | 9 | EXTERNAL SOURCES: 10 | VTMagic: 11 | :path: ./ 12 | 13 | SPEC CHECKSUMS: 14 | VTMagic: b49e5f456dbcbfd9a3588ba92417233a105bc193 15 | 16 | PODFILE CHECKSUM: 9904a30729e9a4e8ceff7f85b5976ad299be7ee4 17 | 18 | COCOAPODS: 1.0.1 19 | -------------------------------------------------------------------------------- /VTMagicDemo/Classes/Detail/VTRelateViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // VTRelateViewController.h 3 | // VTMagic 4 | // 5 | // Created by tianzhuo on 7/7/16. 6 | // Copyright © 2016 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface VTRelateViewController : UITableViewController 12 | 13 | /** 14 | * 菜单信息 15 | */ 16 | @property (nonatomic, strong) MenuInfo *menuInfo; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /VTMagicDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // VTMagicView 4 | // 5 | // Created by tianzhuo on 14-11-11. 6 | // Copyright (c) 2014年 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | target 'VTMagic' do 5 | # Uncomment this line if you're using Swift or would like to use dynamic frameworks 6 | # use_frameworks! 7 | 8 | # Pods for VTMagic 9 | pod 'VTMagic', :path => './' 10 | 11 | target 'VTMagicTests' do 12 | inherit! :search_paths 13 | # Pods for testing 14 | end 15 | 16 | end 17 | -------------------------------------------------------------------------------- /VTMagicDemo/Classes/Home/VTGridViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // GridViewController.h 3 | // VTMagicView 4 | // 5 | // Created by tianzhuo on 14/12/30. 6 | // Copyright (c) 2014年 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class MenuInfo; 12 | @interface VTGridViewController : UICollectionViewController 13 | 14 | /** 15 | * 菜单信息 16 | */ 17 | @property (nonatomic, strong) MenuInfo *menuInfo; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /VTMagicDemo/Classes/Home/VTRecomViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // RecomViewController.h 3 | // VTMagicView 4 | // 5 | // Created by tianzhuo on 14-11-13. 6 | // Copyright (c) 2014年 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class MenuInfo; 12 | @interface VTRecomViewController : UITableViewController 13 | 14 | /** 15 | * 菜单信息 16 | */ 17 | @property (nonatomic, strong) MenuInfo *menuInfo; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /VTMagicDemo/Images.xcassets/Home/image_0.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "image_0.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /VTMagicDemo/Images.xcassets/Home/image_1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "image_1.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /VTMagicDemo/Images.xcassets/Home/image_2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "image_2.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /VTMagicDemo/Images.xcassets/Home/image_3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "image_3.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /VTMagicDemo/Images.xcassets/Home/image_4.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "image_4.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /VTMagicDemo/Images.xcassets/Home/image_5.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "image_5.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /VTMagicDemo/Images.xcassets/Home/image_6.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "image_6.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /VTMagicDemo/Images.xcassets/Home/image_7.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "image_7.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /VTMagicDemo/Images.xcassets/Home/image_8.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "image_8.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /VTMagicDemo/Images.xcassets/Home/image_9.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "image_9.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /VTMagicDemo/Images.xcassets/Home/image_10.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "image_10.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /VTMagicDemo/Images.xcassets/Home/image_11.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "image_11.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /VTMagicDemo/Images.xcassets/Home/image_12.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "image_12.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /VTMagicDemo/Images.xcassets/Center/magic_arrow.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "magic_arrow.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /VTMagicDemo/Images.xcassets/Center/magic_search.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "magic_search@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /VTMagicDemo/Classes/Home/View/VTRecomCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // VTRecomCell.h 3 | // VTMagicView 4 | // 5 | // Created by tianzhuo on 5/27/16. 6 | // Copyright © 2016 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface VTRecomCell : UITableViewCell 12 | 13 | @property (nonatomic, strong) UIImageView *iconView; 14 | @property (nonatomic, strong) UILabel *titleLabel; 15 | @property (nonatomic, strong) UILabel *descLabel; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /VTMagicDemo/Classes/Home/View/VTGridViewCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // VTGridViewCell.h 3 | // VTMagicView 4 | // 5 | // Created by tianzhuo on 5/27/16. 6 | // Copyright © 2016 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface VTGridViewCell : UICollectionViewCell 12 | 13 | @property (nonatomic, strong) UIImageView *imageView; 14 | @property (nonatomic, strong) UILabel *titleLabel; 15 | @property (nonatomic, strong) UILabel *commentLabel; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-VTMagicTests/Pods-VTMagicTests.debug.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/VTMagic" 3 | LIBRARY_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/VTMagic" 4 | OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/VTMagic" 5 | OTHER_LDFLAGS = $(inherited) -ObjC 6 | PODS_BUILD_DIR = $BUILD_DIR 7 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-VTMagicTests/Pods-VTMagicTests.release.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/VTMagic" 3 | LIBRARY_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/VTMagic" 4 | OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/VTMagic" 5 | OTHER_LDFLAGS = $(inherited) -ObjC 6 | PODS_BUILD_DIR = $BUILD_DIR 7 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | -------------------------------------------------------------------------------- /Pods/Target Support Files/VTMagic/VTMagic.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/VTMagic 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/VTMagic" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/VTMagic" 4 | OTHER_LDFLAGS = -framework "UIKit" 5 | PODS_BUILD_DIR = $BUILD_DIR 6 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_ROOT = ${SRCROOT} 8 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 9 | SKIP_INSTALL = YES 10 | -------------------------------------------------------------------------------- /VTMagic.xcodeproj/xcuserdata/tianzhuo.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-VTMagic/Pods-VTMagic.debug.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/VTMagic" 3 | LIBRARY_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/VTMagic" 4 | OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/VTMagic" 5 | OTHER_LDFLAGS = $(inherited) -ObjC -l"VTMagic" -framework "UIKit" 6 | PODS_BUILD_DIR = $BUILD_DIR 7 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-VTMagic/Pods-VTMagic.release.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/VTMagic" 3 | LIBRARY_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/VTMagic" 4 | OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/VTMagic" 5 | OTHER_LDFLAGS = $(inherited) -ObjC -l"VTMagic" -framework "UIKit" 6 | PODS_BUILD_DIR = $BUILD_DIR 7 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | -------------------------------------------------------------------------------- /VTMagicDemo/Classes/Models/MenuInfo.m: -------------------------------------------------------------------------------- 1 | // 2 | // MenuInfo.m 3 | // VTMagic 4 | // 5 | // Created by tianzhuo on 6/30/16. 6 | // Copyright © 2016 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import "MenuInfo.h" 10 | 11 | @implementation MenuInfo 12 | 13 | + (instancetype)menuInfoWithTitl:(NSString *)title { 14 | MenuInfo *menu = [[MenuInfo alloc] init]; 15 | menu.menuId = [NSString stringWithFormat:@"%d", arc4random_uniform(100000)]; 16 | menu.title = title; 17 | return menu; 18 | } 19 | 20 | - (NSString *)description { 21 | return [NSString stringWithFormat:@"<%@: %p title: %@ menuId: %@>", [self class], self, _title, _menuId]; 22 | } 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /VTMagic/UIScrollView+VTMagic.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIScrollView+VTMagic.h 3 | // VTMagic 4 | // 5 | // Created by tianzhuo on 15/7/9. 6 | // Copyright (c) 2015年 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UIScrollView (VTMagic) 12 | 13 | /** 14 | * 判断指定的frame是否在当前屏幕的可视范围内 15 | * 16 | * @param frame 视图的frame 17 | * @param preloading 是否需要预加载 18 | * 19 | * @return 是否需要显示 20 | */ 21 | - (BOOL)vtm_isNeedDisplayWithFrame:(CGRect)frame preloading:(BOOL)preloading; 22 | /** 23 | * 判断menuItem的frame是否需要显示在菜单栏上 24 | * 25 | * @param frame item的frame 26 | * 27 | * @return 是否需要显示在菜单栏上 28 | */ 29 | - (BOOL)vtm_isItemNeedDisplayWithFrame:(CGRect)frame; 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /VTMagicDemo/Classes/Models/MenuInfo.h: -------------------------------------------------------------------------------- 1 | // 2 | // MenuInfo.h 3 | // VTMagic 4 | // 5 | // Created by tianzhuo on 6/30/16. 6 | // Copyright © 2016 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface MenuInfo : NSObject 12 | 13 | /** 14 | * 菜单标题 15 | */ 16 | @property (nonatomic, copy) NSString *title; 17 | /** 18 | * 菜单id 19 | */ 20 | @property (nonatomic, copy) NSString *menuId; 21 | /** 22 | * 最近一次刷新时间,自动刷新时间间隔为1h 23 | */ 24 | @property (nonatomic, assign) NSTimeInterval lastTime; 25 | 26 | /** 27 | * 根据标题自动生成相应menu 28 | * 29 | * @param title 标题 30 | * 31 | * @return MenuInfo对象 32 | */ 33 | + (instancetype)menuInfoWithTitl:(NSString *)title; 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /VTMagic/UIViewController+VTMagic.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController+VTMagic.h 3 | // VTMagic 4 | // 5 | // Created by tianzhuo on 15/7/9. 6 | // Copyright (c) 2015年 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "VTMagicProtocol.h" 11 | 12 | @interface UIViewController (VTMagic) 13 | 14 | /** 15 | * 缓存重用标识 16 | */ 17 | @property (nonatomic, copy) NSString *reuseIdentifier; 18 | 19 | /** 20 | * 主控制器 21 | */ 22 | @property (nonatomic, weak, readonly) UIViewController *magicController; 23 | 24 | /** 25 | * 当前控制器的页面索引,仅当前显示的和预加载的控制器有相应索引, 26 | * 若没有找到相应索引则返回NSNotFound 27 | * 28 | * @return 页面索引 29 | */ 30 | - (NSInteger)vtm_pageIndex; 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /VTMagicDemo/Classes/Models/DataManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // DataManager.h 3 | // VTMagic 4 | // 5 | // Created by tianzhuo on 7/21/16. 6 | // Copyright © 2016 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface DataManager : NSObject 12 | 13 | /** 14 | * 数据管理对象单例 15 | * 16 | * @return self 17 | */ 18 | + (instancetype)sharedInstance; 19 | /** 20 | * 保存页面数据 21 | * 22 | * @param info 页面数据 23 | * @param menuId 菜单id 24 | */ 25 | - (void)savePageInfo:(NSArray *)infoList menuId:(NSString *)menuId; 26 | /** 27 | * 根据menuId获取相应页面的数据 28 | * 29 | * @param menuId 菜单id 30 | * 31 | * @return 页面数据,可为nil 32 | */ 33 | - (NSArray *)pageInfoWithMenuId:(NSString *)menuId; 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /VTMagic.xcodeproj/xcuserdata/zhuo.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | VTMagicView.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | AEA0D4F21A11E4AB00229B72 16 | 17 | primary 18 | 19 | 20 | AEA0D50B1A11E4AB00229B72 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /VTMagic.xcodeproj/xcuserdata/tianzhuo.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | VTMagic.xcscheme 8 | 9 | isShown 10 | 11 | orderHint 12 | 0 13 | 14 | 15 | SuppressBuildableAutocreation 16 | 17 | AEA0D4F21A11E4AB00229B72 18 | 19 | primary 20 | 21 | 22 | AEA0D50B1A11E4AB00229B72 23 | 24 | primary 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /VTMagicTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | tianzhuo.com.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /VTMagicDemo/Classes/Detail/VTChatViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // VTChatViewController.h 3 | // VTMagic 4 | // 5 | // Created by tianzhuo on 7/8/16. 6 | // Copyright © 2016 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class VTChatViewController; 12 | @protocol VTChatViewControllerDelegate 13 | 14 | @optional 15 | /** 16 | * 聊天室接到新消息时触发 17 | * 18 | * @param chatViewController self 19 | */ 20 | - (void)chatViewControllerDidReciveNewMessages:(VTChatViewController *)chatViewController; 21 | 22 | @end 23 | 24 | @interface VTChatViewController : UITableViewController 25 | 26 | /** 27 | * 菜单信息 28 | */ 29 | @property (nonatomic, strong) MenuInfo *menuInfo; 30 | /** 31 | * 代理 32 | */ 33 | @property (nonatomic, weak) id delegate; 34 | 35 | /** 36 | * 销毁定时器 37 | */ 38 | - (void)invalidateTimer; 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-VTMagicTests/Pods-VTMagicTests-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Generated by CocoaPods - https://cocoapods.org 18 | Title 19 | 20 | Type 21 | PSGroupSpecifier 22 | 23 | 24 | StringsTable 25 | Acknowledgements 26 | Title 27 | Acknowledgements 28 | 29 | 30 | -------------------------------------------------------------------------------- /VTMagicDemo/Classes/Models/DataManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // DataManager.m 3 | // VTMagic 4 | // 5 | // Created by tianzhuo on 7/21/16. 6 | // Copyright © 2016 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import "DataManager.h" 10 | 11 | @interface DataManager() 12 | 13 | @property (nonatomic, strong) NSMutableDictionary *dataInfo; 14 | 15 | @end 16 | 17 | @implementation DataManager 18 | 19 | + (instancetype)sharedInstance { 20 | static DataManager *sharedManager = nil; 21 | static dispatch_once_t onceToken; 22 | dispatch_once(&onceToken, ^{ 23 | sharedManager = [[self alloc] init]; 24 | sharedManager.dataInfo = [[NSMutableDictionary alloc] init]; 25 | }); 26 | return sharedManager; 27 | } 28 | 29 | - (void)savePageInfo:(NSArray *)infoList menuId:(NSString *)menuId { 30 | if (menuId) { 31 | [_dataInfo setObject:[NSArray arrayWithArray:infoList] forKey:menuId]; 32 | } 33 | } 34 | 35 | - (NSArray *)pageInfoWithMenuId:(NSString *)menuId { 36 | return [_dataInfo objectForKey:menuId]; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /VTMagicTests/VTMagicTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // VTMagicViewTests.m 3 | // VTMagicViewTests 4 | // 5 | // Created by tianzhuo on 14-11-11. 6 | // Copyright (c) 2014年 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface VTMagicTests : XCTestCase 13 | 14 | @end 15 | 16 | @implementation VTMagicTests 17 | 18 | - (void)setUp { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown { 24 | // Put teardown code here. This method is called after the invocation of each test method in the class. 25 | [super tearDown]; 26 | } 27 | 28 | - (void)testExample { 29 | // This is an example of a functional test case. 30 | XCTAssert(YES, @"Pass"); 31 | } 32 | 33 | - (void)testPerformanceExample { 34 | // This is an example of a performance test case. 35 | [self measureBlock:^{ 36 | // Put the code you want to measure the time of here. 37 | }]; 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /Pods/Local Podspecs/VTMagic.podspec.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "VTMagic", 3 | "version": "1.2.4", 4 | "license": { 5 | "type": "MIT" 6 | }, 7 | "homepage": "https://github.com/tianzhuo112/VTMagic", 8 | "authors": { 9 | "tianzhuo": "tianzhuo112@163.com" 10 | }, 11 | "summary": "A page container for iOS.", 12 | "description": "VTMagic is a page container library for iOS, it can manage many different pages. VTMagicView contains a menu bar and a content view, all pages are add to content view.", 13 | "source": { 14 | "git": "https://github.com/tianzhuo112/VTMagic.git", 15 | "tag": "1.2.4" 16 | }, 17 | "public_header_files": "VTMagic/VTMagic.h", 18 | "source_files": "VTMagic/VTMagic.h", 19 | "platforms": { 20 | "ios": "6.0" 21 | }, 22 | "requires_arc": true, 23 | "frameworks": "UIKit", 24 | "subspecs": [ 25 | { 26 | "name": "Core", 27 | "platforms": { 28 | "ios": "6.0" 29 | }, 30 | "exclude_files": "VTMagic/VTMagic.h", 31 | "source_files": "VTMagic/**/*.{h,m}" 32 | } 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/tianzhuo.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Pods-VTMagic.xcscheme 8 | 9 | isShown 10 | 11 | 12 | Pods-VTMagicTests.xcscheme 13 | 14 | isShown 15 | 16 | 17 | VTMagic.xcscheme 18 | 19 | isShown 20 | 21 | 22 | 23 | SuppressBuildableAutocreation 24 | 25 | 291875C610C7BCCCC1F40306CB888D15 26 | 27 | primary 28 | 29 | 30 | 7A938632579B2EA1BE9165E1486543D7 31 | 32 | primary 33 | 34 | 35 | F46AF923E4C9E617067E150B11989AC5 36 | 37 | primary 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Victor Tian(tianzhuo112@163.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /VTMagic/UIViewController+VTMagic.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController+VTMagic.m 3 | // VTMagic 4 | // 5 | // Created by tianzhuo on 15/7/9. 6 | // Copyright (c) 2015年 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import "UIViewController+VTMagic.h" 10 | #import 11 | #import "VTMagicView.h" 12 | 13 | static const void *kVTReuseIdentifier = &kVTReuseIdentifier; 14 | 15 | @implementation UIViewController (VTMagic) 16 | 17 | #pragma mark - accessor methods 18 | - (void)setReuseIdentifier:(NSString *)reuseIdentifier { 19 | objc_setAssociatedObject(self, kVTReuseIdentifier, reuseIdentifier, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 20 | } 21 | 22 | - (NSString *)reuseIdentifier { 23 | return objc_getAssociatedObject(self, kVTReuseIdentifier); 24 | } 25 | 26 | - (UIViewController *)magicController { 27 | UIViewController *viewController = self.parentViewController; 28 | while (viewController) { 29 | if ([viewController conformsToProtocol:@protocol(VTMagicProtocol)]) break; 30 | viewController = viewController.parentViewController; 31 | } 32 | return (UIViewController *)viewController; 33 | } 34 | 35 | - (NSInteger)vtm_pageIndex { 36 | return [self.magicController.magicView pageIndexForViewController:self]; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /VTMagic/VTMagicProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // VTMagicProtocol.h 3 | // VTMagic 4 | // 5 | // Created by tianzhuo on 15/7/11. 6 | // Copyright (c) 2015年 tianzhuo. All rights reserved. 7 | // 8 | // private protocol 9 | 10 | #import 11 | #import "VTEnumType.h" 12 | 13 | @class VTMagicView; 14 | @protocol VTMagicProtocol 15 | 16 | @required 17 | /** 18 | * 当前页面索引 19 | */ 20 | - (NSInteger)currentPage; 21 | /** 22 | * 当前页面索引的setter方法,外部禁止直接修改 23 | */ 24 | - (void)setCurrentPage:(NSInteger)currentPage; 25 | 26 | /** 27 | * 当前页面控制器 28 | */ 29 | - (__kindof UIViewController *)currentViewController; 30 | /** 31 | * 当前页面控制器的setter方法 32 | * 33 | * @param currentViewController 当前页面控制器,外部禁止直接修改 34 | */ 35 | - (void)setCurrentViewController:(UIViewController *)currentViewController; 36 | 37 | /** 38 | * magicController的显示状态 39 | * 40 | * @return 生命周期状态 41 | */ 42 | - (VTAppearanceState)appearanceState; 43 | 44 | /** 45 | * 任何magicController默认都会有对应的magicView 46 | */ 47 | - (VTMagicView *)magicView; 48 | 49 | @end 50 | 51 | 52 | /** 53 | * magic重用协议 54 | */ 55 | @protocol VTMagicReuseProtocol 56 | 57 | @optional 58 | /** 59 | * 控制器或menuItem即将被重用时触发, 60 | * 由magicController的子页面控制器或自定义的menuItem实现 61 | */ 62 | - (void)vtm_prepareForReuse; 63 | 64 | @end 65 | -------------------------------------------------------------------------------- /VTMagic/UIScrollView+VTMagic.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIScrollView+VTMagic.m 3 | // VTMagic 4 | // 5 | // Created by tianzhuo on 15/7/9. 6 | // Copyright (c) 2015年 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import "UIScrollView+VTMagic.h" 10 | 11 | @implementation UIScrollView (VTMagic) 12 | 13 | - (BOOL)vtm_isNeedDisplayWithFrame:(CGRect)frame preloading:(BOOL)preloading { 14 | CGRect visibleRect = (CGRect){CGPointMake(self.contentOffset.x, 0), self.frame.size}; 15 | CGRect intersectRegion = CGRectIntersection(frame, visibleRect); 16 | BOOL isOnScreen = !CGRectIsNull(intersectRegion) || !CGRectIsEmpty(intersectRegion); 17 | if (!preloading) { 18 | BOOL isNotBorder = 0 != (int)self.contentOffset.x%(int)self.frame.size.width; 19 | return isOnScreen && (isNotBorder ?: 0 != intersectRegion.size.width); 20 | } 21 | return isOnScreen; 22 | } 23 | 24 | - (BOOL)vtm_isItemNeedDisplayWithFrame:(CGRect)frame { 25 | frame.size.width *= 2; 26 | BOOL isOnScreen = [self vtm_isNeedDisplayWithFrame:frame preloading:YES]; 27 | if (isOnScreen) { 28 | return YES; 29 | } 30 | 31 | frame.size.width *= 0.5; 32 | frame.origin.x -= frame.size.width; 33 | isOnScreen = [self vtm_isNeedDisplayWithFrame:frame preloading:YES]; 34 | return isOnScreen; 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /VTMagicDemo/Classes/Detail/VTChatViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // VTChatViewController.m 3 | // VTMagic 4 | // 5 | // Created by tianzhuo on 7/8/16. 6 | // Copyright © 2016 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import "VTChatViewController.h" 10 | #import 11 | 12 | @interface VTChatViewController() 13 | 14 | @property (nonatomic, strong) NSTimer *chatTimer; 15 | 16 | @end 17 | 18 | @implementation VTChatViewController 19 | 20 | - (void)viewDidLoad { 21 | [super viewDidLoad]; 22 | 23 | [self fireChatTimer]; 24 | } 25 | 26 | #pragma mark - functional methods 27 | - (void)fireChatTimer { 28 | if (_chatTimer) { 29 | return; 30 | } 31 | _chatTimer = [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:@selector(notifyReciveMessages) userInfo:nil repeats:YES]; 32 | } 33 | 34 | - (void)invalidateTimer { 35 | if ([_chatTimer isValid]) { 36 | [_chatTimer invalidate]; 37 | } 38 | _chatTimer = nil; 39 | } 40 | 41 | - (void)notifyReciveMessages { 42 | BOOL isDisplayed = [self.magicController.currentViewController isEqual:self]; 43 | if (!isDisplayed && [_delegate respondsToSelector:@selector(chatViewControllerDidReciveNewMessages:)]) { 44 | [_delegate chatViewControllerDidReciveNewMessages:self]; 45 | } 46 | } 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-VTMagic/Pods-VTMagic-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## VTMagic 5 | 6 | The MIT License (MIT) 7 | 8 | Copyright (c) 2016 Victor Tian(tianzhuo112@163.com) 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | Generated by CocoaPods - https://cocoapods.org 29 | -------------------------------------------------------------------------------- /VTMagic.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod spec lint VTMagic.podspec' to ensure this is a 3 | # valid spec and to remove all comments including this before submitting the spec. 4 | # 5 | # To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html 6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | s.name = 'VTMagic' 11 | s.version = '1.2.4' 12 | s.license = { :type => 'MIT' } 13 | s.homepage = 'https://github.com/tianzhuo112/VTMagic' 14 | s.authors = { 'tianzhuo' => 'tianzhuo112@163.com' } 15 | s.summary = 'A page container for iOS.' 16 | s.description = <<-DESC 17 | VTMagic is a page container library for iOS, it can manage many different pages. VTMagicView contains a menu bar and a content view, all pages are add to content view. 18 | DESC 19 | s.source = { :git => 'https://github.com/tianzhuo112/VTMagic.git', :tag => s.version.to_s } 20 | 21 | s.public_header_files = 'VTMagic/VTMagic.h' 22 | s.source_files = 'VTMagic/VTMagic.h' 23 | 24 | s.platform = :ios, "6.0" 25 | s.requires_arc = true 26 | s.frameworks = 'UIKit' 27 | 28 | s.subspec 'Core' do |ss| 29 | ss.ios.deployment_target = '6.0' 30 | ss.exclude_files = 'VTMagic/VTMagic.h' 31 | ss.source_files = 'VTMagic/**/*.{h,m}' 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /VTMagicDemo/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "83.5x83.5", 66 | "scale" : "2x" 67 | } 68 | ], 69 | "info" : { 70 | "version" : 1, 71 | "author" : "xcode" 72 | } 73 | } -------------------------------------------------------------------------------- /VTMagic/VTMagicController.h: -------------------------------------------------------------------------------- 1 | // 2 | // VTMagicController.h 3 | // VTMagicView 4 | // 5 | // Created by tianzhuo on 14-11-11. 6 | // Copyright (c) 2014年 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "UIViewController+VTMagic.h" 11 | #import "UIButton+VTMagic.h" 12 | #import "VTMagicMacros.h" 13 | #import "VTMagicView.h" 14 | 15 | NS_ASSUME_NONNULL_BEGIN 16 | 17 | @interface VTMagicController : UIViewController 18 | 19 | /** 20 | * magic view,等同于self.view 21 | */ 22 | @property (nonatomic, strong) VTMagicView *magicView; 23 | 24 | /** 25 | * 当前页面对应的索引 26 | */ 27 | @property (nonatomic, assign) NSUInteger currentPage; 28 | 29 | /** 30 | * 生命周期状态 31 | */ 32 | @property (nonatomic, assign) VTAppearanceState appearanceState; 33 | 34 | /** 35 | * 当前显示的控制器 36 | */ 37 | @property (nonatomic, strong, nullable) __kindof UIViewController *currentViewController; 38 | 39 | /** 40 | * 屏幕上可见的控制器 41 | */ 42 | @property (nonatomic, strong, readonly) NSArray<__kindof UIViewController *> *viewControllers; 43 | 44 | /** 45 | * 获取索引对应的ViewController 46 | * 若index超出范围或对应控制器不可见,则返回nil 47 | * 48 | * @param index 索引 49 | * 50 | * @return UIViewController对象 51 | */ 52 | - (nullable __kindof UIViewController *)viewControllerAtPage:(NSUInteger)pageIndex; 53 | 54 | /** 55 | * 切换到指定页面 56 | * 57 | * @param pageIndex 页面索引 58 | * @param animated 是否需要动画执行 59 | */ 60 | - (void)switchToPage:(NSUInteger)pageIndex animated:(BOOL)animated; 61 | 62 | @end 63 | 64 | NS_ASSUME_NONNULL_END 65 | -------------------------------------------------------------------------------- /VTMagic/UIColor+VTMagic.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor+VTMagic.h 3 | // VTMagic 4 | // 5 | // Created by tianzhuo on 15/7/23. 6 | // Copyright (c) 2015年 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /** 12 | * VTColor结构体对象,用来存放颜色的RGBA值 13 | */ 14 | typedef struct { 15 | CGFloat red; 16 | CGFloat green; 17 | CGFloat blue; 18 | CGFloat alph; 19 | } VTColor; 20 | 21 | /** 各项值为0的VTColor对象 */ 22 | CG_EXTERN const VTColor VTColorZero; 23 | 24 | /** 等比放大VTColor对象的各项值 */ 25 | CG_EXTERN VTColor VTColorScale(VTColor color, CGFloat scale); 26 | 27 | /** 将两个VTColor对象的各项值相加,计算其和 */ 28 | CG_EXTERN VTColor VTColorAdd(VTColor aColor1, VTColor aColor2); 29 | 30 | /** 将两个VTColor的各项值相减,计算其差值 */ 31 | CG_EXTERN VTColor VTColorReduce(VTColor aColor, VTColor reductionColor); 32 | 33 | /** 判断两个VTColor对象是否相等 */ 34 | CG_EXTERN BOOL VTColorIsEqual(VTColor aColor1, VTColor aColor2); 35 | 36 | /** 判断VTColor对象的各项值是否为0 */ 37 | CG_EXTERN BOOL VTColorIsZero(VTColor aColor); 38 | 39 | /** 初始化一个VTColor结构体对象 */ 40 | CG_EXTERN VTColor VTColorMake(CGFloat red, CGFloat green, CGFloat blue, CGFloat alph); 41 | 42 | @interface UIColor (VTMagic) 43 | 44 | /** 45 | * 将UIColor转成对应的RGBA色值 46 | * 47 | * @return VTColor结构体 48 | */ 49 | - (VTColor)vtm_changeToVTColor; 50 | 51 | /** 52 | * 将VTColor转成对应的UIColor 53 | * 54 | * @param color VTColor 55 | * 56 | * @return UIColor对象 57 | */ 58 | + (UIColor *)vtm_colorWithVTColor:(VTColor)color; 59 | 60 | /** 61 | * 按指定比例对给定的两种颜色进行合成,并返回对应的UIColor对象 62 | * 63 | * @param baseColor 基础颜色 64 | * @param anoColor 参考颜色 65 | * @param scale 合成比例,scale为0时返回baseColor对应的颜色,scale为1时返回anoColor对应的颜色 66 | * 67 | * @return 合成颜色UIColor对象 68 | */ 69 | + (UIColor *)vtm_compositeColor:(VTColor)baseColor anoColor:(VTColor)anoColor scale:(CGFloat)scale; 70 | 71 | @end 72 | -------------------------------------------------------------------------------- /VTMagicDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | NSAppTransportSecurity 26 | 27 | NSAllowsArbitraryLoads 28 | 29 | 30 | UILaunchStoryboardName 31 | LaunchScreen 32 | UIRequiredDeviceCapabilities 33 | 34 | armv7 35 | 36 | UISupportedInterfaceOrientations 37 | 38 | UIInterfaceOrientationPortrait 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UISupportedInterfaceOrientations~ipad 43 | 44 | UIInterfaceOrientationPortrait 45 | UIInterfaceOrientationPortraitUpsideDown 46 | UIInterfaceOrientationLandscapeLeft 47 | UIInterfaceOrientationLandscapeRight 48 | 49 | UIViewControllerBasedStatusBarAppearance 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /VTMagic/VTEnumType.h: -------------------------------------------------------------------------------- 1 | // 2 | // VTEnumType.h 3 | // VTMagic 4 | // 5 | // Created by tianzhuo on 1/17/16. 6 | // Copyright © 2016 tianzhuo. All rights reserved. 7 | // 8 | 9 | #ifndef VTEnumType_h 10 | #define VTEnumType_h 11 | 12 | /** 导航栏布局样式 */ 13 | typedef NS_ENUM(NSUInteger, VTLayoutStyle) { 14 | /** 默认样式,item自适应文本宽度,间距由itemSpacing决定 */ 15 | VTLayoutStyleDefault, 16 | /** items等分导航条宽度,常用于item数较少时 */ 17 | VTLayoutStyleDivide, 18 | /** 导航栏居中布局,间距由itemSpacing决定 */ 19 | VTLayoutStyleCenter, 20 | }; 21 | 22 | /** 页面滑动切换样式 */ 23 | typedef NS_ENUM(NSUInteger, VTSwitchStyle) { 24 | /** 默认样式,切换时有颜色渐变效果 */ 25 | VTSwitchStyleDefault, 26 | /** 延迟响应切换 */ 27 | VTSwitchStyleStiff, 28 | /** ??? */ 29 | VTSwitchStyleUnknown, 30 | }; 31 | 32 | /** 页面切换事件 */ 33 | typedef NS_ENUM(NSUInteger, VTSwitchEvent) { 34 | /** 加载 */ 35 | VTSwitchEventLoad, 36 | /** 滑动 */ 37 | VTSwitchEventScroll, 38 | /** 点击 */ 39 | VTSwitchEventClick, 40 | /** 未知 */ 41 | VTSwitchEventUnkown, 42 | }; 43 | 44 | /** 导航栏滑块样式 */ 45 | typedef NS_ENUM(NSUInteger, VTSliderStyle) { 46 | /** 默认显示下划线 */ 47 | VTSliderStyleDefault, 48 | /** 气泡样式,该样式下需结合bubbleInset和bubbleRadius使用 */ 49 | VTSliderStyleBubble, 50 | }; 51 | 52 | /** UIPanGestureRecognizer手势方向 */ 53 | typedef NS_ENUM(NSUInteger, VTPanRecognizerDirection) { 54 | /** 初始方向 */ 55 | VTPanRecognizerDirectionUndefined, 56 | /** 垂直方向 */ 57 | VTPanRecognizerDirectionVertical, 58 | /** 水平方向 */ 59 | VTPanRecognizerDirectionHorizontal, 60 | }; 61 | 62 | /** 页面生命周期状态状态 */ 63 | typedef NS_ENUM(NSUInteger, VTAppearanceState) { 64 | /** 默认状态,已经消失 */ 65 | VTAppearanceStateDidDisappear, 66 | /** 即将消失 */ 67 | VTAppearanceStateWillDisappear, 68 | /** 即将显示 */ 69 | VTAppearanceStateWillAppear, 70 | /** 已经显示 */ 71 | VTAppearanceStateDidAppear, 72 | }; 73 | 74 | #endif /* VTEnumType_h */ 75 | -------------------------------------------------------------------------------- /VTMagic/VTMagicMacros.h: -------------------------------------------------------------------------------- 1 | // 2 | // VTMagicMacros.h 3 | // VTMagicView 4 | // 5 | // Created by tianzhuo on 15/1/6. 6 | // Copyright (c) 2015年 tianzhuo. All rights reserved. 7 | // 8 | 9 | #ifndef VTMagicMacros_h 10 | #define VTMagicMacros_h 11 | 12 | /** 自定义Log,日志开关 0-关闭 1-开启 */ 13 | #define __LOGDEBUG__ (0) 14 | 15 | #if defined(__LOGDEBUG__) && __LOGDEBUG__ && DEBUG 16 | #define VTLog(...) NSLog(__VA_ARGS__) 17 | #else 18 | #define VTLog(...) 19 | #endif 20 | 21 | // deprecated macro 22 | #define VT_DEPRECATED_IN(VERSION) __attribute__((deprecated("This property has been deprecated and will be removed in VTMagic " VERSION "."))) 23 | 24 | // weakSelf 25 | #define __DEFINE_WEAK_SELF__ __weak __typeof(&*self) weakSelf = self; 26 | #define __DEFINE_STRONG_SELF__ __strong __typeof(&*weakSelf) strongSelf = weakSelf; 27 | 28 | // 打印当前方法名 29 | #define VTPRINT_METHOD VTLog(@"==%@:%p running method '%@'==", self.class, self, NSStringFromSelector(_cmd)); 30 | 31 | // 打印方法运行时间 32 | #define TIME_BEGIN NSDate * startTime = [NSDate date]; 33 | #define TIME_END VTLog(@"time interval: %f", -[startTime timeIntervalSinceNow]); 34 | 35 | // 设置RGBA颜色值 36 | #define RGBACOLOR(r,g,b,a) [UIColor colorWithRed:(r)/255.0f green:(g)/255.0f blue:(b)/255.0f alpha:(a)] 37 | #define RGBCOLOR(r,g,b) [UIColor colorWithRed:(r)/255.0f green:(g)/255.0f blue:(b)/255.0f alpha:(1.f)] 38 | // 十六进制转UIColor 39 | #define kVTColorFromHex(hexValue) [UIColor \ 40 | colorWithRed:((float)((hexValue & 0xFF0000) >> 16))/255.0 \ 41 | green:((float)((hexValue & 0xFF00) >> 8))/255.0 \ 42 | blue:((float)(hexValue & 0xFF))/255.0 alpha:1.0] 43 | 44 | // 判断设备是否是iPhone 45 | #define kiPhoneDevice ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) 46 | #define KiPhoneX ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(1125, 2436), [[UIScreen mainScreen] currentMode].size) : NO) 47 | 48 | 49 | // tabbar高度 50 | #define VTTABBAR_HEIGHT (49) 51 | // 状态栏高度 52 | #define VTSTATUSBAR_HEIGHT (KiPhoneX ? 44 : 20) 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /VTMagicDemo/Classes/Detail/View/VTMenuItem.m: -------------------------------------------------------------------------------- 1 | // 2 | // VTMenuItem.m 3 | // VTMagic 4 | // 5 | // Created by tianzhuo on 7/8/16. 6 | // Copyright © 2016 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import "VTMenuItem.h" 10 | #import 11 | 12 | @interface VTMenuItem() 13 | 14 | @property (nonatomic, strong) UIView *dotView; 15 | 16 | @end 17 | 18 | @implementation VTMenuItem 19 | 20 | - (instancetype)initWithFrame:(CGRect)frame { 21 | self = [super initWithFrame:frame]; 22 | if (self) { 23 | _dotHidden = YES; 24 | _dotView = [[UIView alloc] init]; 25 | _dotView.translatesAutoresizingMaskIntoConstraints = NO; 26 | _dotView.backgroundColor = [UIColor redColor]; 27 | _dotView.layer.masksToBounds = YES; 28 | _dotView.layer.cornerRadius = 4.f; 29 | _dotView.hidden = _dotHidden; 30 | [self addSubview:_dotView]; 31 | 32 | [self setNeedsUpdateConstraints]; 33 | } 34 | return self; 35 | } 36 | 37 | - (void)updateConstraints { 38 | [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[_dotView(8)]-20-|" 39 | options:0 40 | metrics:nil 41 | views:NSDictionaryOfVariableBindings(_dotView)]]; 42 | [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-5-[_dotView(8)]" 43 | options:0 44 | metrics:nil 45 | views:NSDictionaryOfVariableBindings(_dotView)]]; 46 | 47 | [super updateConstraints]; 48 | } 49 | 50 | - (void)vtm_prepareForReuse { 51 | VTLog(@"menuItem will be reused: %@", self); 52 | } 53 | 54 | #pragma mark - accessor methods 55 | - (void)setDotHidden:(BOOL)dotHidden { 56 | _dotHidden = dotHidden; 57 | _dotView.hidden = dotHidden; 58 | } 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /VTMagicDemo/Classes/Home/View/VTGridViewCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // VTGridViewCell.m 3 | // VTMagicView 4 | // 5 | // Created by tianzhuo on 5/27/16. 6 | // Copyright © 2016 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import "VTGridViewCell.h" 10 | 11 | @interface VTGridViewCell() 12 | 13 | 14 | @end 15 | 16 | @implementation VTGridViewCell 17 | 18 | - (instancetype)initWithFrame:(CGRect)frame { 19 | self = [super initWithFrame:frame]; 20 | if (self) { 21 | _imageView = [[UIImageView alloc] init]; 22 | _imageView.translatesAutoresizingMaskIntoConstraints = NO; 23 | [self.contentView addSubview:_imageView]; 24 | 25 | _titleLabel = [[UILabel alloc] init]; 26 | _titleLabel.translatesAutoresizingMaskIntoConstraints = NO; 27 | _titleLabel.font = [UIFont systemFontOfSize:15]; 28 | _titleLabel.numberOfLines = 0; 29 | [self.contentView addSubview:_titleLabel]; 30 | 31 | _commentLabel = [[UILabel alloc] init]; 32 | _commentLabel.textAlignment = NSTextAlignmentRight; 33 | _commentLabel.translatesAutoresizingMaskIntoConstraints = NO; 34 | _commentLabel.font = [UIFont systemFontOfSize:10]; 35 | [self.contentView addSubview:_commentLabel]; 36 | 37 | [self setNeedsUpdateConstraints]; 38 | } 39 | return self; 40 | } 41 | 42 | - (void)updateConstraints { 43 | [self addContraints:@"H:|-0-[_imageView]-0-|" forViews:NSDictionaryOfVariableBindings(_imageView)]; 44 | [self addContraints:@"V:|-0-[_imageView(99)]" forViews:NSDictionaryOfVariableBindings(_imageView)]; 45 | 46 | [self addContraints:@"H:|-0-[_titleLabel]-0-|" forViews:NSDictionaryOfVariableBindings(_titleLabel)]; 47 | [self addContraints:@"V:[_imageView]-5-[_titleLabel(45)]" forViews:NSDictionaryOfVariableBindings(_imageView, _titleLabel)]; 48 | 49 | [self addContraints:@"H:|-0-[_commentLabel]-5-|" forViews:NSDictionaryOfVariableBindings(_commentLabel)]; 50 | [self addContraints:@"V:[_commentLabel]-10-|" forViews:NSDictionaryOfVariableBindings(_commentLabel)]; 51 | 52 | [super updateConstraints]; 53 | } 54 | 55 | - (void)addContraints:(NSString *)constraint forViews:(NSDictionary *)views { 56 | [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:constraint options:0 metrics:nil views:views]]; 57 | } 58 | 59 | @end 60 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-VTMagic/Pods-VTMagic-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | The MIT License (MIT) 18 | 19 | Copyright (c) 2016 Victor Tian(tianzhuo112@163.com) 20 | 21 | Permission is hereby granted, free of charge, to any person obtaining a copy 22 | of this software and associated documentation files (the "Software"), to deal 23 | in the Software without restriction, including without limitation the rights 24 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 25 | copies of the Software, and to permit persons to whom the Software is 26 | furnished to do so, subject to the following conditions: 27 | 28 | The above copyright notice and this permission notice shall be included in all 29 | copies or substantial portions of the Software. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 34 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 36 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 37 | SOFTWARE. 38 | 39 | Title 40 | VTMagic 41 | Type 42 | PSGroupSpecifier 43 | 44 | 45 | FooterText 46 | Generated by CocoaPods - https://cocoapods.org 47 | Title 48 | 49 | Type 50 | PSGroupSpecifier 51 | 52 | 53 | StringsTable 54 | Acknowledgements 55 | Title 56 | Acknowledgements 57 | 58 | 59 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/tianzhuo.xcuserdatad/xcschemes/VTMagic.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 45 | 46 | 52 | 53 | 55 | 56 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/tianzhuo.xcuserdatad/xcschemes/Pods-VTMagic.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 45 | 46 | 52 | 53 | 55 | 56 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/tianzhuo.xcuserdatad/xcschemes/Pods-VTMagicTests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 45 | 46 | 52 | 53 | 55 | 56 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /VTMagicDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // VTMagicView 4 | // 5 | // Created by tianzhuo on 14-11-11. 6 | // Copyright (c) 2014年 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "VTHomeViewController.h" 11 | #import "VTTabBarController.h" 12 | 13 | @interface AppDelegate () 14 | 15 | @end 16 | 17 | @implementation AppDelegate 18 | 19 | 20 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 21 | // Override point for customization after application launch. 22 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 23 | self.window.rootViewController = [[VTTabBarController alloc] init]; 24 | [self.window makeKeyAndVisible]; 25 | return YES; 26 | } 27 | 28 | - (void)applicationWillResignActive:(UIApplication *)application { 29 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 30 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 31 | } 32 | 33 | - (void)applicationDidEnterBackground:(UIApplication *)application { 34 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 35 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 36 | } 37 | 38 | - (void)applicationWillEnterForeground:(UIApplication *)application { 39 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 40 | } 41 | 42 | - (void)applicationDidBecomeActive:(UIApplication *)application { 43 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 44 | } 45 | 46 | - (void)applicationWillTerminate:(UIApplication *)application { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /VTMagicDemo/Classes/Detail/VTRelateViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // VTRelateViewController.m 3 | // VTMagic 4 | // 5 | // Created by tianzhuo on 7/7/16. 6 | // Copyright © 2016 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import "VTRelateViewController.h" 10 | #import "VTRecomCell.h" 11 | 12 | @interface VTRelateViewController () 13 | 14 | @property (nonatomic, strong) NSMutableArray *newsList; 15 | 16 | @end 17 | 18 | @implementation VTRelateViewController 19 | 20 | - (void)viewDidLoad { 21 | [super viewDidLoad]; 22 | 23 | self.tableView.scrollsToTop = NO; 24 | self.view.backgroundColor = RGBCOLOR(239, 239, 239); 25 | self.tableView.rowHeight = 70.f; 26 | 27 | [self fetchNewsData]; 28 | [self.tableView reloadData]; 29 | } 30 | 31 | - (void)viewWillAppear:(BOOL)animated { 32 | [super viewWillAppear:animated]; 33 | 34 | VTPRINT_METHOD 35 | } 36 | 37 | - (void)viewDidAppear:(BOOL)animated { 38 | [super viewDidAppear:animated]; 39 | 40 | self.tableView.scrollsToTop = YES; 41 | VTPRINT_METHOD 42 | } 43 | 44 | - (void)viewWillDisappear:(BOOL)animated { 45 | [super viewWillDisappear:animated]; 46 | 47 | self.tableView.scrollsToTop = NO; 48 | VTPRINT_METHOD 49 | } 50 | 51 | - (void)viewDidDisappear:(BOOL)animated { 52 | [super viewDidDisappear:animated]; 53 | 54 | VTPRINT_METHOD 55 | } 56 | 57 | #pragma mark - Table view data source 58 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 59 | return _newsList.count; 60 | } 61 | 62 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 63 | VTRecomCell *cell = nil; 64 | static NSString *cellID = @"cell.Identifier"; 65 | cell = [tableView dequeueReusableCellWithIdentifier:cellID]; 66 | if (!cell) { 67 | cell = [[VTRecomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID]; 68 | } 69 | NSString *imageName = _newsList[indexPath.row]; 70 | [cell.iconView setImage:[UIImage imageNamed:imageName]]; 71 | cell.titleLabel.text = @"标题标题"; 72 | cell.descLabel.text = @"详情描述详情描述详情描述详情描述详情描述详情描述"; 73 | return cell; 74 | } 75 | 76 | #pragma mark - VTMagicReuseProtocol 77 | - (void)vtm_prepareForReuse { 78 | // reset content offset 79 | NSLog(@"clear old data if needed:%@", self); 80 | [self.tableView setContentOffset:CGPointZero]; 81 | } 82 | 83 | #pragma mark - functional methods 84 | - (void)fetchNewsData { 85 | _newsList = [[NSMutableArray alloc] init]; 86 | for (NSInteger index = 0; index < 50; index++) { 87 | [_newsList addObject:[NSString stringWithFormat:@"image_%d", arc4random_uniform(13)]]; 88 | } 89 | } 90 | 91 | @end 92 | -------------------------------------------------------------------------------- /VTMagicDemo/Classes/Home/View/VTRecomCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // VTRecomCell.m 3 | // VTMagicView 4 | // 5 | // Created by tianzhuo on 5/27/16. 6 | // Copyright © 2016 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import "VTRecomCell.h" 10 | 11 | @interface VTRecomCell() 12 | 13 | 14 | @end 15 | 16 | @implementation VTRecomCell 17 | 18 | - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { 19 | self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; 20 | if (self) { 21 | _iconView = [[UIImageView alloc] init]; 22 | _iconView.translatesAutoresizingMaskIntoConstraints = NO; 23 | _iconView.contentMode = UIViewContentModeScaleAspectFill; 24 | _iconView.clipsToBounds = YES; 25 | [self.contentView addSubview:_iconView]; 26 | 27 | _titleLabel = [[UILabel alloc] init]; 28 | _titleLabel.translatesAutoresizingMaskIntoConstraints = NO; 29 | _titleLabel.font = [UIFont systemFontOfSize:15]; 30 | [self.contentView addSubview:_titleLabel]; 31 | 32 | _descLabel = [[UILabel alloc] init]; 33 | _descLabel.translatesAutoresizingMaskIntoConstraints = NO; 34 | _descLabel.numberOfLines = 0; 35 | _descLabel.font = [UIFont systemFontOfSize:12]; 36 | [self.contentView addSubview:_descLabel]; 37 | 38 | [self setNeedsUpdateConstraints]; 39 | } 40 | return self; 41 | } 42 | 43 | - (void)updateConstraints { 44 | [self addContraints:@"H:|-15-[_iconView(100)]" forViews:NSDictionaryOfVariableBindings(_iconView)]; 45 | [self addContraints:@"V:|-5-[_iconView]-5-|" forViews:NSDictionaryOfVariableBindings(_iconView)]; 46 | 47 | [self addContraints:@"H:[_iconView]-10-[_titleLabel]-10-|" forViews:NSDictionaryOfVariableBindings(_iconView, _titleLabel)]; 48 | NSString *titleContraints = [NSString stringWithFormat:@"V:|-5-[_titleLabel(%f)]", _titleLabel.font.lineHeight]; 49 | [self addContraints:titleContraints forViews:NSDictionaryOfVariableBindings(_titleLabel)]; 50 | 51 | [self addContraints:@"H:[_iconView]-10-[_descLabel]-10-|" forViews:NSDictionaryOfVariableBindings(_iconView, _descLabel)]; 52 | [self addContraints:@"V:[_titleLabel]-5-[_descLabel]-5-|" forViews:NSDictionaryOfVariableBindings(_titleLabel, _descLabel)]; 53 | 54 | [super updateConstraints]; 55 | } 56 | 57 | - (void)addContraints:(NSString *)constraint forViews:(NSDictionary *)views { 58 | [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:constraint options:0 metrics:nil views:views]]; 59 | } 60 | 61 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated { 62 | [super setSelected:selected animated:animated]; 63 | 64 | // Configure the view for the selected state 65 | } 66 | 67 | @end 68 | -------------------------------------------------------------------------------- /VTMagicDemo/Classes/Data/VTWebViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // VTWebViewController.m 3 | // VTMagic 4 | // 5 | // Created by tianzhuo on 6/1/16. 6 | // Copyright © 2016 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import "VTWebViewController.h" 10 | #import 11 | #import 12 | 13 | @interface VTWebViewController() 14 | 15 | @property (nonatomic, strong) WKWebView *webView; 16 | @property (nonatomic, strong) UIPanGestureRecognizer *panRecognizer; 17 | 18 | @end 19 | 20 | @implementation VTWebViewController 21 | 22 | - (void)viewDidLoad { 23 | [super viewDidLoad]; 24 | 25 | [self.view addSubview:self.webView]; 26 | 27 | NSURL *url = [NSURL URLWithString:@"https://www.baidu.com"]; 28 | [_webView loadRequest:[NSURLRequest requestWithURL:url]]; 29 | [self.view setNeedsUpdateConstraints]; 30 | } 31 | 32 | - (void)viewDidAppear:(BOOL)animated { 33 | [super viewDidAppear:animated]; 34 | 35 | self.webView.scrollView.scrollsToTop = YES; 36 | VTPRINT_METHOD 37 | } 38 | 39 | - (void)viewWillDisappear:(BOOL)animated { 40 | [super viewWillDisappear:animated]; 41 | 42 | self.webView.scrollView.scrollsToTop = NO; 43 | VTPRINT_METHOD 44 | } 45 | 46 | #pragma mark - VTMagicReuseProtocol 47 | - (void)vtm_prepareForReuse { 48 | // reset content offset 49 | NSLog(@"clear old data if needed:%@", self); 50 | [self.webView.scrollView setContentOffset:CGPointZero]; 51 | } 52 | 53 | #pragma mark - UIPanGestureRecognizer 54 | - (void)handlePanGesture:(UIPanGestureRecognizer *)recognizer { 55 | // 如果内嵌webView时无法响应滑动手势,可以这样处理 56 | // [self.magicController.magicView handlePanGesture:recognizer]; 57 | } 58 | 59 | - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { 60 | return YES; 61 | } 62 | 63 | #pragma mark - accessors 64 | - (WKWebView *)webView { 65 | if (!_webView) { 66 | _webView = [[WKWebView alloc] initWithFrame:self.view.frame]; 67 | _webView.scrollView.contentInset = UIEdgeInsetsMake(0, 0, VTTABBAR_HEIGHT, 0); 68 | _webView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; 69 | [_webView.scrollView addGestureRecognizer:self.panRecognizer]; 70 | _webView.scrollView.alwaysBounceHorizontal = NO; 71 | _webView.scrollView.scrollsToTop = NO; 72 | _webView.layer.masksToBounds = YES; 73 | } 74 | return _webView; 75 | } 76 | 77 | - (UIPanGestureRecognizer *)panRecognizer { 78 | if (!_panRecognizer) { 79 | _panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)]; 80 | _panRecognizer.cancelsTouchesInView = YES; 81 | _panRecognizer.delegate = self; 82 | } 83 | return _panRecognizer; 84 | } 85 | 86 | @end 87 | -------------------------------------------------------------------------------- /VTMagic/UIColor+VTMagic.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor+VTMagic.m 3 | // VTMagic 4 | // 5 | // Created by tianzhuo on 15/7/23. 6 | // Copyright (c) 2015年 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import "UIColor+VTMagic.h" 10 | #import "VTMagicMacros.h" 11 | 12 | const VTColor VTColorZero; 13 | 14 | VTColor VTColorMake(CGFloat red, CGFloat green, CGFloat blue, CGFloat alph) { 15 | VTColor color; 16 | color.red = red; color.green = green; 17 | color.blue = blue; color.alph = alph; 18 | return color; 19 | } 20 | 21 | VTColor VTColorScale(VTColor color, CGFloat scale) { 22 | VTColor scaleColor; 23 | scaleColor.red = color.red * scale; 24 | scaleColor.green = color.green * scale; 25 | scaleColor.blue = color.blue * scale; 26 | scaleColor.alph = color.alph * scale; 27 | return scaleColor; 28 | } 29 | 30 | VTColor VTColorAdd(VTColor aColor1, VTColor aColor2) { 31 | VTColor finalColor; 32 | finalColor.red = aColor1.red + aColor2.red; 33 | finalColor.green = aColor1.green + aColor2.green; 34 | finalColor.blue = aColor1.blue + aColor2.blue; 35 | finalColor.alph = aColor1.alph + aColor2.alph; 36 | return finalColor; 37 | } 38 | 39 | VTColor VTColorReduce(VTColor aColor, VTColor reductionColor) { 40 | VTColor finalColor; 41 | finalColor.red = aColor.red - reductionColor.red; 42 | finalColor.green = aColor.green - reductionColor.green; 43 | finalColor.blue = aColor.blue - reductionColor.blue; 44 | finalColor.alph = aColor.alph - reductionColor.alph; 45 | return finalColor; 46 | } 47 | 48 | BOOL VTColorIsEqual(VTColor aColor1, VTColor aColor2) { 49 | if (aColor1.red != aColor2.red) { 50 | return NO; 51 | } 52 | if (aColor1.green != aColor2.green) { 53 | return NO; 54 | } 55 | if (aColor1.blue != aColor2.blue) { 56 | return NO; 57 | } 58 | if (aColor1.alph != aColor2.alph) { 59 | return NO; 60 | } 61 | return YES; 62 | } 63 | 64 | BOOL VTColorIsZero(VTColor aColor) { 65 | return VTColorIsEqual(VTColorZero, aColor); 66 | } 67 | 68 | @implementation UIColor (VTMagic) 69 | 70 | - (VTColor)vtm_changeToVTColor { 71 | VTColor color; 72 | if ([self respondsToSelector:@selector(getRed:green:blue:alpha:)]) { 73 | [self getRed:&color.red green:&color.green blue:&color.blue alpha:&color.alph]; 74 | } else { 75 | VTLog(@"change UIColor to VTColor error"); 76 | } 77 | return color; 78 | } 79 | 80 | + (UIColor *)vtm_colorWithVTColor:(VTColor)color { 81 | return [UIColor colorWithRed:color.red green:color.green blue:color.blue alpha:color.alph]; 82 | } 83 | 84 | + (UIColor *)vtm_compositeColor:(VTColor)baseColor anoColor:(VTColor)anoColor scale:(CGFloat)scale { 85 | VTColor diffColor = VTColorReduce(anoColor, baseColor); 86 | VTColor offsetColor = VTColorScale(diffColor, scale); 87 | return [self vtm_colorWithVTColor:VTColorAdd(baseColor, offsetColor)]; 88 | } 89 | 90 | @end 91 | -------------------------------------------------------------------------------- /VTMagicDemo/VTTabBarController.m: -------------------------------------------------------------------------------- 1 | // 2 | // VTTabBarController.m 3 | // VTMagic 4 | // 5 | // Created by tianzhuo on 6/1/16. 6 | // Copyright © 2016 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import "VTTabBarController.h" 10 | #import "VTHomeViewController.h" 11 | #import "VTCenterViewController.h" 12 | #import "VTDivideViewController.h" 13 | #import "VTBubbleViewController.h" 14 | #import "VTDataViewController.h" 15 | 16 | @implementation VTTabBarController 17 | 18 | - (void)viewDidLoad { 19 | [super viewDidLoad]; 20 | 21 | VTHomeViewController *homeVC = [[VTHomeViewController alloc] init]; 22 | UINavigationController *homeNav = [[UINavigationController alloc]initWithRootViewController:homeVC]; 23 | homeNav.navigationBarHidden = YES; 24 | UITabBarItem *homeItem = [self creatTabBarItemWithTitle:@"首页"]; 25 | homeNav.tabBarItem = homeItem; 26 | 27 | VTBubbleViewController *bubbleVC = [[VTBubbleViewController alloc] init]; 28 | UINavigationController *bubbleNav = [[UINavigationController alloc]initWithRootViewController:bubbleVC]; 29 | // bubbleNav.navigationBarHidden = YES; 30 | UITabBarItem *bubbleItem = [self creatTabBarItemWithTitle:@"气泡"]; 31 | bubbleNav.tabBarItem = bubbleItem; 32 | 33 | VTCenterViewController *centerVC = [[VTCenterViewController alloc] init]; 34 | UINavigationController *centerNav = [[UINavigationController alloc]initWithRootViewController:centerVC]; 35 | centerNav.navigationBarHidden = YES; 36 | UITabBarItem *centerItem = [self creatTabBarItemWithTitle:@"居中"]; 37 | centerNav.tabBarItem = centerItem; 38 | 39 | VTDivideViewController *divideVC = [[VTDivideViewController alloc] init]; 40 | UINavigationController *divideNav = [[UINavigationController alloc]initWithRootViewController:divideVC]; 41 | divideNav.navigationBarHidden = YES; 42 | UITabBarItem *divideItem = [self creatTabBarItemWithTitle:@"平分"]; 43 | divideNav.tabBarItem = divideItem; 44 | 45 | VTDataViewController*dataVC = [[VTDataViewController alloc] init]; 46 | UINavigationController *dataNav = [[UINavigationController alloc]initWithRootViewController:dataVC]; 47 | dataNav.navigationBarHidden = YES; 48 | UITabBarItem *dataItem = [self creatTabBarItemWithTitle:@"webview"]; 49 | dataNav.tabBarItem = dataItem; 50 | 51 | self.viewControllers = @[homeNav, bubbleNav, centerNav, divideNav, dataNav]; 52 | 53 | [[UITabBar appearance] setBarTintColor:RGBCOLOR(239, 239, 239)]; 54 | [[UITabBar appearance] setAlpha:0.75]; 55 | } 56 | 57 | - (UITabBarItem *)creatTabBarItemWithTitle:(NSString*)title { 58 | UITabBarItem *item = [[UITabBarItem alloc] initWithTitle:title image:nil selectedImage:nil]; 59 | [item setTitleTextAttributes:@{NSForegroundColorAttributeName:RGBCOLOR(169, 37, 37)} forState:UIControlStateSelected]; 60 | [item setTitleTextAttributes:@{NSForegroundColorAttributeName:[UIColor blackColor]} forState:UIControlStateNormal]; 61 | // item.imageInsets = UIEdgeInsetsMake(0, 0, 0, 0); 62 | return item; 63 | } 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /CHANGELOG_CN.md: -------------------------------------------------------------------------------- 1 | #变更日志 2 | 所有关于这个项目的重要修改都会记录在这个文件中。 3 | 4 | --- 5 | 6 | ## [1.2.4](https://github.com/tianzhuo112/VTMagic/releases/tag/1.2.4) 7 | 发布于2016年八月10日,周三 8 | 9 | ### 更新 10 | - 完善生命周期逻辑,修复在`viewWillAppear:`方法中调用`reloadData`、`reloadDataToPage:`和`switchToPage:animated:`方法时,生命周期异常的问题; 11 | - 删除枚举样式`VTLayoutStyleCustom`,自定义`menuItem`宽度时,直接设置`itemWidth`即可; 12 | - 新增代理方法`itemWidthAtIndex:`和`sliderWidthAtIndex:`,以便自定义任意itemIndex对应的`menuItem`和`sliderView`的宽度; 13 | - 废弃属性needExtendBottom,若想实现半透明效果,请将`edgesForExtendedLayout`设为`UIRectEdgeAll`,具体可参见demo; 14 | - 修复初次调用`viewDidAppear:`方法时,页面frame不正确的问题; 15 | - 修复某种特殊情况下点击边缘菜单项,导航菜单没有自动显示下一项的问题; 16 | - 完善demo工程,新增页面复用时的数据处理逻辑; 17 | - 修复有重名菜单项时,导航菜单聚焦错误的问题; 18 | - 当导航菜单为空时,自动隐藏`sliderView`; 19 | - 重命名内部文件; 20 | - 其它逻辑优化; 21 | 22 | 23 | ## [1.2.3](https://github.com/tianzhuo112/VTMagic/releases/tag/1.2.3) 24 | 发布于2016年七月6日,周三 25 | 26 | ### 更新 27 | - 新增预加载开关,通过修改属性`needPreloading`可控制页面是否需要预加载; 28 | - 新增自定义`sliderView`和`separatorView`的逻辑; 29 | - 新增方法`clearMemoryCache`,以便可以在需要的时候手动清除所有缓存; 30 | - 新增方法`pageIndexForViewController:`以获取任意页面对应的索引;同时新增分类方法`vtm_pageIndex`,以便可以快速获取当前页面的索引; 31 | - 将方法`updateMenuTitles`调整为`reloadMenuTitles`,使其更符合见名知意规范,将属性`needExtendedBottom`重命名为`needExtendBottom`; 32 | - 修复调用`handlePanGesture:`方法时无法准确聚焦菜单栏的问题; 33 | 34 | 35 | ## [1.2.2](https://github.com/tianzhuo112/VTMagic/releases/tag/1.2.2) 36 | 发布于2016年六月29日,周三 37 | 38 | ### 更新 39 | - 新增方法`reloadDataToPage:`,支持刷新数据时直接定位到指定页面,简化逻辑; 40 | - 优化生命周期逻辑,修复多次调用`viewDidAppear:`等方法的问题,完善了多级嵌套VTMagic时,生命周期方法触发异常的问题; 41 | - 调整`VTMagicViewDelegate`代理方法名,`viewDidAppeare:`改为`viewDidAppear:`,`viewDidDisappeare:`改为`viewDidDisappear:`。 42 | 43 | 44 | ## [1.2.1](https://github.com/tianzhuo112/VTMagic/releases/tag/1.2.1) 45 | 发布于2016年六月19日,周日 46 | 47 | ### 更新 48 | - 优化菜单栏显示逻辑,以便menuItem消失时更加自然; 49 | - 新增属性`itemScale`,以使menuItem在切换时能有放大缩小的效果。 50 | 51 | 52 | ## [1.2.0](https://github.com/tianzhuo112/VTMagic/releases/tag/1.2.0) 53 | 发布于2016年六月13日,周一 54 | 55 | ### 更新 56 | - 新增属性`sliderExtension`,以确保滑块两侧相对菜单文本的延长量始终一致; 57 | - 优化VTMagicView内部子视图布局逻辑。 58 | 59 | 60 | ## [1.1.0](https://github.com/tianzhuo112/VTMagic/releases/tag/1.1.0) 61 | 发布于2016年六月4日,周六 62 | 63 | ### 更新 64 | - 新增属性`sliderStyle`,同时增加气泡样式`VTSliderStyleBubble`; 65 | - demo中新增**气泡**模块。 66 | 67 | 68 | ## [1.0.2](https://github.com/tianzhuo112/VTMagic/releases/tag/1.0.2) 69 | 发布于2016年六月2日,周四 70 | 71 | ### 更新 72 | - 导航栏新增了居中布局样式`VTLayoutStyleCenter`; 73 | - 优化了`headerView`的隐藏和显示逻辑; 74 | - demo中新增**居中**、**平分**、**webview**等模块。 75 | 76 | ## [1.0.1](https://github.com/tianzhuo112/VTMagic/releases/tag/1.0.1) 77 | 发布于2016年六月1日,周三 78 | 79 | ### 更新 80 | - 调整了VTMagic在Pods中显示的文件结构。 81 | 82 | 83 | ## [1.0.0](https://github.com/tianzhuo112/VTMagic/releases/tag/1.0.0) 84 | 初始版发布于2016年五月31日,周二 85 | 86 | ### 特性概要 87 | - 每个页面都是一个完整独立的控制器,友好支持个性化自定义; 88 | - 页面切换时能准确触发相应的生命周期方法(`viewWillAppear:`等),便于管理各自页面的数据加载和其它逻辑处理; 89 | - 导航栏支持多种布局样式,包括自适应文本宽度、自动平分、居中布局以及自定义宽度等; 90 | - 导航菜单项(menuItem)支持自定义,menuItem不止能显示文本,还可显示图片; 91 | - 可以在任意子控制器中,通过`self.magicController`获取最近的上层主控制器,方便跨层级处理逻辑; 92 | - 支持内嵌webview,若滑动手势无法响应,可以通过`handlePanGesture:`解决; 93 | - 支持页面重用和横竖屏切换; 94 | - 更多特性请参见`VTMagicView.h`文件。 -------------------------------------------------------------------------------- /VTMagic/VTContentView.h: -------------------------------------------------------------------------------- 1 | // 2 | // VTContentView.h 3 | // VTMagicView 4 | // 5 | // Created by tianzhuo on 14/12/29. 6 | // Copyright (c) 2014年 tianzhuo. All rights reserved. 7 | // 内容页 8 | 9 | #import 10 | #import "VTMagicMacros.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @class VTContentView; 15 | /** 16 | * 数据源协议 17 | */ 18 | @protocol VTContentViewDataSource 19 | /** 20 | * 根据索引获取对应的控制器 21 | * 22 | * @param contentView self 23 | * @param pageIndex 索引 24 | * 25 | * @return 当前索引对应的控制器 26 | */ 27 | - (nullable UIViewController *)contentView:(VTContentView *)contentView viewControllerAtPage:(NSUInteger)pageIndex; 28 | 29 | @end 30 | 31 | @interface VTContentView : UIScrollView 32 | 33 | /** 34 | * 数据源 35 | */ 36 | @property (nonatomic, weak, nullable) id dataSource; 37 | 38 | /** 39 | * 页面数量 40 | */ 41 | @property (nonatomic, assign) NSUInteger pageCount; 42 | 43 | /** 44 | * 当前页面索引 45 | */ 46 | @property (nonatomic, assign) NSUInteger currentPage; 47 | 48 | /** 49 | * 是否需要预加载下一页,默认YES 50 | */ 51 | @property (nonatomic, assign) BOOL needPreloading; 52 | 53 | /** 54 | * 当前屏幕上已加载的控制器 55 | */ 56 | @property (nonatomic, strong, readonly) NSArray *visibleList; 57 | 58 | #pragma mark - public methods 59 | /** 60 | * 刷新数据 61 | */ 62 | - (void)reloadData; 63 | 64 | /** 65 | * 重置所有内容页的frame 66 | */ 67 | - (void)resetPageFrames; 68 | 69 | /** 70 | * 清除所有缓存的页面 71 | */ 72 | - (void)clearMemoryCache; 73 | 74 | /** 75 | * 根据控制器获取对应的页面索引,仅当前显示的和预加载的控制器有相应索引, 76 | * 若没有找到相应索引则返回NSNotFound 77 | * 78 | * @param viewController 页面控制器 79 | * 80 | * @return 页面索引 81 | */ 82 | - (NSInteger)pageIndexForViewController:(nullable UIViewController *)viewController; 83 | 84 | /** 85 | * 根据页面索引获取对应页面的frame 86 | * 87 | * @param pageIndex 页面索引 88 | * 89 | * @return 页面索引 90 | */ 91 | - (CGRect)frameOfViewControllerAtPage:(NSUInteger)pageIndex; 92 | 93 | /** 94 | * 获取索引对应的ViewController 95 | * 若index超出范围或对应控制器不可见,则返回nil 96 | * 97 | * @param pageIndex 索引 98 | * 99 | * @return UIViewController对象 100 | */ 101 | - (nullable UIViewController *)viewControllerAtPage:(NSUInteger)pageIndex; 102 | 103 | /** 104 | * 根据索引生成对应的ViewController,若对应ViewController已经存在,则直接返回 105 | * 106 | * @param pageIndex 索引 107 | * 108 | * @return UIViewController对象 109 | */ 110 | - (nullable UIViewController *)creatViewControllerAtPage:(NSUInteger)pageIndex; 111 | 112 | /** 113 | * 获取索引对应的ViewController,当ViewController为nil时,根据autoCreate的值决定是否创建 114 | * 115 | * @param pageIndex 索引 116 | * @param autoCreate 是否需要自动创建新的ViewController 117 | * 118 | * @return UIViewController对象 119 | */ 120 | - (nullable UIViewController *)viewControllerAtPage:(NSUInteger)pageIndex autoCreate:(BOOL)autoCreate; 121 | 122 | /** 123 | * 根据缓存标识查询可重用的UIViewController 124 | * 125 | * @param identifier 缓存重用标识 126 | * 127 | * @return 可重用的视图控制器 128 | */ 129 | - (nullable __kindof UIViewController *)dequeueReusablePageWithIdentifier:(NSString *)identifier; 130 | 131 | @end 132 | 133 | NS_ASSUME_NONNULL_END 134 | -------------------------------------------------------------------------------- /VTMagicDemo/Classes/Home/VTRecomViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // RecomViewController.m 3 | // VTMagicView 4 | // 5 | // Created by tianzhuo on 14-11-13. 6 | // Copyright (c) 2014年 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import "VTRecomViewController.h" 10 | #import "VTDetailViewController.h" 11 | #import "VTRecomCell.h" 12 | 13 | @interface VTRecomViewController () 14 | 15 | @property (nonatomic, strong) NSMutableArray *newsList; 16 | 17 | @end 18 | 19 | @implementation VTRecomViewController 20 | 21 | - (void)viewDidLoad { 22 | [super viewDidLoad]; 23 | 24 | self.tableView.scrollsToTop = NO; 25 | self.tableView.contentInset = UIEdgeInsetsMake(0, 0, VTTABBAR_HEIGHT, 0); 26 | self.view.backgroundColor = RGBCOLOR(239, 239, 239); 27 | self.tableView.rowHeight = 70.f; 28 | 29 | [self fetchNewsData]; 30 | [self.tableView reloadData]; 31 | } 32 | 33 | - (void)viewWillAppear:(BOOL)animated { 34 | [super viewWillAppear:animated]; 35 | 36 | VTPRINT_METHOD 37 | } 38 | 39 | - (void)viewDidAppear:(BOOL)animated { 40 | [super viewDidAppear:animated]; 41 | 42 | self.tableView.scrollsToTop = YES; 43 | VTPRINT_METHOD 44 | } 45 | 46 | - (void)viewWillDisappear:(BOOL)animated { 47 | [super viewWillDisappear:animated]; 48 | 49 | self.tableView.scrollsToTop = NO; 50 | VTPRINT_METHOD 51 | } 52 | 53 | - (void)viewDidDisappear:(BOOL)animated { 54 | [super viewDidDisappear:animated]; 55 | 56 | VTPRINT_METHOD 57 | } 58 | 59 | #pragma mark - Table view data source 60 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 61 | return _newsList.count; 62 | } 63 | 64 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 65 | VTRecomCell *cell = nil; 66 | static NSString *cellID = @"cell.Identifier"; 67 | cell = [tableView dequeueReusableCellWithIdentifier:cellID]; 68 | if (!cell) { 69 | cell = [[VTRecomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID]; 70 | } 71 | NSString *imageName = [NSString stringWithFormat:@"image_%ld", (long)indexPath.row%13]; 72 | [cell.iconView setImage:[UIImage imageNamed:imageName]]; 73 | cell.titleLabel.text = @"标题标题"; 74 | cell.descLabel.text = @"景点描述景点描述景点描述景点描述景点描述"; 75 | return cell; 76 | } 77 | 78 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 79 | VTDetailViewController *detailViewController = [[VTDetailViewController alloc] init]; 80 | detailViewController.hidesBottomBarWhenPushed = YES; 81 | [self.navigationController pushViewController:detailViewController animated:YES]; 82 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 83 | } 84 | 85 | #pragma mark - VTMagicReuseProtocol 86 | - (void)vtm_prepareForReuse { 87 | // reset content offset 88 | NSLog(@"clear old data if needed:%@", self); 89 | [self.tableView setContentOffset:CGPointZero]; 90 | } 91 | 92 | #pragma mark - functional methods 93 | - (void)fetchNewsData { 94 | _newsList = [[NSMutableArray alloc] init]; 95 | for (NSInteger index = 0; index < 50; index++) { 96 | [_newsList addObject:[NSString stringWithFormat:@"新闻%ld", (long)index]]; 97 | } 98 | } 99 | 100 | @end 101 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-VTMagic/Pods-VTMagic-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 6 | 7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 8 | 9 | install_framework() 10 | { 11 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 12 | local source="${BUILT_PRODUCTS_DIR}/$1" 13 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 14 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 15 | elif [ -r "$1" ]; then 16 | local source="$1" 17 | fi 18 | 19 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 20 | 21 | if [ -L "${source}" ]; then 22 | echo "Symlinked..." 23 | source="$(readlink "${source}")" 24 | fi 25 | 26 | # use filter instead of exclude so missing patterns dont' throw errors 27 | echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 28 | rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 29 | 30 | local basename 31 | basename="$(basename -s .framework "$1")" 32 | binary="${destination}/${basename}.framework/${basename}" 33 | if ! [ -r "$binary" ]; then 34 | binary="${destination}/${basename}" 35 | fi 36 | 37 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 38 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 39 | strip_invalid_archs "$binary" 40 | fi 41 | 42 | # Resign the code if required by the build settings to avoid unstable apps 43 | code_sign_if_enabled "${destination}/$(basename "$1")" 44 | 45 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 46 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 47 | local swift_runtime_libs 48 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 49 | for lib in $swift_runtime_libs; do 50 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 51 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 52 | code_sign_if_enabled "${destination}/${lib}" 53 | done 54 | fi 55 | } 56 | 57 | # Signs a framework with the provided identity 58 | code_sign_if_enabled() { 59 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 60 | # Use the current code_sign_identitiy 61 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 62 | echo "/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements \"$1\"" 63 | /usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements "$1" 64 | fi 65 | } 66 | 67 | # Strip invalid architectures 68 | strip_invalid_archs() { 69 | binary="$1" 70 | # Get architectures for current file 71 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)" 72 | stripped="" 73 | for arch in $archs; do 74 | if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then 75 | # Strip non-valid architectures in-place 76 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 77 | stripped="$stripped $arch" 78 | fi 79 | done 80 | if [[ "$stripped" ]]; then 81 | echo "Stripped $binary of architectures:$stripped" 82 | fi 83 | } 84 | 85 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-VTMagicTests/Pods-VTMagicTests-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 6 | 7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 8 | 9 | install_framework() 10 | { 11 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 12 | local source="${BUILT_PRODUCTS_DIR}/$1" 13 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 14 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 15 | elif [ -r "$1" ]; then 16 | local source="$1" 17 | fi 18 | 19 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 20 | 21 | if [ -L "${source}" ]; then 22 | echo "Symlinked..." 23 | source="$(readlink "${source}")" 24 | fi 25 | 26 | # use filter instead of exclude so missing patterns dont' throw errors 27 | echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 28 | rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 29 | 30 | local basename 31 | basename="$(basename -s .framework "$1")" 32 | binary="${destination}/${basename}.framework/${basename}" 33 | if ! [ -r "$binary" ]; then 34 | binary="${destination}/${basename}" 35 | fi 36 | 37 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 38 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 39 | strip_invalid_archs "$binary" 40 | fi 41 | 42 | # Resign the code if required by the build settings to avoid unstable apps 43 | code_sign_if_enabled "${destination}/$(basename "$1")" 44 | 45 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 46 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 47 | local swift_runtime_libs 48 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 49 | for lib in $swift_runtime_libs; do 50 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 51 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 52 | code_sign_if_enabled "${destination}/${lib}" 53 | done 54 | fi 55 | } 56 | 57 | # Signs a framework with the provided identity 58 | code_sign_if_enabled() { 59 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 60 | # Use the current code_sign_identitiy 61 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 62 | echo "/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements \"$1\"" 63 | /usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements "$1" 64 | fi 65 | } 66 | 67 | # Strip invalid architectures 68 | strip_invalid_archs() { 69 | binary="$1" 70 | # Get architectures for current file 71 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)" 72 | stripped="" 73 | for arch in $archs; do 74 | if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then 75 | # Strip non-valid architectures in-place 76 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 77 | stripped="$stripped $arch" 78 | fi 79 | done 80 | if [[ "$stripped" ]]; then 81 | echo "Stripped $binary of architectures:$stripped" 82 | fi 83 | } 84 | 85 | -------------------------------------------------------------------------------- /VTMagicDemo/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 21 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /VTMagicDemo/Classes/Data/VTDataViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // VTDataViewController.m 3 | // VTMagic 4 | // 5 | // Created by tianzhuo on 6/1/16. 6 | // Copyright © 2016 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import "VTDataViewController.h" 10 | #import "VTWebViewController.h" 11 | #import 12 | 13 | @interface VTDataViewController() 14 | 15 | @property (nonatomic, strong) VTMagicController *magicController; 16 | @property (nonatomic, strong) NSArray *menuList; 17 | 18 | @end 19 | 20 | @implementation VTDataViewController 21 | 22 | - (void)viewDidLoad { 23 | [super viewDidLoad]; 24 | 25 | self.edgesForExtendedLayout = UIRectEdgeAll; 26 | self.view.backgroundColor = [UIColor whiteColor]; 27 | [self addChildViewController:self.magicController]; 28 | [self.view addSubview:_magicController.view]; 29 | [self.view setNeedsUpdateConstraints]; 30 | 31 | [self generateTestData]; 32 | [_magicController.magicView reloadData]; 33 | } 34 | 35 | - (void)viewWillAppear:(BOOL)animated { 36 | [super viewWillAppear:animated]; 37 | 38 | [self.navigationController setNavigationBarHidden:YES animated:YES]; 39 | } 40 | 41 | - (void)updateViewConstraints { 42 | UIView *magicView = _magicController.view; 43 | [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[magicView]-0-|" 44 | options:0 45 | metrics:nil 46 | views:NSDictionaryOfVariableBindings(magicView)]]; 47 | [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[magicView]-0-|" 48 | options:0 49 | metrics:nil 50 | views:NSDictionaryOfVariableBindings(magicView)]]; 51 | 52 | [super updateViewConstraints]; 53 | } 54 | 55 | #pragma mark - VTMagicViewDataSource 56 | - (NSArray *)menuTitlesForMagicView:(VTMagicView *)magicView { 57 | return _menuList; 58 | } 59 | 60 | - (UIButton *)magicView:(VTMagicView *)magicView menuItemAtIndex:(NSUInteger)itemIndex { 61 | static NSString *itemIdentifier = @"itemIdentifier"; 62 | UIButton *menuItem = [magicView dequeueReusableItemWithIdentifier:itemIdentifier]; 63 | if (!menuItem) { 64 | menuItem = [UIButton buttonWithType:UIButtonTypeCustom]; 65 | [menuItem setTitleColor:RGBCOLOR(50, 50, 50) forState:UIControlStateNormal]; 66 | [menuItem setTitleColor:RGBCOLOR(169, 37, 37) forState:UIControlStateSelected]; 67 | menuItem.titleLabel.font = [UIFont fontWithName:@"Helvetica" size:15.f]; 68 | } 69 | return menuItem; 70 | } 71 | 72 | - (UIViewController *)magicView:(VTMagicView *)magicView viewControllerAtPage:(NSUInteger)pageIndex { 73 | static NSString *pageId = @"page.identifier"; 74 | VTWebViewController *webviewController = [magicView dequeueReusablePageWithIdentifier:pageId]; 75 | if (!webviewController) { 76 | webviewController = [[VTWebViewController alloc] init]; 77 | } 78 | return webviewController; 79 | } 80 | 81 | #pragma mark - functional methods 82 | - (void)generateTestData { 83 | _menuList = @[@"潜龙榜", @"美人榜", @"神功榜", @"奇物榜"]; 84 | } 85 | 86 | #pragma mark - accessor methods 87 | - (VTMagicController *)magicController { 88 | if (!_magicController) { 89 | _magicController = [[VTMagicController alloc] init]; 90 | _magicController.view.translatesAutoresizingMaskIntoConstraints = NO; 91 | _magicController.magicView.navigationColor = [UIColor whiteColor]; 92 | _magicController.magicView.sliderColor = RGBCOLOR(169, 37, 37); 93 | _magicController.magicView.switchStyle = VTSwitchStyleDefault; 94 | _magicController.magicView.layoutStyle = VTLayoutStyleDivide; 95 | _magicController.magicView.navigationHeight = 44.f; 96 | _magicController.magicView.againstStatusBar = YES; 97 | _magicController.magicView.sliderExtension = 10.0; 98 | _magicController.magicView.dataSource = self; 99 | _magicController.magicView.delegate = self; 100 | } 101 | return _magicController; 102 | } 103 | 104 | @end 105 | -------------------------------------------------------------------------------- /VTMagicDemo/Classes/Divide/VTDivideViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // VTDivideViewController.m 3 | // VTMagic 4 | // 5 | // Created by tianzhuo on 6/1/16. 6 | // Copyright © 2016 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import "VTDivideViewController.h" 10 | #import "VTGridViewController.h" 11 | #import 12 | 13 | @interface VTDivideViewController() 14 | 15 | @property (nonatomic, strong) NSArray *menuList; 16 | 17 | @end 18 | 19 | @implementation VTDivideViewController 20 | 21 | - (void)viewDidLoad { 22 | [super viewDidLoad]; 23 | 24 | self.edgesForExtendedLayout = UIRectEdgeAll; 25 | self.view.backgroundColor = [UIColor whiteColor]; 26 | self.magicView.navigationColor = [UIColor whiteColor]; 27 | self.magicView.layoutStyle = VTLayoutStyleDivide; 28 | self.magicView.switchStyle = VTSwitchStyleStiff; 29 | self.magicView.navigationHeight = 44.f; 30 | self.magicView.againstStatusBar = YES; 31 | self.magicView.needPreloading = NO; 32 | [self integrateComponents]; 33 | [self configCustomSlider]; 34 | 35 | [self generateTestData]; 36 | [self.magicView reloadData]; 37 | } 38 | 39 | - (void)viewWillAppear:(BOOL)animated { 40 | [super viewWillAppear:animated]; 41 | 42 | [self.navigationController setNavigationBarHidden:YES animated:YES]; 43 | } 44 | 45 | #pragma mark - VTMagicViewDataSource 46 | - (NSArray *)menuTitlesForMagicView:(VTMagicView *)magicView { 47 | NSMutableArray *titleList = [NSMutableArray array]; 48 | for (MenuInfo *menu in _menuList) { 49 | [titleList addObject:menu.title]; 50 | } 51 | return titleList; 52 | } 53 | 54 | - (UIButton *)magicView:(VTMagicView *)magicView menuItemAtIndex:(NSUInteger)itemIndex { 55 | static NSString *itemIdentifier = @"itemIdentifier"; 56 | UIButton *menuItem = [magicView dequeueReusableItemWithIdentifier:itemIdentifier]; 57 | if (!menuItem) { 58 | menuItem = [UIButton buttonWithType:UIButtonTypeCustom]; 59 | [menuItem setTitleColor:RGBCOLOR(50, 50, 50) forState:UIControlStateNormal]; 60 | [menuItem setTitleColor:RGBCOLOR(169, 37, 37) forState:UIControlStateSelected]; 61 | menuItem.titleLabel.font = [UIFont fontWithName:@"Helvetica" size:15.f]; 62 | } 63 | return menuItem; 64 | } 65 | 66 | - (UIViewController *)magicView:(VTMagicView *)magicView viewControllerAtPage:(NSUInteger)pageIndex { 67 | static NSString *gridId = @"grid.identifier"; 68 | VTGridViewController *viewController = [magicView dequeueReusablePageWithIdentifier:gridId]; 69 | if (!viewController) { 70 | viewController = [[VTGridViewController alloc] init]; 71 | } 72 | viewController.menuInfo = _menuList[pageIndex]; 73 | return viewController; 74 | } 75 | 76 | #pragma mark - actions 77 | - (void)subscribeAction { 78 | NSLog(@"取消/恢复菜单栏选中状态"); 79 | // select/deselect menu item 80 | if (self.magicView.isDeselected) { 81 | [self.magicView reselectMenuItem]; 82 | self.magicView.sliderHidden = NO; 83 | } else { 84 | [self.magicView deselectMenuItem]; 85 | self.magicView.sliderHidden = YES; 86 | } 87 | } 88 | 89 | #pragma mark - functional methods 90 | - (void)generateTestData { 91 | _menuList = @[[MenuInfo menuInfoWithTitl:@"国内"], [MenuInfo menuInfoWithTitl:@"国外"], 92 | [MenuInfo menuInfoWithTitl:@"港澳"], [MenuInfo menuInfoWithTitl:@"台湾"]]; 93 | } 94 | 95 | - (void)integrateComponents { 96 | UIButton *rightButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 50, 30)]; 97 | [rightButton addTarget:self action:@selector(subscribeAction) forControlEvents:UIControlEventTouchUpInside]; 98 | [rightButton setTitleColor:RGBACOLOR(169, 37, 37, 0.6) forState:UIControlStateSelected]; 99 | [rightButton setTitleColor:RGBCOLOR(169, 37, 37) forState:UIControlStateNormal]; 100 | [rightButton setTitle:@"+" forState:UIControlStateNormal]; 101 | rightButton.titleLabel.font = [UIFont boldSystemFontOfSize:28]; 102 | rightButton.center = self.view.center; 103 | self.magicView.rightNavigatoinItem = rightButton; 104 | } 105 | 106 | - (void)configCustomSlider { 107 | UIImageView *sliderView = [[UIImageView alloc] init]; 108 | [sliderView setImage:[UIImage imageNamed:@"magic_arrow"]]; 109 | sliderView.contentMode = UIViewContentModeScaleAspectFit; 110 | [self.magicView setSliderView:sliderView]; 111 | self.magicView.sliderHeight = 5.f; 112 | self.magicView.sliderOffset = -2; 113 | } 114 | 115 | @end 116 | -------------------------------------------------------------------------------- /VTMagic.xcworkspace/xcuserdata/tianzhuo.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 20 | 21 | 22 | 24 | 36 | 37 | 38 | 40 | 52 | 53 | 54 | 56 | 68 | 69 | 70 | 72 | 84 | 85 | 86 | 88 | 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /VTMagic.xcodeproj/xcuserdata/zhuo.xcuserdatad/xcschemes/VTMagicView.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 75 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 94 | 100 | 101 | 102 | 103 | 105 | 106 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /CHANGELOG_EN.md: -------------------------------------------------------------------------------- 1 | #Change Log 2 | All notable changes to this project will be documented in this file. 3 | 4 | --- 5 | 6 | ## [1.2.4](https://github.com/tianzhuo112/VTMagic/releases/tag/1.2.4) (08/10/2016) 7 | Released on Wednesday, August 10, 2016. 8 | 9 | ### changed 10 | - Optimize appearance callbacks, fix unbalance error when call `reloadData`、`reloadDataToPage:`and `switchToPage:animated:` in `viewWillAppear:` method; 11 | - Delete `VTLayoutStyleCustom`, you can set the width of `menuItem` by `itemWidth` directly, it does not work when the value of `layoutStyle` is `VTLayoutStyleDivide`; 12 | - Add `magicView:itemWidthAtIndex:` and `magicView:sliderWidthAtIndex:` delegate methods, you can custom the width of `menuItem` or `sliderView` at `itemIndex`; 13 | - Deprecate property `needExtendBottom`, if you want realize translucent effect, you should set value `UIRectEdgeAll` to `edgesForExtendedLayout`; 14 | - Update the frame of page view before appearance callbacks be called; 15 | - Fix menu bar cannot auto scroll in some special conditions; 16 | - Improved the demo project, to show how to cache and read the page data; 17 | - Auto hide `sliderView` when menu titles are empty; 18 | - Rename internal files; 19 | - Other optimization; 20 | 21 | 22 | ## [1.2.3](https://github.com/tianzhuo112/VTMagic/releases/tag/1.2.3) (07/06/2016) 23 | Released on Wednesday, July 6, 2016. 24 | 25 | ### changed 26 | - Add preloading switch, you can control preloading logic by modifying property `needPreloading`. 27 | - You can custom `sliderView` and `separatorView` if you need. 28 | - You can clear memory cache by method `clearMemoryCache`. 29 | - Add new method `pageIndexForViewController:`, you can get the `pageIndex` of any page controller by this method, and also you can get the `pageIndex` of current page controller by category method `vtm_pageIndex` conveniently. 30 | - Method `updateMenuTitles` was renamed `reloadMenuTitles`, and property `needExtendedBottom` was renamed `needExtendBottom`. 31 | - Fix bug: unable to accurately focus on menu item when switch by `handlePanGesture:`. 32 | 33 | 34 | ## [1.2.2](https://github.com/tianzhuo112/VTMagic/releases/tag/1.2.2) (06/29/2016) 35 | Released on Wednesday, June 29, 2016. 36 | 37 | ### changed 38 | - Add new method `reloadDataToPage:`, support reload data and switch to specified page at the same time. 39 | - Optimize appearance logic, fix bug that multiple calls appearance methods(`viewWillAppear:`,etc), optimize multiple nesting. 40 | - Modify protocol `VTMagicViewDelegate`, method `viewDidAppeare:` was renamed `viewDidAppear:`, and method `viewDidDisappeare:` was renamed `viewDidDisappear:`. 41 | 42 | 43 | ## [1.2.1](https://github.com/tianzhuo112/VTMagic/releases/tag/1.2.1) (06/19/2016) 44 | Released on Sunday, June 19, 2016. 45 | 46 | ### changed 47 | - Add property `itemScale`, if you change it, menuItem will have zoom effect when switching. 48 | - Optimize menu bar. 49 | 50 | ## [1.2.0](https://github.com/tianzhuo112/VTMagic/releases/tag/1.2.0) (06/13/2016) 51 | Released on Monday, June 13, 2016. 52 | 53 | ### changed 54 | - Add property `sliderExtension`. 55 | - Optimize layout. 56 | 57 | 58 | ## [1.1.0](https://github.com/tianzhuo112/VTMagic/releases/tag/1.1.0) (06/04/2016) 59 | Released on Sunday, June 4, 2016. 60 | 61 | ### changed 62 | - Add property `sliderStyle`,you can show a bubble in menu bar by setting `sliderStyle` to `VTSliderStyleBubble`. 63 | - Add **Center** in demo project. 64 | 65 | ## [1.0.2](https://github.com/tianzhuo112/VTMagic/releases/tag/1.0.2) (06/02/2016) 66 | Released on Thursday, June 2, 2016. 67 | 68 | ### changed 69 | - Add new layout style `VTLayoutStyleCenter`. 70 | - Improved the demo project, add modules **Center**、**Divide** and **Data**. 71 | 72 | 73 | ## [1.0.1](https://github.com/tianzhuo112/VTMagic/releases/tag/1.0.1) (06/01/2016) 74 | Released on Wednesday, June 1, 2016. 75 | 76 | ### changed 77 | - Adjust the file directory. 78 | 79 | 80 | ## [1.0.0](https://github.com/tianzhuo112/VTMagic/releases/tag/1.0.0) (05/31/2016) 81 | The initial version is released on Tuesday, May 31, 2016. 82 | 83 | ### Features 84 | - You can custom every page controller by different identifier if you need, each one is independent. 85 | - VTMagic will automatically calls the appearance methods when user switches the page, it is convenient for you to manage the data, or do some other things. 86 | - Menu bar supports multiple layout style, you can change it by `layoutStyle`. 87 | - You can also custom `menuItem` if you need, it can display text and image. 88 | - You can get the nearest magicController in any child view controller which is conforms to ``, after you import file `VTMagic.h`. 89 | - You can embed webview in any page, if the page can't scroll, you should call method handlePanGesture: to fix it(please refer to **Data** module). 90 | - The page is reusable and support auto rotate. 91 | - For more information please read file `VTMagicView.h`. 92 | 93 | 94 | -------------------------------------------------------------------------------- /VTMagic.xcodeproj/xcuserdata/tianzhuo.xcuserdatad/xcschemes/VTMagic.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 67 | 68 | 78 | 80 | 86 | 87 | 88 | 89 | 90 | 91 | 97 | 99 | 105 | 106 | 107 | 108 | 110 | 111 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /VTMagic/VTMagicController.m: -------------------------------------------------------------------------------- 1 | // 2 | // VTMagicController.m 3 | // VTMagicView 4 | // 5 | // Created by tianzhuo on 14-11-11. 6 | // Copyright (c) 2014年 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import "VTMagicController.h" 10 | 11 | @interface VTMagicController () 12 | 13 | 14 | @end 15 | 16 | @implementation VTMagicController 17 | 18 | #pragma mark - Lifecycle 19 | - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { 20 | self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; 21 | if (self) { 22 | if ([self respondsToSelector:@selector(edgesForExtendedLayout)]) { 23 | self.edgesForExtendedLayout = UIRectEdgeNone; 24 | } else if ([self respondsToSelector:@selector(setWantsFullScreenLayout:)]) { 25 | [self setValue:@YES forKey:@"wantsFullScreenLayout"]; 26 | } 27 | } 28 | return self; 29 | } 30 | 31 | - (void)loadView { 32 | [super loadView]; 33 | 34 | self.view = self.magicView; 35 | } 36 | 37 | - (void)viewWillAppear:(BOOL)animated { 38 | [super viewWillAppear:animated]; 39 | 40 | self.appearanceState = VTAppearanceStateWillAppear; 41 | if (!_magicView.isSwitching) { 42 | [_currentViewController beginAppearanceTransition:YES animated:animated]; 43 | } 44 | } 45 | 46 | - (void)viewDidAppear:(BOOL)animated { 47 | [super viewDidAppear:animated]; 48 | 49 | self.appearanceState = VTAppearanceStateDidAppear; 50 | if (!_magicView.isSwitching) { 51 | [_currentViewController endAppearanceTransition]; 52 | } 53 | } 54 | 55 | - (void)viewWillDisappear:(BOOL)animated { 56 | [super viewWillDisappear:animated]; 57 | 58 | self.appearanceState = VTAppearanceStateWillDisappear; 59 | if (!_magicView.isSwitching) { 60 | [_currentViewController beginAppearanceTransition:NO animated:animated]; 61 | } 62 | } 63 | 64 | - (void)viewDidDisappear:(BOOL)animated { 65 | [super viewDidDisappear:animated]; 66 | 67 | self.appearanceState = VTAppearanceStateDidDisappear; 68 | if (!_magicView.isSwitching) { 69 | [_currentViewController endAppearanceTransition]; 70 | } 71 | } 72 | 73 | #pragma mark - forward appearance methods 74 | - (BOOL)shouldAutomaticallyForwardAppearanceMethods { 75 | return NO; 76 | } 77 | 78 | #pragma mark - functional methods 79 | - (UIViewController *)viewControllerAtPage:(NSUInteger)pageIndex { 80 | return [self.magicView viewControllerAtPage:pageIndex]; 81 | } 82 | 83 | - (void)switchToPage:(NSUInteger)pageIndex animated:(BOOL)animated { 84 | [self.magicView switchToPage:pageIndex animated:animated]; 85 | } 86 | 87 | #pragma mark - VTMagicViewDataSource 88 | - (NSArray *)menuTitlesForMagicView:(VTMagicView *)magicView { 89 | return @[]; 90 | } 91 | 92 | - (UIButton *)magicView:(VTMagicView *)magicView menuItemAtIndex:(NSUInteger)itemIndex { 93 | static NSString *itemIdentifier = @"itemIdentifier"; 94 | UIButton *menuItem = [magicView dequeueReusableItemWithIdentifier:itemIdentifier]; 95 | if (!menuItem) { 96 | menuItem = [UIButton buttonWithType:UIButtonTypeCustom]; 97 | [menuItem setTitleColor:RGBCOLOR(50, 50, 50) forState:UIControlStateNormal]; 98 | [menuItem setTitleColor:RGBCOLOR(169, 37, 37) forState:UIControlStateSelected]; 99 | menuItem.titleLabel.font = [UIFont fontWithName:@"Helvetica" size:15.f]; 100 | } 101 | return menuItem; 102 | } 103 | 104 | - (UIViewController *)magicView:(VTMagicView *)magicView viewControllerAtPage:(NSUInteger)pageInde { 105 | static NSString *pageId = @"page.identifier"; 106 | UIViewController *viewController = [magicView dequeueReusablePageWithIdentifier:pageId]; 107 | if (!viewController) { 108 | viewController = [[UIViewController alloc] init]; 109 | } 110 | return viewController; 111 | } 112 | 113 | #pragma mark - VTMagicViewDelegate 114 | - (void)magicView:(VTMagicView *)magicView viewDidAppear:(__kindof UIViewController *)viewController atPage:(NSUInteger)pageIndex { 115 | VTLog(@"index:%ld viewControllerDidAppear:%@", (long)pageIndex, viewController.view); 116 | } 117 | 118 | - (void)magicView:(VTMagicView *)magicView viewDidDisappear:(__kindof UIViewController *)viewController atPage:(NSUInteger)pageIndex { 119 | VTLog(@"index:%ld viewControllerDidDisappear:%@", (long)pageIndex, viewController.view); 120 | } 121 | 122 | - (void)magicView:(VTMagicView *)magicView didSelectItemAtIndex:(NSUInteger)itemIndex { 123 | VTLog(@"didSelectItemAtIndex:%ld", (long)itemIndex); 124 | } 125 | 126 | #pragma mark - accessor methods 127 | - (VTMagicView *)magicView { 128 | if (!_magicView) { 129 | _magicView = [[VTMagicView alloc] initWithFrame:[UIScreen mainScreen].bounds]; 130 | _magicView.autoresizesSubviews = YES; 131 | _magicView.magicController = self; 132 | _magicView.delegate = self; 133 | _magicView.dataSource = self; 134 | [self.view setNeedsLayout]; 135 | } 136 | return _magicView; 137 | } 138 | 139 | - (NSArray *)viewControllers { 140 | return self.magicView.viewControllers; 141 | } 142 | 143 | @end 144 | -------------------------------------------------------------------------------- /VTMagicDemo/Classes/Bubble/VTBubbleViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // VTBubbleViewController.m 3 | // VTMagic 4 | // 5 | // Created by tianzhuo on 6/4/16. 6 | // Copyright © 2016 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import "VTBubbleViewController.h" 10 | #import "VTGridViewController.h" 11 | #import "VTMenuItem.h" 12 | 13 | @interface VTBubbleViewController () 14 | 15 | @property (nonatomic, strong) NSArray *menuList; 16 | 17 | @end 18 | 19 | @implementation VTBubbleViewController 20 | 21 | #pragma mark - Lifecycle 22 | - (void)viewDidLoad { 23 | [super viewDidLoad]; 24 | 25 | self.magicView.navigationHeight = 44; 26 | self.magicView.displayCentered = YES; 27 | // self.magicView.againstStatusBar = YES; 28 | // self.edgesForExtendedLayout = UIRectEdgeAll; 29 | self.view.backgroundColor = [UIColor whiteColor]; 30 | self.magicView.headerView.backgroundColor = RGBCOLOR(243, 40, 47); 31 | self.magicView.layoutStyle = VTLayoutStyleDefault; 32 | self.magicView.navigationColor = [UIColor whiteColor]; 33 | self.magicView.sliderStyle = VTSliderStyleBubble; 34 | self.magicView.sliderColor = RGBCOLOR(229, 229, 229); 35 | self.magicView.bubbleInset = UIEdgeInsetsMake(2, 7, 2, 7); 36 | self.magicView.bubbleRadius = 10; 37 | [self integrateComponents]; 38 | 39 | [self addNotification]; 40 | [self generateTestData]; 41 | [self.magicView reloadDataToPage:2]; 42 | } 43 | 44 | - (void)viewWillAppear:(BOOL)animated { 45 | [super viewWillAppear:animated]; 46 | 47 | [self.navigationController setNavigationBarHidden:NO animated:YES]; 48 | } 49 | 50 | - (void)dealloc { 51 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 52 | } 53 | 54 | #pragma mark - NSNotification 55 | - (void)addNotification { 56 | [self removeNotification]; 57 | [[NSNotificationCenter defaultCenter] addObserver:self 58 | selector:@selector(statusBarOrientationChange:) 59 | name:UIApplicationDidChangeStatusBarOrientationNotification 60 | object:nil]; 61 | } 62 | 63 | - (void)removeNotification { 64 | [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidChangeStatusBarOrientationNotification object:nil]; 65 | } 66 | 67 | - (void)statusBarOrientationChange:(NSNotification *)notification { 68 | [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:YES]; 69 | } 70 | 71 | #pragma mark - VTMagicViewDataSource 72 | - (NSArray *)menuTitlesForMagicView:(VTMagicView *)magicView { 73 | NSMutableArray *titleList = [NSMutableArray array]; 74 | for (MenuInfo *menu in _menuList) { 75 | [titleList addObject:menu.title]; 76 | } 77 | return titleList; 78 | } 79 | 80 | - (UIButton *)magicView:(VTMagicView *)magicView menuItemAtIndex:(NSUInteger)itemIndex { 81 | static NSString *itemIdentifier = @"itemIdentifier"; 82 | VTMenuItem *menuItem = [magicView dequeueReusableItemWithIdentifier:itemIdentifier]; 83 | if (!menuItem) { 84 | menuItem = [VTMenuItem buttonWithType:UIButtonTypeCustom]; 85 | [menuItem setTitleColor:RGBCOLOR(50, 50, 50) forState:UIControlStateNormal]; 86 | [menuItem setTitleColor:RGBCOLOR(169, 37, 37) forState:UIControlStateSelected]; 87 | menuItem.titleLabel.font = [UIFont fontWithName:@"Helvetica" size:15.f]; 88 | } 89 | return menuItem; 90 | } 91 | 92 | - (UIViewController *)magicView:(VTMagicView *)magicView viewControllerAtPage:(NSUInteger)pageIndex { 93 | static NSString *gridId = @"grid.identifier"; 94 | VTGridViewController *viewController = [magicView dequeueReusablePageWithIdentifier:gridId]; 95 | if (!viewController) { 96 | viewController = [[VTGridViewController alloc] init]; 97 | } 98 | viewController.menuInfo = _menuList[pageIndex]; 99 | return viewController; 100 | } 101 | 102 | #pragma mark - actions 103 | - (void)subscribeAction { 104 | NSLog(@"subscribeAction"); 105 | // against status bar or not 106 | self.magicView.againstStatusBar = !self.magicView.againstStatusBar; 107 | [self.magicView setHeaderHidden:!self.magicView.isHeaderHidden duration:0.35]; 108 | } 109 | 110 | #pragma mark - functional methods 111 | - (void)integrateComponents { 112 | UIButton *rightButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 50, 30)]; 113 | [rightButton addTarget:self action:@selector(subscribeAction) forControlEvents:UIControlEventTouchUpInside]; 114 | [rightButton setTitleColor:RGBACOLOR(169, 37, 37, 0.6) forState:UIControlStateSelected]; 115 | [rightButton setTitleColor:RGBCOLOR(169, 37, 37) forState:UIControlStateNormal]; 116 | [rightButton setTitle:@"+" forState:UIControlStateNormal]; 117 | rightButton.titleLabel.font = [UIFont boldSystemFontOfSize:28]; 118 | rightButton.center = self.view.center; 119 | self.magicView.rightNavigatoinItem = rightButton; 120 | } 121 | 122 | - (void)generateTestData { 123 | NSString *title = @"省份"; 124 | NSMutableArray *menuList = [[NSMutableArray alloc] initWithCapacity:24]; 125 | for (int index = 0; index < 20; index++) { 126 | title = [NSString stringWithFormat:@"省份%d", index]; 127 | MenuInfo *menu = [MenuInfo menuInfoWithTitl:title]; 128 | [menuList addObject:menu]; 129 | } 130 | _menuList = menuList; 131 | } 132 | 133 | @end 134 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-VTMagic/Pods-VTMagic-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 5 | 6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 7 | > "$RESOURCES_TO_COPY" 8 | 9 | XCASSET_FILES=() 10 | 11 | case "${TARGETED_DEVICE_FAMILY}" in 12 | 1,2) 13 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 14 | ;; 15 | 1) 16 | TARGET_DEVICE_ARGS="--target-device iphone" 17 | ;; 18 | 2) 19 | TARGET_DEVICE_ARGS="--target-device ipad" 20 | ;; 21 | *) 22 | TARGET_DEVICE_ARGS="--target-device mac" 23 | ;; 24 | esac 25 | 26 | realpath() { 27 | DIRECTORY="$(cd "${1%/*}" && pwd)" 28 | FILENAME="${1##*/}" 29 | echo "$DIRECTORY/$FILENAME" 30 | } 31 | 32 | install_resource() 33 | { 34 | if [[ "$1" = /* ]] ; then 35 | RESOURCE_PATH="$1" 36 | else 37 | RESOURCE_PATH="${PODS_ROOT}/$1" 38 | fi 39 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 40 | cat << EOM 41 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 42 | EOM 43 | exit 1 44 | fi 45 | case $RESOURCE_PATH in 46 | *.storyboard) 47 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" 48 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 49 | ;; 50 | *.xib) 51 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" 52 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 53 | ;; 54 | *.framework) 55 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 56 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 57 | echo "rsync -av $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 58 | rsync -av "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 59 | ;; 60 | *.xcdatamodel) 61 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" 62 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 63 | ;; 64 | *.xcdatamodeld) 65 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" 66 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 67 | ;; 68 | *.xcmappingmodel) 69 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" 70 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 71 | ;; 72 | *.xcassets) 73 | ABSOLUTE_XCASSET_FILE=$(realpath "$RESOURCE_PATH") 74 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 75 | ;; 76 | *) 77 | echo "$RESOURCE_PATH" 78 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 79 | ;; 80 | esac 81 | } 82 | 83 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 84 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 85 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 86 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 87 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 88 | fi 89 | rm -f "$RESOURCES_TO_COPY" 90 | 91 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] 92 | then 93 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 94 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 95 | while read line; do 96 | if [[ $line != "`realpath $PODS_ROOT`*" ]]; then 97 | XCASSET_FILES+=("$line") 98 | fi 99 | done <<<"$OTHER_XCASSETS" 100 | 101 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 102 | fi 103 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-VTMagicTests/Pods-VTMagicTests-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 5 | 6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 7 | > "$RESOURCES_TO_COPY" 8 | 9 | XCASSET_FILES=() 10 | 11 | case "${TARGETED_DEVICE_FAMILY}" in 12 | 1,2) 13 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 14 | ;; 15 | 1) 16 | TARGET_DEVICE_ARGS="--target-device iphone" 17 | ;; 18 | 2) 19 | TARGET_DEVICE_ARGS="--target-device ipad" 20 | ;; 21 | *) 22 | TARGET_DEVICE_ARGS="--target-device mac" 23 | ;; 24 | esac 25 | 26 | realpath() { 27 | DIRECTORY="$(cd "${1%/*}" && pwd)" 28 | FILENAME="${1##*/}" 29 | echo "$DIRECTORY/$FILENAME" 30 | } 31 | 32 | install_resource() 33 | { 34 | if [[ "$1" = /* ]] ; then 35 | RESOURCE_PATH="$1" 36 | else 37 | RESOURCE_PATH="${PODS_ROOT}/$1" 38 | fi 39 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 40 | cat << EOM 41 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 42 | EOM 43 | exit 1 44 | fi 45 | case $RESOURCE_PATH in 46 | *.storyboard) 47 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" 48 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 49 | ;; 50 | *.xib) 51 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" 52 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 53 | ;; 54 | *.framework) 55 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 56 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 57 | echo "rsync -av $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 58 | rsync -av "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 59 | ;; 60 | *.xcdatamodel) 61 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" 62 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 63 | ;; 64 | *.xcdatamodeld) 65 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" 66 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 67 | ;; 68 | *.xcmappingmodel) 69 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" 70 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 71 | ;; 72 | *.xcassets) 73 | ABSOLUTE_XCASSET_FILE=$(realpath "$RESOURCE_PATH") 74 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 75 | ;; 76 | *) 77 | echo "$RESOURCE_PATH" 78 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 79 | ;; 80 | esac 81 | } 82 | 83 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 84 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 85 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 86 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 87 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 88 | fi 89 | rm -f "$RESOURCES_TO_COPY" 90 | 91 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] 92 | then 93 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 94 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 95 | while read line; do 96 | if [[ $line != "`realpath $PODS_ROOT`*" ]]; then 97 | XCASSET_FILES+=("$line") 98 | fi 99 | done <<<"$OTHER_XCASSETS" 100 | 101 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 102 | fi 103 | -------------------------------------------------------------------------------- /VTMagicDemo/Classes/Detail/VTDetailViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // VTDetailViewController.m 3 | // VTMagic 4 | // 5 | // Created by tianzhuo on 7/7/16. 6 | // Copyright © 2016 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import "VTDetailViewController.h" 10 | #import "VTRelateViewController.h" 11 | #import "VTChatViewController.h" 12 | #import 13 | #import "VTMenuItem.h" 14 | 15 | @interface VTDetailViewController() 16 | 17 | @property (nonatomic, strong) VTMagicController *magicController; 18 | @property (nonatomic, strong) VTChatViewController *chatViewController; 19 | @property (nonatomic, strong) NSArray *menuList; 20 | @property (nonatomic, assign) BOOL dotHidden; 21 | 22 | @end 23 | 24 | @implementation VTDetailViewController 25 | 26 | - (void)viewDidLoad { 27 | [super viewDidLoad]; 28 | 29 | self.edgesForExtendedLayout = UIRectEdgeNone; 30 | self.view.backgroundColor = [UIColor blackColor]; 31 | [self addChildViewController:self.magicController]; 32 | [self.view addSubview:_magicController.view]; 33 | [self.view setNeedsUpdateConstraints]; 34 | 35 | _menuList = @[@"详情", @"热门", @"相关", @"聊天"]; 36 | [_magicController.magicView reloadData]; 37 | } 38 | 39 | - (void)viewWillAppear:(BOOL)animated { 40 | [super viewWillAppear:animated]; 41 | 42 | [self.navigationController setNavigationBarHidden:NO animated:YES]; 43 | } 44 | 45 | - (void)viewDidDisappear:(BOOL)animated { 46 | [super viewDidDisappear:animated]; 47 | 48 | [_chatViewController invalidateTimer]; 49 | } 50 | 51 | - (void)updateViewConstraints { 52 | UIView *magicView = _magicController.view; 53 | [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[magicView]-0-|" 54 | options:0 55 | metrics:nil 56 | views:NSDictionaryOfVariableBindings(magicView)]]; 57 | [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-200-[magicView]-0-|" 58 | options:0 59 | metrics:nil 60 | views:NSDictionaryOfVariableBindings(magicView)]]; 61 | 62 | [super updateViewConstraints]; 63 | } 64 | 65 | #pragma mark - VTMagicViewDataSource 66 | - (NSArray *)menuTitlesForMagicView:(VTMagicView *)magicView { 67 | return _menuList; 68 | } 69 | 70 | - (UIButton *)magicView:(VTMagicView *)magicView menuItemAtIndex:(NSUInteger)itemIndex { 71 | static NSString *itemIdentifier = @"itemIdentifier"; 72 | VTMenuItem *menuItem = [magicView dequeueReusableItemWithIdentifier:itemIdentifier]; 73 | if (!menuItem) { 74 | menuItem = [VTMenuItem buttonWithType:UIButtonTypeCustom]; 75 | [menuItem setTitleColor:RGBCOLOR(50, 50, 50) forState:UIControlStateNormal]; 76 | [menuItem setTitleColor:RGBCOLOR(169, 37, 37) forState:UIControlStateSelected]; 77 | menuItem.titleLabel.font = [UIFont fontWithName:@"Helvetica" size:15.f]; 78 | } 79 | menuItem.dotHidden = (_menuList.count - 1 == itemIndex) ? _dotHidden : YES; 80 | return menuItem; 81 | } 82 | 83 | - (UIViewController *)magicView:(VTMagicView *)magicView viewControllerAtPage:(NSUInteger)pageIndex { 84 | if (_menuList.count - 1 == pageIndex) { 85 | return self.chatViewController; 86 | } 87 | 88 | static NSString *gridId = @"relate.identifier"; 89 | VTRelateViewController *viewController = [magicView dequeueReusablePageWithIdentifier:gridId]; 90 | if (!viewController) { 91 | viewController = [[VTRelateViewController alloc] init]; 92 | } 93 | viewController.menuInfo = _menuList[pageIndex]; 94 | return viewController; 95 | } 96 | 97 | - (void)magicView:(VTMagicView *)magicView viewDidAppear:(__kindof UIViewController *)viewController atPage:(NSUInteger)pageIndex { 98 | if ([viewController isEqual:_chatViewController]) { 99 | _dotHidden = YES; 100 | [magicView reloadMenuTitles]; 101 | } 102 | } 103 | 104 | #pragma mark - VTChatViewControllerDelegate 105 | - (void)chatViewControllerDidReciveNewMessages:(VTChatViewController *)chatViewController { 106 | _dotHidden = NO; 107 | [_magicController.magicView reloadMenuTitles]; 108 | } 109 | 110 | #pragma mark - accessor methods 111 | - (VTMagicController *)magicController { 112 | if (!_magicController) { 113 | _magicController = [[VTMagicController alloc] init]; 114 | _magicController.view.translatesAutoresizingMaskIntoConstraints = NO; 115 | _magicController.magicView.navigationColor = [UIColor whiteColor]; 116 | _magicController.magicView.sliderColor = RGBCOLOR(169, 37, 37); 117 | _magicController.magicView.switchStyle = VTSwitchStyleStiff; 118 | _magicController.magicView.layoutStyle = VTLayoutStyleDivide; 119 | _magicController.magicView.navigationHeight = 40.f; 120 | _magicController.magicView.sliderExtension = 10.f; 121 | _magicController.magicView.dataSource = self; 122 | _magicController.magicView.delegate = self; 123 | } 124 | return _magicController; 125 | } 126 | 127 | - (VTChatViewController *)chatViewController { 128 | if (!_chatViewController) { 129 | _chatViewController = [[VTChatViewController alloc] init]; 130 | _chatViewController.delegate = self; 131 | } 132 | return _chatViewController; 133 | } 134 | 135 | @end 136 | -------------------------------------------------------------------------------- /VTMagic/VTMenuBar.h: -------------------------------------------------------------------------------- 1 | // 2 | // VTMenuBar.h 3 | // VTMagicView 4 | // 5 | // Created by tianzhuo on 15/1/6. 6 | // Copyright (c) 2015年 tianzhuo. All rights reserved. 7 | // 菜单栏 8 | 9 | #import 10 | #import "VTEnumType.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @class VTMenuBar; 15 | @protocol VTMenuBarDatasource 16 | /** 17 | * 根据index获取对应索引的menuItem 18 | * 19 | * @param menuBar self 20 | * @param index 需要加载的menuItem对应的索引 21 | * 22 | * @return 当前索引对应的按钮 23 | */ 24 | - (nullable UIButton *)menuBar:(VTMenuBar *)menuBar menuItemAtIndex:(NSUInteger)itemIndex; 25 | 26 | @end 27 | 28 | @protocol VTMenuBarDelegate 29 | 30 | @optional 31 | /** 32 | * 导航菜单item被点击时触发 33 | * 34 | * @param menuBar self 35 | * @param itemIndex 被选中的按钮索引 36 | */ 37 | - (void)menuBar:(VTMenuBar *)menuBar didSelectItemAtIndex:(NSUInteger)itemIndex; 38 | 39 | /** 40 | * 根据itemIndex获取对应menuItem的宽度,若返回结果为0,内部将自动计算其宽度 41 | * 42 | * @param magicView self 43 | * @param itemIndex menuItem对应的索引 44 | * 45 | * @return menuItem的宽度 46 | */ 47 | - (CGFloat)menuBar:(VTMenuBar *)menuBar itemWidthAtIndex:(NSUInteger)itemIndex; 48 | 49 | /** 50 | * 根据itemIndex获取对应slider的宽度,若返回结果为0,内部将自动计算其宽度 51 | * 52 | * @param magicView self 53 | * @param itemIndex slider对应的索引 54 | * 55 | * @return slider的宽度 56 | */ 57 | - (CGFloat)menuBar:(VTMenuBar *)menuBar sliderWidthAtIndex:(NSUInteger)itemIndex; 58 | 59 | @end 60 | 61 | @interface VTMenuBar : UIScrollView 62 | 63 | /** 64 | * 数据源 65 | */ 66 | @property (nonatomic, weak, nullable) id datasource; 67 | 68 | /** 69 | * 代理 70 | */ 71 | @property (nonatomic, weak, nullable) id delegate; 72 | 73 | /** 74 | * 菜单名数组,字符串类型 75 | */ 76 | @property (nonatomic, strong, nullable) NSArray<__kindof NSString *> *menuTitles; 77 | 78 | /** 79 | * 导航菜单布局样式 80 | */ 81 | @property (nonatomic, assign) VTLayoutStyle layoutStyle; 82 | 83 | /** 84 | * 导航栏滑块样式,默认显示下划线 85 | */ 86 | @property (nonatomic, assign) VTSliderStyle sliderStyle; 87 | 88 | /** 89 | * 当前选中item对应的索引 90 | */ 91 | @property (nonatomic, assign) NSUInteger currentIndex; 92 | 93 | /** 94 | * 当前被选中的item 95 | */ 96 | @property (nonatomic, strong, readonly, nullable) UIButton *selectedItem; 97 | 98 | /** 99 | * 导航菜单item的选中状态是否已被取消,默认NO 100 | */ 101 | @property (nonatomic, assign, getter=isDeselected) BOOL deselected; 102 | 103 | /** 104 | * 是否需要跳过layout,菜单栏被滑动时该属性无效,强制布局 105 | */ 106 | @property (nonatomic, assign) BOOL needSkipLayout; 107 | 108 | /** 109 | * item之间的实际间距 110 | */ 111 | @property (nonatomic, assign) CGFloat acturalSpacing; 112 | 113 | /** 114 | * 自定义item宽度,默认0,当设置改属性时,itemSpacing的设置无效 115 | * 116 | * @warning 该属性在VTLayoutStyleDivide样式下无效 117 | */ 118 | @property (nonatomic, assign) CGFloat itemWidth; 119 | 120 | /** 121 | * 两个导航菜单item文本之间的间距,默认是25,其优先级低于itemWidth 122 | * 如果菜单item包含图片,则实际间距可能会更小 123 | * 124 | * @warning 该属性在VTLayoutStyleDivide样式下无效 125 | */ 126 | @property (nonatomic, assign) CGFloat itemSpacing; 127 | 128 | /** 129 | * menuItem被选中时文本的放大倍数,默认1.0 130 | * 可根据需要设置合适的数值,通常不宜超过1.5 131 | */ 132 | @property (nonatomic, assign) CGFloat itemScale; 133 | 134 | /** 135 | * 导航菜单的inset,对leftHeaderView和rightHeaderView无效 136 | */ 137 | @property (nonatomic, assign) UIEdgeInsets menuInset; 138 | 139 | /** 140 | * 气泡相对menuItem文本的edgeInsets,默认(2, 5, 2, 5) 141 | */ 142 | @property (nonatomic, assign) UIEdgeInsets bubbleInset; 143 | 144 | /** 145 | * 顶部导航栏滑块高度,默认2 146 | * 147 | * @warning 非VTSliderStyleDefault样式,该属性无效 148 | */ 149 | @property (nonatomic, assign) CGFloat sliderHeight; 150 | 151 | /** 152 | * 顶部导航栏滑块宽度,VTSliderStyleDefault样式下滑块宽度默认与item宽度一致 153 | * 154 | * @warning 非VTSliderStyleDefault样式,该属性无效 155 | */ 156 | @property (nonatomic, assign) CGFloat sliderWidth; 157 | 158 | /** 159 | * 滑块宽度延长量,0表示滑块宽度与文本宽度一致,该属性优先级低于sliderWidth 160 | * 161 | * @warning 非VTSliderStyleDefault样式或sliderWidth有效时,该属性无效 162 | */ 163 | @property (nonatomic, assign) CGFloat sliderExtension; 164 | 165 | /** 166 | * 顶部导航栏滑块相对导航底部的偏移量,默认0,上偏为负 167 | * 168 | * @warning 非VTSliderStyleDefault样式,该属性无效 169 | */ 170 | @property (nonatomic, assign) CGFloat sliderOffset; 171 | 172 | #pragma mark - public methods 173 | /** 174 | * 刷新数据 175 | */ 176 | - (void)reloadData; 177 | 178 | /** 179 | * 重置所有item的frame 180 | */ 181 | - (void)resetItemFrames; 182 | 183 | /** 184 | * 更新被选中按钮,并根据需要决定是否调整menuItem文本的大小 185 | * 186 | * @param transformScale 是否改变scale 187 | */ 188 | - (void)updateSelectedItem:(BOOL)transformScale; 189 | 190 | /** 191 | * 取消导航菜单item的选中状态 192 | */ 193 | - (void)deselectMenuItem; 194 | 195 | /** 196 | * 恢复导航菜单item的选中状态 197 | */ 198 | - (void)reselectMenuItem; 199 | 200 | /** 201 | * 根据索引获取对应item对应的frame 202 | * 203 | * @param index 索引 204 | * 205 | * @return 当前索引对应item的frame 206 | */ 207 | - (CGRect)itemFrameAtIndex:(NSUInteger)index; 208 | 209 | /** 210 | * 根据索引获取slider当前的frame 211 | * 212 | * @param index 索引 213 | * 214 | * @return 当前索引对应的slider的frame 215 | */ 216 | - (CGRect)sliderFrameAtIndex:(NSUInteger)index; 217 | 218 | /** 219 | * 根据索引获取当前页面显示的item,不在窗口上显示的则为nil 220 | * 221 | * @param index 索引 222 | * 223 | * @return 当前索引对应的item 224 | */ 225 | - (nullable UIButton *)itemAtIndex:(NSUInteger)index; 226 | 227 | /** 228 | * 根据索引生成对应的item,若对应item已经存在,则直接返回 229 | * 230 | * @param index 索引 231 | * 232 | * @return 当前索引对应的item 233 | */ 234 | - (nullable UIButton *)createItemAtIndex:(NSUInteger)index; 235 | 236 | /** 237 | * 根据重用标识查询可重用的category item 238 | * 239 | * @param identifier 重用标识 240 | * 241 | * @return 缓存池中取出的category item 242 | */ 243 | - (nullable __kindof UIButton *)dequeueReusableItemWithIdentifier:(NSString *)identifier; 244 | 245 | @end 246 | 247 | NS_ASSUME_NONNULL_END 248 | -------------------------------------------------------------------------------- /VTMagicDemo/Classes/Home/VTGridViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // GridViewController.m 3 | // VTMagicView 4 | // 5 | // Created by tianzhuo on 14/12/30. 6 | // Copyright (c) 2014年 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import "VTGridViewController.h" 10 | #import "VTDetailViewController.h" 11 | #import 12 | #import "VTGridViewCell.h" 13 | #import "DataManager.h" 14 | 15 | #define IPHONELESS6 ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? 640 == [[UIScreen mainScreen] currentMode].size.width : NO) 16 | static NSString *reuseIdentifier = @"grid.reuse.identifier"; 17 | 18 | @interface VTGridViewController() 19 | 20 | @property (nonatomic, strong) NSMutableArray *infoList; 21 | 22 | @end 23 | 24 | @implementation VTGridViewController 25 | 26 | - (instancetype)init { 27 | BOOL iPhoneDevice = kiPhoneDevice; 28 | UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; 29 | layout.scrollDirection = UICollectionViewScrollDirectionVertical; 30 | layout.sectionInset = iPhoneDevice ?UIEdgeInsetsMake(10, 10, 10, 10) : UIEdgeInsetsMake(20, 20, 20, 20); 31 | layout.itemSize = iPhoneDevice ? (IPHONELESS6 ? CGSizeMake(140, 175) : CGSizeMake(115, 170)) : CGSizeMake(188, 188); 32 | layout.minimumInteritemSpacing = iPhoneDevice ? 2 : 11; 33 | layout.minimumLineSpacing = iPhoneDevice ? 10 : 11; 34 | return [super initWithCollectionViewLayout:layout]; 35 | } 36 | 37 | - (void)viewDidLoad { 38 | [super viewDidLoad]; 39 | 40 | self.collectionView.scrollsToTop = NO; 41 | self.collectionView.backgroundColor = RGBCOLOR(239, 239, 239); 42 | self.collectionView.contentInset = UIEdgeInsetsMake(0, 0, VTTABBAR_HEIGHT, 0); 43 | [self.collectionView registerClass:[VTGridViewCell class] forCellWithReuseIdentifier:reuseIdentifier]; 44 | } 45 | 46 | - (void)viewWillAppear:(BOOL)animated { 47 | [super viewWillAppear:animated]; 48 | 49 | VTPRINT_METHOD 50 | } 51 | 52 | - (void)viewDidAppear:(BOOL)animated { 53 | [super viewDidAppear:animated]; 54 | 55 | [self refreshPageIfNeeded]; 56 | self.collectionView.scrollsToTop = YES; 57 | NSInteger pageIndex = [self vtm_pageIndex]; 58 | NSLog(@"current page index is: %ld", (long)pageIndex); 59 | VTPRINT_METHOD 60 | } 61 | 62 | - (void)viewWillDisappear:(BOOL)animated { 63 | [super viewWillDisappear:animated]; 64 | 65 | [self cancelNetworkRequest]; 66 | self.collectionView.scrollsToTop = NO; 67 | VTPRINT_METHOD 68 | } 69 | 70 | - (void)viewDidDisappear:(BOOL)animated { 71 | [super viewDidDisappear:animated]; 72 | 73 | [self savePageInfo]; 74 | VTPRINT_METHOD 75 | } 76 | 77 | #pragma mark UICollectionViewDataSource 78 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { 79 | return _infoList.count; 80 | } 81 | 82 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { 83 | VTGridViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath]; 84 | cell.backgroundColor = [UIColor whiteColor]; 85 | NSString *imageName = _infoList[indexPath.item]; 86 | [cell.imageView setImage:[UIImage imageNamed:imageName]]; 87 | cell.commentLabel.text = [NSString stringWithFormat:@"%d人出游", arc4random_uniform(9999)]; 88 | cell.titleLabel.text = @"景点介绍,景点介绍,景点介绍。。"; 89 | return cell; 90 | } 91 | 92 | - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { 93 | NSInteger currentPage = self.magicController.currentPage; 94 | NSLog(@"==didSelectItemAtIndexPath%@ \n current page is: %ld==", indexPath, (long)currentPage); 95 | VTDetailViewController *detailViewController = [[VTDetailViewController alloc] init]; 96 | detailViewController.hidesBottomBarWhenPushed = YES; 97 | [self.navigationController pushViewController:detailViewController animated:YES]; 98 | } 99 | 100 | #pragma mark - VTMagicReuseProtocol 101 | - (void)vtm_prepareForReuse { 102 | // reset content offset 103 | NSLog(@"clear old data if needed:%@", self); 104 | [_infoList removeAllObjects]; 105 | [self.collectionView reloadData]; 106 | [self.collectionView setContentOffset:CGPointZero]; 107 | } 108 | 109 | #pragma mark - functional methods 110 | - (void)refreshPageIfNeeded { 111 | NSTimeInterval currentStamp = [[NSDate date] timeIntervalSince1970]; 112 | if (self.infoList.count && currentStamp - _menuInfo.lastTime < 60 * 60) { 113 | return; 114 | } 115 | 116 | // 模拟网络请求延时,根据_menuInfo.menuId请求对应菜单项的相关信息 117 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.02 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 118 | _menuInfo.lastTime = currentStamp; 119 | [self handleNetworkSuccess]; 120 | }); 121 | } 122 | 123 | - (void)cancelNetworkRequest { 124 | // 由于页面可能会被重用,需要取消不必要的网络请求 125 | // NSLog(@"maybe you should cancel network request in here"); 126 | } 127 | 128 | - (void)handleNetworkSuccess { 129 | NSLog(@"==模拟网络请求成功后刷新页面=="); 130 | for (NSInteger index = 0; index < 50; index++) { 131 | [self.infoList addObject:[NSString stringWithFormat:@"image_%d", arc4random_uniform(13)]]; 132 | } 133 | [self.collectionView reloadData]; 134 | } 135 | 136 | - (void)savePageInfo { 137 | [[DataManager sharedInstance] savePageInfo:_infoList menuId:_menuInfo.menuId]; 138 | } 139 | 140 | - (void)loadLocalData { 141 | NSArray *cacheList = [[DataManager sharedInstance] pageInfoWithMenuId:_menuInfo.menuId]; 142 | [_infoList addObjectsFromArray:cacheList]; 143 | [self.collectionView reloadData]; 144 | } 145 | 146 | #pragma mark - accessor methods 147 | - (void)setMenuInfo:(MenuInfo *)menuInfo { 148 | _menuInfo = menuInfo; 149 | [self loadLocalData]; 150 | } 151 | 152 | - (NSMutableArray *)infoList { 153 | if (!_infoList) { 154 | _infoList = [[NSMutableArray alloc] init]; 155 | } 156 | return _infoList; 157 | } 158 | 159 | @end 160 | -------------------------------------------------------------------------------- /VTMagicDemo/Classes/Center/VTCenterViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // VTCenterViewController.m 3 | // VTMagic 4 | // 5 | // Created by tianzhuo on 6/1/16. 6 | // Copyright © 2016 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import "VTCenterViewController.h" 10 | #import "VTGridViewController.h" 11 | #import 12 | 13 | #define kSearchBarWidth (60.0f) 14 | 15 | @interface VTCenterViewController() 16 | 17 | @property (nonatomic, strong) VTMagicController *magicController; 18 | @property (nonatomic, strong) VTGridViewController *topicViewController; 19 | @property (nonatomic, strong) VTGridViewController *forumViewController; 20 | @property (nonatomic, strong) NSArray *menuList; 21 | 22 | @end 23 | 24 | @implementation VTCenterViewController 25 | 26 | - (void)viewDidLoad { 27 | [super viewDidLoad]; 28 | 29 | self.edgesForExtendedLayout = UIRectEdgeAll; 30 | self.view.backgroundColor = [UIColor whiteColor]; 31 | [self addChildViewController:self.magicController]; 32 | [self.view addSubview:_magicController.view]; 33 | [self.view setNeedsUpdateConstraints]; 34 | [self integrateComponents]; 35 | 36 | [self generateTestData]; 37 | [_magicController.magicView reloadData]; 38 | } 39 | 40 | - (void)viewWillAppear:(BOOL)animated { 41 | [super viewWillAppear:animated]; 42 | 43 | [self.navigationController setNavigationBarHidden:YES animated:YES]; 44 | } 45 | 46 | - (void)updateViewConstraints { 47 | UIView *magicView = _magicController.view; 48 | [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[magicView]-0-|" 49 | options:0 50 | metrics:nil 51 | views:NSDictionaryOfVariableBindings(magicView)]]; 52 | [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[magicView]-0-|" 53 | options:0 54 | metrics:nil 55 | views:NSDictionaryOfVariableBindings(magicView)]]; 56 | 57 | [super updateViewConstraints]; 58 | } 59 | 60 | #pragma mark - functional methods 61 | - (void)integrateComponents { 62 | UIButton *searchButton = [UIButton buttonWithType:UIButtonTypeCustom]; 63 | [searchButton setImage:[UIImage imageNamed:@"magic_search"] forState:UIControlStateNormal]; 64 | [searchButton addTarget:self action:@selector(searchAction:) forControlEvents:UIControlEventTouchUpInside]; 65 | searchButton.imageView.contentMode = UIViewContentModeScaleAspectFit; 66 | searchButton.frame = CGRectMake(0, 0, kSearchBarWidth, 20); 67 | [self.magicController.magicView setRightNavigatoinItem:searchButton]; 68 | } 69 | 70 | #pragma mark - actions 71 | - (void)searchAction:(UIButton *)sender { 72 | NSLog(@"searchAction"); 73 | } 74 | 75 | #pragma mark - VTMagicViewDataSource 76 | - (NSArray *)menuTitlesForMagicView:(VTMagicView *)magicView { 77 | NSMutableArray *titleList = [NSMutableArray array]; 78 | for (MenuInfo *menu in _menuList) { 79 | [titleList addObject:menu.title]; 80 | } 81 | return titleList; 82 | } 83 | 84 | - (UIButton *)magicView:(VTMagicView *)magicView menuItemAtIndex:(NSUInteger)itemIndex { 85 | static NSString *itemIdentifier = @"itemIdentifier"; 86 | UIButton *menuItem = [magicView dequeueReusableItemWithIdentifier:itemIdentifier]; 87 | if (!menuItem) { 88 | menuItem = [UIButton buttonWithType:UIButtonTypeCustom]; 89 | [menuItem setTitleColor:RGBCOLOR(50, 50, 50) forState:UIControlStateNormal]; 90 | [menuItem setTitleColor:RGBCOLOR(169, 37, 37) forState:UIControlStateSelected]; 91 | menuItem.titleLabel.font = [UIFont fontWithName:@"Helvetica" size:15.f]; 92 | } 93 | return menuItem; 94 | } 95 | 96 | - (UIViewController *)magicView:(VTMagicView *)magicView viewControllerAtPage:(NSUInteger)pageIndex { 97 | MenuInfo *menuInfo = _menuList[pageIndex]; 98 | if ([menuInfo.title isEqualToString:@"专题"]) { // if (0 == pageIndex) { 99 | return self.topicViewController; 100 | } else { 101 | return self.forumViewController; 102 | } 103 | } 104 | 105 | #pragma mark - functional methods 106 | - (void)generateTestData { 107 | _menuList = @[[MenuInfo menuInfoWithTitl:@"专题"], [MenuInfo menuInfoWithTitl:@"论坛"]]; 108 | } 109 | 110 | #pragma mark - accessor methods 111 | - (VTMagicController *)magicController { 112 | if (!_magicController) { 113 | _magicController = [[VTMagicController alloc] init]; 114 | _magicController.view.translatesAutoresizingMaskIntoConstraints = NO; 115 | _magicController.magicView.navigationInset = UIEdgeInsetsMake(0, kSearchBarWidth, 0, 0); 116 | _magicController.magicView.navigationColor = [UIColor whiteColor]; 117 | _magicController.magicView.sliderColor = RGBCOLOR(169, 37, 37); 118 | _magicController.magicView.switchStyle = VTSwitchStyleDefault; 119 | _magicController.magicView.layoutStyle = VTLayoutStyleCenter; 120 | _magicController.magicView.navigationHeight = 44.f; 121 | _magicController.magicView.againstStatusBar = YES; 122 | _magicController.magicView.dataSource = self; 123 | _magicController.magicView.delegate = self; 124 | } 125 | return _magicController; 126 | } 127 | 128 | - (UIViewController *)topicViewController { 129 | if (!_topicViewController) { 130 | _topicViewController = [[VTGridViewController alloc] init]; 131 | } 132 | return _topicViewController; 133 | } 134 | 135 | - (VTGridViewController *)forumViewController { 136 | if (!_forumViewController) { 137 | _forumViewController = [[VTGridViewController alloc] init]; 138 | } 139 | return _forumViewController; 140 | } 141 | 142 | @end 143 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VTMagic 2 | 3 | VTMagic is a page container library for iOS, you can custom every page controller by different identifier if you need. It's so easy to use!([中文手册传送门](http://www.jianshu.com/p/cb2edb21055f)) 4 | 5 | [CHANGELOG][CHANGELOG_EN]\([变更日志][CHANGELOG_CN]) 6 | 7 | ![preview image](Gif/magic.gif) 8 | 9 | 10 | ## Communication 11 | - If you **found a bug**, *and can provide steps to reliably reproduce it*, open an issue. 12 | - If you **have a feature request**, open an issue. 13 | - If you **want to contribute**, submit a pull request. 14 | 15 | 16 | ## Installation 17 | 18 | ### CocoaPods 19 | 20 | VTMagic is available through [CocoaPods](http://cocoapods.org). To install 21 | it, simply add the following line to your Podfile, and then import : 22 | 23 | ```ruby 24 | pod "VTMagic" 25 | ``` 26 | 27 | ### Manually 28 | 29 | 1. Download the project and drop `VTMagic` folder into your project. 30 | 2. Import file `VTMagic.h`. 31 | 32 | ```objective-c 33 | #import "VTMagic.h" 34 | ``` 35 | 36 | ## Requirements 37 | 38 | This library requires iOS 6.0+ and Xcode 7.0+. 39 | 40 | ## Usage 41 | 42 | To run the example project, clone the repo, and run `pod install` from the project directory first. 43 | 44 | ### Integration 45 | 46 | ```objective-c 47 | - (void)viewDidLoad { 48 | [super viewDidLoad]; 49 | 50 | [self addChildViewController:self.magicController]; 51 | [self.view addSubview:_magicController.view]; 52 | 53 | [_magicController.magicView reloadData]; 54 | } 55 | 56 | - (VTMagicController *)magicController { 57 | if (!_magicController) { 58 | _magicController = [[VTMagicController alloc] init]; 59 | _magicController.magicView.navigationColor = [UIColor whiteColor]; 60 | _magicController.magicView.sliderColor = [UIColor redColor]; 61 | _magicController.magicView.layoutStyle = VTLayoutStyleDivide; 62 | _magicController.magicView.switchStyle = VTSwitchStyleDefault; 63 | _magicController.magicView.navigationHeight = 40.f; 64 | _magicController.magicView.dataSource = self; 65 | _magicController.magicView.delegate = self; 66 | } 67 | return _magicController; 68 | } 69 | ``` 70 | or like this 71 | ```objective-c 72 | #import "VTMagicController.h" 73 | 74 | @interface ViewController : VTMagicController 75 | 76 | @end 77 | ``` 78 | 79 | ```objective-c 80 | @implementation ViewController 81 | 82 | - (void)viewDidLoad { 83 | [super viewDidLoad]; 84 | 85 | self.magicView.navigationColor = [UIColor whiteColor]; 86 | self.magicView.sliderColor = [UIColor redColor]; 87 | self.magicView.layoutStyle = VTLayoutStyleDefault; 88 | self.magicView.switchStyle = VTSwitchStyleDefault; 89 | self.magicView.navigationHeight = 40.f; 90 | self.magicView.dataSource = self; 91 | self.magicView.delegate = self; 92 | 93 | [self.magicView reloadData]; 94 | } 95 | ``` 96 | 97 | ### Protocols 98 | 99 | You must conform to ``. `` and `` are optional. 100 | 101 | #### VTMagicViewDataSource 102 | 103 | ```objective-c 104 | - (NSArray *)menuTitlesForMagicView:(VTMagicView *)magicView { 105 | return _menuList; 106 | } 107 | 108 | - (UIButton *)magicView:(VTMagicView *)magicView menuItemAtIndex:(NSUInteger)itemIndex { 109 | static NSString *itemIdentifier = @"itemIdentifier"; 110 | UIButton *menuItem = [magicView dequeueReusableItemWithIdentifier:itemIdentifier]; 111 | if (!menuItem) { 112 | menuItem = [UIButton buttonWithType:UIButtonTypeCustom]; 113 | [menuItem setTitleColor:RGBCOLOR(50, 50, 50) forState:UIControlStateNormal]; 114 | [menuItem setTitleColor:RGBCOLOR(169, 37, 37) forState:UIControlStateSelected]; 115 | menuItem.titleLabel.font = [UIFont fontWithName:@"Helvetica" size:16.f]; 116 | } 117 | return menuItem; 118 | } 119 | 120 | - (UIViewController *)magicView:(VTMagicView *)magicView viewControllerAtPage:(NSUInteger)pageIndex { 121 | if (0 == pageIndex) { 122 | static NSString *recomId = @"recom.identifier"; 123 | VTRecomViewController *recomViewController = [magicView dequeueReusablePageWithIdentifier:recomId]; 124 | if (!recomViewController) { 125 | recomViewController = [[VTRecomViewController alloc] init]; 126 | } 127 | return recomViewController; 128 | } 129 | 130 | static NSString *gridId = @"grid.identifier"; 131 | VTGridViewController *gridViewController = [magicView dequeueReusablePageWithIdentifier:gridId]; 132 | if (!gridViewController) { 133 | gridViewController = [[VTGridViewController alloc] init]; 134 | } 135 | return gridViewController; 136 | } 137 | ``` 138 | 139 | #### VTMagicViewDelegate 140 | 141 | ```objective-c 142 | - (void)magicView:(VTMagicView *)magicView viewDidAppear:(UIViewController *)viewController atPage:(NSUInteger)pageIndex { 143 | NSLog(@"pageIndex:%ld viewDidAppear:%@", (long)pageIndex, viewController.view); 144 | } 145 | 146 | - (void)magicView:(VTMagicView *)magicView viewDidDisappear:(UIViewController *)viewController atPage:(NSUInteger)pageIndex { 147 | NSLog(@"pageIndex:%ld viewDidDisappear:%@", (long)pageIndex, viewController.view); 148 | } 149 | 150 | - (void)magicView:(VTMagicView *)magicView didSelectItemAtIndex:(NSUInteger)itemIndex { 151 | NSLog(@"didSelectItemAtIndex:%ld", (long)itemIndex); 152 | } 153 | ``` 154 | 155 | #### VTMagicReuseProtocol 156 | 157 | This method will be called when page is reused, you should clear old data or reset content offset in here. 158 | 159 | ```objective-c 160 | - (void)vtm_prepareForReuse { 161 | NSLog(@"clear old data if needed:%@", self); 162 | } 163 | ``` 164 | 165 | ## Features 166 | 167 | #### Appearance callbacks 168 | 169 | VTMagic will automatically calls the appearance callbacks when user switches the page, maybe you should do something in here, e.g. refresh page info. 170 | 171 | ```objective-c 172 | - (void)viewDidAppear:(BOOL)animated { 173 | [super viewDidAppear:animated]; 174 | 175 | // do something... 176 | } 177 | 178 | - (void)viewWillDisappear:(BOOL)animated { 179 | [super viewWillDisappear:animated]; 180 | 181 | // do something... 182 | } 183 | 184 | ``` 185 | 186 | #### Get magicController 187 | 188 | You can get the nearest magicController in any child view controller which is conforms to ``, after you import file `VTMagic.h`. 189 | 190 | ```objective-c 191 | NSInteger currentPage = self.magicController.currentPage; 192 | UIViewController *viewController = self.magicController.currentViewController; 193 | ``` 194 | 195 | #### Switch to specified page 196 | 197 | You can switch to some specified page. 198 | 199 | ```objective-c 200 | [self.magicView switchToPage:3 animated:YES]; 201 | [self.magicController switchToPage:3 animated:YES]; 202 | ``` 203 | #### Get specified view controller 204 | 205 | You can get any page controller by page index, it will return nil if the page is not on the screen. 206 | 207 | ```objective-c 208 | UIViewController *viewController = [self.magicView viewControllerAtPage:3]; 209 | UIViewController *viewController = [self.magicController viewControllerAtPage:3]; 210 | ``` 211 | 212 | 213 | ## Author 214 | 215 | **VictorTian** *email: tianzhuo112@163.com* 216 | 217 | 218 | ## License 219 | 220 | VTMagic is released under the MIT license. See LICENSE for details. 221 | 222 | 223 | 224 | 225 | 226 | [CHANGELOG_CN]: CHANGELOG_CN.md 227 | [CHANGELOG_EN]: CHANGELOG_EN.md 228 | 229 | -------------------------------------------------------------------------------- /VTMagic/VTContentView.m: -------------------------------------------------------------------------------- 1 | // 2 | // VTContentView.m 3 | // VTMagicView 4 | // 5 | // Created by tianzhuo on 14/12/29. 6 | // Copyright (c) 2014年 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import "VTContentView.h" 10 | #import "UIViewController+VTMagic.h" 11 | #import "UIScrollView+VTMagic.h" 12 | 13 | @interface VTContentView() 14 | 15 | @property (nonatomic, strong) NSMutableDictionary *visibleDict; // 屏幕上可见的控制器 16 | @property (nonatomic, strong) NSMutableArray *indexList; // 索引集合 17 | @property (nonatomic, strong) NSMutableArray *frameList; // 控制器的坐标集合 18 | @property (nonatomic, strong) NSString *identifier; // 重用标识符 19 | @property (nonatomic, strong) NSCache *pageCache; // 缓存池 20 | 21 | @end 22 | 23 | @implementation VTContentView 24 | 25 | #pragma mark - Lifecycle 26 | - (instancetype)initWithFrame:(CGRect)frame { 27 | self = [super initWithFrame:frame]; 28 | if (self) { 29 | _needPreloading = YES; 30 | _indexList = [[NSMutableArray alloc] init]; 31 | _frameList = [[NSMutableArray alloc] init]; 32 | _visibleDict = [[NSMutableDictionary alloc] init]; 33 | _pageCache = [[NSCache alloc] init]; 34 | _pageCache.name = @"com.tianzhuo.magic"; 35 | _pageCache.countLimit = 10; 36 | 37 | #if TARGET_OS_IPHONE 38 | [[NSNotificationCenter defaultCenter] addObserver:self 39 | selector:@selector(clearMemoryCache) 40 | name:UIApplicationDidReceiveMemoryWarningNotification 41 | object:nil]; 42 | #endif 43 | } 44 | return self; 45 | } 46 | 47 | - (void)dealloc { 48 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 49 | } 50 | 51 | - (void)layoutSubviews { 52 | [super layoutSubviews]; 53 | 54 | if (CGRectIsEmpty(self.frame)) { 55 | return; 56 | } 57 | 58 | CGFloat offset = self.contentOffset.x; 59 | CGFloat width = self.frame.size.width; 60 | BOOL isNotBorder = 0 != (int)offset%(int)width; 61 | NSInteger currentPage = offset/self.frame.size.width; 62 | if (_currentPage == currentPage && isNotBorder) { 63 | return; 64 | } 65 | 66 | _currentPage = currentPage; 67 | CGRect frame = CGRectZero; 68 | UIViewController *viewController = nil; 69 | NSArray *pathList = [_visibleDict allKeys]; 70 | for (NSIndexPath *indexPath in pathList) { 71 | frame = [_frameList[indexPath.row] CGRectValue]; 72 | viewController = _visibleDict[indexPath]; 73 | // 控制器若移出屏幕则将其视图从父类中移除,并添加到缓存池中 74 | if (![self vtm_isNeedDisplayWithFrame:frame preloading:_needPreloading]) { 75 | [self moveViewControllerToCache:viewController]; 76 | [_visibleDict removeObjectForKey:indexPath]; 77 | } else { 78 | viewController.view.frame = frame; 79 | } 80 | } 81 | 82 | NSMutableArray *tempPaths = [_indexList mutableCopy]; 83 | [tempPaths removeObjectsInArray:pathList]; 84 | for (NSIndexPath *indexPath in tempPaths) { 85 | frame = [_frameList[indexPath.row] CGRectValue]; 86 | if ([self vtm_isNeedDisplayWithFrame:frame preloading:_needPreloading]) { 87 | [self loadViewControllerAtIndexPath:indexPath]; 88 | } 89 | } 90 | } 91 | 92 | #pragma mark - functional methods 93 | - (void)reloadData { 94 | [self resetCacheData]; 95 | [self resetPageFrames]; 96 | [self setNeedsLayout]; 97 | [self layoutIfNeeded]; 98 | } 99 | 100 | -(void)resetCacheData { 101 | [_indexList removeAllObjects]; 102 | for (NSInteger i = 0; i < _pageCount; i++) { 103 | NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0]; 104 | [_indexList addObject:indexPath]; 105 | } 106 | 107 | // reload时清除所有页面 108 | NSArray *viewControllers = [_visibleDict allValues]; 109 | for (UIViewController *viewController in viewControllers) { 110 | [self moveViewControllerToCache:viewController]; 111 | } 112 | [_visibleDict removeAllObjects]; 113 | } 114 | 115 | - (void)moveViewControllerToCache:(UIViewController *)viewController { 116 | [viewController willMoveToParentViewController:nil]; 117 | [viewController.view removeFromSuperview]; 118 | [viewController removeFromParentViewController]; 119 | 120 | // 添加到缓存池 121 | NSMutableSet *cacheSet = [_pageCache objectForKey:viewController.reuseIdentifier]; 122 | if (!cacheSet) cacheSet = [[NSMutableSet alloc] init]; 123 | [cacheSet addObject:viewController]; 124 | [_pageCache setObject:cacheSet forKey:viewController.reuseIdentifier]; 125 | } 126 | 127 | - (void)resetPageFrames { 128 | [_frameList removeAllObjects]; 129 | CGRect frame = self.bounds; 130 | for (NSIndexPath *indexPath in _indexList) { 131 | frame.origin.x = indexPath.row * frame.size.width; 132 | [_frameList addObject:[NSValue valueWithCGRect:frame]]; 133 | } 134 | self.contentSize = CGSizeMake(CGRectGetMaxX(frame), 0); 135 | self.contentOffset = CGPointMake(CGRectGetWidth(frame)*_currentPage, 0); 136 | } 137 | 138 | #pragma mark - 根据页面控制器获取对应的索引 139 | - (NSInteger)pageIndexForViewController:(UIViewController *)viewController { 140 | for (NSIndexPath *indexPath in _visibleDict.allKeys) { 141 | if ([viewController isEqual:_visibleDict[indexPath]]) { 142 | return indexPath.row; 143 | } 144 | } 145 | return NSNotFound; 146 | } 147 | 148 | - (CGRect)frameOfViewControllerAtPage:(NSUInteger)pageIndex 149 | { 150 | if (_frameList.count <= pageIndex) { 151 | return CGRectZero; 152 | } 153 | return [_frameList[pageIndex] CGRectValue]; 154 | } 155 | 156 | #pragma mark - 根据索引获取页面控制器 157 | - (UIViewController *)viewControllerAtPage:(NSUInteger)pageIndex { 158 | return [self viewControllerAtPage:pageIndex autoCreate:NO]; 159 | } 160 | 161 | - (UIViewController *)creatViewControllerAtPage:(NSUInteger)pageIndex { 162 | return [self viewControllerAtPage:pageIndex autoCreate:YES]; 163 | } 164 | 165 | - (UIViewController *)viewControllerAtPage:(NSUInteger)pageIndex autoCreate:(BOOL)autoCreate { 166 | if (_pageCount <= pageIndex) { 167 | return nil; 168 | } 169 | 170 | NSIndexPath *indexPath = [NSIndexPath indexPathForRow:pageIndex inSection:0]; 171 | UIViewController *viewController = [_visibleDict objectForKey:indexPath]; 172 | if (!viewController && autoCreate) { 173 | viewController = [self loadViewControllerAtIndexPath:indexPath]; 174 | } 175 | return viewController; 176 | } 177 | 178 | - (UIViewController *)loadViewControllerAtIndexPath:(NSIndexPath *)indexPath { 179 | UIViewController *viewController = [_dataSource contentView:self viewControllerAtPage:indexPath.row]; 180 | if (viewController) { 181 | viewController.reuseIdentifier = _identifier; 182 | CGRect pageFrame = [_frameList[indexPath.row] CGRectValue]; 183 | viewController.view.frame = pageFrame; 184 | [self addSubview:viewController.view]; 185 | [_visibleDict setObject:viewController forKey:indexPath]; 186 | } 187 | _identifier = nil; 188 | return viewController; 189 | } 190 | 191 | #pragma mark - 根据缓存标识查询可重用的视图控制器 192 | - (UIViewController *)dequeueReusablePageWithIdentifier:(NSString *)identifier { 193 | _identifier = identifier; 194 | NSMutableSet *cacheSet = [_pageCache objectForKey:identifier]; 195 | UIViewController *viewController = [cacheSet anyObject]; 196 | if (viewController) { 197 | [cacheSet removeObject:viewController]; 198 | [_pageCache setObject:cacheSet forKey:identifier]; 199 | } 200 | return viewController; 201 | } 202 | 203 | #pragma mark - clear memory cache 204 | - (void)clearMemoryCache { 205 | [_pageCache removeAllObjects]; 206 | } 207 | 208 | #pragma mark - accessor methods 209 | - (NSArray *)visibleList { 210 | return [_visibleDict allValues]; 211 | } 212 | 213 | @end 214 | -------------------------------------------------------------------------------- /VTMagicDemo/Classes/Home/VTHomeViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // VTMagicView 4 | // 5 | // Created by tianzhuo on 14-11-11. 6 | // Copyright (c) 2014年 tianzhuo. All rights reserved. 7 | // 8 | 9 | #import "VTHomeViewController.h" 10 | #import "VTRecomViewController.h" 11 | #import "VTGridViewController.h" 12 | 13 | @interface VTHomeViewController () 14 | 15 | @property (nonatomic, strong) NSArray *menuList; 16 | @property (nonatomic, assign) BOOL autoSwitch; 17 | 18 | @end 19 | 20 | @implementation VTHomeViewController 21 | 22 | #pragma mark - Lifecycle 23 | - (void)viewDidLoad { 24 | [super viewDidLoad]; 25 | 26 | // self.magicView.bounces = YES; 27 | // self.magicView.headerHidden = NO; 28 | // self.magicView.itemSpacing = 20.f; 29 | // self.magicView.switchEnabled = YES; 30 | // self.magicView.separatorHidden = NO; 31 | // self.magicView.acturalSpacing = 10; 32 | self.magicView.itemScale = 1.2; 33 | self.magicView.headerHeight = 40; 34 | self.magicView.navigationHeight = 44; 35 | self.magicView.againstStatusBar = YES; 36 | // self.magicView.sliderExtension = 5.0; 37 | // self.magicView.switchStyle = VTSwitchStyleStiff; 38 | // self.magicView.navigationInset = UIEdgeInsetsMake(0, 50, 0, 0); 39 | self.magicView.headerView.backgroundColor = RGBCOLOR(243, 40, 47); 40 | self.magicView.navigationColor = [UIColor whiteColor]; 41 | self.magicView.layoutStyle = VTLayoutStyleDefault; 42 | self.view.backgroundColor = RGBCOLOR(243, 40, 47); 43 | self.edgesForExtendedLayout = UIRectEdgeAll; 44 | [self integrateComponents]; 45 | [self configSeparatorView]; 46 | 47 | [self addNotification]; 48 | [self generateTestData]; 49 | [self.magicView reloadData]; 50 | } 51 | 52 | - (void)viewWillAppear:(BOOL)animated { 53 | [super viewWillAppear:animated]; 54 | 55 | [self.navigationController setNavigationBarHidden:YES animated:YES]; 56 | _autoSwitch = 0 != self.tabBarController.selectedIndex; 57 | } 58 | 59 | - (void)viewDidAppear:(BOOL)animated { 60 | [super viewDidAppear:animated]; 61 | 62 | if (_autoSwitch) { 63 | [self.magicView switchToPage:0 animated:YES]; 64 | _autoSwitch = NO; 65 | } 66 | } 67 | 68 | - (void)dealloc { 69 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 70 | } 71 | 72 | #pragma mark - NSNotification 73 | - (void)addNotification { 74 | [self removeNotification]; 75 | [[NSNotificationCenter defaultCenter] addObserver:self 76 | selector:@selector(statusBarOrientationChange:) 77 | name:UIApplicationDidChangeStatusBarOrientationNotification 78 | object:nil]; 79 | } 80 | 81 | - (void)removeNotification { 82 | [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidChangeStatusBarOrientationNotification object:nil]; 83 | } 84 | 85 | - (void)statusBarOrientationChange:(NSNotification *)notification { 86 | [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:YES]; 87 | } 88 | 89 | #pragma mark - VTMagicViewDataSource 90 | - (NSArray *)menuTitlesForMagicView:(VTMagicView *)magicView { 91 | NSMutableArray *titleList = [NSMutableArray array]; 92 | for (MenuInfo *menu in _menuList) { 93 | [titleList addObject:menu.title]; 94 | } 95 | return titleList; 96 | } 97 | 98 | - (UIButton *)magicView:(VTMagicView *)magicView menuItemAtIndex:(NSUInteger)itemIndex { 99 | static NSString *itemIdentifier = @"itemIdentifier"; 100 | UIButton *menuItem = [magicView dequeueReusableItemWithIdentifier:itemIdentifier]; 101 | if (!menuItem) { 102 | menuItem = [UIButton buttonWithType:UIButtonTypeCustom]; 103 | [menuItem setTitleColor:RGBCOLOR(50, 50, 50) forState:UIControlStateNormal]; 104 | [menuItem setTitleColor:RGBCOLOR(169, 37, 37) forState:UIControlStateSelected]; 105 | menuItem.titleLabel.font = [UIFont fontWithName:@"Helvetica" size:15.f]; 106 | } 107 | // 默认会自动完成赋值 108 | // MenuInfo *menuInfo = _menuList[itemIndex]; 109 | // [menuItem setTitle:menuInfo.title forState:UIControlStateNormal]; 110 | return menuItem; 111 | } 112 | 113 | - (UIViewController *)magicView:(VTMagicView *)magicView viewControllerAtPage:(NSUInteger)pageIndex { 114 | MenuInfo *menuInfo = _menuList[pageIndex]; 115 | if (0 == pageIndex) { 116 | static NSString *recomId = @"recom.identifier"; 117 | VTRecomViewController *recomViewController = [magicView dequeueReusablePageWithIdentifier:recomId]; 118 | if (!recomViewController) { 119 | recomViewController = [[VTRecomViewController alloc] init]; 120 | } 121 | recomViewController.menuInfo = menuInfo; 122 | return recomViewController; 123 | } 124 | 125 | static NSString *gridId = @"grid.identifier"; 126 | VTGridViewController *viewController = [magicView dequeueReusablePageWithIdentifier:gridId]; 127 | if (!viewController) { 128 | viewController = [[VTGridViewController alloc] init]; 129 | } 130 | viewController.menuInfo = menuInfo; 131 | return viewController; 132 | } 133 | 134 | #pragma mark - VTMagicViewDelegate 135 | - (void)magicView:(VTMagicView *)magicView viewDidAppear:(__kindof UIViewController *)viewController atPage:(NSUInteger)pageIndex { 136 | // NSLog(@"index:%ld viewDidAppear:%@", (long)pageIndex, viewController.view); 137 | } 138 | 139 | - (void)magicView:(VTMagicView *)magicView viewDidDisappear:(__kindof UIViewController *)viewController atPage:(NSUInteger)pageIndex { 140 | // NSLog(@"index:%ld viewDidDisappear:%@", (long)pageIndex, viewController.view); 141 | } 142 | 143 | - (void)magicView:(VTMagicView *)magicView didSelectItemAtIndex:(NSUInteger)itemIndex { 144 | // NSLog(@"didSelectItemAtIndex:%ld", (long)itemIndex); 145 | } 146 | 147 | #pragma mark - actions 148 | - (void)subscribeAction { 149 | NSLog(@"subscribeAction"); 150 | // against status bar or not 151 | // self.magicView.againstStatusBar = !self.magicView.againstStatusBar; 152 | [self.magicView setHeaderHidden:!self.magicView.isHeaderHidden duration:0.35]; 153 | } 154 | 155 | #pragma mark - functional methods 156 | - (void)generateTestData { 157 | NSString *title = @"推荐"; 158 | NSMutableArray *menuList = [[NSMutableArray alloc] initWithCapacity:24]; 159 | [menuList addObject:[MenuInfo menuInfoWithTitl:title]]; 160 | for (int index = 0; index < 20; index++) { 161 | title = [NSString stringWithFormat:@"省份%d", index]; 162 | MenuInfo *menu = [MenuInfo menuInfoWithTitl:title]; 163 | [menuList addObject:menu]; 164 | } 165 | _menuList = menuList; 166 | } 167 | 168 | - (void)integrateComponents { 169 | UIButton *rightButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 50, 30)]; 170 | [rightButton addTarget:self action:@selector(subscribeAction) forControlEvents:UIControlEventTouchUpInside]; 171 | [rightButton setTitleColor:RGBACOLOR(169, 37, 37, 0.6) forState:UIControlStateSelected]; 172 | [rightButton setTitleColor:RGBCOLOR(169, 37, 37) forState:UIControlStateNormal]; 173 | [rightButton setTitle:@"+" forState:UIControlStateNormal]; 174 | rightButton.titleLabel.font = [UIFont boldSystemFontOfSize:28]; 175 | rightButton.center = self.view.center; 176 | self.magicView.rightNavigatoinItem = rightButton; 177 | } 178 | 179 | - (void)configSeparatorView { 180 | // UIImageView *separatorView = [[UIImageView alloc] init]; 181 | // [self.magicView setSeparatorView:separatorView]; 182 | self.magicView.separatorHeight = 2.f; 183 | self.magicView.separatorColor = RGBCOLOR(22, 146, 211); 184 | self.magicView.navigationView.layer.shadowColor = RGBCOLOR(22, 146, 211).CGColor; 185 | self.magicView.navigationView.layer.shadowOffset = CGSizeMake(0, 0.5); 186 | self.magicView.navigationView.layer.shadowOpacity = 0.8; 187 | self.magicView.navigationView.clipsToBounds = NO; 188 | } 189 | 190 | @end 191 | --------------------------------------------------------------------------------