├── .gitignore ├── LICENSE ├── README.md ├── Resource └── emitterViewAnimation │ ├── emitterView.gif │ ├── emitterView1.gif │ └── emitterViewbg.gif ├── SMDemo.xcworkspace └── contents.xcworkspacedata └── SMDemo ├── SMDemoList.xcodeproj ├── SMDemo.xcworkspace │ └── contents.xcworkspacedata ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── SMDemoList ├── Main │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ ├── Contents.json │ │ ├── btn_action.imageset │ │ │ ├── Contents.json │ │ │ └── btn_action@2x.png │ │ ├── camera_blur_0.imageset │ │ │ ├── Contents.json │ │ │ └── camera_blur_0.jpg │ │ ├── camera_blur_1.imageset │ │ │ ├── Contents.json │ │ │ └── camera_blur_1.jpg │ │ ├── camera_blur_2.imageset │ │ │ ├── Contents.json │ │ │ └── camera_blur_2.jpg │ │ ├── camera_blur_3.imageset │ │ │ ├── Contents.json │ │ │ └── camera_blur_3.jpg │ │ └── fa-link.imageset │ │ │ ├── Contents.json │ │ │ └── fa-link@2x.png │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Info.plist │ ├── ViewController.h │ ├── ViewController.m │ └── main.m ├── SMCrash │ ├── NSArray+Crash.h │ ├── NSArray+Crash.m │ ├── NSDictionary+Crash.h │ ├── NSDictionary+Crash.m │ ├── NSMutableArray+Crash.h │ ├── NSMutableArray+Crash.m │ ├── NSMutableDictionary+Crash.h │ ├── NSMutableDictionary+Crash.m │ ├── NSNull+Crash.h │ ├── NSNull+Crash.m │ ├── SMIAvoidCrash.h │ └── SMIAvoidCrash.m ├── SMDanmaku │ ├── SMDanmaku.bundle │ │ ├── landscape_liuguang_blue@2x.png │ │ ├── landscape_liuguang_gold@2x.png │ │ ├── landscape_liuguang_green@2x.png │ │ ├── landscape_liuguang_red@2x.png │ │ ├── my_badge_guard@2x.png │ │ ├── my_badge_noble_1@2x.png │ │ ├── my_badge_noble_2@2x.png │ │ ├── my_badge_noble_3@2x.png │ │ ├── my_badge_noble_4@2x.png │ │ ├── my_badge_noble_5@2x.png │ │ └── my_badge_noble_6@2x.png │ ├── SMDanmakuView.h │ ├── SMDanmakuView.m │ ├── SMDanmakuViewController.h │ └── SMDanmakuViewController.m ├── SMEmitter │ ├── SMEmitterButton.h │ ├── SMEmitterButton.m │ ├── SMEmitterView.h │ ├── SMEmitterView.m │ ├── SMEmitterViewController.h │ ├── SMEmitterViewController.m │ └── emitter.bundle │ │ ├── Sparkle1.png │ │ ├── Sparkle2.png │ │ ├── Sparkle3.png │ │ ├── heartH.png │ │ ├── heartN.png │ │ ├── mouya │ │ ├── icon_mobile_live_praise_0@2x.png │ │ ├── icon_mobile_live_praise_1@2x.png │ │ ├── icon_mobile_live_praise_2@2x.png │ │ ├── icon_mobile_live_praise_3@2x.png │ │ ├── icon_mobile_live_praise_4@2x.png │ │ ├── icon_mobile_live_praise_5@2x.png │ │ ├── icon_mobile_live_praise_6@2x.png │ │ ├── icon_mobile_live_praise_7@2x.png │ │ └── icon_mobile_live_praise_8@2x.png │ │ ├── mouzhu │ │ ├── heart0.png │ │ ├── heart0@2x.png │ │ ├── heart1.png │ │ ├── heart10.png │ │ ├── heart10@2x.png │ │ ├── heart11.png │ │ ├── heart11@2x.png │ │ ├── heart12.png │ │ ├── heart12@2x.png │ │ ├── heart13.png │ │ ├── heart13@2x.png │ │ ├── heart14.png │ │ ├── heart14@2x.png │ │ ├── heart15.png │ │ ├── heart15@2x.png │ │ ├── heart16.png │ │ ├── heart16@2x.png │ │ ├── heart17.png │ │ ├── heart17@2x.png │ │ ├── heart18.png │ │ ├── heart18@2x.png │ │ ├── heart19.png │ │ ├── heart19@2x.png │ │ ├── heart1@2x.png │ │ ├── heart2.png │ │ ├── heart20.png │ │ ├── heart20@2x.png │ │ ├── heart2@2x.png │ │ ├── heart3.png │ │ ├── heart3@2x.png │ │ ├── heart4.png │ │ ├── heart4@2x.png │ │ ├── heart5.png │ │ ├── heart5@2x.png │ │ ├── heart6.png │ │ ├── heart6@2x.png │ │ ├── heart7.png │ │ ├── heart7@2x.png │ │ ├── heart8.png │ │ ├── heart8@2x.png │ │ ├── heart9.png │ │ └── heart9@2x.png │ │ ├── zanH.png │ │ └── zanN.png ├── SMICropImageView │ ├── SMICropBottomView.h │ ├── SMICropBottomView.m │ ├── SMICropMaskerView.h │ ├── SMICropMaskerView.m │ ├── SMICropViewController.h │ └── SMICropViewController.m ├── SMLayout │ └── SMLayout.swift └── SMVideoPlayer │ ├── SMICellVideoFullViewController.h │ ├── SMICellVideoFullViewController.m │ ├── SMICellVideoItemsView.h │ ├── SMICellVideoItemsView.m │ ├── SMICellVideoView.h │ └── SMICellVideoView.m ├── iOS-MVX.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata └── iOS-MVX ├── AppDelegate.swift ├── Assets.xcassets └── AppIcon.appiconset │ └── Contents.json ├── Base.lproj ├── LaunchScreen.storyboard └── Main.storyboard ├── Info.plist ├── MVC ├── MVCModel.swift ├── MVCVC.swift └── MVCView.swift ├── MVP ├── MVPModel.swift ├── MVPVC.swift ├── MVPView.swift └── Presenter.swift ├── MVVM ├── MVVMModel.swift ├── MVVMVC.swift ├── MVVMView.swift ├── MVVMViewModel.swift └── TableView │ ├── MVVMCell.swift │ ├── MVVMCell.xib │ ├── MVVMController.swift │ └── MVVMVM.swift └── ViewController.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xcuserstate 23 | 24 | ## Obj-C/Swift specific 25 | *.hmap 26 | *.ipa 27 | *.dSYM.zip 28 | *.dSYM 29 | 30 | # CocoaPods 31 | # 32 | # We recommend against adding the Pods directory to your .gitignore. However 33 | # you should judge for yourself, the pros and cons are mentioned at: 34 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 35 | # 36 | # Pods/ 37 | 38 | # Carthage 39 | # 40 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 41 | # Carthage/Checkouts 42 | 43 | Carthage/Build 44 | 45 | # fastlane 46 | # 47 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 48 | # screenshots whenever they are needed. 49 | # For more information about the recommended setup visit: 50 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 51 | 52 | fastlane/report.xml 53 | fastlane/screenshots 54 | 55 | #Code Injection 56 | # 57 | # After new code Injection tools there's a generated folder /iOSInjectionProject 58 | # https://github.com/johnno1962/injectionforxcode 59 | 60 | iOSInjectionProject/ 61 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 simon 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Demo 3 | 4 | -------------- 5 | 6 |

