├── .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 | 
9 |
10 | 
11 |
12 |
13 |
14 | 在使用中有任何问题都可以提 issue, 欢迎加入QQ群:475814382
15 |
16 | --------------
17 | ### SMEmitterView
18 |  
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 | 
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 |
--------------------------------------------------------------------------------