7 | 8 | ![enter image description here](https://img.shields.io/badge/iOS-Animation-brightgreen.svg) 9 | 10 | ![enter image description here](https://img.shields.io/badge/license-MIT-green.svg?style=flat) 11 | 12 | 13 | 14 | 在使用中有任何问题都可以提 issue, 欢迎加入QQ群:475814382 15 | 16 | -------------- 17 | ### SMEmitterView 18 | ![image](https://github.com/icoderRo/SMAnimationDemo/blob/master/Resource/emitterViewAnimation/emitterView.gif) ![image](https://github.com/icoderRo/SMAnimationDemo/blob/master/Resource/emitterViewAnimation/emitterViewbg.gif) 19 | 20 | #### SMEmitterView Usage 21 | ``` Objc 22 | // 1.基本创建 23 | SMEmitterView *emitterView = [[SMEmitterView alloc] init]; 24 | emitterView.frame = CGRectMake(10, 120, width, 400); 25 | [self.view addSubview:emitterView]; 26 | 27 | // 2.可以设置 粒子大小和发射源位置 28 | emitterView.emitterSize = CGSizeMake(36, 36); 29 | emitterView.positionType = SMEmitterPositionLeft; 30 | 31 | // 3.可以设置粒子, 默认为绘制心形 32 | emitterView.images = images; 33 | 34 | // 4.开始, 暂停, 恢复, 停止 35 | [self.emitterView fireWithEmitterCount:100]; 36 | [self.emitterView resume]; 37 | [self.emitterView pause]; 38 | [self.emitterView stop]; 39 | 40 | // 6.可以监听view的点击 41 | emitterView.delegate = self; 42 | - (void)emitterView:(SMEmitterView *)emitterView didAddEmitterCount:(NSUInteger)emitterCount { 43 | NSLog(@"%zd", emitterCount); 44 | } 45 | ``` 46 | -- 47 | 48 | ### SMEmitterButton 49 | ![image](https://github.com/icoderRo/SMAnimationDemo/blob/master/Resource/emitterViewAnimation/emitterView1.gif) 50 | 51 | #### SMEmitterButton Usage 52 | ```Objc 53 | // 1.创建 54 | SMEmitterButton *btn = [[SMEmitterButton alloc] initWithEffectType:SMEffectType frame:CGRectMake(30, 550, 46, 46)]; 55 | 56 | // 2.1 使用SMEffectEmitter效果, 传入图片数组 57 | btn.emitters = @[[UIImage imageWithContentsOfFile:path(@"emitter", @"bundle", @"Sparkle2")]]; 58 | 59 | // 2.2 使用SMEffectWare效果, 设置wareType(SMWareLayerCircle,SMWareLayerHeart)类型和颜色 60 | btn.wareType = SMWareLayerHeart; 61 | btn.wareColor = [UIColor redColor]; 62 | 63 | . 64 | . 65 | . 66 | ``` 67 | 68 | ##### LICENSE - "MIT License" 69 | 70 | 71 | -------------------------------------------------------------------------------- /Resource/emitterViewAnimation/emitterView.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/Resource/emitterViewAnimation/emitterView.gif -------------------------------------------------------------------------------- /Resource/emitterViewAnimation/emitterView1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/Resource/emitterViewAnimation/emitterView1.gif -------------------------------------------------------------------------------- /Resource/emitterViewAnimation/emitterViewbg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/Resource/emitterViewAnimation/emitterViewbg.gif -------------------------------------------------------------------------------- /SMDemo.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /SMDemo/SMDemoList.xcodeproj/SMDemo.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /SMDemo/SMDemoList.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | F4D182941F501FFF0044330F /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = F4D182761F501FFF0044330F /* AppDelegate.m */; }; 11 | F4D182951F501FFF0044330F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F4D182771F501FFF0044330F /* Assets.xcassets */; }; 12 | F4D182961F501FFF0044330F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F4D182781F501FFF0044330F /* LaunchScreen.storyboard */; }; 13 | F4D182971F501FFF0044330F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F4D1827A1F501FFF0044330F /* Main.storyboard */; }; 14 | F4D182981F501FFF0044330F /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = F4D1827C1F501FFF0044330F /* Info.plist */; }; 15 | F4D182991F501FFF0044330F /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = F4D1827D1F501FFF0044330F /* main.m */; }; 16 | F4D1829A1F501FFF0044330F /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F4D1827F1F501FFF0044330F /* ViewController.m */; }; 17 | F4D1829B1F501FFF0044330F /* SMCompressViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F4D182821F501FFF0044330F /* SMCompressViewController.m */; }; 18 | F4D1829C1F501FFF0044330F /* SMDanmaku.bundle in Resources */ = {isa = PBXBuildFile; fileRef = F4D182841F501FFF0044330F /* SMDanmaku.bundle */; }; 19 | F4D1829D1F501FFF0044330F /* SMDanmakuView.m in Sources */ = {isa = PBXBuildFile; fileRef = F4D182861F501FFF0044330F /* SMDanmakuView.m */; }; 20 | F4D1829E1F501FFF0044330F /* SMDanmakuViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F4D182881F501FFF0044330F /* SMDanmakuViewController.m */; }; 21 | F4D1829F1F501FFF0044330F /* emitter.bundle in Resources */ = {isa = PBXBuildFile; fileRef = F4D1828A1F501FFF0044330F /* emitter.bundle */; }; 22 | F4D182A01F501FFF0044330F /* SMEmitterButton.m in Sources */ = {isa = PBXBuildFile; fileRef = F4D1828C1F501FFF0044330F /* SMEmitterButton.m */; }; 23 | F4D182A11F501FFF0044330F /* SMEmitterView.m in Sources */ = {isa = PBXBuildFile; fileRef = F4D1828E1F501FFF0044330F /* SMEmitterView.m */; }; 24 | F4D182A21F501FFF0044330F /* SMEmitterViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F4D182901F501FFF0044330F /* SMEmitterViewController.m */; }; 25 | F4D182A31F501FFF0044330F /* SMAVPlayerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F4D182931F501FFF0044330F /* SMAVPlayerViewController.m */; }; 26 | F4E65B171F53B28E00412481 /* SMLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4E65B161F53B28E00412481 /* SMLayout.swift */; }; 27 | /* End PBXBuildFile section */ 28 | 29 | /* Begin PBXFileReference section */ 30 | F45F77851E092AD4008AD07E /* SMDemoList.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SMDemoList.app; sourceTree = BUILT_PRODUCTS_DIR; }; 31 | F4D182751F501FFF0044330F /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 32 | F4D182761F501FFF0044330F /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 33 | F4D182771F501FFF0044330F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 34 | F4D182791F501FFF0044330F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 35 | F4D1827B1F501FFF0044330F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 36 | F4D1827C1F501FFF0044330F /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 37 | F4D1827D1F501FFF0044330F /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 38 | F4D1827E1F501FFF0044330F /* ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 39 | F4D1827F1F501FFF0044330F /* ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 40 | F4D182811F501FFF0044330F /* SMCompressViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SMCompressViewController.h; sourceTree = ""; }; 41 | F4D182821F501FFF0044330F /* SMCompressViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SMCompressViewController.m; sourceTree = ""; }; 42 | F4D182841F501FFF0044330F /* SMDanmaku.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = SMDanmaku.bundle; sourceTree = ""; }; 43 | F4D182851F501FFF0044330F /* SMDanmakuView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SMDanmakuView.h; sourceTree = ""; }; 44 | F4D182861F501FFF0044330F /* SMDanmakuView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SMDanmakuView.m; sourceTree = ""; }; 45 | F4D182871F501FFF0044330F /* SMDanmakuViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SMDanmakuViewController.h; sourceTree = ""; }; 46 | F4D182881F501FFF0044330F /* SMDanmakuViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SMDanmakuViewController.m; sourceTree = ""; }; 47 | F4D1828A1F501FFF0044330F /* emitter.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = emitter.bundle; sourceTree = ""; }; 48 | F4D1828B1F501FFF0044330F /* SMEmitterButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SMEmitterButton.h; sourceTree = ""; }; 49 | F4D1828C1F501FFF0044330F /* SMEmitterButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SMEmitterButton.m; sourceTree = ""; }; 50 | F4D1828D1F501FFF0044330F /* SMEmitterView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SMEmitterView.h; sourceTree = ""; }; 51 | F4D1828E1F501FFF0044330F /* SMEmitterView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SMEmitterView.m; sourceTree = ""; }; 52 | F4D1828F1F501FFF0044330F /* SMEmitterViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SMEmitterViewController.h; sourceTree = ""; }; 53 | F4D182901F501FFF0044330F /* SMEmitterViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SMEmitterViewController.m; sourceTree = ""; }; 54 | F4D182921F501FFF0044330F /* SMAVPlayerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SMAVPlayerViewController.h; sourceTree = ""; }; 55 | F4D182931F501FFF0044330F /* SMAVPlayerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SMAVPlayerViewController.m; sourceTree = ""; }; 56 | F4E65B161F53B28E00412481 /* SMLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SMLayout.swift; sourceTree = ""; }; 57 | /* End PBXFileReference section */ 58 | 59 | /* Begin PBXFrameworksBuildPhase section */ 60 | F45F77821E092AD4008AD07E /* Frameworks */ = { 61 | isa = PBXFrameworksBuildPhase; 62 | buildActionMask = 2147483647; 63 | files = ( 64 | ); 65 | runOnlyForDeploymentPostprocessing = 0; 66 | }; 67 | /* End PBXFrameworksBuildPhase section */ 68 | 69 | /* Begin PBXGroup section */ 70 | F45F777C1E092AD4008AD07E = { 71 | isa = PBXGroup; 72 | children = ( 73 | F4D182731F501FFF0044330F /* SMDemoList */, 74 | F45F77861E092AD4008AD07E /* Products */, 75 | ); 76 | sourceTree = ""; 77 | }; 78 | F45F77861E092AD4008AD07E /* Products */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | F45F77851E092AD4008AD07E /* SMDemoList.app */, 82 | ); 83 | name = Products; 84 | sourceTree = ""; 85 | }; 86 | F4D182731F501FFF0044330F /* SMDemoList */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | F4E65B151F53B28E00412481 /* SMLayout */, 90 | F4D182741F501FFF0044330F /* Main */, 91 | F4D182801F501FFF0044330F /* SMCompressVideoToMP4 */, 92 | F4D182831F501FFF0044330F /* SMDanmaku */, 93 | F4D182891F501FFF0044330F /* SMEmitter */, 94 | F4D182911F501FFF0044330F /* SMVideoPlayer */, 95 | ); 96 | path = SMDemoList; 97 | sourceTree = ""; 98 | }; 99 | F4D182741F501FFF0044330F /* Main */ = { 100 | isa = PBXGroup; 101 | children = ( 102 | F4D182751F501FFF0044330F /* AppDelegate.h */, 103 | F4D182761F501FFF0044330F /* AppDelegate.m */, 104 | F4D182771F501FFF0044330F /* Assets.xcassets */, 105 | F4D182781F501FFF0044330F /* LaunchScreen.storyboard */, 106 | F4D1827A1F501FFF0044330F /* Main.storyboard */, 107 | F4D1827C1F501FFF0044330F /* Info.plist */, 108 | F4D1827D1F501FFF0044330F /* main.m */, 109 | F4D1827E1F501FFF0044330F /* ViewController.h */, 110 | F4D1827F1F501FFF0044330F /* ViewController.m */, 111 | ); 112 | path = Main; 113 | sourceTree = ""; 114 | }; 115 | F4D182801F501FFF0044330F /* SMCompressVideoToMP4 */ = { 116 | isa = PBXGroup; 117 | children = ( 118 | F4D182811F501FFF0044330F /* SMCompressViewController.h */, 119 | F4D182821F501FFF0044330F /* SMCompressViewController.m */, 120 | ); 121 | path = SMCompressVideoToMP4; 122 | sourceTree = ""; 123 | }; 124 | F4D182831F501FFF0044330F /* SMDanmaku */ = { 125 | isa = PBXGroup; 126 | children = ( 127 | F4D182841F501FFF0044330F /* SMDanmaku.bundle */, 128 | F4D182851F501FFF0044330F /* SMDanmakuView.h */, 129 | F4D182861F501FFF0044330F /* SMDanmakuView.m */, 130 | F4D182871F501FFF0044330F /* SMDanmakuViewController.h */, 131 | F4D182881F501FFF0044330F /* SMDanmakuViewController.m */, 132 | ); 133 | path = SMDanmaku; 134 | sourceTree = ""; 135 | }; 136 | F4D182891F501FFF0044330F /* SMEmitter */ = { 137 | isa = PBXGroup; 138 | children = ( 139 | F4D1828A1F501FFF0044330F /* emitter.bundle */, 140 | F4D1828B1F501FFF0044330F /* SMEmitterButton.h */, 141 | F4D1828C1F501FFF0044330F /* SMEmitterButton.m */, 142 | F4D1828D1F501FFF0044330F /* SMEmitterView.h */, 143 | F4D1828E1F501FFF0044330F /* SMEmitterView.m */, 144 | F4D1828F1F501FFF0044330F /* SMEmitterViewController.h */, 145 | F4D182901F501FFF0044330F /* SMEmitterViewController.m */, 146 | ); 147 | path = SMEmitter; 148 | sourceTree = ""; 149 | }; 150 | F4D182911F501FFF0044330F /* SMVideoPlayer */ = { 151 | isa = PBXGroup; 152 | children = ( 153 | F4D182921F501FFF0044330F /* SMAVPlayerViewController.h */, 154 | F4D182931F501FFF0044330F /* SMAVPlayerViewController.m */, 155 | ); 156 | path = SMVideoPlayer; 157 | sourceTree = ""; 158 | }; 159 | F4E65B151F53B28E00412481 /* SMLayout */ = { 160 | isa = PBXGroup; 161 | children = ( 162 | F4E65B161F53B28E00412481 /* SMLayout.swift */, 163 | ); 164 | path = SMLayout; 165 | sourceTree = ""; 166 | }; 167 | /* End PBXGroup section */ 168 | 169 | /* Begin PBXNativeTarget section */ 170 | F45F77841E092AD4008AD07E /* SMDemoList */ = { 171 | isa = PBXNativeTarget; 172 | buildConfigurationList = F45F779C1E092AD4008AD07E /* Build configuration list for PBXNativeTarget "SMDemoList" */; 173 | buildPhases = ( 174 | F45F77811E092AD4008AD07E /* Sources */, 175 | F45F77821E092AD4008AD07E /* Frameworks */, 176 | F45F77831E092AD4008AD07E /* Resources */, 177 | ); 178 | buildRules = ( 179 | ); 180 | dependencies = ( 181 | ); 182 | name = SMDemoList; 183 | productName = SMAninationGroup; 184 | productReference = F45F77851E092AD4008AD07E /* SMDemoList.app */; 185 | productType = "com.apple.product-type.application"; 186 | }; 187 | /* End PBXNativeTarget section */ 188 | 189 | /* Begin PBXProject section */ 190 | F45F777D1E092AD4008AD07E /* Project object */ = { 191 | isa = PBXProject; 192 | attributes = { 193 | LastUpgradeCheck = 0810; 194 | ORGANIZATIONNAME = simon; 195 | TargetAttributes = { 196 | F45F77841E092AD4008AD07E = { 197 | CreatedOnToolsVersion = 8.1; 198 | DevelopmentTeam = 38QS4M9J37; 199 | ProvisioningStyle = Automatic; 200 | }; 201 | }; 202 | }; 203 | buildConfigurationList = F45F77801E092AD4008AD07E /* Build configuration list for PBXProject "SMDemoList" */; 204 | compatibilityVersion = "Xcode 3.2"; 205 | developmentRegion = English; 206 | hasScannedForEncodings = 0; 207 | knownRegions = ( 208 | en, 209 | Base, 210 | ); 211 | mainGroup = F45F777C1E092AD4008AD07E; 212 | productRefGroup = F45F77861E092AD4008AD07E /* Products */; 213 | projectDirPath = ""; 214 | projectRoot = ""; 215 | targets = ( 216 | F45F77841E092AD4008AD07E /* SMDemoList */, 217 | ); 218 | }; 219 | /* End PBXProject section */ 220 | 221 | /* Begin PBXResourcesBuildPhase section */ 222 | F45F77831E092AD4008AD07E /* Resources */ = { 223 | isa = PBXResourcesBuildPhase; 224 | buildActionMask = 2147483647; 225 | files = ( 226 | F4D182981F501FFF0044330F /* Info.plist in Resources */, 227 | F4D182971F501FFF0044330F /* Main.storyboard in Resources */, 228 | F4D1829F1F501FFF0044330F /* emitter.bundle in Resources */, 229 | F4D1829C1F501FFF0044330F /* SMDanmaku.bundle in Resources */, 230 | F4D182951F501FFF0044330F /* Assets.xcassets in Resources */, 231 | F4D182961F501FFF0044330F /* LaunchScreen.storyboard in Resources */, 232 | ); 233 | runOnlyForDeploymentPostprocessing = 0; 234 | }; 235 | /* End PBXResourcesBuildPhase section */ 236 | 237 | /* Begin PBXSourcesBuildPhase section */ 238 | F45F77811E092AD4008AD07E /* Sources */ = { 239 | isa = PBXSourcesBuildPhase; 240 | buildActionMask = 2147483647; 241 | files = ( 242 | F4D182A01F501FFF0044330F /* SMEmitterButton.m in Sources */, 243 | F4D1829B1F501FFF0044330F /* SMCompressViewController.m in Sources */, 244 | F4D1829D1F501FFF0044330F /* SMDanmakuView.m in Sources */, 245 | F4D1829E1F501FFF0044330F /* SMDanmakuViewController.m in Sources */, 246 | F4D1829A1F501FFF0044330F /* ViewController.m in Sources */, 247 | F4D182991F501FFF0044330F /* main.m in Sources */, 248 | F4D182A31F501FFF0044330F /* SMAVPlayerViewController.m in Sources */, 249 | F4D182A21F501FFF0044330F /* SMEmitterViewController.m in Sources */, 250 | F4D182941F501FFF0044330F /* AppDelegate.m in Sources */, 251 | F4D182A11F501FFF0044330F /* SMEmitterView.m in Sources */, 252 | F4E65B171F53B28E00412481 /* SMLayout.swift in Sources */, 253 | ); 254 | runOnlyForDeploymentPostprocessing = 0; 255 | }; 256 | /* End PBXSourcesBuildPhase section */ 257 | 258 | /* Begin PBXVariantGroup section */ 259 | F4D182781F501FFF0044330F /* LaunchScreen.storyboard */ = { 260 | isa = PBXVariantGroup; 261 | children = ( 262 | F4D182791F501FFF0044330F /* Base */, 263 | ); 264 | name = LaunchScreen.storyboard; 265 | sourceTree = ""; 266 | }; 267 | F4D1827A1F501FFF0044330F /* Main.storyboard */ = { 268 | isa = PBXVariantGroup; 269 | children = ( 270 | F4D1827B1F501FFF0044330F /* Base */, 271 | ); 272 | name = Main.storyboard; 273 | sourceTree = ""; 274 | }; 275 | /* End PBXVariantGroup section */ 276 | 277 | /* Begin XCBuildConfiguration section */ 278 | F45F779A1E092AD4008AD07E /* Debug */ = { 279 | isa = XCBuildConfiguration; 280 | buildSettings = { 281 | ALWAYS_SEARCH_USER_PATHS = NO; 282 | CLANG_ANALYZER_NONNULL = YES; 283 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 284 | CLANG_CXX_LIBRARY = "libc++"; 285 | CLANG_ENABLE_MODULES = YES; 286 | CLANG_ENABLE_OBJC_ARC = YES; 287 | CLANG_WARN_BOOL_CONVERSION = YES; 288 | CLANG_WARN_CONSTANT_CONVERSION = YES; 289 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 290 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 291 | CLANG_WARN_EMPTY_BODY = YES; 292 | CLANG_WARN_ENUM_CONVERSION = YES; 293 | CLANG_WARN_INFINITE_RECURSION = YES; 294 | CLANG_WARN_INT_CONVERSION = YES; 295 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 296 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 297 | CLANG_WARN_UNREACHABLE_CODE = YES; 298 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 299 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 300 | COPY_PHASE_STRIP = NO; 301 | DEBUG_INFORMATION_FORMAT = dwarf; 302 | ENABLE_STRICT_OBJC_MSGSEND = YES; 303 | ENABLE_TESTABILITY = YES; 304 | GCC_C_LANGUAGE_STANDARD = gnu99; 305 | GCC_DYNAMIC_NO_PIC = NO; 306 | GCC_NO_COMMON_BLOCKS = YES; 307 | GCC_OPTIMIZATION_LEVEL = 0; 308 | GCC_PREPROCESSOR_DEFINITIONS = ( 309 | "DEBUG=1", 310 | "$(inherited)", 311 | ); 312 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 313 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 314 | GCC_WARN_UNDECLARED_SELECTOR = YES; 315 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 316 | GCC_WARN_UNUSED_FUNCTION = YES; 317 | GCC_WARN_UNUSED_VARIABLE = YES; 318 | IPHONEOS_DEPLOYMENT_TARGET = 10.1; 319 | MTL_ENABLE_DEBUG_INFO = YES; 320 | ONLY_ACTIVE_ARCH = YES; 321 | SDKROOT = iphoneos; 322 | }; 323 | name = Debug; 324 | }; 325 | F45F779B1E092AD4008AD07E /* Release */ = { 326 | isa = XCBuildConfiguration; 327 | buildSettings = { 328 | ALWAYS_SEARCH_USER_PATHS = NO; 329 | CLANG_ANALYZER_NONNULL = YES; 330 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 331 | CLANG_CXX_LIBRARY = "libc++"; 332 | CLANG_ENABLE_MODULES = YES; 333 | CLANG_ENABLE_OBJC_ARC = YES; 334 | CLANG_WARN_BOOL_CONVERSION = YES; 335 | CLANG_WARN_CONSTANT_CONVERSION = YES; 336 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 337 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 338 | CLANG_WARN_EMPTY_BODY = YES; 339 | CLANG_WARN_ENUM_CONVERSION = YES; 340 | CLANG_WARN_INFINITE_RECURSION = YES; 341 | CLANG_WARN_INT_CONVERSION = YES; 342 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 343 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 344 | CLANG_WARN_UNREACHABLE_CODE = YES; 345 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 346 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 347 | COPY_PHASE_STRIP = NO; 348 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 349 | ENABLE_NS_ASSERTIONS = NO; 350 | ENABLE_STRICT_OBJC_MSGSEND = YES; 351 | GCC_C_LANGUAGE_STANDARD = gnu99; 352 | GCC_NO_COMMON_BLOCKS = YES; 353 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 354 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 355 | GCC_WARN_UNDECLARED_SELECTOR = YES; 356 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 357 | GCC_WARN_UNUSED_FUNCTION = YES; 358 | GCC_WARN_UNUSED_VARIABLE = YES; 359 | IPHONEOS_DEPLOYMENT_TARGET = 10.1; 360 | MTL_ENABLE_DEBUG_INFO = NO; 361 | SDKROOT = iphoneos; 362 | VALIDATE_PRODUCT = YES; 363 | }; 364 | name = Release; 365 | }; 366 | F45F779D1E092AD4008AD07E /* Debug */ = { 367 | isa = XCBuildConfiguration; 368 | buildSettings = { 369 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 370 | DEVELOPMENT_TEAM = 38QS4M9J37; 371 | INFOPLIST_FILE = "$(SRCROOT)/SMDemoList/Main/Info.plist"; 372 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 373 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 374 | PRODUCT_BUNDLE_IDENTIFIER = com.icoderRo.github.com.SMDemoList; 375 | PRODUCT_NAME = "$(TARGET_NAME)"; 376 | }; 377 | name = Debug; 378 | }; 379 | F45F779E1E092AD4008AD07E /* Release */ = { 380 | isa = XCBuildConfiguration; 381 | buildSettings = { 382 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 383 | DEVELOPMENT_TEAM = 38QS4M9J37; 384 | INFOPLIST_FILE = "$(SRCROOT)/SMDemoList/Main/Info.plist"; 385 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 386 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 387 | PRODUCT_BUNDLE_IDENTIFIER = com.icoderRo.github.com.SMDemoList; 388 | PRODUCT_NAME = "$(TARGET_NAME)"; 389 | }; 390 | name = Release; 391 | }; 392 | /* End XCBuildConfiguration section */ 393 | 394 | /* Begin XCConfigurationList section */ 395 | F45F77801E092AD4008AD07E /* Build configuration list for PBXProject "SMDemoList" */ = { 396 | isa = XCConfigurationList; 397 | buildConfigurations = ( 398 | F45F779A1E092AD4008AD07E /* Debug */, 399 | F45F779B1E092AD4008AD07E /* Release */, 400 | ); 401 | defaultConfigurationIsVisible = 0; 402 | defaultConfigurationName = Release; 403 | }; 404 | F45F779C1E092AD4008AD07E /* Build configuration list for PBXNativeTarget "SMDemoList" */ = { 405 | isa = XCConfigurationList; 406 | buildConfigurations = ( 407 | F45F779D1E092AD4008AD07E /* Debug */, 408 | F45F779E1E092AD4008AD07E /* Release */, 409 | ); 410 | defaultConfigurationIsVisible = 0; 411 | defaultConfigurationName = Release; 412 | }; 413 | /* End XCConfigurationList section */ 414 | }; 415 | rootObject = F45F777D1E092AD4008AD07E /* Project object */; 416 | } 417 | -------------------------------------------------------------------------------- /SMDemo/SMDemoList.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SMDemo/SMDemoList/Main/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // SMAninationGroup 4 | // 5 | // Created by simon on 16/12/20 6 | // Copyright © 2016年 simon. 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 | -------------------------------------------------------------------------------- /SMDemo/SMDemoList/Main/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // SMAninationGroup 4 | // 5 | // Created by simon on 16/12/20 6 | // Copyright © 2016年 simon. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "ViewController.h" 11 | 12 | @interface AppDelegate () 13 | 14 | @end 15 | 16 | @implementation AppDelegate 17 | 18 | 19 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 20 | // Override point for customization after application launch. 21 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 22 | UIViewController *vc = [[ViewController alloc] init]; 23 | self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:vc]; 24 | [self.window makeKeyAndVisible]; 25 | return YES; 26 | } 27 | 28 | 29 | - (void)applicationWillResignActive:(UIApplication *)application { 30 | // 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. 31 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 32 | } 33 | 34 | 35 | - (void)applicationDidEnterBackground:(UIApplication *)application { 36 | // 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. 37 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 38 | } 39 | 40 | 41 | - (void)applicationWillEnterForeground:(UIApplication *)application { 42 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 43 | } 44 | 45 | 46 | - (void)applicationDidBecomeActive:(UIApplication *)application { 47 | // 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. 48 | } 49 | 50 | 51 | - (void)applicationWillTerminate:(UIApplication *)application { 52 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 53 | } 54 | 55 | 56 | @end 57 | -------------------------------------------------------------------------------- /SMDemo/SMDemoList/Main/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | } 43 | ], 44 | "info" : { 45 | "version" : 1, 46 | "author" : "xcode" 47 | } 48 | } -------------------------------------------------------------------------------- /SMDemo/SMDemoList/Main/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /SMDemo/SMDemoList/Main/Assets.xcassets/btn_action.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "btn_action@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 | } -------------------------------------------------------------------------------- /SMDemo/SMDemoList/Main/Assets.xcassets/btn_action.imageset/btn_action@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/Main/Assets.xcassets/btn_action.imageset/btn_action@2x.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/Main/Assets.xcassets/camera_blur_0.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "camera_blur_0.jpg", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /SMDemo/SMDemoList/Main/Assets.xcassets/camera_blur_0.imageset/camera_blur_0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/Main/Assets.xcassets/camera_blur_0.imageset/camera_blur_0.jpg -------------------------------------------------------------------------------- /SMDemo/SMDemoList/Main/Assets.xcassets/camera_blur_1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "camera_blur_1.jpg", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /SMDemo/SMDemoList/Main/Assets.xcassets/camera_blur_1.imageset/camera_blur_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/Main/Assets.xcassets/camera_blur_1.imageset/camera_blur_1.jpg -------------------------------------------------------------------------------- /SMDemo/SMDemoList/Main/Assets.xcassets/camera_blur_2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "camera_blur_2.jpg", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /SMDemo/SMDemoList/Main/Assets.xcassets/camera_blur_2.imageset/camera_blur_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/Main/Assets.xcassets/camera_blur_2.imageset/camera_blur_2.jpg -------------------------------------------------------------------------------- /SMDemo/SMDemoList/Main/Assets.xcassets/camera_blur_3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "camera_blur_3.jpg", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /SMDemo/SMDemoList/Main/Assets.xcassets/camera_blur_3.imageset/camera_blur_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/Main/Assets.xcassets/camera_blur_3.imageset/camera_blur_3.jpg -------------------------------------------------------------------------------- /SMDemo/SMDemoList/Main/Assets.xcassets/fa-link.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "fa-link@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 | } -------------------------------------------------------------------------------- /SMDemo/SMDemoList/Main/Assets.xcassets/fa-link.imageset/fa-link@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/Main/Assets.xcassets/fa-link.imageset/fa-link@2x.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/Main/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /SMDemo/SMDemoList/Main/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /SMDemo/SMDemoList/Main/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 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /SMDemo/SMDemoList/Main/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // SMAninationGroup 4 | // 5 | // Created by simon on 16/12/20 6 | // Copyright © 2016年 simon. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /SMDemo/SMDemoList/Main/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // SMHeartStarAnimation 4 | // 5 | // Created by simon on 16/12/16. 6 | // Copyright © 2016年 simon. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "SMEmitterViewController.h" 11 | #import "SMDanmakuViewController.h" 12 | #import "SMCompressViewController.h" 13 | #import "SMAVPlayerViewController.h" 14 | @interface ViewController () 15 | @property (nonatomic, strong) NSMutableArray *array; 16 | @property (nonatomic, strong) UITableView *tableView; 17 | 18 | @end 19 | 20 | @implementation ViewController 21 | 22 | - (void)viewDidLoad { 23 | 24 | self.array = [NSMutableArray array]; 25 | self.title = @"animations"; 26 | [self.array addObject:@{ @"SMEmitter" : @"SMEmitterViewController"}]; 27 | [self.array addObject:@{ @"SMDanmaku" : @"SMDanmakuViewController"}]; 28 | [self.array addObject:@{ @"SMCompress" : @"SMCompressViewController"}]; 29 | [self.array addObject:@{ @"SMAVPlayerView" : @"SMAVPlayerViewController"}]; 30 | 31 | self.tableView = [[UITableView alloc] initWithFrame:self.view.frame]; 32 | self.tableView.delegate = self; 33 | self.tableView.dataSource = self; 34 | self.tableView.rowHeight = 66; 35 | [self.view addSubview:self.tableView]; 36 | } 37 | 38 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 39 | return self.array.count; 40 | } 41 | 42 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 43 | 44 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ID"]; 45 | 46 | if (!cell) { 47 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"ID"]; 48 | } 49 | 50 | NSDictionary *dict = self.array[indexPath.row]; 51 | NSString *text = dict.allKeys.lastObject; 52 | cell.textLabel.text = text; 53 | 54 | return cell; 55 | } 56 | 57 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 58 | 59 | NSDictionary *dict = self.array[indexPath.row]; 60 | NSString *className = dict.allValues.lastObject; 61 | Class class = NSClassFromString(className); 62 | if (class) { 63 | UIViewController *ctrl = class.new; 64 | ctrl.view.backgroundColor = [UIColor whiteColor]; 65 | ctrl.title = dict.allKeys.lastObject; 66 | [self.navigationController pushViewController:ctrl animated:YES]; 67 | } 68 | } 69 | 70 | @end 71 | -------------------------------------------------------------------------------- /SMDemo/SMDemoList/Main/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // SMAninationGroup 4 | // 5 | // Created by simon on 16/12/20 0. 6 | // Copyright © 2016年 simon. 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 | -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMCrash/NSArray+Crash.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSArray+Crash.h 3 | // smifun 4 | // 5 | // Created by simon on 17/3/6. 6 | // Copyright © 2017年 SMI HOLDING GROUP. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSArray (Crash) 12 | + (void)sm_avoidCrashExchangeMethod; 13 | @end 14 | -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMCrash/NSArray+Crash.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSArray+Crash.m 3 | // smifun 4 | // 5 | // Created by simon on 17/3/6. 6 | // Copyright © 2017年 SMI HOLDING GROUP. All rights reserved. 7 | // 8 | 9 | #import "NSArray+Crash.h" 10 | #import "SMIAvoidCrash.h" 11 | 12 | @implementation NSArray (Crash) 13 | 14 | + (void)sm_avoidCrashExchangeMethod { 15 | 16 | Class __NSArray = NSClassFromString(@"NSArray"); 17 | Class __NSArrayI = NSClassFromString(@"__NSArrayI"); 18 | Class __NSSingleObjectArrayI = NSClassFromString(@"__NSSingleObjectArrayI"); 19 | Class __NSArray0 = NSClassFromString(@"__NSArray0"); 20 | 21 | /// arrayWithObjects:count: 22 | [SMIAvoidCrash exchangeClassMethod:[self class] method1Sel:@selector(arrayWithObjects:count:) method2Sel:@selector(sm_arrayWithObjects:count:)]; 23 | /// arrayWithObject: 24 | [SMIAvoidCrash exchangeClassMethod:[self class] method1Sel:@selector(arrayWithObject:) method2Sel:@selector(sm_arrayWithObject:)]; 25 | 26 | //objectAtIndex: 27 | [SMIAvoidCrash exchangeInstanceMethod:__NSArrayI method1Sel:@selector(objectAtIndex:) method2Sel:@selector(sm_NSArrayIAtIndexI:)]; 28 | [SMIAvoidCrash exchangeInstanceMethod:__NSSingleObjectArrayI method1Sel:@selector(objectAtIndex:) method2Sel:@selector(sm_NSSingleObjectArrayIAtIndexI:)]; 29 | [SMIAvoidCrash exchangeInstanceMethod:__NSArray0 method1Sel:@selector(objectAtIndex:) method2Sel:@selector(sm_NSArray0AtIndexI:)]; 30 | 31 | //getObjects:range: 32 | [SMIAvoidCrash exchangeInstanceMethod:__NSArray method1Sel:@selector(getObjects:range:) method2Sel:@selector(sm_getNSArrayObjects:range:)]; 33 | [SMIAvoidCrash exchangeInstanceMethod:__NSSingleObjectArrayI method1Sel:@selector(getObjects:range:) method2Sel:@selector(sm_getNSSingleObjectArrayIObjects:range:)]; 34 | [SMIAvoidCrash exchangeInstanceMethod:__NSArrayI method1Sel:@selector(getObjects:range:) method2Sel:@selector(sm_getNSArrayIObjects:range:)]; 35 | } 36 | 37 | + (instancetype)sm_arrayWithObject:(const id _Nonnull __unsafe_unretained *)object { 38 | 39 | id instance = nil; 40 | 41 | @try { 42 | instance = [self sm_arrayWithObject:object]; 43 | } 44 | @catch (NSException *exception) { 45 | SLog(@"NSArray init nil %s%@",__func__,[exception callStackSymbols]); 46 | } 47 | @finally { 48 | return instance; 49 | } 50 | } 51 | 52 | + (instancetype)sm_arrayWithObjects:(const id _Nonnull __unsafe_unretained *)objects count:(NSUInteger)cnt { 53 | 54 | id instance = nil; 55 | 56 | @try { 57 | instance = [self sm_arrayWithObjects:objects count:cnt]; 58 | } 59 | @catch (NSException *exception) { 60 | 61 | SLog(@"NSArray init nil %s%@",__func__,[exception callStackSymbols]); 62 | 63 | /// 删除为nil的数据 64 | NSInteger newObjsIndex = 0; 65 | id _Nonnull __unsafe_unretained newObjects[cnt]; 66 | 67 | for (int i = 0; i < cnt; i++) { 68 | if (objects[i] != nil) { 69 | newObjects[newObjsIndex] = objects[i]; 70 | newObjsIndex++; 71 | } 72 | } 73 | instance = [self sm_arrayWithObjects:newObjects count:newObjsIndex]; 74 | } 75 | @finally { 76 | return instance; 77 | } 78 | } 79 | 80 | /// objectAtIndex: 81 | - (id)sm_NSSingleObjectArrayIAtIndexI:(NSUInteger)index { 82 | 83 | id object = nil; 84 | 85 | @try { 86 | object = [self sm_NSSingleObjectArrayIAtIndexI:index]; 87 | } 88 | @catch (NSException *exception) { 89 | SLog(@"NSArray get cross %s%@",__func__,[exception callStackSymbols]); 90 | 91 | } 92 | @finally { 93 | return object; 94 | } 95 | } 96 | 97 | - (id)sm_NSArrayIAtIndexI:(NSUInteger)index { 98 | id object = nil; 99 | 100 | @try { 101 | object = [self sm_NSArrayIAtIndexI:index]; 102 | } 103 | @catch (NSException *exception) { 104 | SLog(@"NSArray get cross %s%@",__func__,[exception callStackSymbols]); 105 | 106 | } 107 | @finally { 108 | return object; 109 | } 110 | } 111 | 112 | - (id)sm_NSArray0AtIndexI:(NSUInteger)index { 113 | id object = nil; 114 | 115 | @try { 116 | object = [self sm_NSArray0AtIndexI:index]; 117 | } 118 | @catch (NSException *exception) { 119 | SLog(@"NSArray get cross %s%@",__func__,[exception callStackSymbols]); 120 | 121 | } 122 | @finally { 123 | return object; 124 | } 125 | } 126 | 127 | /// getObjects:range: 128 | - (void)sm_getNSSingleObjectArrayIObjects:(__unsafe_unretained id _Nonnull *)objects range:(NSRange)range { 129 | 130 | @try { 131 | [self sm_getNSSingleObjectArrayIObjects:objects range:range]; 132 | } @catch (NSException *exception) { 133 | 134 | SLog(@"NSArray getObjects:range cross %s%@",__func__,[exception callStackSymbols]); 135 | 136 | } @finally { 137 | 138 | } 139 | } 140 | 141 | - (void)sm_getNSArrayObjects:(__unsafe_unretained id _Nonnull *)objects range:(NSRange)range { 142 | 143 | @try { 144 | [self sm_getNSArrayObjects:objects range:range]; 145 | } @catch (NSException *exception) { 146 | 147 | SLog(@"NSArray getObjects:range cross %s%@",__func__,[exception callStackSymbols]); 148 | 149 | } @finally { 150 | 151 | } 152 | } 153 | 154 | - (void)sm_getNSArrayIObjects:(__unsafe_unretained id _Nonnull *)objects range:(NSRange)range { 155 | 156 | @try { 157 | [self sm_getNSArrayIObjects:objects range:range]; 158 | } @catch (NSException *exception) { 159 | 160 | SLog(@"NSArray getObjects:range cross %s%@",__func__,[exception callStackSymbols]); 161 | 162 | } @finally { 163 | 164 | } 165 | } 166 | 167 | 168 | @end 169 | -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMCrash/NSDictionary+Crash.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSDictionary+Crash.h 3 | // smifun 4 | // 5 | // Created by simon on 17/3/6. 6 | // Copyright © 2017年 SMI HOLDING GROUP. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSDictionary (Crash) 12 | + (void)sm_avoidCrashExchangeMethod; 13 | @end 14 | -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMCrash/NSDictionary+Crash.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSDictionary+Crash.m 3 | // smifun 4 | // 5 | // Created by simon on 17/3/6. 6 | // Copyright © 2017年 SMI HOLDING GROUP. All rights reserved. 7 | // 8 | 9 | #import "NSDictionary+Crash.h" 10 | #import "SMIAvoidCrash.h" 11 | @implementation NSDictionary (Crash) 12 | + (void)sm_avoidCrashExchangeMethod { 13 | 14 | [SMIAvoidCrash exchangeClassMethod:self method1Sel:@selector(dictionaryWithObjects:forKeys:count:) method2Sel:@selector(sm_dictionaryWithObjects:forKeys:count:)]; 15 | } 16 | 17 | + (instancetype)sm_dictionaryWithObjects:(const id _Nonnull __unsafe_unretained *)objects forKeys:(const id _Nonnull __unsafe_unretained *)keys count:(NSUInteger)cnt { 18 | 19 | id instance = nil; 20 | 21 | @try { 22 | instance = [self sm_dictionaryWithObjects:objects forKeys:keys count:cnt]; 23 | } 24 | @catch (NSException *exception) { 25 | 26 | SLog(@"set a nil objec for NSDictionary %s%@",__func__,[exception callStackSymbols]); 27 | 28 | /// reInit 29 | NSUInteger index = 0; 30 | id _Nonnull __unsafe_unretained newObjects[cnt]; 31 | id _Nonnull __unsafe_unretained newkeys[cnt]; 32 | 33 | for (int i = 0; i < cnt; i++) { 34 | if (objects[i] && keys[i]) { 35 | newObjects[index] = objects[i]; 36 | newkeys[index] = keys[i]; 37 | index++; 38 | } 39 | } 40 | instance = [self sm_dictionaryWithObjects:newObjects forKeys:newkeys count:index]; 41 | } 42 | @finally { 43 | return instance; 44 | } 45 | } 46 | @end 47 | -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMCrash/NSMutableArray+Crash.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSMutableArray+Crash.h 3 | // smifun 4 | // 5 | // Created by simon on 17/3/6. 6 | // Copyright © 2017年 SMI HOLDING GROUP. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSMutableArray (Crash) 12 | + (void)sm_avoidCrashExchangeMethod; 13 | @end 14 | -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMCrash/NSMutableArray+Crash.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSMutableArray+Crash.m 3 | // smifun 4 | // 5 | // Created by simon on 17/3/6. 6 | // Copyright © 2017年 SMI HOLDING GROUP. All rights reserved. 7 | // 8 | 9 | #import "NSMutableArray+Crash.h" 10 | #import "SMIAvoidCrash.h" 11 | @implementation NSMutableArray (Crash) 12 | + (void)sm_avoidCrashExchangeMethod { 13 | 14 | Class smArrayClass = NSClassFromString(@"__NSArrayM"); 15 | 16 | /// objectAtIndex: 17 | [SMIAvoidCrash exchangeInstanceMethod:smArrayClass method1Sel:@selector(objectAtIndex:) method2Sel:@selector(sm_objectAtIndexM:)]; 18 | 19 | /// insertObject:atIndex: 20 | [SMIAvoidCrash exchangeInstanceMethod:smArrayClass method1Sel: @selector(insertObject:atIndex:) method2Sel:@selector(sm_insertObject:atIndex:)]; 21 | 22 | /// setObject:atIndexedSubscript: 23 | [SMIAvoidCrash exchangeInstanceMethod:smArrayClass method1Sel: @selector(setObject:atIndexedSubscript:) method2Sel:@selector(sm_setObject:atIndexedSubscript:)]; 24 | 25 | /// remove 26 | [SMIAvoidCrash exchangeInstanceMethod:smArrayClass method1Sel: @selector(removeObjectAtIndex:) method2Sel:@selector(sm_removeObjectAtIndex:)]; 27 | 28 | /// getObjects:range: 29 | [SMIAvoidCrash exchangeInstanceMethod:smArrayClass method1Sel:@selector(getObjects:range:) method2Sel:@selector(sm_getObjects:range:)]; 30 | } 31 | 32 | - (void)sm_setObject:(id)smObject atIndexedSubscript:(NSUInteger)index { 33 | 34 | @try { 35 | [self sm_setObject:smObject atIndexedSubscript:index]; 36 | } 37 | @catch (NSException *exception) { 38 | SLog(@"NSMutableArray Set cross %s%@",__func__,[exception callStackSymbols]); 39 | 40 | } 41 | @finally { 42 | 43 | } 44 | } 45 | 46 | - (void)sm_insertObject:(id)smObject atIndex:(NSUInteger)index { 47 | 48 | @try { 49 | [self sm_insertObject:smObject atIndex:index]; 50 | } 51 | @catch (NSException *exception) { 52 | SLog(@"NSMutableArray Insert cross %s%@",__func__,[exception callStackSymbols]); 53 | 54 | } 55 | @finally { 56 | 57 | } 58 | } 59 | 60 | - (id)sm_objectAtIndexM:(NSUInteger)index { 61 | 62 | id object = nil; 63 | 64 | @try { 65 | object = [self sm_objectAtIndexM:index]; 66 | } 67 | @catch (NSException *exception) { 68 | SLog(@"NSMutableArray get cross %s%@",__func__,[exception callStackSymbols]); 69 | 70 | } 71 | @finally { 72 | return object; 73 | } 74 | } 75 | 76 | - (void)sm_removeObjectAtIndex:(NSUInteger)index { 77 | @try { 78 | [self sm_removeObjectAtIndex:index]; 79 | } 80 | @catch (NSException *exception) { 81 | SLog(@"NSMutableArray remove corss %s%@",__func__,[exception callStackSymbols]); 82 | 83 | } 84 | @finally { 85 | 86 | } 87 | } 88 | 89 | - (void)sm_getObjects:(__unsafe_unretained id _Nonnull *)objects range:(NSRange)range { 90 | 91 | @try { 92 | 93 | [self sm_getObjects:objects range:range]; 94 | 95 | } @catch (NSException *exception) { 96 | 97 | SLog(@"NSMutableArray getObjects cross %s%@",__func__,[exception callStackSymbols]); 98 | } @finally { 99 | 100 | } 101 | } 102 | 103 | @end 104 | -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMCrash/NSMutableDictionary+Crash.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSMutableDictionary+Crash.h 3 | // smifun 4 | // 5 | // Created by simon on 17/3/6. 6 | // Copyright © 2017年 SMI HOLDING GROUP. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSMutableDictionary (Crash) 12 | + (void)sm_avoidCrashExchangeMethod; 13 | @end 14 | -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMCrash/NSMutableDictionary+Crash.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSMutableDictionary+Crash.m 3 | // smifun 4 | // 5 | // Created by simon on 17/3/6. 6 | // Copyright © 2017年 SMI HOLDING GROUP. All rights reserved. 7 | // 8 | 9 | #import "NSMutableDictionary+Crash.h" 10 | #import "SMIAvoidCrash.h" 11 | @implementation NSMutableDictionary (Crash) 12 | + (void)sm_avoidCrashExchangeMethod { 13 | Class dictionaryM = NSClassFromString(@"__NSDictionaryM"); 14 | /// set 15 | [SMIAvoidCrash exchangeInstanceMethod:dictionaryM method1Sel:@selector(setObject:forKey:) method2Sel:@selector(sm_setObject:forKey:)]; 16 | 17 | /// remove 18 | [SMIAvoidCrash exchangeInstanceMethod:dictionaryM method1Sel:@selector(removeObjectForKey:) method2Sel:@selector(sm_removeObjectForKey:)]; 19 | } 20 | 21 | - (void)sm_removeObjectForKey:(id)key { 22 | 23 | @try { 24 | [self sm_removeObjectForKey:key]; 25 | } 26 | @catch (NSException *exception) { 27 | SLog(@"NSMutableDictionary remove a null key %s%@",__func__,[exception callStackSymbols]) 28 | } 29 | @finally { 30 | 31 | } 32 | } 33 | 34 | - (void)sm_setObject:(id)smObject forKey:(id)key { 35 | 36 | @try { 37 | [self sm_setObject:smObject forKey:key]; 38 | } 39 | @catch (NSException *exception) { 40 | SLog(@"NSMutableDictionary set a nil object %s%@",__func__,[exception callStackSymbols]); 41 | 42 | } 43 | @finally { 44 | 45 | } 46 | } 47 | @end 48 | -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMCrash/NSNull+Crash.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSNull+Crash.h 3 | // smifun 4 | // 5 | // Created by simon on 17/3/6. 6 | // Copyright © 2017年 SMI HOLDING GROUP. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSNull (Crash) 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMCrash/NSNull+Crash.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSNull+Crash.m 3 | // smifun 4 | // 5 | // Created by simon on 17/3/6. 6 | // Copyright © 2017年 SMI HOLDING GROUP. All rights reserved. 7 | // 8 | 9 | #import "NSNull+Crash.h" 10 | 11 | @implementation NSNull (Crash) 12 | + (void)sm_avoidCrashExchangeMethod { 13 | 14 | [SMIAvoidCrash exchangeInstanceMethod:self method1Sel:@selector(methodSignatureForSelector:) method2Sel:@selector(sm_methodSignatureForSelector:)]; 15 | 16 | [SMIAvoidCrash exchangeInstanceMethod:self method1Sel:@selector(forwardInvocation:) method2Sel:@selector(sm_forwardInvocation:)]; 17 | } 18 | 19 | - (NSMethodSignature *)sm_methodSignatureForSelector:(SEL)aSelector { 20 | NSMethodSignature *sig = [self sm_methodSignatureForSelector:aSelector]; 21 | if (sig) {return sig;} 22 | return [NSMethodSignature signatureWithObjCTypes:@encode(void)]; 23 | } 24 | 25 | - (void)sm_forwardInvocation:(NSInvocation *)anInvocation { 26 | NSUInteger returnLength = [[anInvocation methodSignature] methodReturnLength]; 27 | if (!returnLength) {return;} 28 | 29 | char buffer[returnLength]; 30 | memset(buffer, 0, returnLength); 31 | 32 | [anInvocation setReturnValue:buffer]; 33 | } 34 | @end 35 | -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMCrash/SMIAvoidCrash.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMIAvoidCrash.h 3 | // smifun 4 | // 5 | // Created by simon on 17/3/10. 6 | // Copyright © 2017年 SMI HOLDING GROUP. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | @interface SMIAvoidCrash : NSObject 12 | + (void)openAvoidCrash; 13 | 14 | + (void)exchangeClassMethod:(Class)smClass 15 | method1Sel:(SEL)method1Sel 16 | method2Sel:(SEL)method2Sel; 17 | 18 | + (void)exchangeInstanceMethod:(Class)smClass 19 | method1Sel:(SEL)method1Sel 20 | method2Sel:(SEL)method2Sel; 21 | 22 | /// 假设需要手机崩溃的统计信息, 可再此统一处理崩溃回调 23 | @end 24 | -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMCrash/SMIAvoidCrash.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMIAvoidCrash.m 3 | // smifun 4 | // 5 | // Created by simon on 17/3/10. 6 | // Copyright © 2017年 SMI HOLDING GROUP. All rights reserved. 7 | // 8 | 9 | #import "SMIAvoidCrash.h" 10 | #import "NSNull+Crash.h" 11 | #import "NSArray+Crash.h" 12 | #import "NSDictionary+Crash.h" 13 | #import "NSMutableArray+Crash.h" 14 | #import "NSMutableDictionary+Crash.h" 15 | #import "NSNull+Crash.h" 16 | 17 | @implementation SMIAvoidCrash 18 | + (void)openAvoidCrash { 19 | static dispatch_once_t onceToken; 20 | dispatch_once(&onceToken, ^{ 21 | [NSArray sm_avoidCrashExchangeMethod]; 22 | [NSMutableArray sm_avoidCrashExchangeMethod]; 23 | [NSDictionary sm_avoidCrashExchangeMethod]; 24 | [NSMutableDictionary sm_avoidCrashExchangeMethod]; 25 | // [NSNull sm_av] 26 | }); 27 | 28 | } 29 | 30 | + (void)exchangeClassMethod:(Class)smClass method1Sel:(SEL)method1Sel method2Sel:(SEL)method2Sel { 31 | Method method1 = class_getClassMethod(smClass, method1Sel); 32 | Method method2 = class_getClassMethod(smClass, method2Sel); 33 | method_exchangeImplementations(method1, method2); 34 | } 35 | 36 | + (void)exchangeInstanceMethod:(Class)smClass method1Sel:(SEL)method1Sel method2Sel:(SEL)method2Sel { 37 | 38 | Method originalMethod = class_getInstanceMethod(smClass, method1Sel); 39 | Method swizzledMethod = class_getInstanceMethod(smClass, method2Sel); 40 | 41 | BOOL addMethod = class_addMethod(smClass, method1Sel, method_getImplementation(swizzledMethod),method_getTypeEncoding(swizzledMethod)); 42 | 43 | if (addMethod) { 44 | class_replaceMethod(smClass, method2Sel, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); 45 | } else { 46 | method_exchangeImplementations(originalMethod, swizzledMethod); 47 | } 48 | } 49 | @end 50 | -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMDanmaku/SMDanmaku.bundle/landscape_liuguang_blue@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMDanmaku/SMDanmaku.bundle/landscape_liuguang_blue@2x.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMDanmaku/SMDanmaku.bundle/landscape_liuguang_gold@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMDanmaku/SMDanmaku.bundle/landscape_liuguang_gold@2x.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMDanmaku/SMDanmaku.bundle/landscape_liuguang_green@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMDanmaku/SMDanmaku.bundle/landscape_liuguang_green@2x.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMDanmaku/SMDanmaku.bundle/landscape_liuguang_red@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMDanmaku/SMDanmaku.bundle/landscape_liuguang_red@2x.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMDanmaku/SMDanmaku.bundle/my_badge_guard@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMDanmaku/SMDanmaku.bundle/my_badge_guard@2x.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMDanmaku/SMDanmaku.bundle/my_badge_noble_1@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMDanmaku/SMDanmaku.bundle/my_badge_noble_1@2x.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMDanmaku/SMDanmaku.bundle/my_badge_noble_2@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMDanmaku/SMDanmaku.bundle/my_badge_noble_2@2x.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMDanmaku/SMDanmaku.bundle/my_badge_noble_3@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMDanmaku/SMDanmaku.bundle/my_badge_noble_3@2x.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMDanmaku/SMDanmaku.bundle/my_badge_noble_4@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMDanmaku/SMDanmaku.bundle/my_badge_noble_4@2x.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMDanmaku/SMDanmaku.bundle/my_badge_noble_5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMDanmaku/SMDanmaku.bundle/my_badge_noble_5@2x.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMDanmaku/SMDanmaku.bundle/my_badge_noble_6@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMDanmaku/SMDanmaku.bundle/my_badge_noble_6@2x.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMDanmaku/SMDanmakuView.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMDanmakuView.h 3 | // SMAninationGroup 4 | // 5 | // Created by simon on 17/1/3. 6 | // Copyright © 2017年 simon. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | #define kScreenW [UIScreen mainScreen].bounds.size.width 13 | #define KScreenH [UIScreen mainScreen].bounds.size.height 14 | 15 | NS_ASSUME_NONNULL_BEGIN 16 | @interface SMDanmakuView : UIView 17 | @property (nullable, nonatomic, strong) UIImage *backgroundImage; 18 | @property (nullable, nonatomic, strong) UIColor *danmakuBackgroundColor; 19 | @property (nullable, nonatomic, strong) UIImage *danmakuBackgroundImage; 20 | 21 | - (void)fireWithAttributedText:(NSMutableAttributedString *)attributedText; 22 | @end 23 | 24 | @interface SMDanmakuLayer : CALayer 25 | @property (nonatomic, copy) NSMutableAttributedString *attributedText; 26 | @property (nullable, nonatomic, strong) UIImage *backgroundImage; 27 | @end 28 | 29 | @interface NSMutableAttributedString (SMDanmakuText) 30 | + (NSMutableAttributedString *)attachmentStringWithImage:(UIImage *)image size:(CGSize)size font:(UIFont *)font; 31 | - (void)setTextColor:(nullable UIColor *)textColor range:(NSRange)range; 32 | - (void)setFont:(nullable UIFont *)font range:(NSRange)range; 33 | @end 34 | 35 | @interface SMTextRunDelegate : NSObject 36 | @property (nonatomic, assign) CTRunDelegateRef CTRunDelegate; 37 | @property (nonatomic, assign) CGFloat ascent; 38 | @property (nonatomic, assign) CGFloat descent; 39 | @property (nonatomic, assign) CGFloat width; 40 | @property (nullable, nonatomic, strong) NSDictionary *userInfo; 41 | @end 42 | NS_ASSUME_NONNULL_END 43 | -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMDanmaku/SMDanmakuView.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMDanmakuView.m 3 | // SMAninationGroup 4 | // 5 | // Created by simon on 17/1/3. 6 | // Copyright © 2017年 simon. All rights reserved. 7 | // 8 | 9 | #import "SMDanmakuView.h" 10 | 11 | NSString *const SMTextAttachmentAttributeName = @"SMTextAttachment"; 12 | NSString *const SMTextAttachmentToken = @"\uFFFC"; 13 | 14 | #warning 后面提供接口, 自行设置 15 | CGFloat const margin = 10; 16 | 17 | @implementation SMDanmakuView 18 | 19 | - (void)setBackgroundImage:(UIImage *)backgroundImage { 20 | if (!backgroundImage) return; 21 | 22 | _backgroundImage = backgroundImage; 23 | UIImageView *imageView = [[UIImageView alloc] initWithImage:backgroundImage]; 24 | imageView.userInteractionEnabled = YES; 25 | [self addSubview:imageView]; 26 | } 27 | 28 | - (void)fireWithAttributedText:(NSMutableAttributedString *)attributedText { 29 | if (attributedText.length <= 0) return; 30 | 31 | SMDanmakuLayer *layer = [[SMDanmakuLayer alloc] init]; 32 | // TODO: 33 | layer.frame = CGRectMake(0, 0, 500, 100); 34 | layer.backgroundColor = [UIColor redColor].CGColor; 35 | layer.backgroundImage = _danmakuBackgroundImage; 36 | layer.attributedText = attributedText.mutableCopy; 37 | [layer setNeedsDisplay]; 38 | [self.layer addSublayer:layer]; 39 | } 40 | 41 | @end 42 | 43 | @implementation SMDanmakuLayer 44 | - (instancetype)init { 45 | if (self = [super init]) { 46 | static CGFloat scale; 47 | static dispatch_once_t onceToken; 48 | dispatch_once(&onceToken, ^{ 49 | scale = [UIScreen mainScreen].scale; 50 | }); 51 | self.contentsScale = scale; 52 | } 53 | return self; 54 | } 55 | 56 | - (void)setNeedsDisplay { 57 | [self display]; 58 | } 59 | 60 | - (void)display { 61 | 62 | UIGraphicsBeginImageContextWithOptions(self.bounds.size , self.opaque, self.contentsScale); 63 | 64 | CGContextRef context = UIGraphicsGetCurrentContext(); 65 | CGContextTranslateCTM(context, 0, self.bounds.size.height); 66 | CGContextScaleCTM(context, 1.0, -1.0); 67 | CGContextSetTextMatrix(context, CGAffineTransformIdentity); 68 | 69 | CGSize suggestSize; 70 | CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)_attributedText); 71 | 72 | CGFloat width = self.bounds.size.width; 73 | CGFloat height = self.bounds.size.height; 74 | // 横屏不对 75 | if (width > kScreenW) width = kScreenW; 76 | CGSize constraints = CGSizeMake(ceil(width), ceil(height)); 77 | { 78 | CGRect rect = {self.bounds.origin, constraints}; 79 | CGMutablePathRef path = CGPathCreateMutable(); 80 | CGPathAddRect(path, NULL, rect); 81 | CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL); 82 | 83 | CFArrayRef lines = CTFrameGetLines(frame); 84 | CTLineRef line = CFArrayGetValueAtIndex(lines, 1); 85 | CFRange range = CTLineGetStringRange(line); 86 | 87 | 88 | suggestSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, range, NULL, constraints, NULL); 89 | CFRelease(path); 90 | CFRelease(frame); 91 | } 92 | 93 | CGMutablePathRef path = CGPathCreateMutable(); 94 | CGRect rect = {self.bounds.origin, suggestSize}; 95 | NSLog(@"%@", NSStringFromCGRect(rect)); 96 | CGPathAddRect(path, NULL, rect); 97 | CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL); 98 | 99 | 100 | 101 | // transform 102 | 103 | 104 | CGRect backgroundRect = rect; 105 | // backgroundRect.origin.x += 20; 106 | // backgroundRect.size.width -= 20; 107 | CGContextDrawImage(context, backgroundRect, _backgroundImage.CGImage); 108 | 109 | CTFrameDraw(frame, context); 110 | 111 | // calculate 112 | NSArray *lines = (NSArray *)CTFrameGetLines(frame); 113 | CGPoint lineOrigins[lines.count]; 114 | CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), lineOrigins); 115 | 116 | for (int i = 0; i < lines.count; ++i) { 117 | CTLineRef line = (__bridge CTLineRef)lines[i]; 118 | 119 | for (id obj in (NSArray *)CTLineGetGlyphRuns(line)) { 120 | CTRunRef run = (__bridge CTRunRef)obj; 121 | NSDictionary *runAtt = (NSDictionary *)CTRunGetAttributes(run); 122 | // if not image continue 123 | CTRunDelegateRef delegate = (__bridge CTRunDelegateRef)[runAtt valueForKey:(id)kCTRunDelegateAttributeName]; 124 | if (delegate == nil) continue; 125 | UIImage *image = runAtt[SMTextAttachmentAttributeName]; 126 | if (!image) continue; 127 | 128 | // calculate bounds 129 | CGFloat ascent, descent; 130 | CGRect runBounds; 131 | runBounds.size.width = CTRunGetTypographicBounds(run, CFRangeMake(0, 0), &ascent, &descent, NULL) - margin; 132 | runBounds.size.height = ascent + descent; 133 | 134 | CGFloat offsetX = CTLineGetOffsetForStringIndex(line, CTRunGetStringRange(run).location, NULL); 135 | runBounds.origin.x = lineOrigins[i].x + offsetX + margin * 0.5; 136 | runBounds.origin.y = lineOrigins[i].y - descent; 137 | 138 | CGPathRef pathRef = CTFrameGetPath(frame); 139 | CGRect colRect = CGPathGetBoundingBox(pathRef); 140 | CGRect imageBounds = CGRectOffset(runBounds, colRect.origin.x, colRect.origin.y); 141 | 142 | CGContextDrawImage(context, imageBounds, image.CGImage); 143 | } 144 | 145 | } 146 | 147 | CFRelease(framesetter); 148 | CFRelease(path); 149 | CFRelease(frame); 150 | 151 | 152 | UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 153 | UIGraphicsEndImageContext(); 154 | self.contents = (__bridge id)(image.CGImage); 155 | } 156 | 157 | @end 158 | 159 | @implementation NSMutableAttributedString (SMDanmakuText) 160 | 161 | //+ (NSMutableAttributedString *)backgroupImageStringWithImage:(UIImage *)image { 162 | // 163 | //} 164 | 165 | + (NSMutableAttributedString *)attachmentStringWithImage:(UIImage *)image size:(CGSize)size font:(UIFont *)font { 166 | 167 | NSMutableAttributedString *attrs = [[NSMutableAttributedString alloc] initWithString:SMTextAttachmentToken]; 168 | 169 | [attrs setImageAttachment:image range:NSMakeRange(0, attrs.length)]; 170 | 171 | CGFloat fontHeight = font.ascender - font.descender; 172 | CGFloat offsetY = font.ascender - fontHeight * 0.5; 173 | 174 | CGFloat ascent = size.height * 0.5 + offsetY; 175 | CGFloat descent = size.height - ascent; 176 | CGFloat width = size.width + margin; 177 | 178 | SMTextRunDelegate *delegate = [[SMTextRunDelegate alloc] init]; 179 | delegate.width = width; 180 | delegate.ascent = ascent; 181 | delegate.descent = descent; 182 | 183 | CTRunDelegateRef delegateRef = delegate.CTRunDelegate; 184 | 185 | [attrs setRunDelegate:delegateRef range:NSMakeRange(0, attrs.length)]; 186 | if (delegateRef) CFRelease(delegateRef); 187 | 188 | return attrs; 189 | 190 | } 191 | 192 | #pragma mark - Attribute 193 | - (void)setFont:(UIFont *)font range:(NSRange)range { 194 | [self setAttribute:NSFontAttributeName value:font range:range]; 195 | } 196 | 197 | - (void)setTextColor:(UIColor *)textColor range:(NSRange)range { 198 | [self setAttribute:NSForegroundColorAttributeName value:textColor range:range]; 199 | } 200 | 201 | - (void)setRunDelegate:(CTRunDelegateRef)runDelegate range:(NSRange)range { 202 | [self setAttribute:(id)kCTRunDelegateAttributeName value:(__bridge id)runDelegate range:range]; 203 | } 204 | 205 | - (void)setImageAttachment:(UIImage *)image range:(NSRange)range { 206 | [self setAttribute:SMTextAttachmentAttributeName value:image range:range]; 207 | } 208 | 209 | - (void)setAttribute:(NSString *)name value:(id)value range:(NSRange)range { 210 | if (!name || [NSNull isEqual:name]) return; 211 | 212 | if (value && ![NSNull isEqual:value]) { 213 | [self addAttribute:name value:value range:range]; 214 | }else { 215 | [self removeAttribute:name range:range]; 216 | } 217 | } 218 | 219 | - (void)removeAttributesInRange:(NSRange)range { 220 | [self setAttributes:nil range:range]; 221 | } 222 | 223 | @end 224 | 225 | @implementation SMTextRunDelegate 226 | static void DeallocCallback(void *ref) { 227 | SMTextRunDelegate *self = (__bridge_transfer SMTextRunDelegate *)(ref); 228 | self = nil; 229 | } 230 | 231 | static CGFloat GetAscentCallback(void *ref) { 232 | SMTextRunDelegate *self = (__bridge SMTextRunDelegate *)(ref); 233 | return self.ascent; 234 | } 235 | 236 | static CGFloat GetDecentCallback(void *ref) { 237 | SMTextRunDelegate *self = (__bridge SMTextRunDelegate *)(ref); 238 | return self.descent; 239 | } 240 | 241 | static CGFloat GetWidthCallback(void *ref) { 242 | SMTextRunDelegate *self = (__bridge SMTextRunDelegate *)(ref); 243 | return self.width; 244 | } 245 | 246 | - (CTRunDelegateRef)CTRunDelegate { 247 | CTRunDelegateCallbacks callbacks; 248 | memset(&callbacks,0,sizeof(CTRunDelegateCallbacks)); 249 | callbacks.version = kCTRunDelegateVersion1; 250 | callbacks.dealloc = DeallocCallback; 251 | callbacks.getAscent = GetAscentCallback; 252 | callbacks.getDescent = GetDecentCallback; 253 | callbacks.getWidth = GetWidthCallback; 254 | return CTRunDelegateCreate(&callbacks, (__bridge_retained void *)([self copy])); 255 | } 256 | 257 | - (void)encodeWithCoder:(NSCoder *)encoder { 258 | [encoder encodeFloat:_ascent forKey:@"ascent"]; 259 | [encoder encodeFloat:_descent forKey:@"descent"]; 260 | [encoder encodeFloat:_width forKey:@"width"]; 261 | [encoder encodeObject:_userInfo forKey:@"userInfo"]; 262 | } 263 | 264 | - (instancetype)initWithCoder:(NSCoder *)decoder { 265 | self = [super init]; 266 | _ascent = [decoder decodeFloatForKey:@"ascent"]; 267 | _descent = [decoder decodeFloatForKey:@"descent"]; 268 | _width = [decoder decodeFloatForKey:@"width"]; 269 | _userInfo = [decoder decodeObjectForKey:@"userInfo"]; 270 | return self; 271 | } 272 | 273 | - (id)copyWithZone:(NSZone *)zone { 274 | typeof(self) delegate = [self.class new]; 275 | delegate.ascent = self.ascent; 276 | delegate.descent = self.descent; 277 | delegate.width = self.width; 278 | delegate.userInfo = self.userInfo; 279 | 280 | return delegate; 281 | } 282 | @end 283 | -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMDanmaku/SMDanmakuViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMBarrageViewController.h 3 | // SMAninationGroup 4 | // 5 | // Created by simon on 16/12/23. 6 | 7 | // Copyright © 2016年 simon. All rights reserved. 8 | // 9 | 10 | #import 11 | 12 | @interface SMDanmakuViewController : UIViewController 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMDanmaku/SMDanmakuViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMBarrageViewController.m 3 | // SMAninationGroup 4 | // 5 | // Created by simon on 16/12/23. 6 | 7 | // Copyright © 2016年 simon. All rights reserved. 8 | // 9 | 10 | #import "SMDanmakuViewController.h" 11 | #import "SMDanmakuView.h" 12 | #define SMRGBColor(r, g, b) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:1] 13 | #define SMRandColor SMRGBColor(arc4random_uniform(255), arc4random_uniform(255), arc4random_uniform(255)) 14 | #define path(resurce, type, component) [[[NSBundle mainBundle] pathForResource:resurce ofType:type] stringByAppendingPathComponent:component] 15 | 16 | @interface SMDanmakuViewController () 17 | @property (nonatomic, weak) SMDanmakuView *danmakuView; 18 | @end 19 | 20 | @implementation SMDanmakuViewController 21 | 22 | - (void)viewDidLoad { 23 | [super viewDidLoad]; 24 | 25 | SMDanmakuView *View = [[SMDanmakuView alloc] initWithFrame:CGRectMake(10, 74, kScreenW - 20, 400)]; 26 | View.layer.cornerRadius = 5; 27 | View.layer.masksToBounds = YES; 28 | View.backgroundImage = [UIImage imageNamed:@"camera_blur_0"]; 29 | View.danmakuBackgroundImage = [UIImage imageWithContentsOfFile:path(@"SMDanmaku", @"bundle", @"landscape_liuguang_blue")]; 30 | [self.view addSubview:View]; 31 | _danmakuView = View; 32 | 33 | UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake(kScreenW * 0.3, 500, 100, 70)]; 34 | [btn.titleLabel setFont:[UIFont systemFontOfSize:22]]; 35 | [btn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; 36 | [btn addTarget:self action:@selector(fire:) forControlEvents:UIControlEventTouchUpInside]; 37 | [btn setTitle:@"fire" forState:UIControlStateNormal]; 38 | [self.view addSubview:btn]; 39 | } 40 | 41 | - (void)fire:(UIButton *)btn { 42 | UIFont *font = [UIFont systemFontOfSize:17]; 43 | UIImage *img = [UIImage imageNamed:@"fa-link"]; 44 | 45 | NSMutableAttributedString *text = [[NSMutableAttributedString alloc]initWithString:@"弹幕图文测试"]; 46 | NSMutableAttributedString *attachmentStr = [NSMutableAttributedString attachmentStringWithImage:img size:CGSizeMake(30, 30) font:font]; 47 | 48 | 49 | NSMutableAttributedString *sendStr = [[NSMutableAttributedString alloc] init]; 50 | 51 | [sendStr appendAttributedString:attachmentStr]; 52 | [sendStr appendAttributedString:text]; 53 | 54 | [sendStr setFont:[UIFont systemFontOfSize:15] range:NSMakeRange(0, sendStr.length)]; 55 | [sendStr setTextColor:[UIColor blueColor] range:NSMakeRange(0, sendStr.length)]; 56 | 57 | NSUInteger location = sendStr.length; 58 | 59 | [sendStr appendAttributedString:attachmentStr]; 60 | [sendStr appendAttributedString:text]; 61 | 62 | [sendStr setFont:font range:NSMakeRange(0, sendStr.length)]; 63 | [sendStr setTextColor:[UIColor whiteColor] range:NSMakeRange(location, sendStr.length - location)]; 64 | 65 | [sendStr appendAttributedString:attachmentStr]; 66 | [sendStr appendAttributedString:text]; 67 | [sendStr appendAttributedString:attachmentStr]; 68 | [sendStr appendAttributedString:text]; 69 | [self.danmakuView fireWithAttributedText:sendStr]; 70 | } 71 | @end 72 | -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/SMEmitterButton.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMEmitterButton.h 3 | // SMAninationGroup 4 | // 5 | // Created by simon on 16/12/20 6 | // Copyright © 2016年 simon. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef NS_ENUM(NSUInteger, SMEffectType) { 12 | SMEffectEmitter = 0, 13 | SMEffectWare = 1, 14 | }; 15 | 16 | typedef NS_ENUM(NSUInteger, SMWareLayerType) { 17 | SMWareLayerCircle = 0, 18 | SMWareLayerHeart = 1, 19 | }; 20 | 21 | /// only SMEffectType == SMEffectWare 22 | @interface SMWareLayer : CALayer 23 | 24 | @property (nonatomic, assign) SMWareLayerType wareType; 25 | @property (nonatomic, strong) UIColor *color; 26 | 27 | @end 28 | 29 | @interface SMEmitterButton : UIButton 30 | 31 | /** 32 | creat method 33 | 34 | effectType: use "SMEffectEmitter" or "SMEffectWare" 35 | warn: if SMEffectType == SMEffectEmitter, must set "emitters" 36 | 37 | @param frame frame 38 | @return SMEitterBUtton 39 | */ 40 | - (instancetype)initWithEffectType:(SMEffectType)effectType frame:(CGRect)frame; 41 | 42 | 43 | /// default is blueColor 44 | @property (nonatomic, strong) UIColor *wareColor; 45 | 46 | 47 | /// only SMEffectType == SMEffectEmitter 48 | @property (nonatomic, assign) SMWareLayerType wareType; 49 | 50 | 51 | /// warn: if SMEffectType == SMEffectEmitter, must "set" 52 | @property (nonatomic, strong) NSArray *emitters; 53 | 54 | 55 | - (instancetype)initWithFrame:(CGRect)frame UNAVAILABLE_ATTRIBUTE; 56 | 57 | - (instancetype)init UNAVAILABLE_ATTRIBUTE; 58 | 59 | + (instancetype)new UNAVAILABLE_ATTRIBUTE; 60 | 61 | + (instancetype)buttonWithType:(UIButtonType)buttonType UNAVAILABLE_ATTRIBUTE; 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/SMEmitterButton.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMEmitterButton.m 3 | // SMAninationGroup 4 | // 5 | // Created by simon on 16/12/20 6 | // Copyright © 2016年 simon. All rights reserved. 7 | // 8 | 9 | #import "SMEmitterButton.h" 10 | #define kBoundsW self.bounds.size.width 11 | #define kBoundsH self.bounds.size.height 12 | @interface SMEmitterButton () 13 | @property (nonatomic, strong) CAEmitterLayer *emitterLayer; 14 | @property (nonatomic, strong) NSMutableArray *keyPaths; 15 | 16 | @property (nonatomic, strong) SMWareLayer *waveLayer; 17 | @property (nonatomic, assign) SMEffectType effectType; 18 | 19 | @end 20 | 21 | @implementation SMEmitterButton 22 | 23 | - (CAEmitterLayer *)emitterLayer { 24 | if (!_emitterLayer) { 25 | _emitterLayer = [CAEmitterLayer layer]; 26 | _emitterLayer.name = @"emitterLayer"; 27 | _emitterLayer.emitterShape = kCAEmitterLayerCircle; 28 | _emitterLayer.emitterMode = kCAEmitterLayerOutline; 29 | _emitterLayer.renderMode = kCAEmitterLayerOldestLast; 30 | _emitterLayer.masksToBounds = NO; 31 | _emitterLayer.zPosition = -1; 32 | _emitterLayer.position = CGPointMake(kBoundsW * 0.5, kBoundsH * 0.5); 33 | 34 | [self.layer addSublayer:_emitterLayer]; 35 | } 36 | 37 | return _emitterLayer; 38 | } 39 | 40 | - (SMWareLayer *)waveLayer { 41 | if (!_waveLayer) { 42 | _waveLayer = [SMWareLayer layer]; 43 | CGFloat diameter = kBoundsW; 44 | if (kBoundsH > kBoundsW) diameter = kBoundsH; 45 | _waveLayer.bounds = CGRectMake(0,0, diameter, diameter); 46 | _waveLayer.position = CGPointMake(kBoundsW * 0.5, kBoundsH * 0.5); 47 | _waveLayer.opacity = 0; 48 | _waveLayer.color = [UIColor blueColor]; 49 | 50 | [self.layer insertSublayer:_waveLayer atIndex:0]; 51 | } 52 | 53 | return _waveLayer; 54 | } 55 | 56 | - (NSMutableArray *)keyPaths { 57 | if (!_keyPaths) { 58 | _keyPaths = [NSMutableArray array]; 59 | } 60 | 61 | return _keyPaths; 62 | } 63 | 64 | - (instancetype)initWithEffectType:(SMEffectType)effectType frame:(CGRect)frame { 65 | if (self = [super initWithFrame:frame]) { 66 | 67 | [self setupWithEffectType:effectType]; 68 | } 69 | 70 | return self; 71 | } 72 | 73 | - (void)setupWithEffectType:(SMEffectType)effectType { 74 | 75 | _effectType = effectType; 76 | 77 | switch (effectType) { 78 | case SMEffectEmitter: 79 | [self emitterLayer]; 80 | break; 81 | case SMEffectWare: 82 | [self waveLayer]; 83 | break; 84 | default: 85 | break; 86 | } 87 | } 88 | 89 | - (void)setWareColor:(UIColor *)wareColor { 90 | 91 | _wareColor = wareColor; 92 | 93 | if (_effectType == SMEffectEmitter) return; 94 | 95 | self.waveLayer.color = wareColor; 96 | [self.waveLayer setNeedsDisplay]; 97 | } 98 | 99 | - (void)setWareType:(SMWareLayerType)wareType { 100 | _wareType = wareType; 101 | 102 | if (_effectType == SMEffectEmitter) return; 103 | self.waveLayer.wareType = wareType; 104 | [self.waveLayer setNeedsDisplay]; 105 | } 106 | 107 | - (void)setEmitters:(NSArray *)emitters { 108 | 109 | _emitters = emitters; 110 | if (_effectType == SMEffectWare) return; 111 | 112 | NSString *keyPath = nil; 113 | NSString *name = nil; 114 | NSMutableArray *emitterCells = [NSMutableArray array]; 115 | 116 | for (int i = 0; i < emitters.count; i++) { 117 | UIImage *image = emitters[i]; 118 | CAEmitterCell *emitter = [CAEmitterCell emitterCell]; 119 | name = [NSString stringWithFormat:@"emitter%zd", i]; 120 | keyPath = [NSString stringWithFormat:@"emitterCells.%@.birthRate", name]; 121 | emitter.name = name; 122 | emitter.alphaRange = 0.10; 123 | emitter.alphaSpeed = -1.0; 124 | emitter.lifetime = 0.7; 125 | emitter.lifetimeRange = 0.3; 126 | emitter.velocity = 40.00; 127 | emitter.velocityRange = 5.00; 128 | emitter.scale = 0.1; 129 | emitter.scaleRange = 0.02; 130 | emitter.birthRate = 0; 131 | 132 | emitter.contents = (__bridge id _Nullable)image.CGImage; 133 | [emitterCells addObject:emitter]; 134 | [self.keyPaths addObject:keyPath]; 135 | } 136 | 137 | _emitterLayer.emitterCells = emitterCells; 138 | } 139 | 140 | // start animation 141 | - (void)setSelected:(BOOL)selected { 142 | [super setSelected:selected]; 143 | 144 | if (_effectType == SMEffectEmitter) { 145 | [self emitter]; 146 | } else { 147 | [self wave]; 148 | } 149 | } 150 | 151 | #pragma mark - emitter 152 | - (void)emitter { 153 | 154 | CAKeyframeAnimation *scaleAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale"]; 155 | scaleAnimation.duration = 0.25; 156 | 157 | if (self.isSelected) { 158 | scaleAnimation.values = @[@1.1, @1.3, @1.5, @1.2 , @1.0]; 159 | 160 | [self startEmitter]; 161 | 162 | } else { 163 | scaleAnimation.values = @[@0.8, @0.6, @0.8, @1.0]; 164 | } 165 | 166 | scaleAnimation.calculationMode = kCAAnimationCubic; 167 | [self.layer addAnimation:scaleAnimation forKey:@"transform.scale"]; 168 | } 169 | 170 | - (void)startEmitter { 171 | for (NSString *keyPath in self.keyPaths) { 172 | [self.emitterLayer setValue:@1500 forKeyPath:keyPath]; 173 | } 174 | self.emitterLayer.beginTime = CACurrentMediaTime(); 175 | 176 | [self performSelector:@selector(stopEmitter) withObject:nil afterDelay:0.1]; 177 | } 178 | 179 | - (void)stopEmitter { 180 | for (NSString *keyPath in self.keyPaths) { 181 | [self.emitterLayer setValue:@0 forKeyPath:keyPath]; 182 | } 183 | } 184 | 185 | #pragma mark - wave 186 | - (void)wave { 187 | 188 | CAKeyframeAnimation *scaleAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale"]; 189 | scaleAnimation.duration = 0.25; 190 | 191 | if (self.isSelected) { 192 | scaleAnimation.values = @[@1.1, @1.3, @1.5, @1.2 , @1.0]; 193 | 194 | [self startWare]; 195 | 196 | } else { 197 | scaleAnimation.values = @[@0.8, @0.6, @0.8, @1.0]; 198 | } 199 | 200 | scaleAnimation.calculationMode = kCAAnimationCubic; 201 | [self.layer addAnimation:scaleAnimation forKey:@"transform.scale"]; 202 | 203 | 204 | } 205 | 206 | -(void)startWare { 207 | 208 | NSTimeInterval duration = 1.6; 209 | 210 | CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale.xy"]; 211 | scaleAnimation.fromValue = @0.2; 212 | scaleAnimation.toValue = @2.2; 213 | scaleAnimation.duration = duration; 214 | 215 | CAKeyframeAnimation *opacityAnimation = [CAKeyframeAnimation animationWithKeyPath:@"opacity"]; 216 | opacityAnimation.duration = duration; 217 | opacityAnimation.values = @[@.0,@0.4,@0.7,@0]; 218 | // opacityAnimation.toValue = @0; 219 | 220 | CAMediaTimingFunction * defaultCurve = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault]; 221 | CAAnimationGroup * animationGroup = [CAAnimationGroup animation]; 222 | animationGroup.duration = duration; 223 | animationGroup.repeatCount = 1; 224 | animationGroup.timingFunction = defaultCurve; 225 | animationGroup.animations = @[scaleAnimation, opacityAnimation]; 226 | [_waveLayer addAnimation:animationGroup forKey:@"wareAnimation"]; 227 | 228 | } 229 | 230 | - (void)setHighlighted:(BOOL)highlighted{} 231 | @end 232 | 233 | @implementation SMWareLayer 234 | 235 | - (instancetype)init { 236 | if (self = [super init]) { 237 | static CGFloat scale; 238 | static dispatch_once_t onceToken; 239 | dispatch_once(&onceToken, ^{ 240 | scale = [UIScreen mainScreen].scale; 241 | }); 242 | self.contentsScale = scale; 243 | } 244 | return self; 245 | } 246 | 247 | - (void)setNeedsDisplay { 248 | [self display]; 249 | } 250 | 251 | - (void)display { 252 | 253 | UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.opaque, self.contentsScale); 254 | // path 255 | UIBezierPath *path = [UIBezierPath bezierPath]; 256 | 257 | switch (self.wareType) { 258 | case SMWareLayerHeart: 259 | [self heart:path]; 260 | break; 261 | case SMWareLayerCircle: 262 | [self circle:path]; 263 | break; 264 | default: 265 | break; 266 | } 267 | 268 | // draw 269 | [[self color] setStroke]; 270 | 271 | [path stroke]; 272 | 273 | 274 | UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 275 | UIGraphicsEndImageContext(); 276 | self.contents = (__bridge id)(image.CGImage); 277 | } 278 | 279 | - (void)heart:(UIBezierPath *)path { 280 | CGFloat padding = 4.0; 281 | CGRect rect = self.bounds; 282 | CGFloat radius = floor((CGRectGetWidth(rect) - 2 * padding) / padding); 283 | 284 | // start point 285 | CGPoint point = CGPointMake(floor(CGRectGetWidth(rect) / 2.0), CGRectGetHeight(rect) - padding); 286 | [path moveToPoint:point]; 287 | 288 | //left curve ponit 289 | CGPoint leftPoint = CGPointMake(padding, floor(CGRectGetHeight(rect) / 2.4)); 290 | [path addQuadCurveToPoint:leftPoint controlPoint:CGPointMake(leftPoint.x, leftPoint.y + radius)]; 291 | [path addArcWithCenter:CGPointMake(leftPoint.x + radius, leftPoint.y) radius:radius startAngle:M_PI endAngle:0 clockwise:YES]; 292 | 293 | //right curve ponit 294 | CGPoint rightPoint = CGPointMake(leftPoint.x + 2 * radius, leftPoint.y); 295 | [path addArcWithCenter:CGPointMake(rightPoint.x + radius, rightPoint.y) radius:radius startAngle:M_PI endAngle:0 clockwise:YES]; 296 | 297 | //end point 298 | CGPoint endPoint = CGPointMake(leftPoint.x + 4 * radius, rightPoint.y); 299 | [path addQuadCurveToPoint:point controlPoint:CGPointMake(endPoint.x, endPoint.y + radius)]; 300 | // setter 301 | path.lineWidth = 2; 302 | path.lineCapStyle = kCGLineCapRound; 303 | path.lineJoinStyle = kCGLineCapRound; 304 | 305 | } 306 | 307 | - (void)circle:(UIBezierPath *)path { 308 | 309 | [path addArcWithCenter:CGPointMake(kBoundsW * 0.5, kBoundsH * 0.5) radius:kBoundsH * 0.5 - 5 startAngle:0.0 endAngle:180.0 clockwise:YES]; 310 | // setter 311 | path.lineWidth = 2; 312 | 313 | } 314 | 315 | @end 316 | -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/SMEmitterView.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMEmitterView.h 3 | // SMAninationGroup 4 | // 5 | // Created by simon on 16/12/20 6 | // Copyright © 2016年 simon. All rights reserved. 7 | // 8 | 9 | #import 10 | @class SMEmitterView; 11 | @interface SMEmitterLayer : CALayer 12 | @end 13 | 14 | typedef NS_ENUM(NSUInteger, SMEmitterPositionType) { 15 | SMEmitterPositionCenter = 0, 16 | SMEmitterPositionRight = 1, 17 | SMEmitterPositionLeft = 2, 18 | }; 19 | 20 | @protocol SMEmitterViewDelegate 21 | 22 | @optional 23 | 24 | /// Listening to self click event 25 | - (void)emitterView:(SMEmitterView *)emitterView didAddEmitterCount:(NSUInteger)emitterCount; 26 | 27 | @end 28 | 29 | @interface SMEmitterView : UIView 30 | 31 | /** 32 | use: "- (void)fireWithEmitterCount:(NSUInteger)emitterCount" 33 | to start fire 34 | 35 | if set the "images" then use image, otherwise use default drawHeart 36 | 37 | warning: when enterBackground we call "pause" to stop animation, 38 | in the background, when we receive message, 39 | we only receive message, the view is dismiss and no need to animation, 40 | so when the view is showed and want to regain animation, must to call "resume" 41 | */ 42 | 43 | 44 | /** 45 | open fire and add the conut to current fire 46 | 47 | @param emitterCount "need add count to current fire" 48 | */ 49 | - (void)fireWithEmitterCount:(NSUInteger)emitterCount; 50 | 51 | 52 | /** 53 | the emitter use image 54 | 55 | images: UIImage array 56 | */ 57 | @property (nonatomic, strong) NSArray *images; 58 | 59 | 60 | /// the position of emitters style 61 | @property (nonatomic, assign) SMEmitterPositionType positionType; 62 | 63 | 64 | /// return total emitter count 65 | @property (nonatomic, assign, readonly) NSUInteger totalCount; 66 | 67 | 68 | /// the emitters size, default is 36 69 | @property (nonatomic, assign) CGSize emitterSize; 70 | 71 | 72 | /// delegate: return emitters by self 73 | @property (nonatomic, weak) id delegate; 74 | 75 | 76 | /// if need pause, then pause animation 77 | - (void)pause; 78 | 79 | 80 | /// if paused, then you can resume animation 81 | - (void)resume; 82 | 83 | 84 | /// if need dealloc, then stop 85 | - (void)stop; 86 | 87 | @end 88 | -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/SMEmitterView.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMEmitterView.m 3 | // SMAninationGroup 4 | // 5 | // Created by simon on 16/12/20 6 | // Copyright © 2016年 simon. All rights reserved. 7 | // 8 | 9 | #import "SMEmitterView.h" 10 | 11 | #define SMRGBColor(r, g, b) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:1] 12 | #define SMRandColor SMRGBColor(arc4random_uniform(255), arc4random_uniform(255), arc4random_uniform(255)) 13 | #define emitterWH 36 14 | 15 | @interface SMEmitterView () 16 | 17 | @property (nonatomic, strong) NSMutableSet *reusePool; 18 | @property (nonatomic, strong) NSMutableArray *displayPool; 19 | 20 | 21 | @property (nonatomic, strong) NSTimer *timer; 22 | 23 | @property (nonatomic, assign) NSUInteger emitterCount; 24 | @property (nonatomic, assign) NSUInteger totalCount; 25 | 26 | @property (nonatomic, assign) BOOL paused; 27 | 28 | 29 | @end 30 | 31 | @implementation SMEmitterView 32 | 33 | #pragma mark - lazy 34 | - (NSTimer *)timer { 35 | if (!_timer) { 36 | _timer = [NSTimer timerWithTimeInterval:0.05 target:self selector:@selector(fire) userInfo:nil repeats:YES]; 37 | [[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes]; 38 | } 39 | 40 | return _timer; 41 | } 42 | 43 | - (NSMutableArray *)displayPool { 44 | if (!_displayPool) { 45 | _displayPool = [NSMutableArray array]; 46 | } 47 | return _displayPool; 48 | } 49 | 50 | - (NSMutableSet *)reusePool { 51 | if (!_reusePool) { 52 | _reusePool = [NSMutableSet set]; 53 | } 54 | return _reusePool; 55 | } 56 | 57 | #pragma mark - LifeCycle 58 | - (instancetype)initWithFrame:(CGRect)frame { 59 | if (self = [super initWithFrame:frame]) { 60 | _emitterCount = 0; 61 | _totalCount = 0; 62 | _emitterSize = CGSizeMake(emitterWH, emitterWH); 63 | } 64 | 65 | return self; 66 | } 67 | 68 | - (void)fire { 69 | 70 | if (_emitterCount <= 0) { 71 | [self stop]; 72 | return; 73 | } 74 | 75 | _emitterCount--; 76 | // NSLog(@"%zd", _emitterCount); 77 | SMEmitterLayer *layer = [self dequeueReuseLayer]; 78 | [self.layer addSublayer:layer]; 79 | [self.displayPool addObject:layer]; 80 | 81 | // direction -1 / 1 82 | int direction = 1 - (2 * arc4random_uniform(2)); 83 | CGPathRef path = [self pathWithDirection:direction]; 84 | NSTimeInterval animationDuration = 7; 85 | 86 | CAKeyframeAnimation *keyFrameAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"]; 87 | keyFrameAnimation.path = path; 88 | keyFrameAnimation.duration = animationDuration; 89 | 90 | CAKeyframeAnimation *rotationAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation.z"]; 91 | // 0.1 - 0.6 92 | CGFloat scaleRotate = (float)(1 + arc4random() % 6) / 10; 93 | rotationAnimation.values = @[@(0), @(M_PI_4 * direction * scaleRotate), @(-M_PI_4 * direction * scaleRotate)]; 94 | rotationAnimation.duration = animationDuration; 95 | 96 | CABasicAnimation *alphaAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"]; 97 | alphaAnimation.fromValue = @(0.9); 98 | alphaAnimation.toValue = @(0); 99 | alphaAnimation.duration =animationDuration; 100 | 101 | CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale"]; 102 | scaleAnimation.fromValue = @(0); 103 | scaleAnimation.toValue = @(1); 104 | scaleAnimation.duration = 0.2; 105 | 106 | CAAnimationGroup *animationGroup = [CAAnimationGroup animation]; 107 | animationGroup.repeatCount = 1; 108 | animationGroup.removedOnCompletion = NO; 109 | animationGroup.duration = animationDuration; 110 | animationGroup.fillMode = kCAFillModeForwards; 111 | animationGroup.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; 112 | animationGroup.animations = @[keyFrameAnimation, rotationAnimation,alphaAnimation, scaleAnimation]; 113 | animationGroup.delegate = self; 114 | [layer addAnimation:animationGroup forKey:@"icoderRo.github.com"]; 115 | 116 | } 117 | 118 | /// return animation path 119 | - (CGPathRef)pathWithDirection:(int)direction { 120 | 121 | CGFloat centerX; 122 | CGFloat width = self.bounds.size.width; 123 | 124 | switch (_positionType) { 125 | case SMEmitterPositionCenter: 126 | centerX = width * 0.5; 127 | break; 128 | case SMEmitterPositionRight: 129 | centerX = width - _emitterSize.width - 15; 130 | if (centerX <= 0) { 131 | centerX = width * 0.5; 132 | } 133 | break; 134 | 135 | case SMEmitterPositionLeft: 136 | centerX = _emitterSize.width + 15; 137 | if (centerX <= 0) { 138 | centerX = width * 0.5; 139 | } 140 | break; 141 | 142 | default: 143 | break; 144 | } 145 | 146 | int random = arc4random_uniform(4); 147 | 148 | // path 149 | UIBezierPath *path = [UIBezierPath bezierPath]; 150 | 151 | // startPoint 152 | CGFloat startY = self.bounds.size.height - random; 153 | CGFloat startX = centerX + (random * direction); 154 | CGPoint startPoint = CGPointMake(startX, startY); 155 | [path moveToPoint:startPoint]; 156 | 157 | // control point 158 | random = (arc4random() % 91) + 10; 159 | int randomY = (arc4random() % 91) + 15; 160 | 161 | CGFloat controlX1 = centerX - (direction * random); 162 | CGFloat controlY1 = self.bounds.size.height * 0.5 + (randomY * direction); 163 | CGPoint controlP1 = CGPointMake(controlX1, controlY1); 164 | 165 | CGFloat controlX2 = centerX + ((random - 12 )* direction); 166 | CGFloat controlY2 = controlY1; 167 | CGPoint controlP2 = CGPointMake(controlX2, controlY2); 168 | 169 | // endPoint 170 | randomY = (arc4random() % 21) + 15; 171 | random = (arc4random() % 15) + 15; 172 | CGFloat endY = randomY + direction; 173 | CGFloat endX = startX +(random * direction); 174 | CGPoint endPoint = CGPointMake(endX, endY); 175 | 176 | [path addCurveToPoint:endPoint controlPoint1:controlP1 controlPoint2:controlP2]; 177 | 178 | return path.CGPath; 179 | } 180 | 181 | - (id)dequeueReuseLayer { 182 | 183 | SMEmitterLayer *layer = [self.reusePool anyObject]; 184 | 185 | if (layer) { 186 | [self.reusePool removeObject:layer]; 187 | return layer; 188 | } 189 | 190 | layer = [SMEmitterLayer layer]; 191 | layer.frame = CGRectMake(0, 0, _emitterSize.width, _emitterSize.height); 192 | 193 | if (self.images.count > 0) { 194 | int random = arc4random_uniform((int)self.images.count); 195 | layer.contents = (__bridge id _Nullable)(((UIImage *)self.images[random]).CGImage); 196 | return layer; 197 | } 198 | 199 | [layer setNeedsDisplay]; 200 | 201 | return layer; 202 | } 203 | 204 | - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { 205 | 206 | SMEmitterLayer *layer = self.displayPool.firstObject; 207 | [self.displayPool removeObject:layer]; 208 | [layer removeAllAnimations]; 209 | [layer removeFromSuperlayer]; 210 | [self.reusePool addObject:layer]; 211 | 212 | if (self.displayPool.count <= 0) { 213 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 214 | if (self.displayPool.count <= 0) { 215 | [self.reusePool removeAllObjects]; 216 | } 217 | }); 218 | } 219 | } 220 | 221 | #pragma mark - Fire 222 | 223 | /** 224 | when enterBackground we call "pause" to stop animation, 225 | in the background, when we receive message, 226 | we only receive message, the view is dismiss so no need to animation, 227 | so when the view is showed and want to regain animation, must to call "resume" 228 | */ 229 | - (void)fireWithEmitterCount:(NSUInteger)emitterCount { 230 | _emitterCount += emitterCount; 231 | _totalCount += emitterCount; 232 | if (self.paused) return; 233 | [self timer]; 234 | } 235 | 236 | - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 237 | NSUInteger count = 1; 238 | [self fireWithEmitterCount:count]; 239 | if (self.delegate && [self.delegate respondsToSelector:@selector(emitterView:didAddEmitterCount:)]) { 240 | [self.delegate emitterView:self didAddEmitterCount:count]; 241 | } 242 | } 243 | 244 | #pragma mark - Control 245 | - (void)pause { 246 | self.paused = YES; 247 | CFTimeInterval pausedTime = [self.layer convertTime:CACurrentMediaTime() 248 | fromLayer:nil]; 249 | self.layer.speed = 0.0; 250 | self.layer.timeOffset = pausedTime; 251 | [_timer invalidate]; 252 | _timer = nil; 253 | } 254 | 255 | - (void)resume { 256 | if (!self.paused) return; 257 | CFTimeInterval pausedTime = [self.layer timeOffset]; 258 | self.layer.speed = 1.0; 259 | self.layer.timeOffset = 0.0; 260 | self.layer.beginTime = 0.0; 261 | CFTimeInterval timeSincePause = [self.layer convertTime:CACurrentMediaTime() 262 | fromLayer:nil] - pausedTime; 263 | self.layer.beginTime = timeSincePause; 264 | self.paused = NO; 265 | [self timer]; 266 | } 267 | 268 | - (void)stop { 269 | [_timer invalidate]; 270 | _timer = nil; 271 | _emitterCount = 0; 272 | } 273 | 274 | - (NSUInteger)totalCount { 275 | return _totalCount; 276 | } 277 | 278 | - (void)dealloc { 279 | [self stop]; 280 | } 281 | @end 282 | 283 | 284 | @interface SMEmitterLayer () 285 | 286 | @end 287 | 288 | @implementation SMEmitterLayer 289 | 290 | - (instancetype)init { 291 | if (self = [super init]) { 292 | static CGFloat scale; 293 | static dispatch_once_t onceToken; 294 | dispatch_once(&onceToken, ^{ 295 | scale = [UIScreen mainScreen].scale; 296 | }); 297 | self.contentsScale = scale; 298 | } 299 | return self; 300 | } 301 | 302 | - (void)setNeedsDisplay { 303 | [self display]; 304 | } 305 | 306 | - (void)display { 307 | 308 | UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.opaque, self.contentsScale); 309 | 310 | CGFloat padding = 4.0; 311 | CGRect rect = self.bounds; 312 | CGFloat radius = floor((CGRectGetWidth(rect) - 2 * padding) / padding); 313 | 314 | // path 315 | UIBezierPath *path = [UIBezierPath bezierPath]; 316 | 317 | // start point 318 | CGPoint point = CGPointMake(floor(CGRectGetWidth(rect) / 2.0), CGRectGetHeight(rect) - padding); 319 | [path moveToPoint:point]; 320 | 321 | //left curve ponit 322 | CGPoint leftPoint = CGPointMake(padding, floor(CGRectGetHeight(rect) / 2.4)); 323 | [path addQuadCurveToPoint:leftPoint controlPoint:CGPointMake(leftPoint.x, leftPoint.y + radius)]; 324 | [path addArcWithCenter:CGPointMake(leftPoint.x + radius, leftPoint.y) radius:radius startAngle:M_PI endAngle:0 clockwise:YES]; 325 | 326 | //right curve ponit 327 | CGPoint rightPoint = CGPointMake(leftPoint.x + 2 * radius, leftPoint.y); 328 | [path addArcWithCenter:CGPointMake(rightPoint.x + radius, rightPoint.y) radius:radius startAngle:M_PI endAngle:0 clockwise:YES]; 329 | 330 | //end point 331 | CGPoint endPoint = CGPointMake(leftPoint.x + 4 * radius, rightPoint.y); 332 | [path addQuadCurveToPoint:point controlPoint:CGPointMake(endPoint.x, endPoint.y + radius)]; 333 | 334 | // setter 335 | path.lineWidth = 1; 336 | path.lineCapStyle = kCGLineCapRound; 337 | path.lineJoinStyle = kCGLineCapRound; 338 | 339 | // draw 340 | [[UIColor whiteColor] setStroke]; 341 | [SMRandColor setFill]; 342 | [path fill]; 343 | [path stroke]; 344 | 345 | UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 346 | UIGraphicsEndImageContext(); 347 | self.contents = (__bridge id)(image.CGImage); 348 | } 349 | @end 350 | -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/SMEmitterViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMEmitterViewController.h 3 | // SMAninationGroup 4 | // 5 | // Created by simon on 16/12/20 6 | // Copyright © 2016年 simon. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SMEmitterViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/SMEmitterViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMEmitterViewController.m 3 | // SMAninationGroup 4 | // 5 | // Created by simon on 16/12/20 6 | // Copyright © 2016年 simon. All rights reserved. 7 | // 8 | 9 | #import "SMEmitterViewController.h" 10 | #import "SMEmitterView.h" 11 | #import "SMEmitterButton.h" 12 | 13 | #define kScreenW [UIScreen mainScreen].bounds.size.width 14 | #define KScreenH [UIScreen mainScreen].bounds.size.height 15 | #define path(resurce, type, component) [[[NSBundle mainBundle] pathForResource:resurce ofType:type] stringByAppendingPathComponent:component] 16 | 17 | @interface SMEmitterViewController () 18 | @property (nonatomic, weak) SMEmitterView *emitterView; 19 | @property (nonatomic, weak) SMEmitterView *emitterView1; 20 | @property (nonatomic, weak) SMEmitterView *emitterView2; 21 | @end 22 | 23 | @implementation SMEmitterViewController 24 | 25 | - (void)loadView { 26 | UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"camera_blur_1"]]; 27 | imageView.userInteractionEnabled = YES; 28 | self.view = imageView; 29 | } 30 | 31 | - (void)viewDidLoad { 32 | [super viewDidLoad]; 33 | 34 | CGFloat width = (kScreenW / 3); 35 | 36 | NSString *path = path(@"emitter", @"bundle", @"heartH"); 37 | NSString *path1 = path(@"emitter", @"bundle", @"heartN"); 38 | 39 | NSString *path2 = path(@"emitter", @"bundle", @"zanH"); 40 | NSString *path3 = path(@"emitter", @"bundle", @"zanN"); 41 | 42 | NSArray *sparkles = @[[UIImage imageWithContentsOfFile:path(@"emitter", @"bundle", @"Sparkle1")], [UIImage imageWithContentsOfFile:path(@"emitter", @"bundle", @"Sparkle3")]]; 43 | 44 | { 45 | SMEmitterView *emitterView = [[SMEmitterView alloc] init]; 46 | emitterView.frame = CGRectMake(10, 120, width, 400); 47 | emitterView.backgroundColor = [[UIColor grayColor] colorWithAlphaComponent:0.2]; 48 | emitterView.emitterSize = CGSizeMake(36, 36); 49 | emitterView.positionType = SMEmitterPositionLeft; 50 | emitterView.delegate = self; 51 | [self.view addSubview:emitterView]; 52 | 53 | _emitterView = emitterView; 54 | 55 | } 56 | 57 | { 58 | SMEmitterView *emitterView = [[SMEmitterView alloc] init]; 59 | emitterView.frame = CGRectMake(width + 10, 120, width, 400); 60 | emitterView.backgroundColor = [[UIColor cyanColor] colorWithAlphaComponent:0.2]; 61 | emitterView.positionType = SMEmitterPositionRight; 62 | emitterView.emitterSize = CGSizeMake(46, 46); 63 | emitterView.delegate = self; 64 | NSString *path = path(@"emitter", @"bundle", @"mouzhu"); 65 | NSMutableArray *images = [NSMutableArray arrayWithCapacity:21]; 66 | NSString *imageName = nil; 67 | UIImage *image = nil; 68 | for (int i = 0; i < 21; i++) { 69 | imageName = [NSString stringWithFormat:@"heart%zd",i]; 70 | image = [UIImage imageWithContentsOfFile:[path stringByAppendingPathComponent:imageName]]; 71 | [images addObject:image]; 72 | } 73 | 74 | emitterView.images = images.copy; 75 | [self.view addSubview:emitterView]; 76 | _emitterView1 = emitterView; 77 | 78 | } 79 | 80 | { 81 | SMEmitterView *emitterView = [[SMEmitterView alloc] init]; 82 | emitterView.frame = CGRectMake(2 * width + 10, 120, width, 400); 83 | emitterView.backgroundColor = [[UIColor blueColor] colorWithAlphaComponent:0.2]; 84 | emitterView.positionType = SMEmitterPositionCenter; 85 | emitterView.emitterSize = CGSizeMake(23, 20); 86 | emitterView.delegate = self; 87 | NSString *path = path(@"emitter", @"bundle", @"mouya"); 88 | NSMutableArray *images = [NSMutableArray arrayWithCapacity:9]; 89 | NSString *imageName = nil; 90 | UIImage *image = nil; 91 | for (int i = 0; i < 9; i++) { 92 | imageName = [NSString stringWithFormat:@"icon_mobile_live_praise_%zd",i]; 93 | image = [UIImage imageWithContentsOfFile:[path stringByAppendingPathComponent:imageName]]; 94 | [images addObject:image]; 95 | } 96 | 97 | emitterView.images = images.copy; 98 | [self.view addSubview:emitterView]; 99 | _emitterView2 = emitterView; 100 | 101 | } 102 | 103 | { 104 | SMEmitterButton *btn = [[SMEmitterButton alloc] initWithEffectType:SMEffectEmitter frame:CGRectMake(30, 550, 46, 46)]; 105 | btn.emitters = @[[UIImage imageWithContentsOfFile:path(@"emitter", @"bundle", @"Sparkle2")]]; 106 | [btn setBackgroundImage:[UIImage imageWithContentsOfFile:path2] forState:UIControlStateSelected]; 107 | [btn setBackgroundImage:[UIImage imageWithContentsOfFile:path3] forState:UIControlStateNormal]; 108 | [btn.titleLabel setFont:[UIFont systemFontOfSize:12]]; 109 | [btn addTarget:self action:@selector(fire:) forControlEvents:UIControlEventTouchUpInside]; 110 | [btn setTitle:@"star" forState:UIControlStateNormal]; 111 | [self.view addSubview:btn]; 112 | } 113 | 114 | { 115 | SMEmitterButton *btn = [[SMEmitterButton alloc] initWithEffectType:SMEffectEmitter frame:CGRectMake(120, 550, 46, 46)]; 116 | btn.emitters = sparkles; 117 | [btn setBackgroundImage:[UIImage imageWithContentsOfFile:path] forState:UIControlStateSelected]; 118 | [btn setBackgroundImage:[UIImage imageWithContentsOfFile:path1] forState:UIControlStateNormal]; 119 | [btn.titleLabel setFont:[UIFont systemFontOfSize:12]]; 120 | [btn addTarget:self action:@selector(stop:) forControlEvents:UIControlEventTouchUpInside]; 121 | [btn setTitle:@"stop" forState:UIControlStateNormal]; 122 | [self.view addSubview:btn]; 123 | } 124 | 125 | { 126 | SMEmitterButton *btn = [[SMEmitterButton alloc] initWithEffectType:SMEffectWare frame:CGRectMake(210, 550, 46, 46)]; 127 | btn.wareType = SMWareLayerHeart; 128 | btn.wareColor = [UIColor redColor]; 129 | [btn setBackgroundImage:[UIImage imageWithContentsOfFile:path] forState:UIControlStateSelected]; 130 | [btn setBackgroundImage:[UIImage imageWithContentsOfFile:path1] forState:UIControlStateNormal]; 131 | [btn.titleLabel setFont:[UIFont systemFontOfSize:12]]; 132 | [btn addTarget:self action:@selector(pause:) forControlEvents:UIControlEventTouchUpInside]; 133 | [btn setTitle:@"pause" forState:UIControlStateNormal]; 134 | [self.view addSubview:btn]; 135 | } 136 | 137 | { 138 | SMEmitterButton *btn = [[SMEmitterButton alloc] initWithEffectType:SMEffectWare frame:CGRectMake(300, 553, 40, 40)]; 139 | btn.wareType = SMWareLayerCircle; 140 | btn.wareColor = [UIColor yellowColor] ; 141 | [btn setBackgroundImage:[self imageWithColor:[UIColor grayColor]] forState:UIControlStateNormal]; 142 | [btn setBackgroundImage:[self imageWithColor:[[UIColor yellowColor] colorWithAlphaComponent:0.7]] forState:UIControlStateSelected]; 143 | 144 | [btn.titleLabel setFont:[UIFont systemFontOfSize:11]]; 145 | [btn addTarget:self action:@selector(resume:) forControlEvents:UIControlEventTouchUpInside]; 146 | [btn setTitle:@"resume" forState:UIControlStateNormal]; 147 | [self.view addSubview:btn]; 148 | } 149 | 150 | 151 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pauseNote) name:UIApplicationDidEnterBackgroundNotification object:nil]; 152 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resumeNote) name:UIApplicationDidBecomeActiveNotification object:nil]; 153 | } 154 | 155 | - (void)fire:(UIButton *)btn { 156 | [self.emitterView fireWithEmitterCount:100]; 157 | [self.emitterView1 fireWithEmitterCount:100]; 158 | [self.emitterView2 fireWithEmitterCount:100]; 159 | btn.selected = !btn.isSelected; 160 | 161 | } 162 | 163 | - (void)stop { 164 | [self.emitterView stop]; 165 | [self.emitterView1 stop]; 166 | [self.emitterView2 stop]; 167 | } 168 | 169 | - (void)stop:(UIButton *)btn { 170 | [self stop]; 171 | btn.selected = !btn.isSelected; 172 | } 173 | 174 | - (void)pause:(UIButton *)btn { 175 | [self pauseNote]; 176 | btn.selected = !btn.isSelected; 177 | } 178 | 179 | - (void)resume:(UIButton *)btn { 180 | [self resumeNote]; 181 | btn.selected = !btn.isSelected; 182 | } 183 | 184 | - (void)pauseNote { 185 | [self.emitterView pause]; 186 | [self.emitterView1 pause]; 187 | [self.emitterView2 pause]; 188 | } 189 | 190 | - (void)resumeNote { 191 | [self.emitterView resume]; 192 | [self.emitterView1 resume]; 193 | [self.emitterView2 resume]; 194 | } 195 | 196 | - (void)dealloc { 197 | [self stop]; 198 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 199 | } 200 | 201 | #pragma mark - 202 | - (void)emitterView:(SMEmitterView *)emitterView didAddEmitterCount:(NSUInteger)emitterCount { 203 | 204 | NSLog(@"%zd", emitterCount); 205 | 206 | // socket... 207 | } 208 | 209 | #pragma mark - 210 | - (UIImage *)imageWithColor:(UIColor *)color 211 | { 212 | CGRect rect = CGRectMake(0, 0, 40, 40); 213 | UIGraphicsBeginImageContext(rect.size); 214 | CGContextRef context = UIGraphicsGetCurrentContext(); 215 | CGContextSetFillColorWithColor(context,color.CGColor); 216 | CGContextFillEllipseInRect(context, rect); 217 | UIImage *img = UIGraphicsGetImageFromCurrentImageContext(); 218 | UIGraphicsEndImageContext(); 219 | 220 | return img; 221 | } 222 | @end 223 | -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/Sparkle1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/Sparkle1.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/Sparkle2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/Sparkle2.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/Sparkle3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/Sparkle3.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/heartH.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/heartH.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/heartN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/heartN.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouya/icon_mobile_live_praise_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouya/icon_mobile_live_praise_0@2x.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouya/icon_mobile_live_praise_1@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouya/icon_mobile_live_praise_1@2x.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouya/icon_mobile_live_praise_2@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouya/icon_mobile_live_praise_2@2x.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouya/icon_mobile_live_praise_3@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouya/icon_mobile_live_praise_3@2x.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouya/icon_mobile_live_praise_4@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouya/icon_mobile_live_praise_4@2x.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouya/icon_mobile_live_praise_5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouya/icon_mobile_live_praise_5@2x.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouya/icon_mobile_live_praise_6@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouya/icon_mobile_live_praise_6@2x.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouya/icon_mobile_live_praise_7@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouya/icon_mobile_live_praise_7@2x.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouya/icon_mobile_live_praise_8@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouya/icon_mobile_live_praise_8@2x.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart0.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart0@2x.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart1.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart10.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart10@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart10@2x.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart11.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart11@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart11@2x.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart12.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart12@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart12@2x.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart13.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart13@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart13@2x.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart14.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart14@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart14@2x.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart15.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart15@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart15@2x.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart16.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart16@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart16@2x.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart17.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart17@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart17@2x.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart18.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart18@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart18@2x.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart19.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart19@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart19@2x.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart1@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart1@2x.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart2.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart20.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart20@2x.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart2@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart2@2x.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart3.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart3@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart3@2x.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart4.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart4@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart4@2x.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart5.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart5@2x.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart6.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart6@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart6@2x.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart7.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart7@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart7@2x.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart8.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart8@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart8@2x.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart9.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart9@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/mouzhu/heart9@2x.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/zanH.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/zanH.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMEmitter/emitter.bundle/zanN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icoderRo/IRDemo/e0114a69862cc8e953c308ae84bf17bf8390e469/SMDemo/SMDemoList/SMEmitter/emitter.bundle/zanN.png -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMICropImageView/SMICropBottomView.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMICropOperationView.h 3 | // smifun 4 | // 5 | // Created by simon on 17/1/18. 6 | // Copyright © 2017年 SMI HOLDING GROUP. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SMICropBottomView : UIView 12 | @property (nonatomic, strong) UIButton *cancelBtn; 13 | @property (nonatomic, strong) UIButton *sureBtn; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMICropImageView/SMICropBottomView.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMICropOperationView.m 3 | // smifun 4 | // 5 | // Created by simon on 17/1/18. 6 | // Copyright © 2017年 SMI HOLDING GROUP. All rights reserved. 7 | // 8 | 9 | #import "SMICropBottomView.h" 10 | 11 | @interface SMICropBottomView () 12 | @end 13 | 14 | @implementation SMICropBottomView 15 | 16 | - (instancetype)initWithFrame:(CGRect)frame { 17 | if (self = [super initWithFrame:frame]) { 18 | _cancelBtn = [[UIButton alloc] init]; 19 | 20 | [_cancelBtn setTitle:@"取消" forState:UIControlStateNormal]; 21 | _cancelBtn.titleLabel.font = [UIFont boldSystemFontOfSize:SMIValue(18)]; 22 | [_cancelBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; 23 | [self addSubview:_cancelBtn]; 24 | 25 | _sureBtn = [[UIButton alloc] init]; 26 | [_sureBtn setTitle:@"确认" forState:UIControlStateNormal]; 27 | _sureBtn.titleLabel.font = [UIFont boldSystemFontOfSize:SMIValue(18)]; 28 | [_sureBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; 29 | [self addSubview:_sureBtn]; 30 | } 31 | 32 | return self; 33 | } 34 | 35 | - (void)layoutSubviews { 36 | [super layoutSubviews]; 37 | 38 | [_cancelBtn mas_makeConstraints:^(MASConstraintMaker *make) { 39 | make.left.equalTo(self).offset(SMIValue(40)); 40 | make.centerY.equalTo(self); 41 | }]; 42 | 43 | [_sureBtn mas_makeConstraints:^(MASConstraintMaker *make) { 44 | make.right.equalTo(self).offset(-SMIValue(40)); 45 | make.centerY.equalTo(self); 46 | }]; 47 | } 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMICropImageView/SMICropMaskerView.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMICropMaskerView.h 3 | // smifun 4 | // 5 | // Created by simon on 17/1/18. 6 | // Copyright © 2017年 SMI HOLDING GROUP. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SMICropMaskerView : UIView 12 | /// 蒙版颜色 13 | @property (nonatomic, strong) UIColor *maskerColor; 14 | /// 裁剪框 15 | @property (nonatomic, strong) UIBezierPath *cropBoxPath; 16 | @end 17 | -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMICropImageView/SMICropMaskerView.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMICropMaskerView.m 3 | // smifun 4 | // 5 | // Created by simon on 17/1/18. 6 | // Copyright © 2017年 SMI HOLDING GROUP. All rights reserved. 7 | // 8 | 9 | #import "SMICropMaskerView.h" 10 | 11 | @implementation SMICropMaskerView 12 | 13 | - (void)drawRect:(CGRect)rect { 14 | [super drawRect:rect]; 15 | 16 | CGContextRef context = UIGraphicsGetCurrentContext(); 17 | CGContextClearRect(context, rect); 18 | 19 | UIBezierPath *clipPath = [UIBezierPath bezierPathWithRect:self.bounds]; 20 | 21 | [clipPath appendPath:self.cropBoxPath]; 22 | 23 | clipPath.usesEvenOddFillRule = YES; 24 | [clipPath addClip]; 25 | 26 | if (!self.maskerColor) { 27 | self.maskerColor = [UIColor blackColor]; 28 | CGContextSetAlpha(context, 0.3); 29 | } 30 | [self.maskerColor setFill]; 31 | [clipPath fill]; 32 | } 33 | 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMICropImageView/SMICropViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMICropViewController.h 3 | // smifun 4 | // 5 | // Created by simon on 17/1/17. 6 | // Copyright © 2017年 SMI HOLDING GROUP. All rights reserved. 7 | // 8 | 9 | typedef NS_ENUM(NSUInteger, SMICropViewType) { 10 | SMICropViewTypeRectangle, 11 | SMICropViewTypeSquare, 12 | }; 13 | 14 | #import 15 | 16 | @interface SMICropViewController : UIViewController 17 | @property (nonatomic, strong) UIImage *sourceImage; 18 | @property (nonatomic, copy) void (^imageHandle)(UIImage *image); 19 | @property (nonatomic, assign) SMICropViewType cropType; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMICropImageView/SMICropViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMICropViewController.m 3 | // smifun 4 | // 5 | // Created by simon on 17/1/17. 6 | // Copyright © 2017年 SMI HOLDING GROUP. All rights reserved. 7 | // 8 | 9 | #import "SMICropViewController.h" 10 | #import "SMICropMaskerView.h" 11 | #import "SMICropBottomView.h" 12 | 13 | @interface SMICropViewController () 14 | @property (nonatomic, strong) UIScrollView *scrollView; 15 | @property (nonatomic, strong) UIImageView *imageView; 16 | @property (nonatomic, strong) UIImageView *cropBoxView; 17 | @property (nonatomic, strong) SMICropMaskerView *maskerView; 18 | @property (nonatomic, strong) SMICropBottomView *bottomView; 19 | 20 | @end 21 | 22 | @implementation SMICropViewController 23 | 24 | - (void)viewDidLoad { 25 | [super viewDidLoad]; 26 | self.view.backgroundColor = [UIColor blackColor]; 27 | self.automaticallyAdjustsScrollViewInsets = NO; 28 | 29 | [self setupScrollerView]; 30 | [self setupMaskerView]; 31 | [self setupCropBoxView]; 32 | [self setupImageView]; 33 | 34 | [self setupGesAndDraw]; 35 | [self setupBottomView]; 36 | } 37 | 38 | - (void)setupScrollerView { 39 | _scrollView = [[UIScrollView alloc]init]; 40 | _scrollView.delegate = self; 41 | _scrollView.frame = self.view.bounds; 42 | [self.view addSubview:_scrollView]; 43 | } 44 | 45 | - (void)setupCropBoxView { 46 | _cropBoxView = [[UIImageView alloc]init]; 47 | UIImage *image = [UIImage imageNamed:@"CropBox"]; 48 | image = [image resizableImageWithCapInsets:UIEdgeInsetsMake(10, 10, 10, 10) resizingMode:UIImageResizingModeStretch]; 49 | _cropBoxView.image = image; 50 | _cropBoxView.backgroundColor = [UIColor clearColor]; 51 | if (self.cropType == SMICropViewTypeSquare) { 52 | _cropBoxView.size = CGSizeMake(SMIValue(300), SMIValue(300)); 53 | } else { 54 | _cropBoxView.size = CGSizeMake(SMIValue(343), SMIValue(210)); 55 | } 56 | _cropBoxView.center = self.view.center; 57 | [self.view addSubview:_cropBoxView]; 58 | } 59 | 60 | - (void)setupMaskerView { 61 | 62 | _maskerView = [[SMICropMaskerView alloc] init]; 63 | _maskerView.backgroundColor = [UIColor clearColor]; 64 | _maskerView.maskerColor = HEXCOLORAL(51, 0.3); 65 | _maskerView.userInteractionEnabled = NO; 66 | _maskerView.frame = self.view.bounds; 67 | [self.view addSubview:_maskerView]; 68 | } 69 | 70 | - (void)setupImageView { 71 | _imageView = [[UIImageView alloc]init]; 72 | _imageView.contentMode = UIViewContentModeScaleAspectFit; 73 | _imageView.userInteractionEnabled = YES; 74 | _imageView.image = self.sourceImage; 75 | 76 | _imageView.frame = CGRectMake(0, 0, self.sourceImage.size.width, self.sourceImage.size.height); 77 | CGSize imageSize = self.sourceImage.size; 78 | self.scrollView.frame = self.view.bounds; 79 | self.scrollView.contentSize = imageSize; 80 | [self.scrollView addSubview:self.imageView]; 81 | 82 | CGFloat scale = 0.0f; 83 | CGSize cropBoxSize = self.cropBoxView.bounds.size; 84 | scale = MAX(self.view.width/imageSize.width, self.view.height/imageSize.height); 85 | CGSize scaledSize = (CGSize){floorf(imageSize.width * scale), floorf(imageSize.height * scale)}; 86 | CGFloat minimumZoomScale = MAX(cropBoxSize.width/imageSize.width, cropBoxSize.height/imageSize.height); 87 | 88 | self.scrollView.minimumZoomScale = minimumZoomScale; 89 | self.scrollView.maximumZoomScale = 3.0f; 90 | 91 | self.scrollView.zoomScale = scale; 92 | self.scrollView.contentSize = scaledSize; 93 | 94 | CGRect cropBoxFrame = self.cropBoxView.frame; 95 | 96 | if (cropBoxFrame.size.width < scaledSize.width - FLT_EPSILON || cropBoxFrame.size.height < scaledSize.height - FLT_EPSILON) { 97 | CGPoint offset = CGPointZero; 98 | offset.x = -floorf((CGRectGetWidth(self.scrollView.frame) - scaledSize.width) * 0.5f); 99 | offset.y = -floorf((CGRectGetHeight(self.scrollView.frame) - scaledSize.height) * 0.5f); 100 | self.scrollView.contentOffset = offset; 101 | } 102 | 103 | self.scrollView.contentInset = (UIEdgeInsets){CGRectGetMinY(cropBoxFrame), 104 | CGRectGetMinX(cropBoxFrame), 105 | CGRectGetMaxY(self.view.bounds) - CGRectGetMaxY(cropBoxFrame), 106 | CGRectGetMaxX(self.view.bounds) - CGRectGetMaxX(cropBoxFrame)}; 107 | } 108 | 109 | - (void)setupGesAndDraw { 110 | if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) { 111 | self.navigationController.interactivePopGestureRecognizer.enabled = NO; 112 | } 113 | 114 | self.maskerView.cropBoxPath = [UIBezierPath bezierPathWithRect:self.cropBoxView.frame]; 115 | [self.maskerView setNeedsDisplay]; 116 | } 117 | 118 | - (void)setupBottomView { 119 | 120 | CGFloat height = SMIValue(49); 121 | 122 | _bottomView = [[SMICropBottomView alloc] initWithFrame:CGRectMake(0, self.view.height - height, self.view.width, height)]; 123 | _bottomView.backgroundColor = HEXCOLORAL(71, 0.3); 124 | [self.view addSubview:_bottomView]; 125 | 126 | [_bottomView.cancelBtn addTarget:self action:@selector(didClickCancelBtn) forControlEvents:UIControlEventTouchUpInside]; 127 | [_bottomView.sureBtn addTarget:self action:@selector(didClickSureBtn) forControlEvents:UIControlEventTouchUpInside]; 128 | } 129 | 130 | - (void)didClickCancelBtn { 131 | 132 | [self.navigationController popViewControllerAnimated:YES]; 133 | } 134 | 135 | - (void)didClickSureBtn { 136 | 137 | if (self.imageHandle) { 138 | self.imageHandle([self cropBoxImage]); 139 | } 140 | } 141 | 142 | #pragma mark - action 143 | - (CGRect)imageCropBoxFrame { 144 | CGSize imageSize = self.imageView.image.size; 145 | CGSize contentSize = self.scrollView.contentSize; 146 | CGRect cropBoxFrame = self.cropBoxView.frame; 147 | CGPoint contentOffset = self.scrollView.contentOffset; 148 | UIEdgeInsets edgeInsets = self.scrollView.contentInset; 149 | 150 | CGRect frame = CGRectZero; 151 | frame.origin.x = floorf((contentOffset.x + edgeInsets.left) * (imageSize.width / contentSize.width)); 152 | frame.origin.x = MAX(0, frame.origin.x); 153 | frame.origin.y = floorf((contentOffset.y + edgeInsets.top) * (imageSize.height / contentSize.height)); 154 | frame.origin.y = MAX(0, frame.origin.y); 155 | frame.size.width = ceilf(cropBoxFrame.size.width * (imageSize.width / contentSize.width)); 156 | frame.size.width = MIN(imageSize.width, frame.size.width); 157 | frame.size.height = ceilf(cropBoxFrame.size.height * (imageSize.height / contentSize.height)); 158 | frame.size.height = MIN(imageSize.height, frame.size.height); 159 | 160 | return frame; 161 | } 162 | 163 | - (void)viewWillAppear:(BOOL)animated { 164 | [super viewWillAppear:animated]; 165 | [self.navigationController setNavigationBarHidden:YES animated:NO]; 166 | } 167 | 168 | - (void)viewWillDisappear:(BOOL)animated { 169 | [super viewWillDisappear:animated]; 170 | [self.navigationController setNavigationBarHidden:NO animated:NO]; 171 | 172 | if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) { 173 | self.navigationController.interactivePopGestureRecognizer.enabled = YES; 174 | } 175 | } 176 | 177 | - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView { 178 | return self.imageView; 179 | } 180 | 181 | - (UIImage *)cropBoxImage { 182 | return [self.imageView.image croppedImageWithFrame:[self imageCropBoxFrame]]; 183 | } 184 | 185 | @end 186 | -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMLayout/SMLayout.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GoodsSpecLayout.swift 3 | // CollectionViewLayout 4 | // 5 | // Created by simon on 2017/4/6. 6 | // Copyright © 2017年 simon. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// 类似于商品规格, 搜索关键字的布局 12 | 13 | class SMLayout: UICollectionViewFlowLayout { 14 | 15 | } 16 | 17 | /// 重写items的布局 18 | extension SMLayout { 19 | override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { 20 | guard let attributes = super.layoutAttributesForElements(in: rect) else { return nil } 21 | 22 | var i = 1 23 | for _ in 0.. 10 | @interface SMICellVideoFullView : UIView 11 | @end 12 | 13 | @class SMICellVideoFullViewController; 14 | @protocol SMICellVideoFullViewControllerDelegate 15 | 16 | - (void)fullViewControllerLayoutIfNeed:(SMICellVideoFullViewController *)fullViewController; 17 | 18 | @end 19 | 20 | @interface SMICellVideoFullViewController : UIViewController 21 | @property (nonatomic, weak) id delegate; 22 | @end 23 | -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMVideoPlayer/SMICellVideoFullViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMICellVideoFullViewController.m 3 | // smifun 4 | // 5 | // Created by simon on 17/4/8. 6 | // Copyright © 2017年 SMI HOLDING GROUP. All rights reserved. 7 | // 8 | 9 | #import "SMICellVideoFullViewController.h" 10 | #import "SMICellVideoView.h" 11 | 12 | @implementation SMICellVideoFullView 13 | - (instancetype)initWithFrame:(CGRect)frame { 14 | if (self = [super initWithFrame:frame]) { 15 | self.autoresizesSubviews = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth; 16 | self.backgroundColor = [UIColor blackColor]; 17 | } 18 | 19 | return self; 20 | } 21 | 22 | @end 23 | 24 | @implementation SMICellVideoFullViewController 25 | 26 | - (void)loadView { 27 | self.view = [[SMICellVideoFullView alloc] init]; 28 | } 29 | 30 | - (void)handleDeviceOrientationDidChange:(UIInterfaceOrientation)interfaceOrientation { 31 | UIDevice *device = [UIDevice currentDevice] ; 32 | 33 | switch (device.orientation) { 34 | case UIDeviceOrientationLandscapeLeft: 35 | case UIDeviceOrientationLandscapeRight: 36 | case UIDeviceOrientationPortrait: 37 | if ([self.delegate respondsToSelector:@selector(fullViewControllerLayoutIfNeed:)]) { 38 | [self.delegate fullViewControllerLayoutIfNeed:self]; 39 | } 40 | break; 41 | 42 | default: 43 | break; 44 | } 45 | 46 | } 47 | 48 | - (void)viewDidAppear:(BOOL)animated { 49 | [super viewDidAppear:animated]; 50 | [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; 51 | 52 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleDeviceOrientationDidChange:) name:UIDeviceOrientationDidChangeNotification object:nil]; 53 | } 54 | 55 | - (void)viewDidDisappear:(BOOL)animated { 56 | [super viewDidDisappear:animated]; 57 | [[NSNotificationCenter defaultCenter] removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil ]; 58 | 59 | [[UIDevice currentDevice]endGeneratingDeviceOrientationNotifications]; 60 | } 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMVideoPlayer/SMICellVideoItemsView.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMICellVideoItemsView.h 3 | // smifun 4 | // 5 | // Created by simon on 17/4/8. 6 | // Copyright © 2017年 SMI HOLDING GROUP. All rights reserved. 7 | // 8 | 9 | #import 10 | @class SMICellVideoItemsView; 11 | @protocol SMICellVideoItemsViewDelegate 12 | 13 | - (void)itemsView:(SMICellVideoItemsView *)itemsView didClickStartPlay:(BOOL)isStart; 14 | 15 | - (void)itemsView:(SMICellVideoItemsView *)itemsView didClickOrientationWithLandscape:(BOOL)isLandscape; 16 | 17 | - (void)itemsViewDidStartSlider:(SMICellVideoItemsView *)itemsView; 18 | - (void)itemsView:(SMICellVideoItemsView *)itemsView didDragSliderWithValue:(float)value; 19 | - (void)itemsView:(SMICellVideoItemsView *)itemsView didSliderWithValue:(float)value; 20 | 21 | @end 22 | @interface SMICellVideoItemsView : UIView 23 | @property (nonatomic, weak) id delegate; 24 | @property (nonatomic, strong, readonly) UISlider *progressSlider; 25 | @property (nonatomic, strong, readonly) UILabel *totalTimeLabel; 26 | @property (nonatomic, strong, readonly) UILabel *currentTimeLabel; 27 | @property (nonatomic, strong, readonly) UIButton *playOrPauseBtn; 28 | @end 29 | -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMVideoPlayer/SMICellVideoItemsView.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMICellVideoItemsView.m 3 | // smifun 4 | // 5 | // Created by simon on 17/4/8. 6 | // Copyright © 2017年 SMI HOLDING GROUP. All rights reserved. 7 | // 8 | 9 | /// 小控件 10 | #import "SMICellVideoItemsView.h" 11 | 12 | @interface SMICellVideoItemsView () 13 | @property (nonatomic, strong) UISlider *progressSlider; 14 | @property (nonatomic, strong) UIButton *playOrPauseBtn; 15 | @property (nonatomic, strong) UILabel *totalTimeLabel; 16 | @property (nonatomic, strong) UILabel *currentTimeLabel; 17 | @property (nonatomic, strong) UIButton *orientationBtn; 18 | @end 19 | 20 | @implementation SMICellVideoItemsView 21 | 22 | - (UISlider *)progressSlider { 23 | if (!_progressSlider) { 24 | _progressSlider = [[UISlider alloc] init]; 25 | [_progressSlider setThumbImage:[UIImage imageNamed:@"sliderVideo"] forState:UIControlStateNormal]; 26 | [self.progressSlider setMaximumTrackImage:[UIImage imageNamed:@"MaximumTrackImage"] forState:UIControlStateNormal]; 27 | [self.progressSlider setMinimumTrackImage:[UIImage imageNamed:@"MinimumTrackImage"] forState:UIControlStateNormal]; 28 | [_progressSlider addTarget:self action:@selector(sliderValueChange) forControlEvents:UIControlEventValueChanged]; 29 | [_progressSlider addTarget:self action:@selector(startSlider) forControlEvents:UIControlEventTouchDown]; 30 | [_progressSlider addTarget:self action:@selector(slider) forControlEvents:UIControlEventTouchUpInside]; 31 | [self addSubview:_progressSlider]; 32 | } 33 | 34 | return _progressSlider; 35 | } 36 | 37 | - (UIButton *)playOrPauseBtn { 38 | if (!_playOrPauseBtn) { 39 | _playOrPauseBtn = [[UIButton alloc] init]; 40 | [_playOrPauseBtn setImage:[UIImage imageNamed:@"stop"] forState:UIControlStateNormal]; 41 | [_playOrPauseBtn setImage:[UIImage imageNamed:@"shape"] forState:UIControlStateSelected]; 42 | [_playOrPauseBtn addTarget:self action:@selector(clickPlayOrPauseBtn) forControlEvents:UIControlEventTouchDown]; 43 | [self addSubview:_playOrPauseBtn]; 44 | } 45 | 46 | return _playOrPauseBtn; 47 | } 48 | 49 | - (UILabel *)totalTimeLabel { 50 | if (!_totalTimeLabel) { 51 | _totalTimeLabel = [UILabel new]; 52 | _totalTimeLabel.font = [UIFont systemFontOfSize:12]; 53 | _totalTimeLabel.textColor = HEXCOLOR(0xffffff); 54 | _totalTimeLabel.text = @"00:00"; 55 | [_totalTimeLabel sizeToFit]; 56 | [self addSubview:_totalTimeLabel]; 57 | } 58 | 59 | return _totalTimeLabel; 60 | } 61 | 62 | - (UILabel *)currentTimeLabel { 63 | if (!_currentTimeLabel) { 64 | _currentTimeLabel = [UILabel new]; 65 | _currentTimeLabel.font = [UIFont systemFontOfSize:12]; 66 | _currentTimeLabel.textColor = HEXCOLOR(0xffffff); 67 | _currentTimeLabel.text = @"00:00"; 68 | [_currentTimeLabel sizeToFit]; 69 | [self addSubview:_currentTimeLabel]; 70 | } 71 | 72 | return _currentTimeLabel; 73 | } 74 | 75 | - (UIButton *)orientationBtn { 76 | if (!_orientationBtn) { 77 | _orientationBtn = [[UIButton alloc] init]; 78 | [_orientationBtn setImage:[UIImage imageNamed:@"quanping"] forState:UIControlStateNormal]; 79 | [_orientationBtn setImage:[UIImage imageNamed:@"guanbiquanpin"]forState:UIControlStateSelected]; 80 | [_orientationBtn addTarget:self action:@selector(switchOrientation:) forControlEvents:UIControlEventTouchUpInside]; 81 | [_orientationBtn.titleLabel setFont:[UIFont systemFontOfSize:30]]; 82 | [_orientationBtn setTitle:@" " forState:UIControlStateNormal]; 83 | [_orientationBtn setTitle:@" " forState:UIControlStateSelected]; 84 | [self addSubview:_orientationBtn]; 85 | } 86 | 87 | return _orientationBtn; 88 | } 89 | 90 | - (void)clickPlayOrPauseBtn { 91 | if ([self.delegate respondsToSelector:@selector(itemsView:didClickStartPlay:)]) { 92 | self.playOrPauseBtn.selected = !self.playOrPauseBtn.isSelected; 93 | [self.delegate itemsView:self didClickStartPlay:!self.playOrPauseBtn.isSelected]; 94 | } 95 | } 96 | 97 | - (void)switchOrientation:(UIButton *)sender { 98 | 99 | if ([self.delegate respondsToSelector:@selector(itemsView:didClickOrientationWithLandscape:)]) { 100 | sender.selected = !sender.isSelected; 101 | [self.delegate itemsView:self didClickOrientationWithLandscape:sender.selected]; 102 | } 103 | } 104 | 105 | - (void)slider { 106 | if ([self.delegate respondsToSelector:@selector(itemsView:didSliderWithValue:)]) { 107 | [self.delegate itemsView:self didSliderWithValue:self.progressSlider.value]; 108 | } 109 | } 110 | 111 | - (void)startSlider { 112 | if ([self.delegate respondsToSelector:@selector(itemsViewDidStartSlider:)]) { 113 | self.playOrPauseBtn.selected = YES; 114 | [self.delegate itemsViewDidStartSlider:self]; 115 | } 116 | } 117 | 118 | - (void)sliderValueChange { 119 | if ([self.delegate respondsToSelector:@selector(itemsView:didDragSliderWithValue:)]) { 120 | [self.delegate itemsView:self didDragSliderWithValue:self.progressSlider.value]; 121 | } 122 | } 123 | 124 | - (void)layoutSubviews { 125 | [super layoutSubviews]; 126 | [self.currentTimeLabel mas_makeConstraints:^(MASConstraintMaker *make) { 127 | make.left.equalTo(self).offset(16); 128 | make.bottom.equalTo(self).offset(-11); 129 | }]; 130 | 131 | [self.totalTimeLabel mas_makeConstraints:^(MASConstraintMaker *make) { 132 | make.right.equalTo(self).offset(-50); 133 | make.bottom.equalTo(self).offset(-11); 134 | }]; 135 | 136 | [self.progressSlider mas_makeConstraints:^(MASConstraintMaker *make) { 137 | make.left.equalTo(self).offset(54); 138 | make.right.equalTo(self).offset(-90); 139 | make.centerY.equalTo(_totalTimeLabel); 140 | }]; 141 | 142 | [self.playOrPauseBtn mas_makeConstraints:^(MASConstraintMaker *make) { 143 | make.center.equalTo(self); 144 | }]; 145 | 146 | [self.orientationBtn mas_makeConstraints:^(MASConstraintMaker *make) { 147 | make.right.equalTo(self); 148 | make.centerY.equalTo(_totalTimeLabel); 149 | }]; 150 | } 151 | 152 | @end 153 | -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMVideoPlayer/SMICellVideoView.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMICellVideoView.h 3 | // smifun 4 | // 5 | // Created by simon on 17/4/8. 6 | // Copyright © 2017年 SMI HOLDING GROUP. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SMICellVideoView : UIView 12 | + (instancetype)videoView; 13 | @property (nonatomic, weak) UIViewController *contrainerViewController; 14 | @property (nonatomic, weak) UIView *landscapeSuperView; 15 | @property (nonatomic, copy) NSString *urlStr; 16 | - (void)remove; 17 | @end 18 | -------------------------------------------------------------------------------- /SMDemo/SMDemoList/SMVideoPlayer/SMICellVideoView.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMICellVideoView.m 3 | // smifun 4 | // 5 | // Created by simon on 17/4/8. 6 | // Copyright © 2017年 SMI HOLDING GROUP. All rights reserved. 7 | // 8 | 9 | #import "SMICellVideoView.h" 10 | #import "SMICellVideoItemsView.h" 11 | #import "SMICellVideoFullViewController.h" 12 | #import 13 | 14 | @interface SMICellVideoView () 15 | @property (nonatomic, strong) AVPlayer *player; 16 | @property (nonatomic, weak) AVPlayerLayer *playerLayer; 17 | 18 | @property (nonatomic, strong) NSTimer *progressTimer; 19 | 20 | @property (nonatomic, strong) SMICellVideoFullViewController *fullVc; 21 | @property (nonatomic, strong) SMICellVideoItemsView *itemsView; 22 | 23 | @end 24 | 25 | static NSString *playerStatus = @"status"; 26 | 27 | @implementation SMICellVideoView 28 | + (instancetype)videoView { 29 | static SMICellVideoView *view = nil; 30 | static dispatch_once_t onceToken; 31 | dispatch_once(&onceToken, ^{ 32 | view = [[self alloc] init]; 33 | }); 34 | return view; 35 | } 36 | 37 | 38 | - (instancetype)init { 39 | if (self = [super init]) { 40 | self.userInteractionEnabled = YES; 41 | [self addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(showItemsView)]]; 42 | self.backgroundColor = [UIColor blackColor]; 43 | self.player = [[AVPlayer alloc] init]; 44 | self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player]; 45 | self.playerLayer.videoGravity = AVLayerVideoGravityResizeAspect; 46 | [self.layer addSublayer:self.playerLayer]; 47 | [self willHideItemsView]; 48 | 49 | } 50 | return self; 51 | } 52 | 53 | - (void)setUrlStr:(NSString *)urlStr { 54 | _urlStr = urlStr; 55 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 56 | if (self.player.currentItem) { 57 | [self.player.currentItem removeObserver:self forKeyPath:playerStatus]; 58 | } 59 | AVPlayerItem *item = [AVPlayerItem playerItemWithURL:[NSURL URLWithString:urlStr]]; 60 | 61 | if (!item) {return;} 62 | 63 | [self.player replaceCurrentItemWithPlayerItem:item]; 64 | 65 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playbackFinished:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil]; 66 | [item addObserver:self forKeyPath:playerStatus options:NSKeyValueObservingOptionNew context:nil]; 67 | 68 | } 69 | 70 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 71 | if ([keyPath isEqualToString:playerStatus]) { 72 | AVPlayerItemStatus status = [[change objectForKey:NSKeyValueChangeNewKey] integerValue]; 73 | if (AVPlayerItemStatusReadyToPlay == status) { 74 | [self removeProgressTimer]; 75 | [self addProgressTimer]; 76 | self.itemsView.totalTimeLabel.text = [self timeStringWithTime:CMTimeGetSeconds(self.player.currentItem.asset.duration)]; 77 | [self.player play]; 78 | } else { 79 | self.itemsView.totalTimeLabel.text = @"00:00"; 80 | self.itemsView.currentTimeLabel.text = @"00:00"; 81 | [self removeProgressTimer]; 82 | 83 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 84 | if (self.player.currentItem) { 85 | [self.player.currentItem removeObserver:self forKeyPath:playerStatus]; 86 | } 87 | } 88 | } 89 | } 90 | 91 | #pragma mark - orientation 92 | - (void)portrait { 93 | 94 | SEL selector = NSSelectorFromString(@"setOrientation:"); 95 | NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[UIDevice instanceMethodSignatureForSelector:selector]]; 96 | [invocation setSelector:selector]; 97 | [invocation setTarget:[UIDevice currentDevice]]; 98 | int val = UIDeviceOrientationPortrait; 99 | [invocation setArgument:&val atIndex:2]; 100 | [invocation invoke]; 101 | 102 | [AppDelegate sharedAppDelegate].canRotate = NO; 103 | [self.fullVc dismissViewControllerAnimated:NO completion:^{ 104 | [self.landscapeSuperView addSubview:self]; 105 | [UIView animateWithDuration:0.15 delay:0.0 options:UIViewAnimationOptionLayoutSubviews animations:^{ 106 | self.frame = self.landscapeSuperView.bounds; 107 | } completion:nil]; 108 | }]; 109 | } 110 | 111 | - (void)landscape { 112 | 113 | [AppDelegate sharedAppDelegate].canRotate = YES; 114 | SEL selector = NSSelectorFromString(@"setOrientation:"); 115 | NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[UIDevice instanceMethodSignatureForSelector:selector]]; 116 | [invocation setSelector:selector]; 117 | [invocation setTarget:[UIDevice currentDevice]]; 118 | int val = UIInterfaceOrientationLandscapeRight; 119 | [invocation setArgument:&val atIndex:2]; 120 | [invocation invoke]; 121 | 122 | [self.contrainerViewController presentViewController:self.fullVc animated:NO completion:^{ 123 | [self.fullVc.view insertSubview:self atIndex:0]; 124 | self.center = self.fullVc.view.center; 125 | [UIView animateWithDuration:0.15 delay:0.0 options:UIViewAnimationOptionLayoutSubviews animations:^{ 126 | self.bounds = self.fullVc.view.bounds; 127 | } completion:nil]; 128 | }]; 129 | } 130 | 131 | #pragma mark - Action 132 | - (void)playbackFinished:(NSNotification *)note { 133 | [self.player.currentItem seekToTime:kCMTimeZero]; 134 | [self.player pause]; 135 | [self.itemsView.playOrPauseBtn setSelected:YES]; 136 | [self showItemsView]; 137 | } 138 | 139 | - (void)addProgressTimer { 140 | self.progressTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateProgressInfo) userInfo:nil repeats:YES]; 141 | [[NSRunLoop mainRunLoop] addTimer:self.progressTimer forMode:NSRunLoopCommonModes]; 142 | } 143 | 144 | - (void)removeProgressTimer { 145 | [self.progressTimer invalidate]; 146 | self.progressTimer = nil; 147 | } 148 | 149 | - (void)updateProgressInfo { 150 | self.itemsView.currentTimeLabel.text = [self timeStringWithTime:CMTimeGetSeconds(self.player.currentTime)]; 151 | 152 | [UIView animateWithDuration:1.0 animations:^{ 153 | self.itemsView.progressSlider.value = CMTimeGetSeconds(self.player.currentTime) / CMTimeGetSeconds(self.player.currentItem.asset.duration); 154 | }]; 155 | } 156 | 157 | #pragma mark - SMICellVideoItemsViewDelegate 158 | - (void)itemsView:(SMICellVideoItemsView *)itemsView didClickStartPlay:(BOOL)isStart { 159 | 160 | if (!isStart) { 161 | [self.player pause]; 162 | [self.itemsView.playOrPauseBtn setSelected:YES]; 163 | [self removeProgressTimer]; 164 | } else { 165 | [self.player play]; 166 | [self addProgressTimer]; 167 | } 168 | 169 | [NSObject cancelPreviousPerformRequestsWithTarget:self]; 170 | [self willHideItemsView]; 171 | } 172 | 173 | - (void)itemsView:(SMICellVideoItemsView *)itemsView didClickOrientationWithLandscape:(BOOL)isLandscape { 174 | 175 | if (isLandscape) { 176 | [self landscape]; 177 | } else { 178 | [self portrait]; 179 | } 180 | } 181 | 182 | - (void)itemsViewDidStartSlider:(SMICellVideoItemsView *)itemsView { 183 | [self.player pause]; 184 | [self removeProgressTimer]; 185 | } 186 | 187 | - (void)itemsView:(SMICellVideoItemsView *)itemsView didDragSliderWithValue:(float)value { 188 | self.itemsView.currentTimeLabel.text = [self timeStringWithTime:CMTimeGetSeconds(self.player.currentItem.duration) * self.itemsView.progressSlider.value]; 189 | } 190 | 191 | - (void)itemsView:(SMICellVideoItemsView *)itemsView didSliderWithValue:(float)value { 192 | [self addProgressTimer]; 193 | NSTimeInterval currentTime = CMTimeGetSeconds(self.player.currentItem.duration) * self.itemsView.progressSlider.value; 194 | [self.player seekToTime:CMTimeMakeWithSeconds(currentTime, NSEC_PER_SEC) toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero]; 195 | } 196 | 197 | #pragma mark - SMICellVideoFullViewControllerDelegate 198 | - (void)fullViewControllerLayoutIfNeed:(SMICellVideoFullViewController *)fullViewController { 199 | self.frame = self.fullVc.view.bounds; 200 | [self layoutIfNeeded]; 201 | } 202 | 203 | #pragma mark - TouchesBegan 204 | - (void)showItemsView { 205 | [UIView animateWithDuration:1.0 animations:^{ 206 | self.itemsView.alpha = 1; 207 | }]; 208 | } 209 | 210 | - (void)willHideItemsView { 211 | [self performSelector:@selector(hideItemsView) withObject:self afterDelay:2.0]; 212 | } 213 | 214 | - (void)hideItemsView { 215 | [UIView animateWithDuration:1.0 animations:^{ 216 | self.itemsView.alpha = 0; 217 | }]; 218 | } 219 | #pragma mark - Lazy 220 | - (SMICellVideoItemsView *)itemsView { 221 | if (!_itemsView) { 222 | _itemsView = [[SMICellVideoItemsView alloc] init]; 223 | _itemsView.delegate = self; 224 | [self addSubview:_itemsView]; 225 | } 226 | return _itemsView; 227 | } 228 | 229 | - (SMICellVideoFullViewController *)fullVc { 230 | if (!_fullVc ) { 231 | _fullVc = [[SMICellVideoFullViewController alloc] init]; 232 | _fullVc.delegate = self; 233 | } 234 | return _fullVc; 235 | } 236 | 237 | - (void)layoutSubviews { 238 | [super layoutSubviews]; 239 | self.playerLayer.frame = self.bounds; 240 | [self.itemsView mas_makeConstraints:^(MASConstraintMaker *make) { 241 | make.edges.equalTo(self); 242 | }]; 243 | } 244 | 245 | - (NSString *)timeStringWithTime:(Float64)currentTime { 246 | NSInteger cMin = currentTime / 60; 247 | NSInteger cSec = (NSInteger)currentTime % 60; 248 | NSString *currentString = [NSString stringWithFormat:@"%02ld:%02ld", cMin, cSec]; 249 | return currentString; 250 | } 251 | 252 | 253 | - (void)remove { 254 | self.itemsView.playOrPauseBtn.selected = YES; 255 | [self.player pause]; 256 | } 257 | 258 | - (void)dealloc { 259 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 260 | if (self.player.currentItem) { 261 | [self.player.currentItem removeObserver:self forKeyPath:playerStatus]; 262 | } 263 | } 264 | 265 | @end 266 | -------------------------------------------------------------------------------- /SMDemo/iOS-MVX.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | F4330FAA1F4EC0D100DECF67 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4330FA91F4EC0D100DECF67 /* AppDelegate.swift */; }; 11 | F4330FAC1F4EC0D100DECF67 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4330FAB1F4EC0D100DECF67 /* ViewController.swift */; }; 12 | F4330FAF1F4EC0D100DECF67 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F4330FAD1F4EC0D100DECF67 /* Main.storyboard */; }; 13 | F4330FB11F4EC0D100DECF67 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F4330FB01F4EC0D100DECF67 /* Assets.xcassets */; }; 14 | F4330FB41F4EC0D100DECF67 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F4330FB21F4EC0D100DECF67 /* LaunchScreen.storyboard */; }; 15 | F4330FBF1F4EC20000DECF67 /* MVCVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4330FBE1F4EC20000DECF67 /* MVCVC.swift */; }; 16 | F4330FC11F4EC21200DECF67 /* MVPVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4330FC01F4EC21200DECF67 /* MVPVC.swift */; }; 17 | F4330FC31F4EC22600DECF67 /* MVVMVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4330FC21F4EC22600DECF67 /* MVVMVC.swift */; }; 18 | F4330FC51F4EC50500DECF67 /* MVCModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4330FC41F4EC50500DECF67 /* MVCModel.swift */; }; 19 | F4330FC71F4EC51600DECF67 /* MVCView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4330FC61F4EC51600DECF67 /* MVCView.swift */; }; 20 | F4330FC91F4ECCB000DECF67 /* MVPView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4330FC81F4ECCB000DECF67 /* MVPView.swift */; }; 21 | F4330FCB1F4ECCE800DECF67 /* MVPModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4330FCA1F4ECCE800DECF67 /* MVPModel.swift */; }; 22 | F4330FCD1F4ECD0A00DECF67 /* Presenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4330FCC1F4ECD0A00DECF67 /* Presenter.swift */; }; 23 | F4330FCF1F4EDAAE00DECF67 /* MVVMModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4330FCE1F4EDAAE00DECF67 /* MVVMModel.swift */; }; 24 | F4330FD11F4EDABC00DECF67 /* MVVMViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4330FD01F4EDABC00DECF67 /* MVVMViewModel.swift */; }; 25 | F4330FD31F4EDAD200DECF67 /* MVVMView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4330FD21F4EDAD200DECF67 /* MVVMView.swift */; }; 26 | F4330FD61F4FC39B00DECF67 /* MVVMController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4330FD51F4FC39B00DECF67 /* MVVMController.swift */; }; 27 | F4330FDB1F4FC55200DECF67 /* MVVMCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4330FD91F4FC55200DECF67 /* MVVMCell.swift */; }; 28 | F4330FDC1F4FC55200DECF67 /* MVVMCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F4330FDA1F4FC55200DECF67 /* MVVMCell.xib */; }; 29 | F4330FDE1F4FC5D400DECF67 /* MVVMVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4330FDD1F4FC5D400DECF67 /* MVVMVM.swift */; }; 30 | /* End PBXBuildFile section */ 31 | 32 | /* Begin PBXFileReference section */ 33 | F4330FA61F4EC0D100DECF67 /* iOS-MVX.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "iOS-MVX.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 34 | F4330FA91F4EC0D100DECF67 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 35 | F4330FAB1F4EC0D100DECF67 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 36 | F4330FAE1F4EC0D100DECF67 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 37 | F4330FB01F4EC0D100DECF67 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 38 | F4330FB31F4EC0D100DECF67 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 39 | F4330FB51F4EC0D100DECF67 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 40 | F4330FBE1F4EC20000DECF67 /* MVCVC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MVCVC.swift; sourceTree = ""; }; 41 | F4330FC01F4EC21200DECF67 /* MVPVC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MVPVC.swift; sourceTree = ""; }; 42 | F4330FC21F4EC22600DECF67 /* MVVMVC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MVVMVC.swift; sourceTree = ""; }; 43 | F4330FC41F4EC50500DECF67 /* MVCModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MVCModel.swift; sourceTree = ""; }; 44 | F4330FC61F4EC51600DECF67 /* MVCView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MVCView.swift; sourceTree = ""; }; 45 | F4330FC81F4ECCB000DECF67 /* MVPView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MVPView.swift; sourceTree = ""; }; 46 | F4330FCA1F4ECCE800DECF67 /* MVPModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MVPModel.swift; sourceTree = ""; }; 47 | F4330FCC1F4ECD0A00DECF67 /* Presenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Presenter.swift; sourceTree = ""; }; 48 | F4330FCE1F4EDAAE00DECF67 /* MVVMModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MVVMModel.swift; sourceTree = ""; }; 49 | F4330FD01F4EDABC00DECF67 /* MVVMViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MVVMViewModel.swift; sourceTree = ""; }; 50 | F4330FD21F4EDAD200DECF67 /* MVVMView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MVVMView.swift; sourceTree = ""; }; 51 | F4330FD51F4FC39B00DECF67 /* MVVMController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MVVMController.swift; sourceTree = ""; }; 52 | F4330FD91F4FC55200DECF67 /* MVVMCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MVVMCell.swift; sourceTree = ""; }; 53 | F4330FDA1F4FC55200DECF67 /* MVVMCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MVVMCell.xib; sourceTree = ""; }; 54 | F4330FDD1F4FC5D400DECF67 /* MVVMVM.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MVVMVM.swift; sourceTree = ""; }; 55 | /* End PBXFileReference section */ 56 | 57 | /* Begin PBXFrameworksBuildPhase section */ 58 | F4330FA31F4EC0D100DECF67 /* Frameworks */ = { 59 | isa = PBXFrameworksBuildPhase; 60 | buildActionMask = 2147483647; 61 | files = ( 62 | ); 63 | runOnlyForDeploymentPostprocessing = 0; 64 | }; 65 | /* End PBXFrameworksBuildPhase section */ 66 | 67 | /* Begin PBXGroup section */ 68 | F4330F9D1F4EC0D100DECF67 = { 69 | isa = PBXGroup; 70 | children = ( 71 | F4330FBD1F4EC0FE00DECF67 /* MVVM */, 72 | F4330FBC1F4EC0FE00DECF67 /* MVP */, 73 | F4330FBB1F4EC0FE00DECF67 /* MVC */, 74 | F4330FA81F4EC0D100DECF67 /* iOS-MVX */, 75 | F4330FA71F4EC0D100DECF67 /* Products */, 76 | ); 77 | sourceTree = ""; 78 | }; 79 | F4330FA71F4EC0D100DECF67 /* Products */ = { 80 | isa = PBXGroup; 81 | children = ( 82 | F4330FA61F4EC0D100DECF67 /* iOS-MVX.app */, 83 | ); 84 | name = Products; 85 | sourceTree = ""; 86 | }; 87 | F4330FA81F4EC0D100DECF67 /* iOS-MVX */ = { 88 | isa = PBXGroup; 89 | children = ( 90 | F4330FA91F4EC0D100DECF67 /* AppDelegate.swift */, 91 | F4330FAB1F4EC0D100DECF67 /* ViewController.swift */, 92 | F4330FAD1F4EC0D100DECF67 /* Main.storyboard */, 93 | F4330FB01F4EC0D100DECF67 /* Assets.xcassets */, 94 | F4330FB21F4EC0D100DECF67 /* LaunchScreen.storyboard */, 95 | F4330FB51F4EC0D100DECF67 /* Info.plist */, 96 | ); 97 | path = "iOS-MVX"; 98 | sourceTree = ""; 99 | }; 100 | F4330FBB1F4EC0FE00DECF67 /* MVC */ = { 101 | isa = PBXGroup; 102 | children = ( 103 | F4330FBE1F4EC20000DECF67 /* MVCVC.swift */, 104 | F4330FC41F4EC50500DECF67 /* MVCModel.swift */, 105 | F4330FC61F4EC51600DECF67 /* MVCView.swift */, 106 | ); 107 | name = MVC; 108 | path = "iOS-MVX/MVC"; 109 | sourceTree = ""; 110 | }; 111 | F4330FBC1F4EC0FE00DECF67 /* MVP */ = { 112 | isa = PBXGroup; 113 | children = ( 114 | F4330FC01F4EC21200DECF67 /* MVPVC.swift */, 115 | F4330FC81F4ECCB000DECF67 /* MVPView.swift */, 116 | F4330FCA1F4ECCE800DECF67 /* MVPModel.swift */, 117 | F4330FCC1F4ECD0A00DECF67 /* Presenter.swift */, 118 | ); 119 | name = MVP; 120 | path = "iOS-MVX/MVP"; 121 | sourceTree = ""; 122 | }; 123 | F4330FBD1F4EC0FE00DECF67 /* MVVM */ = { 124 | isa = PBXGroup; 125 | children = ( 126 | F4330FD41F4FC2CF00DECF67 /* TableView */, 127 | F4330FC21F4EC22600DECF67 /* MVVMVC.swift */, 128 | F4330FCE1F4EDAAE00DECF67 /* MVVMModel.swift */, 129 | F4330FD01F4EDABC00DECF67 /* MVVMViewModel.swift */, 130 | F4330FD21F4EDAD200DECF67 /* MVVMView.swift */, 131 | ); 132 | name = MVVM; 133 | path = "iOS-MVX/MVVM"; 134 | sourceTree = ""; 135 | }; 136 | F4330FD41F4FC2CF00DECF67 /* TableView */ = { 137 | isa = PBXGroup; 138 | children = ( 139 | F4330FD51F4FC39B00DECF67 /* MVVMController.swift */, 140 | F4330FDD1F4FC5D400DECF67 /* MVVMVM.swift */, 141 | F4330FD91F4FC55200DECF67 /* MVVMCell.swift */, 142 | F4330FDA1F4FC55200DECF67 /* MVVMCell.xib */, 143 | ); 144 | path = TableView; 145 | sourceTree = ""; 146 | }; 147 | /* End PBXGroup section */ 148 | 149 | /* Begin PBXNativeTarget section */ 150 | F4330FA51F4EC0D100DECF67 /* iOS-MVX */ = { 151 | isa = PBXNativeTarget; 152 | buildConfigurationList = F4330FB81F4EC0D100DECF67 /* Build configuration list for PBXNativeTarget "iOS-MVX" */; 153 | buildPhases = ( 154 | F4330FA21F4EC0D100DECF67 /* Sources */, 155 | F4330FA31F4EC0D100DECF67 /* Frameworks */, 156 | F4330FA41F4EC0D100DECF67 /* Resources */, 157 | ); 158 | buildRules = ( 159 | ); 160 | dependencies = ( 161 | ); 162 | name = "iOS-MVX"; 163 | productName = "iOS-MVX"; 164 | productReference = F4330FA61F4EC0D100DECF67 /* iOS-MVX.app */; 165 | productType = "com.apple.product-type.application"; 166 | }; 167 | /* End PBXNativeTarget section */ 168 | 169 | /* Begin PBXProject section */ 170 | F4330F9E1F4EC0D100DECF67 /* Project object */ = { 171 | isa = PBXProject; 172 | attributes = { 173 | LastSwiftUpdateCheck = 0830; 174 | LastUpgradeCheck = 0830; 175 | ORGANIZATIONNAME = simon; 176 | TargetAttributes = { 177 | F4330FA51F4EC0D100DECF67 = { 178 | CreatedOnToolsVersion = 8.3.2; 179 | ProvisioningStyle = Automatic; 180 | }; 181 | }; 182 | }; 183 | buildConfigurationList = F4330FA11F4EC0D100DECF67 /* Build configuration list for PBXProject "iOS-MVX" */; 184 | compatibilityVersion = "Xcode 3.2"; 185 | developmentRegion = English; 186 | hasScannedForEncodings = 0; 187 | knownRegions = ( 188 | en, 189 | Base, 190 | ); 191 | mainGroup = F4330F9D1F4EC0D100DECF67; 192 | productRefGroup = F4330FA71F4EC0D100DECF67 /* Products */; 193 | projectDirPath = ""; 194 | projectRoot = ""; 195 | targets = ( 196 | F4330FA51F4EC0D100DECF67 /* iOS-MVX */, 197 | ); 198 | }; 199 | /* End PBXProject section */ 200 | 201 | /* Begin PBXResourcesBuildPhase section */ 202 | F4330FA41F4EC0D100DECF67 /* Resources */ = { 203 | isa = PBXResourcesBuildPhase; 204 | buildActionMask = 2147483647; 205 | files = ( 206 | F4330FB41F4EC0D100DECF67 /* LaunchScreen.storyboard in Resources */, 207 | F4330FB11F4EC0D100DECF67 /* Assets.xcassets in Resources */, 208 | F4330FAF1F4EC0D100DECF67 /* Main.storyboard in Resources */, 209 | F4330FDC1F4FC55200DECF67 /* MVVMCell.xib in Resources */, 210 | ); 211 | runOnlyForDeploymentPostprocessing = 0; 212 | }; 213 | /* End PBXResourcesBuildPhase section */ 214 | 215 | /* Begin PBXSourcesBuildPhase section */ 216 | F4330FA21F4EC0D100DECF67 /* Sources */ = { 217 | isa = PBXSourcesBuildPhase; 218 | buildActionMask = 2147483647; 219 | files = ( 220 | F4330FDE1F4FC5D400DECF67 /* MVVMVM.swift in Sources */, 221 | F4330FC51F4EC50500DECF67 /* MVCModel.swift in Sources */, 222 | F4330FAC1F4EC0D100DECF67 /* ViewController.swift in Sources */, 223 | F4330FD31F4EDAD200DECF67 /* MVVMView.swift in Sources */, 224 | F4330FC31F4EC22600DECF67 /* MVVMVC.swift in Sources */, 225 | F4330FAA1F4EC0D100DECF67 /* AppDelegate.swift in Sources */, 226 | F4330FD11F4EDABC00DECF67 /* MVVMViewModel.swift in Sources */, 227 | F4330FCD1F4ECD0A00DECF67 /* Presenter.swift in Sources */, 228 | F4330FC71F4EC51600DECF67 /* MVCView.swift in Sources */, 229 | F4330FCF1F4EDAAE00DECF67 /* MVVMModel.swift in Sources */, 230 | F4330FD61F4FC39B00DECF67 /* MVVMController.swift in Sources */, 231 | F4330FDB1F4FC55200DECF67 /* MVVMCell.swift in Sources */, 232 | F4330FC11F4EC21200DECF67 /* MVPVC.swift in Sources */, 233 | F4330FC91F4ECCB000DECF67 /* MVPView.swift in Sources */, 234 | F4330FCB1F4ECCE800DECF67 /* MVPModel.swift in Sources */, 235 | F4330FBF1F4EC20000DECF67 /* MVCVC.swift in Sources */, 236 | ); 237 | runOnlyForDeploymentPostprocessing = 0; 238 | }; 239 | /* End PBXSourcesBuildPhase section */ 240 | 241 | /* Begin PBXVariantGroup section */ 242 | F4330FAD1F4EC0D100DECF67 /* Main.storyboard */ = { 243 | isa = PBXVariantGroup; 244 | children = ( 245 | F4330FAE1F4EC0D100DECF67 /* Base */, 246 | ); 247 | name = Main.storyboard; 248 | sourceTree = ""; 249 | }; 250 | F4330FB21F4EC0D100DECF67 /* LaunchScreen.storyboard */ = { 251 | isa = PBXVariantGroup; 252 | children = ( 253 | F4330FB31F4EC0D100DECF67 /* Base */, 254 | ); 255 | name = LaunchScreen.storyboard; 256 | sourceTree = ""; 257 | }; 258 | /* End PBXVariantGroup section */ 259 | 260 | /* Begin XCBuildConfiguration section */ 261 | F4330FB61F4EC0D100DECF67 /* Debug */ = { 262 | isa = XCBuildConfiguration; 263 | buildSettings = { 264 | ALWAYS_SEARCH_USER_PATHS = NO; 265 | CLANG_ANALYZER_NONNULL = YES; 266 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 267 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 268 | CLANG_CXX_LIBRARY = "libc++"; 269 | CLANG_ENABLE_MODULES = YES; 270 | CLANG_ENABLE_OBJC_ARC = YES; 271 | CLANG_WARN_BOOL_CONVERSION = YES; 272 | CLANG_WARN_CONSTANT_CONVERSION = YES; 273 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 274 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 275 | CLANG_WARN_EMPTY_BODY = YES; 276 | CLANG_WARN_ENUM_CONVERSION = YES; 277 | CLANG_WARN_INFINITE_RECURSION = YES; 278 | CLANG_WARN_INT_CONVERSION = YES; 279 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 280 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 281 | CLANG_WARN_UNREACHABLE_CODE = YES; 282 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 283 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 284 | COPY_PHASE_STRIP = NO; 285 | DEBUG_INFORMATION_FORMAT = dwarf; 286 | ENABLE_STRICT_OBJC_MSGSEND = YES; 287 | ENABLE_TESTABILITY = YES; 288 | GCC_C_LANGUAGE_STANDARD = gnu99; 289 | GCC_DYNAMIC_NO_PIC = NO; 290 | GCC_NO_COMMON_BLOCKS = YES; 291 | GCC_OPTIMIZATION_LEVEL = 0; 292 | GCC_PREPROCESSOR_DEFINITIONS = ( 293 | "DEBUG=1", 294 | "$(inherited)", 295 | ); 296 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 297 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 298 | GCC_WARN_UNDECLARED_SELECTOR = YES; 299 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 300 | GCC_WARN_UNUSED_FUNCTION = YES; 301 | GCC_WARN_UNUSED_VARIABLE = YES; 302 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 303 | MTL_ENABLE_DEBUG_INFO = YES; 304 | ONLY_ACTIVE_ARCH = YES; 305 | SDKROOT = iphoneos; 306 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 307 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 308 | }; 309 | name = Debug; 310 | }; 311 | F4330FB71F4EC0D100DECF67 /* Release */ = { 312 | isa = XCBuildConfiguration; 313 | buildSettings = { 314 | ALWAYS_SEARCH_USER_PATHS = NO; 315 | CLANG_ANALYZER_NONNULL = YES; 316 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 317 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 318 | CLANG_CXX_LIBRARY = "libc++"; 319 | CLANG_ENABLE_MODULES = YES; 320 | CLANG_ENABLE_OBJC_ARC = YES; 321 | CLANG_WARN_BOOL_CONVERSION = YES; 322 | CLANG_WARN_CONSTANT_CONVERSION = YES; 323 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 324 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 325 | CLANG_WARN_EMPTY_BODY = YES; 326 | CLANG_WARN_ENUM_CONVERSION = YES; 327 | CLANG_WARN_INFINITE_RECURSION = YES; 328 | CLANG_WARN_INT_CONVERSION = YES; 329 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 330 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 331 | CLANG_WARN_UNREACHABLE_CODE = YES; 332 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 333 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 334 | COPY_PHASE_STRIP = NO; 335 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 336 | ENABLE_NS_ASSERTIONS = NO; 337 | ENABLE_STRICT_OBJC_MSGSEND = YES; 338 | GCC_C_LANGUAGE_STANDARD = gnu99; 339 | GCC_NO_COMMON_BLOCKS = YES; 340 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 341 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 342 | GCC_WARN_UNDECLARED_SELECTOR = YES; 343 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 344 | GCC_WARN_UNUSED_FUNCTION = YES; 345 | GCC_WARN_UNUSED_VARIABLE = YES; 346 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 347 | MTL_ENABLE_DEBUG_INFO = NO; 348 | SDKROOT = iphoneos; 349 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 350 | VALIDATE_PRODUCT = YES; 351 | }; 352 | name = Release; 353 | }; 354 | F4330FB91F4EC0D100DECF67 /* Debug */ = { 355 | isa = XCBuildConfiguration; 356 | buildSettings = { 357 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 358 | INFOPLIST_FILE = "iOS-MVX/Info.plist"; 359 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 360 | PRODUCT_BUNDLE_IDENTIFIER = "com.simon.framework.iOS-MVX"; 361 | PRODUCT_NAME = "$(TARGET_NAME)"; 362 | SWIFT_VERSION = 3.0; 363 | }; 364 | name = Debug; 365 | }; 366 | F4330FBA1F4EC0D100DECF67 /* Release */ = { 367 | isa = XCBuildConfiguration; 368 | buildSettings = { 369 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 370 | INFOPLIST_FILE = "iOS-MVX/Info.plist"; 371 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 372 | PRODUCT_BUNDLE_IDENTIFIER = "com.simon.framework.iOS-MVX"; 373 | PRODUCT_NAME = "$(TARGET_NAME)"; 374 | SWIFT_VERSION = 3.0; 375 | }; 376 | name = Release; 377 | }; 378 | /* End XCBuildConfiguration section */ 379 | 380 | /* Begin XCConfigurationList section */ 381 | F4330FA11F4EC0D100DECF67 /* Build configuration list for PBXProject "iOS-MVX" */ = { 382 | isa = XCConfigurationList; 383 | buildConfigurations = ( 384 | F4330FB61F4EC0D100DECF67 /* Debug */, 385 | F4330FB71F4EC0D100DECF67 /* Release */, 386 | ); 387 | defaultConfigurationIsVisible = 0; 388 | defaultConfigurationName = Release; 389 | }; 390 | F4330FB81F4EC0D100DECF67 /* Build configuration list for PBXNativeTarget "iOS-MVX" */ = { 391 | isa = XCConfigurationList; 392 | buildConfigurations = ( 393 | F4330FB91F4EC0D100DECF67 /* Debug */, 394 | F4330FBA1F4EC0D100DECF67 /* Release */, 395 | ); 396 | defaultConfigurationIsVisible = 0; 397 | defaultConfigurationName = Release; 398 | }; 399 | /* End XCConfigurationList section */ 400 | }; 401 | rootObject = F4330F9E1F4EC0D100DECF67 /* Project object */; 402 | } 403 | -------------------------------------------------------------------------------- /SMDemo/iOS-MVX.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SMDemo/iOS-MVX/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // iOS-MVX 4 | // 5 | // Created by simon on 2017/3/2. 6 | // Copyright © 2017年 simon. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // 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. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // 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. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // 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. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /SMDemo/iOS-MVX/Assets.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 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /SMDemo/iOS-MVX/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /SMDemo/iOS-MVX/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /SMDemo/iOS-MVX/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 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /SMDemo/iOS-MVX/MVC/MVCModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MVCModel.swift 3 | // iOS-MVX 4 | // 5 | // Created by simon on 2017/3/2. 6 | // Copyright © 2017年 simon. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class MVCModel: NSObject { 12 | var name = "" 13 | } 14 | -------------------------------------------------------------------------------- /SMDemo/iOS-MVX/MVC/MVCVC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MVCVC.swift 3 | // iOS-MVX 4 | // 5 | // Created by simon on 2017/3/2. 6 | // Copyright © 2017年 simon. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class MVCVC: UIViewController { 12 | 13 | fileprivate lazy var mvcView: MVCView = { [unowned self] in 14 | let mvcView = MVCView(frame: self.view.bounds) 15 | self.view.addSubview(mvcView) 16 | return mvcView 17 | }() 18 | 19 | fileprivate lazy var mvcModel: MVCModel = { 20 | let mvcModel = MVCModel() 21 | return mvcModel 22 | }() 23 | 24 | override func viewDidLoad() { 25 | super.viewDidLoad() 26 | view.backgroundColor = .white 27 | title = "MVC" 28 | 29 | changeName() 30 | 31 | mvcView.clickNameClosure = {[weak self] in 32 | self?.changeName() 33 | } 34 | } 35 | } 36 | 37 | extension MVCVC { 38 | func changeName() { 39 | let index = arc4random() % 9 40 | 41 | let model = MVCModel() 42 | model.name = "simon" + "\(index)" 43 | mvcView.model = model 44 | } 45 | } 46 | 47 | -------------------------------------------------------------------------------- /SMDemo/iOS-MVX/MVC/MVCView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MVCView.swift 3 | // iOS-MVX 4 | // 5 | // Created by simon on 2017/3/2. 6 | // Copyright © 2017年 simon. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class MVCView: UIView { 12 | 13 | var clickNameClosure: (() -> Void)? 14 | 15 | fileprivate lazy var nameLb: UILabel = { 16 | let nameLb = UILabel(frame: CGRect(x: 100, y: 100, width: 100, height: 100)) 17 | nameLb.textColor = .black 18 | nameLb.textAlignment = .center 19 | nameLb.font = UIFont.systemFont(ofSize: 20) 20 | nameLb.isUserInteractionEnabled = true 21 | 22 | let tap = UITapGestureRecognizer(target: self, action: #selector(clickName)) 23 | nameLb.addGestureRecognizer(tap) 24 | return nameLb 25 | }() 26 | 27 | var model: MVCModel? { 28 | didSet { 29 | nameLb.text = model?.name 30 | } 31 | } 32 | 33 | override init(frame: CGRect) { 34 | super.init(frame: frame) 35 | 36 | addSubview(nameLb) 37 | } 38 | 39 | required init?(coder aDecoder: NSCoder) { 40 | fatalError("init(coder:) has not been implemented") 41 | } 42 | } 43 | 44 | extension MVCView { 45 | 46 | @objc fileprivate func clickName() { 47 | clickNameClosure?() 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /SMDemo/iOS-MVX/MVP/MVPModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MVPModel.swift 3 | // iOS-MVX 4 | // 5 | // Created by simon on 2017/3/2. 6 | // Copyright © 2017年 simon. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class MVPModel: NSObject { 12 | var name = "" 13 | } 14 | -------------------------------------------------------------------------------- /SMDemo/iOS-MVX/MVP/MVPVC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MVPVC.swift 3 | // iOS-MVX 4 | // 5 | // Created by simon on 2017/3/2. 6 | // Copyright © 2017年 simon. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class MVPVC: UIViewController { 12 | 13 | fileprivate lazy var mvpView: MVPView = {[unowned self] in 14 | let mvpView = MVPView(frame: self.view.bounds) 15 | self.view.addSubview(mvpView) 16 | return mvpView 17 | }() 18 | 19 | fileprivate lazy var mvpModel: MVPModel = { 20 | let mvpModel = MVPModel() 21 | return mvpModel 22 | }() 23 | 24 | fileprivate lazy var presenter: Presenter = { 25 | let presenter = Presenter() 26 | return presenter 27 | }() 28 | 29 | override func viewDidLoad() { 30 | super.viewDidLoad() 31 | view.backgroundColor = .white 32 | title = "Passive MVP" 33 | 34 | 35 | presenter.mvpView = mvpView 36 | presenter.mvpModel = mvpModel 37 | 38 | presenter.setName() 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /SMDemo/iOS-MVX/MVP/MVPView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MVPView.swift 3 | // iOS-MVX 4 | // 5 | // Created by simon on 2017/3/2. 6 | // Copyright © 2017年 simon. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @objc protocol MVPViewDelegate: class { 12 | @objc optional func mvpView(_ mvpView: MVPView, didClickText text: String) 13 | } 14 | 15 | class MVPView: UIView { 16 | 17 | weak var delegate: MVPViewDelegate? 18 | 19 | var clickNameClosure: (() -> Void)? 20 | 21 | fileprivate lazy var nameLb: UILabel = { 22 | let nameLb = UILabel(frame: CGRect(x: 100, y: 100, width: 100, height: 100)) 23 | nameLb.textColor = .black 24 | nameLb.textAlignment = .center 25 | nameLb.font = UIFont.systemFont(ofSize: 20) 26 | nameLb.isUserInteractionEnabled = true 27 | 28 | let tap = UITapGestureRecognizer(target: self, action: #selector(clickName)) 29 | nameLb.addGestureRecognizer(tap) 30 | return nameLb 31 | }() 32 | 33 | fileprivate lazy var textLb: UILabel = { 34 | let nameLb = UILabel(frame: CGRect(x: 100, y: 300, width: 100, height: 100)) 35 | nameLb.textColor = .black 36 | nameLb.textAlignment = .center 37 | nameLb.text = "请点击" 38 | nameLb.font = UIFont.systemFont(ofSize: 20) 39 | nameLb.isUserInteractionEnabled = true 40 | 41 | let tap = UITapGestureRecognizer(target: self, action: #selector(clickText)) 42 | nameLb.addGestureRecognizer(tap) 43 | return nameLb 44 | }() 45 | 46 | override init(frame: CGRect) { 47 | super.init(frame: frame) 48 | 49 | addSubview(nameLb) 50 | addSubview(textLb) 51 | } 52 | 53 | required init?(coder aDecoder: NSCoder) { 54 | fatalError("init(coder:) has not been implemented") 55 | } 56 | 57 | } 58 | 59 | extension MVPView { 60 | 61 | @objc fileprivate func clickName() { 62 | clickNameClosure?() 63 | } 64 | 65 | @objc fileprivate func clickText() { 66 | delegate?.mvpView?(self, didClickText: "simon") 67 | } 68 | 69 | func setName(_ name: String?) { 70 | nameLb.text = name 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /SMDemo/iOS-MVX/MVP/Presenter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Presenter.swift 3 | // iOS-MVX 4 | // 5 | // Created by simon on 2017/3/2. 6 | // Copyright © 2017年 simon. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class Presenter: NSObject { 12 | var mvpModel: MVPModel? 13 | var mvpView: MVPView? { 14 | didSet { 15 | mvpView?.delegate = self as MVPViewDelegate 16 | 17 | mvpView?.clickNameClosure = {[weak self] in 18 | self?.setName() 19 | } 20 | } 21 | } 22 | } 23 | 24 | extension Presenter: MVPViewDelegate { 25 | func setName() { 26 | let index = arc4random() % 9 27 | 28 | mvpModel?.name = "simon" + "\(index)" 29 | 30 | mvpView?.setName(mvpModel?.name) 31 | } 32 | 33 | func mvpView(_ mvpView: MVPView, didClickText text: String) { 34 | mvpModel?.name = text 35 | self.mvpView?.setName(text) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /SMDemo/iOS-MVX/MVVM/MVVMModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MVVMModel.swift 3 | // iOS-MVX 4 | // 5 | // Created by simon on 2017/3/2. 6 | // Copyright © 2017年 simon. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class MVVMModel: NSObject { 12 | var name = "" 13 | 14 | } 15 | -------------------------------------------------------------------------------- /SMDemo/iOS-MVX/MVVM/MVVMVC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MVVMVC.swift 3 | // iOS-MVX 4 | // 5 | // Created by simon on 2017/3/2. 6 | // Copyright © 2017年 simon. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class MVVMVC: UIViewController { 12 | 13 | fileprivate lazy var mvvmView: MVVMView = { [unowned self] in 14 | let mvvmView = MVVMView(frame: self.view.bounds) 15 | self.view.addSubview(mvvmView) 16 | return mvvmView 17 | }() 18 | 19 | fileprivate lazy var mvvmModel: MVVMModel = { 20 | let mvvmModel = MVVMModel() 21 | mvvmModel.name = "simon Init" 22 | return mvvmModel 23 | }() 24 | 25 | fileprivate lazy var mvvmVM: MVVMViewModel = { 26 | let mvvmVM = MVVMViewModel() 27 | return mvvmVM 28 | }() 29 | 30 | override func viewDidLoad() { 31 | super.viewDidLoad() 32 | view.backgroundColor = .white 33 | title = "MVVMVC" 34 | 35 | mvvmView.mvvmVM = mvvmVM 36 | mvvmVM.mvvmModel = mvvmModel 37 | 38 | } 39 | } 40 | 41 | -------------------------------------------------------------------------------- /SMDemo/iOS-MVX/MVVM/MVVMView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MVVMView.swift 3 | // iOS-MVX 4 | // 5 | // Created by simon on 2017/3/2. 6 | // Copyright © 2017年 simon. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | 12 | class MVVMView: UIView { 13 | 14 | fileprivate lazy var nameLb: UILabel = { 15 | let nameLb = UILabel(frame: CGRect(x: 100, y: 100, width: 200, height: 100)) 16 | nameLb.textColor = .black 17 | nameLb.textAlignment = .center 18 | nameLb.font = UIFont.systemFont(ofSize: 20) 19 | nameLb.isUserInteractionEnabled = true 20 | 21 | let tap = UITapGestureRecognizer(target: self, action: #selector(clickName)) 22 | nameLb.addGestureRecognizer(tap) 23 | return nameLb 24 | }() 25 | 26 | 27 | var mvvmVM: MVVMViewModel? { 28 | didSet { 29 | mvvmVM?.addObserver(self, forKeyPath: "name", options: [.new, .initial], context: nil) 30 | } 31 | } 32 | 33 | override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { 34 | 35 | if keyPath == "name" { 36 | let name = change?[.newKey] as? String 37 | setName(name) 38 | } 39 | } 40 | 41 | override init(frame: CGRect) { 42 | super.init(frame: frame) 43 | 44 | addSubview(nameLb) 45 | } 46 | 47 | required init?(coder aDecoder: NSCoder) { 48 | fatalError("init(coder:) has not been implemented") 49 | } 50 | 51 | deinit { 52 | mvvmVM?.removeObserver(self, forKeyPath: "name") 53 | } 54 | } 55 | 56 | extension MVVMView { 57 | @objc fileprivate func clickName() { 58 | mvvmVM?.clickName() 59 | } 60 | 61 | fileprivate func setName(_ name: String?) { 62 | nameLb.text = name 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /SMDemo/iOS-MVX/MVVM/MVVMViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MVVMViewModel.swift 3 | // iOS-MVX 4 | // 5 | // Created by simon on 2017/3/2. 6 | // Copyright © 2017年 simon. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class MVVMViewModel: NSObject { 12 | 13 | dynamic var name = "" 14 | 15 | var mvvmModel: MVVMModel? { 16 | didSet { 17 | name = mvvmModel?.name ?? "" 18 | } 19 | } 20 | } 21 | 22 | extension MVVMViewModel { 23 | 24 | func clickName() { 25 | let index = arc4random() % 9 26 | 27 | let mvvmModel = MVVMModel() 28 | mvvmModel.name = "simon" + "\(index)" 29 | self.mvvmModel = mvvmModel 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /SMDemo/iOS-MVX/MVVM/TableView/MVVMCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MVVMCell.swift 3 | // iOS-MVX 4 | // 5 | // Created by simon on 2017/3/2. 6 | // Copyright © 2017年 simon. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class MVVMCell: UITableViewCell { 12 | 13 | fileprivate var index: Int = 0 14 | 15 | var clickNameClosure: ((_ index: Int) -> Void)? 16 | 17 | @IBOutlet weak var nameBtn: UIButton! 18 | 19 | 20 | override func awakeFromNib() { 21 | super.awakeFromNib() 22 | } 23 | 24 | @IBAction func clickName(_ sender: UIButton) { 25 | clickNameClosure?(index) 26 | } 27 | } 28 | 29 | extension MVVMCell { 30 | func setName(_ name: String?, index: Int) { 31 | self.index = index 32 | nameBtn.setTitle(name, for: .normal) 33 | } 34 | } 35 | 36 | -------------------------------------------------------------------------------- /SMDemo/iOS-MVX/MVVM/TableView/MVVMCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /SMDemo/iOS-MVX/MVVM/TableView/MVVMController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MVVMController.swift 3 | // iOS-MVX 4 | // 5 | // Created by simon on 2017/3/2. 6 | // Copyright © 2017年 simon. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class MVVMController: UIViewController { 12 | 13 | fileprivate lazy var mvvmVM: MVVMVM = {return $0}(MVVMVM()) 14 | 15 | fileprivate lazy var tableView: UITableView = {[unowned self] in 16 | let tableView = UITableView(frame: self.view.bounds) 17 | tableView.delegate = self 18 | tableView.dataSource = self 19 | tableView.backgroundColor = .yellow 20 | tableView.rowHeight = 70 21 | tableView.register(UINib(nibName: "MVVMCell", bundle: nil), forCellReuseIdentifier: "cellId") 22 | return tableView 23 | }() 24 | 25 | override func viewDidLoad() { 26 | super.viewDidLoad() 27 | title = "MVVMTableView" 28 | 29 | view.addSubview(tableView) 30 | 31 | mvvmVM.loadData() 32 | mvvmVM.reloadData = {[weak self] in 33 | self?.tableView.reloadData() 34 | } 35 | } 36 | } 37 | 38 | extension MVVMController: UITableViewDelegate, UITableViewDataSource { 39 | 40 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 41 | return mvvmVM.models.count 42 | } 43 | 44 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 45 | let cell = tableView.dequeueReusableCell(withIdentifier: "cellId") 46 | mvvmVM.setCell(cell!, indexPath.row) 47 | return cell! 48 | } 49 | 50 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 51 | 52 | } 53 | } 54 | 55 | -------------------------------------------------------------------------------- /SMDemo/iOS-MVX/MVVM/TableView/MVVMVM.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MVVMVM.swift 3 | // iOS-MVX 4 | // 5 | // Created by simon on 2017/3/2. 6 | // Copyright © 2017年 simon. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | 12 | class MVVMVM: NSObject { 13 | 14 | var reloadData:(() -> Void)? 15 | 16 | lazy var models = [MVVMModel]() 17 | } 18 | 19 | extension MVVMVM { 20 | 21 | func setCell(_ cell: UITableViewCell, _ index: Int) { 22 | 23 | if cell.isKind(of: MVVMCell.self) { 24 | let cell = cell as! MVVMCell 25 | 26 | let model = models[index] 27 | cell.setName(model.name, index: index) 28 | 29 | cell.clickNameClosure = {[weak self] index in 30 | 31 | let model = self?.models[index] 32 | model?.name = "test test.." 33 | self?.reloadData?() 34 | } 35 | } 36 | } 37 | } 38 | 39 | extension MVVMVM { 40 | func loadData() { 41 | 42 | DispatchQueue.main.asyncAfter(deadline: .now() + 2) { 43 | for i in 0..<20 { 44 | 45 | let model = MVVMModel() 46 | model.name = "simon" + "\(i)" 47 | 48 | self.models.append(model) 49 | } 50 | 51 | self.reloadData?() 52 | } 53 | 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /SMDemo/iOS-MVX/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // iOS-MVX 4 | // 5 | // Created by simon on 2017/3/2. 6 | // Copyright © 2017年 simon. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | view.backgroundColor = .white 16 | title = "请触屏" 17 | } 18 | 19 | override func touchesBegan(_ touches: Set, with event: UIEvent?) { 20 | // navigationController?.pushViewController(MVCVC(), animated: true) 21 | // navigationController?.pushViewController(MVPVC(), animated: true) 22 | // navigationController?.pushViewController(MVVMVC(), animated: true) 23 | navigationController?.pushViewController(MVVMController(), animated: true) 24 | 25 | } 26 | 27 | } 28 | 29 | --------------------------------------------------------------------------------