├── .gitignore
├── LICENSE
├── Podfile
├── README.md
├── ScreenShot
└── 1.png
├── TLMessageView.xcodeproj
├── project.pbxproj
└── project.xcworkspace
│ └── contents.xcworkspacedata
├── TLMessageView.xcworkspace
└── contents.xcworkspacedata
└── TLMessageView
├── AppDelegate.h
├── AppDelegate.m
├── Assets.xcassets
├── AppIcon.appiconset
│ └── Contents.json
├── Contents.json
├── actionbar_audio_call_icon.imageset
│ ├── Contents.json
│ └── actionbar_audio_call_icon.png
├── actionbar_camera_icon.imageset
│ ├── Contents.json
│ └── actionbar_camera_icon.png
├── actionbar_file_icon.imageset
│ ├── Contents.json
│ └── actionbar_file_icon.png
├── actionbar_location_icon.imageset
│ ├── Contents.json
│ └── actionbar_location_icon.png
├── actionbar_picture_icon.imageset
│ ├── Contents.json
│ └── actionbar_picture_icon.png
├── actionbar_video_call_icon.imageset
│ ├── Contents.json
│ └── actionbar_video_call_icon.png
├── back.imageset
│ ├── Contents.json
│ └── back1.png
├── chat_from_bg_normal.imageset
│ ├── Contents.json
│ └── chat_from_bg_normal@2x.png
├── chat_from_imagebg_normal.imageset
│ ├── Contents.json
│ └── chat_from_imagebg_normal@2x.png
├── chat_to_bg_normal.imageset
│ ├── Contents.json
│ └── chat_to_bg_normal@2x.png
├── chat_to_imagebg_normal.imageset
│ ├── Contents.json
│ └── chat_to_imagebg_normal@2x.png
├── chatfrom_doctor_icon.imageset
│ ├── Contents.json
│ └── chatfrom_doctor_icon.png
├── emoji_btn_delete.imageset
│ ├── Contents.json
│ └── emoji_btn_delete@2x.png
├── emoji_btn_normal.imageset
│ ├── Contents.json
│ └── emoji_btn_normal.png
├── emoji_hover.imageset
│ ├── Contents.json
│ └── emoji_hover.png
├── from_voice.imageset
│ ├── Contents.json
│ └── from_voice.png
├── from_voice_1.imageset
│ ├── Contents.json
│ └── from_voice_1.png
├── from_voice_2.imageset
│ ├── Contents.json
│ └── from_voice_2.png
├── from_voice_3.imageset
│ ├── Contents.json
│ └── from_voice_3.png
├── icon_+.imageset
│ ├── Contents.json
│ ├── icon_+@2x.png
│ └── icon_+@3x.png
├── icon_duijiang.imageset
│ ├── Contents.json
│ ├── icon_duijiang@2x.png
│ └── icon_duijiang@3x.png
├── icon_kyb.imageset
│ ├── Contents.json
│ ├── icon_kyb@2x.png
│ └── icon_kyb@3x.png
├── icon_xiaolian.imageset
│ ├── Contents.json
│ ├── icon_xiaolian@2x.png
│ └── icon_xiaolian@3x.png
├── news_failinsend.imageset
│ ├── Contents.json
│ ├── news_failinsend@2x.png
│ └── news_failinsend@3x.png
├── photo_preview_selected.imageset
│ ├── Contents.json
│ └── photo_preview_selected.png
├── photo_preview_unselected.imageset
│ ├── Contents.json
│ └── photo_preview_unselected.png
├── photopicker_state_normal.imageset
│ ├── Contents.json
│ └── photopicker_state_normal.png
├── photopicker_state_selected.imageset
│ ├── Contents.json
│ └── photopicker_state_selected.png
├── red_background.imageset
│ ├── Contents.json
│ └── red_background@2x.png
├── return.imageset
│ ├── Contents.json
│ └── return.png
├── selected_full.imageset
│ ├── Contents.json
│ └── selected_full.png
├── to_voice.imageset
│ ├── Contents.json
│ └── to_voice.png
├── to_voice_1.imageset
│ ├── Contents.json
│ └── to_voice_1.png
├── to_voice_2.imageset
│ ├── Contents.json
│ └── to_voice_2.png
├── to_voice_3.imageset
│ ├── Contents.json
│ └── to_voice_3.png
├── unselected_full.imageset
│ ├── Contents.json
│ └── unselected_full.png
├── voice_1.imageset
│ ├── Contents.json
│ └── voice_1.png
├── voice_2.imageset
│ ├── Contents.json
│ └── voice_2.png
├── voice_3.imageset
│ ├── Contents.json
│ └── voice_3.png
├── voice_4.imageset
│ ├── Contents.json
│ └── voice_4.png
├── voice_5.imageset
│ ├── Contents.json
│ └── voice_5.png
├── voice_6.imageset
│ ├── Contents.json
│ └── voice_6.png
├── voice_7.imageset
│ ├── Contents.json
│ └── voice_7.png
└── voice_8.imageset
│ ├── Contents.json
│ └── voice_8.png
├── Base.lproj
└── LaunchScreen.storyboard
├── Chat
├── Cell
│ ├── TLLocationMessageCell.h
│ ├── TLLocationMessageCell.m
│ ├── TLMessageCell.h
│ ├── TLMessageCell.m
│ ├── TLPhotoMessageCell.h
│ ├── TLPhotoMessageCell.m
│ ├── TLPhotoThumbCell.h
│ ├── TLPhotoThumbCell.m
│ ├── TLTextMessageCell.h
│ ├── TLTextMessageCell.m
│ ├── TLVoiceMessageCell.h
│ └── TLVoiceMessageCell.m
├── Controller
│ ├── TLChatViewController.h
│ ├── TLChatViewController.m
│ ├── TLLocationViewController.h
│ ├── TLLocationViewController.m
│ ├── TLPhotoPickerViewController.h
│ ├── TLPhotoPickerViewController.m
│ ├── TLPhotoPreviewViewController.h
│ └── TLPhotoPreviewViewController.m
├── Networking
│ ├── APIDebug.h
│ └── APIDebug.m
├── Vendor
│ ├── LPlaceholderTextView.h
│ ├── LPlaceholderTextView.m
│ ├── NSDate+Utils.h
│ ├── NSDate+Utils.m
│ ├── PHAsset+Extend.h
│ ├── PHAsset+Extend.m
│ ├── TLRCManager.h
│ ├── TLRCManager.m
│ ├── TLRecordVoice.h
│ ├── TLRecordVoice.m
│ ├── TLVoicePlayer.h
│ └── TLVoicePlayer.m
└── View
│ ├── TLButton.h
│ ├── TLButton.m
│ ├── TLChatEmojiBoard.h
│ ├── TLChatEmojiBoard.m
│ ├── TLChatInputView.h
│ ├── TLChatInputView.m
│ ├── TLPhotoBrowser.h
│ ├── TLPhotoBrowser.m
│ ├── TLPluginBoardView.h
│ ├── TLPluginBoardView.m
│ ├── TLRecordVoiceHUD.h
│ └── TLRecordVoiceHUD.m
├── Info.plist
├── TLProjectMacro.h
├── account.plist
└── main.m
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | Pods/
4 | build/
5 | *.pbxuser
6 | !default.pbxuser
7 | *.mode1v3
8 | !default.mode1v3
9 | *.mode2v3
10 | !default.mode2v3
11 | *.perspectivev3
12 | !default.perspectivev3
13 | xcuserdata
14 | *.xccheckout
15 | *.moved-aside
16 | DerivedData
17 | *.hmap
18 | *.ipa
19 | *.xcuserstate
20 | .DS_Store
21 | *.DS_Store
22 | *.xcuserdata
23 | *.dSYMs.zip
24 | *.xcarchive
25 | BiLin/BLVersionHeader.h
26 | *.xcscmblueprint
27 | .flowconfig
28 | tags
29 | npm-debug.log
30 | Podfile.lock
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 GarryGuo
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | 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, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/Podfile:
--------------------------------------------------------------------------------
1 | #use_frameworks!
2 |
3 | platform :ios, '8.0'
4 |
5 | #inhibit_all_warnings!
6 |
7 | target "TLMessageView" do
8 | pod 'Masonry'
9 | pod 'RongCloudIMLib'
10 | pod 'UITableView+FDTemplateLayoutCell', '~> 1.6'
11 | pod 'SDWebImage'
12 | end
13 |
14 | post_install do |installer|
15 | installer.pods_project.targets.each do |target|
16 | target.build_configurations.each do |config|
17 | config.build_settings['ENABLE_BITCODE'] = 'NO'
18 | end
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | TLMessage
2 | ==================
3 |
4 | # TODO
5 | * 群聊
6 | * 文件消息
7 |
8 | # Introduce
9 | * 这是一个IMUI库。对接了目前比较流行的RongCloud实现聊天功能。
10 |
11 | # System Requirement
12 | * iOS 8.0 or later
13 | * Xcode 8.0 or later
14 |
15 | # Character
16 | * 接入融云IMLib,支持Text/Image/Voice/Location消息收发
17 | * 支持发送状态提示,失败重发
18 | * 完全使用AutoLayout布局
19 | * 支持扩展面板扩展
20 | * 支持Emoji输入
21 |
22 | # Depend on Framwork
23 | * Masonry AutoLayout
24 | * FDTemplateLayoutCell Cell自适应高度
25 | * SDWebImage
26 | * RongCloudIMLib 融云通讯能力库
27 |
28 | # Usage
29 | * 克隆项目后,需要使用Cocoapods install
30 |
31 | # Warning!
32 | * 部分图片素材来源于融云IMKit,使用前请替换资源图片
33 | * 目前项目尚处于开发阶段,不建议直接在企业项目中使用。
34 |
35 | # Images
36 |
37 |
--------------------------------------------------------------------------------
/ScreenShot/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/ScreenShot/1.png
--------------------------------------------------------------------------------
/TLMessageView.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/TLMessageView.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/TLMessageView/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // TLMessageView
4 | //
5 | // Created by 郭锐 on 16/8/18.
6 | // Copyright © 2016年 com.garry.message. 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 |
--------------------------------------------------------------------------------
/TLMessageView/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // TLMessageView
4 | //
5 | // Created by 郭锐 on 16/8/18.
6 | // Copyright © 2016年 com.garry.message. All rights reserved.
7 | //
8 |
9 | #import "AppDelegate.h"
10 | #import "TLChatViewController.h"
11 | #import "TLRCManager.h"
12 |
13 | @interface AppDelegate ()
14 |
15 | @end
16 |
17 | @implementation AppDelegate
18 |
19 |
20 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
21 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
22 |
23 | TLChatViewController *vc = [TLChatViewController new];
24 | UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc];
25 |
26 | self.window.rootViewController = nav;
27 | [self.window makeKeyAndVisible];
28 |
29 | [[TLRCManager shareManager] initEnv];
30 |
31 | return YES;
32 | }
33 |
34 | - (void)applicationWillResignActive:(UIApplication *)application {
35 | // 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.
36 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
37 | }
38 |
39 | - (void)applicationDidEnterBackground:(UIApplication *)application {
40 | // 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.
41 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
42 | }
43 |
44 | - (void)applicationWillEnterForeground:(UIApplication *)application {
45 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
46 | }
47 |
48 | - (void)applicationDidBecomeActive:(UIApplication *)application {
49 | // 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.
50 | }
51 |
52 | - (void)applicationWillTerminate:(UIApplication *)application {
53 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
54 | }
55 |
56 | @end
57 |
--------------------------------------------------------------------------------
/TLMessageView/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 | "idiom" : "ipad",
45 | "size" : "20x20",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "20x20",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "29x29",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "29x29",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "40x40",
66 | "scale" : "1x"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "size" : "40x40",
71 | "scale" : "2x"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "size" : "76x76",
76 | "scale" : "1x"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "size" : "76x76",
81 | "scale" : "2x"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "size" : "83.5x83.5",
86 | "scale" : "2x"
87 | }
88 | ],
89 | "info" : {
90 | "version" : 1,
91 | "author" : "xcode"
92 | }
93 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/actionbar_audio_call_icon.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "actionbar_audio_call_icon.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/actionbar_audio_call_icon.imageset/actionbar_audio_call_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/actionbar_audio_call_icon.imageset/actionbar_audio_call_icon.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/actionbar_camera_icon.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "actionbar_camera_icon.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/actionbar_camera_icon.imageset/actionbar_camera_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/actionbar_camera_icon.imageset/actionbar_camera_icon.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/actionbar_file_icon.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "actionbar_file_icon.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/actionbar_file_icon.imageset/actionbar_file_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/actionbar_file_icon.imageset/actionbar_file_icon.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/actionbar_location_icon.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "actionbar_location_icon.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/actionbar_location_icon.imageset/actionbar_location_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/actionbar_location_icon.imageset/actionbar_location_icon.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/actionbar_picture_icon.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "actionbar_picture_icon.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/actionbar_picture_icon.imageset/actionbar_picture_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/actionbar_picture_icon.imageset/actionbar_picture_icon.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/actionbar_video_call_icon.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "actionbar_video_call_icon.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/actionbar_video_call_icon.imageset/actionbar_video_call_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/actionbar_video_call_icon.imageset/actionbar_video_call_icon.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/back.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "back1.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/back.imageset/back1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/back.imageset/back1.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/chat_from_bg_normal.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "chat_from_bg_normal@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 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/chat_from_bg_normal.imageset/chat_from_bg_normal@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/chat_from_bg_normal.imageset/chat_from_bg_normal@2x.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/chat_from_imagebg_normal.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "chat_from_imagebg_normal@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 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/chat_from_imagebg_normal.imageset/chat_from_imagebg_normal@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/chat_from_imagebg_normal.imageset/chat_from_imagebg_normal@2x.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/chat_to_bg_normal.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "chat_to_bg_normal@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 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/chat_to_bg_normal.imageset/chat_to_bg_normal@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/chat_to_bg_normal.imageset/chat_to_bg_normal@2x.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/chat_to_imagebg_normal.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "chat_to_imagebg_normal@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 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/chat_to_imagebg_normal.imageset/chat_to_imagebg_normal@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/chat_to_imagebg_normal.imageset/chat_to_imagebg_normal@2x.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/chatfrom_doctor_icon.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "chatfrom_doctor_icon.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/chatfrom_doctor_icon.imageset/chatfrom_doctor_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/chatfrom_doctor_icon.imageset/chatfrom_doctor_icon.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/emoji_btn_delete.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "emoji_btn_delete@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 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/emoji_btn_delete.imageset/emoji_btn_delete@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/emoji_btn_delete.imageset/emoji_btn_delete@2x.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/emoji_btn_normal.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "emoji_btn_normal.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/emoji_btn_normal.imageset/emoji_btn_normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/emoji_btn_normal.imageset/emoji_btn_normal.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/emoji_hover.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "emoji_hover.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/emoji_hover.imageset/emoji_hover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/emoji_hover.imageset/emoji_hover.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/from_voice.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "from_voice.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/from_voice.imageset/from_voice.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/from_voice.imageset/from_voice.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/from_voice_1.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "from_voice_1.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/from_voice_1.imageset/from_voice_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/from_voice_1.imageset/from_voice_1.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/from_voice_2.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "from_voice_2.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/from_voice_2.imageset/from_voice_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/from_voice_2.imageset/from_voice_2.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/from_voice_3.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "from_voice_3.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/from_voice_3.imageset/from_voice_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/from_voice_3.imageset/from_voice_3.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/icon_+.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "icon_+@2x.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "icon_+@3x.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/icon_+.imageset/icon_+@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/icon_+.imageset/icon_+@2x.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/icon_+.imageset/icon_+@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/icon_+.imageset/icon_+@3x.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/icon_duijiang.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "icon_duijiang@2x.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "icon_duijiang@3x.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/icon_duijiang.imageset/icon_duijiang@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/icon_duijiang.imageset/icon_duijiang@2x.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/icon_duijiang.imageset/icon_duijiang@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/icon_duijiang.imageset/icon_duijiang@3x.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/icon_kyb.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "icon_kyb@2x.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "icon_kyb@3x.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/icon_kyb.imageset/icon_kyb@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/icon_kyb.imageset/icon_kyb@2x.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/icon_kyb.imageset/icon_kyb@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/icon_kyb.imageset/icon_kyb@3x.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/icon_xiaolian.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "icon_xiaolian@2x.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "icon_xiaolian@3x.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/icon_xiaolian.imageset/icon_xiaolian@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/icon_xiaolian.imageset/icon_xiaolian@2x.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/icon_xiaolian.imageset/icon_xiaolian@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/icon_xiaolian.imageset/icon_xiaolian@3x.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/news_failinsend.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "news_failinsend@2x.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "news_failinsend@3x.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/news_failinsend.imageset/news_failinsend@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/news_failinsend.imageset/news_failinsend@2x.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/news_failinsend.imageset/news_failinsend@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/news_failinsend.imageset/news_failinsend@3x.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/photo_preview_selected.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "photo_preview_selected.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/photo_preview_selected.imageset/photo_preview_selected.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/photo_preview_selected.imageset/photo_preview_selected.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/photo_preview_unselected.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "photo_preview_unselected.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/photo_preview_unselected.imageset/photo_preview_unselected.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/photo_preview_unselected.imageset/photo_preview_unselected.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/photopicker_state_normal.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "photopicker_state_normal.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/photopicker_state_normal.imageset/photopicker_state_normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/photopicker_state_normal.imageset/photopicker_state_normal.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/photopicker_state_selected.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "photopicker_state_selected.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/photopicker_state_selected.imageset/photopicker_state_selected.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/photopicker_state_selected.imageset/photopicker_state_selected.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/red_background.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "red_background@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 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/red_background.imageset/red_background@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/red_background.imageset/red_background@2x.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/return.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "return.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/return.imageset/return.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/return.imageset/return.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/selected_full.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "selected_full.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/selected_full.imageset/selected_full.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/selected_full.imageset/selected_full.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/to_voice.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "to_voice.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/to_voice.imageset/to_voice.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/to_voice.imageset/to_voice.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/to_voice_1.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "to_voice_1.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/to_voice_1.imageset/to_voice_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/to_voice_1.imageset/to_voice_1.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/to_voice_2.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "to_voice_2.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/to_voice_2.imageset/to_voice_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/to_voice_2.imageset/to_voice_2.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/to_voice_3.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "to_voice_3.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/to_voice_3.imageset/to_voice_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/to_voice_3.imageset/to_voice_3.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/unselected_full.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "unselected_full.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/unselected_full.imageset/unselected_full.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/unselected_full.imageset/unselected_full.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/voice_1.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "voice_1.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/voice_1.imageset/voice_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/voice_1.imageset/voice_1.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/voice_2.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "voice_2.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/voice_2.imageset/voice_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/voice_2.imageset/voice_2.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/voice_3.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "voice_3.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/voice_3.imageset/voice_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/voice_3.imageset/voice_3.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/voice_4.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "voice_4.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/voice_4.imageset/voice_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/voice_4.imageset/voice_4.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/voice_5.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "voice_5.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/voice_5.imageset/voice_5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/voice_5.imageset/voice_5.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/voice_6.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "voice_6.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/voice_6.imageset/voice_6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/voice_6.imageset/voice_6.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/voice_7.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "voice_7.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/voice_7.imageset/voice_7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/voice_7.imageset/voice_7.png
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/voice_8.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "voice_8.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/TLMessageView/Assets.xcassets/voice_8.imageset/voice_8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timelessg/TLMessage/5d4a530620534adcede7276192d1e79b37d23ead/TLMessageView/Assets.xcassets/voice_8.imageset/voice_8.png
--------------------------------------------------------------------------------
/TLMessageView/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 |
--------------------------------------------------------------------------------
/TLMessageView/Chat/Cell/TLLocationMessageCell.h:
--------------------------------------------------------------------------------
1 | //
2 | // TLLocationMessageCell.h
3 | // TLMessageView
4 | //
5 | // Created by 郭锐 on 16/8/23.
6 | // Copyright © 2016年 com.garry.message. All rights reserved.
7 | //
8 |
9 | #import "TLMessageCell.h"
10 |
11 | @interface TLLocationMessageCell : TLMessageCell
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/TLMessageView/Chat/Cell/TLLocationMessageCell.m:
--------------------------------------------------------------------------------
1 | //
2 | // TLLocationMessageCell.m
3 | // TLMessageView
4 | //
5 | // Created by 郭锐 on 16/8/23.
6 | // Copyright © 2016年 com.garry.message. All rights reserved.
7 | //
8 |
9 | #import "TLLocationMessageCell.h"
10 | #import
11 |
12 | @interface TLLocationMessageCell ()
13 | @property(nonatomic,strong)UIImageView *previewImageView;
14 | @property(nonatomic,strong)UILabel *placeLabel;
15 | @end
16 |
17 | @implementation TLLocationMessageCell
18 | -(instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{
19 | if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
20 | [self.bubbleImageView addSubview:self.previewImageView];
21 | [self.previewImageView mas_makeConstraints:^(MASConstraintMaker *make) {
22 | make.edges.equalTo(self.bubbleImageView).priorityHigh();
23 | }];
24 |
25 | [self.previewImageView addSubview:self.placeLabel];
26 | [self.placeLabel mas_makeConstraints:^(MASConstraintMaker *make) {
27 | make.left.equalTo(self.previewImageView.mas_left).offset(0);
28 | make.right.equalTo(self.previewImageView.mas_right).offset(0);
29 | make.bottom.equalTo(self.previewImageView.mas_bottom).offset(0);
30 | make.height.mas_offset(30);
31 | }];
32 |
33 | UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didClickLocation)];
34 | [self.previewImageView addGestureRecognizer:tap];
35 | }
36 | return self;
37 | }
38 | -(void)didClickLocation{
39 | RCLocationMessage *locMessage = (RCLocationMessage *)self.message.content;
40 |
41 | MKMapItem *currentLocation = [MKMapItem mapItemForCurrentLocation];
42 | MKMapItem *toLocation = [[MKMapItem alloc] initWithPlacemark:[[MKPlacemark alloc] initWithCoordinate:locMessage.location addressDictionary:nil]];
43 | toLocation.name = locMessage.locationName;
44 | NSDictionary *launchOptions = [NSDictionary
45 | dictionaryWithObjects:[NSArray arrayWithObjects:MKLaunchOptionsDirectionsModeDriving, [NSNumber numberWithBool:YES], nil]
46 | forKeys:[NSArray arrayWithObjects:MKLaunchOptionsDirectionsModeKey, MKLaunchOptionsShowsTrafficKey, nil]];
47 | [MKMapItem openMapsWithItems:[NSArray arrayWithObjects:currentLocation, toLocation, nil]
48 | launchOptions:launchOptions];
49 | }
50 | -(void)updateMessage:(RCMessage *)message showDate:(BOOL)showDate{
51 | [super updateMessage:message showDate:showDate];
52 | RCLocationMessage *locMessage = (RCLocationMessage *)message.content;
53 | self.previewImageView.image = locMessage.thumbnailImage;
54 | self.placeLabel.text = locMessage.locationName;
55 |
56 | CGSize size = CGSizeMake(187.5, 150);
57 |
58 | UIImageView *imageViewMask = [[UIImageView alloc] initWithImage:self.bubbleImageView.image];
59 | imageViewMask.frame = CGRectMake(0, 0, size.width, size.height);
60 | self.previewImageView.layer.mask = imageViewMask.layer;
61 |
62 | [self.previewImageView mas_updateConstraints:^(MASConstraintMaker *make) {
63 | make.size.mas_offset(size);
64 | }];
65 | }
66 | -(UIImageView *)previewImageView{
67 | if (!_previewImageView) {
68 | _previewImageView = [[UIImageView alloc] init];
69 | _previewImageView.layer.masksToBounds = YES;
70 | _previewImageView.userInteractionEnabled = YES;
71 | _previewImageView.contentMode = UIViewContentModeScaleAspectFill;
72 | }
73 | return _previewImageView;
74 | }
75 | -(UILabel *)placeLabel{
76 | if (!_placeLabel) {
77 | _placeLabel = [[UILabel alloc] init];
78 | _placeLabel.font = [UIFont systemFontOfSize:14];
79 | _placeLabel.textColor = UIColorFromRGB(0xffffff);
80 | _placeLabel.backgroundColor = UIColorFromRGBA(0x000000, 0.7);
81 | _placeLabel.textAlignment = NSTextAlignmentCenter;
82 | }
83 | return _placeLabel;
84 | }
85 | @end
86 |
--------------------------------------------------------------------------------
/TLMessageView/Chat/Cell/TLMessageCell.h:
--------------------------------------------------------------------------------
1 | //
2 | // TLMessageCell.h
3 | // TLMessageView
4 | //
5 | // Created by 郭锐 on 16/8/18.
6 | // Copyright © 2016年 com.garry.message. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "TLProjectMacro.h"
11 | #import "NSDate+Utils.h"
12 |
13 | #define SMAS(x) [self.constraints addObject:x]
14 |
15 | @interface TLMessageBubble : UIImageView
16 |
17 | @end
18 |
19 | @interface TLMessageCell : UITableViewCell
20 | @property(nonatomic,strong)TLMessageBubble *bubbleImageView;
21 | @property(nonatomic,strong)UIImageView *arrowImageView;
22 | @property(nonatomic,strong)UIView *statusView;
23 | @property(nonatomic,strong)UIActivityIndicatorView *activityIndicator;
24 | @property(nonatomic,strong)UIButton *retryBtn;
25 | @property(nonatomic,strong)UIImageView *avatarImageView;
26 | @property(nonatomic,strong)NSMutableArray *constraints;
27 | @property(nonatomic,strong)UILabel *dateTimeLabel;
28 |
29 | @property(nonatomic,strong)RCMessage *message;
30 |
31 | @property(nonatomic, copy)void(^reSendAction)(RCMessage *message);
32 | @property(nonatomic,strong)void(^clickAvatar)(RCMessageDirection msgDirection);
33 |
34 | -(void)updateDirection:(RCMessageDirection)direction;
35 | -(void)updateMessage:(RCMessage *)message showDate:(BOOL)showDate;
36 | -(void)setMsgStatus:(RCSentStatus)msgStatus;
37 | @end
38 |
--------------------------------------------------------------------------------
/TLMessageView/Chat/Cell/TLMessageCell.m:
--------------------------------------------------------------------------------
1 | //
2 | // TLMessageCell.m
3 | // TLMessageView
4 | //
5 | // Created by 郭锐 on 16/8/18.
6 | // Copyright © 2016年 com.garry.message. All rights reserved.
7 | //
8 |
9 | #import "TLMessageCell.h"
10 |
11 | @interface TLMessageCell ()
12 | @end
13 |
14 | @implementation TLMessageCell
15 | -(instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{
16 | if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
17 | self.backgroundColor = [UIColor clearColor];
18 | self.constraints = [NSMutableArray array];
19 |
20 | self.selectionStyle = UITableViewCellSelectionStyleNone;
21 |
22 | [self.contentView mas_makeConstraints:^(MASConstraintMaker *make) {
23 | make.edges.equalTo(self).priorityHigh();
24 | }];
25 |
26 | [self.contentView addSubview:self.dateTimeLabel];
27 | [self.dateTimeLabel mas_makeConstraints:^(MASConstraintMaker *make) {
28 | make.top.equalTo(self.contentView.mas_top).offset(0);
29 | make.left.equalTo(self.contentView.mas_left).offset(0);
30 | make.right.equalTo(self.contentView.mas_right).offset(0);
31 | make.height.mas_offset(0);
32 | }];
33 |
34 | [self.contentView addSubview:self.avatarImageView];
35 | [self.avatarImageView mas_makeConstraints:^(MASConstraintMaker *make) {
36 | make.top.equalTo(self.dateTimeLabel.mas_bottom).offset(8);
37 | make.size.mas_offset(CGSizeMake(30, 30));
38 | SMAS(make.left.equalTo(self.contentView.mas_left).offset(10));
39 | }];
40 |
41 | [self.contentView addSubview:self.bubbleImageView];
42 | [self.bubbleImageView mas_makeConstraints:^(MASConstraintMaker *make) {
43 | make.top.equalTo(self.avatarImageView.mas_top).offset(0);
44 | make.bottom.equalTo(self.contentView.mas_bottom).offset(-8).priorityHigh();
45 | SMAS(make.left.equalTo(self.avatarImageView.mas_right).offset(7));
46 | SMAS(make.right.lessThanOrEqualTo(self.contentView.mas_right).offset(-78));
47 | }];
48 |
49 | [self.contentView addSubview:self.statusView];
50 | [self.statusView mas_makeConstraints:^(MASConstraintMaker *make) {
51 | make.centerY.equalTo(self.bubbleImageView.mas_centerY).offset(0);
52 | make.size.mas_offset(CGSizeMake(38, 38));
53 | SMAS(make.left.equalTo(self.bubbleImageView.mas_right).offset(0));
54 | }];
55 |
56 | [self.statusView addSubview:self.activityIndicator];
57 | [self.activityIndicator mas_makeConstraints:^(MASConstraintMaker *make) {
58 | make.centerY.equalTo(self.statusView.mas_centerY).offset(0);
59 | make.centerX.equalTo(self.statusView.mas_centerX).offset(0);
60 | make.size.mas_offset(CGSizeMake(10, 10));
61 | }];
62 |
63 | [self.statusView addSubview:self.retryBtn];
64 | [self.retryBtn mas_makeConstraints:^(MASConstraintMaker *make) {
65 | make.centerY.equalTo(self.statusView.mas_centerY).offset(0);
66 | make.centerX.equalTo(self.statusView.mas_centerX).offset(0);
67 | }];
68 |
69 | UITapGestureRecognizer *avatarTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(avatarTapAction)];
70 | [self.avatarImageView addGestureRecognizer:avatarTap];
71 | }
72 | return self;
73 | }
74 | -(void)updateDirection:(RCMessageDirection)direction{
75 | [self removeAllConstraints];
76 |
77 | NSString *bubbleNamed = direction == MessageDirection_RECEIVE ? @"chat_from_bg_normal" : @"chat_to_bg_normal";
78 | self.bubbleImageView.image = [[UIImage imageNamed:bubbleNamed] stretchableImageWithLeftCapWidth:15 topCapHeight:25];
79 |
80 | [self.avatarImageView mas_makeConstraints:^(MASConstraintMaker *make) {
81 | if (direction == MessageDirection_RECEIVE) {
82 | SMAS(make.left.equalTo(self.contentView.mas_left).offset(10));
83 | }else{
84 | SMAS(make.right.equalTo(self.contentView.mas_right).offset(-10));
85 | }
86 | }];
87 |
88 | [self.bubbleImageView mas_makeConstraints:^(MASConstraintMaker *make) {
89 | if (direction == MessageDirection_RECEIVE) {
90 | SMAS(make.left.equalTo(self.avatarImageView.mas_right).offset(7));
91 | SMAS(make.right.lessThanOrEqualTo(self.contentView.mas_right).offset(-78));
92 | }else{
93 | SMAS(make.right.equalTo(self.avatarImageView.mas_left).offset(-7));
94 | SMAS(make.left.greaterThanOrEqualTo(self.contentView.mas_left).offset(78));
95 | }
96 | }];
97 |
98 | [self.statusView mas_makeConstraints:^(MASConstraintMaker *make) {
99 | if (direction == MessageDirection_RECEIVE) {
100 | SMAS(make.left.equalTo(self.bubbleImageView.mas_right).offset(0));
101 | }else{
102 | SMAS(make.right.equalTo(self.bubbleImageView.mas_left).offset(0));
103 | }
104 | }];
105 | }
106 | -(void)updateDate:(long long)date showDate:(BOOL)showDate{
107 | if (!showDate) {
108 | [self.dateTimeLabel mas_updateConstraints:^(MASConstraintMaker *make) {
109 | make.height.mas_offset(0);
110 | }];
111 | self.dateTimeLabel.hidden = YES;
112 | return;
113 | }
114 |
115 | self.dateTimeLabel.hidden = NO;
116 | NSDate *lastDate = [NSDate dateWithTimeIntervalSince1970:date / 1000];
117 | NSString *dateStr; //年月日
118 |
119 | if ([lastDate year] == [[NSDate date] year]) {
120 | NSInteger days = [NSDate daysOffsetBetweenStartDate:lastDate endDate:[NSDate date]];
121 | dateStr = days <= 2 ? [lastDate stringYearMonthDayCompareToday] : [lastDate stringMonthDay];
122 | }else{
123 | dateStr = [lastDate stringYearMonthDay];
124 | }
125 |
126 | [self.dateTimeLabel mas_updateConstraints:^(MASConstraintMaker *make) {
127 | make.height.mas_offset(18);
128 | }];
129 |
130 | self.dateTimeLabel.text = [NSString stringWithFormat:@"%@ %02d:%02d",dateStr,(int)[lastDate hour],(int)[lastDate minute]];
131 | }
132 | -(void)updateMessage:(RCMessage *)message showDate:(BOOL)showDate{
133 | if (!message.content) {
134 | return;
135 | }
136 | [self updateDirection:message.messageDirection];
137 | [self updateDate:message.sentTime showDate:showDate];
138 | self.message = message;
139 | self.msgStatus = message.sentStatus;
140 | }
141 | -(void)setMsgStatus:(RCSentStatus)msgStatus{
142 | if (msgStatus == SentStatus_SENT) {
143 | self.retryBtn.hidden = YES;
144 | self.activityIndicator.hidden = YES;
145 | }
146 | self.retryBtn.hidden = msgStatus != SentStatus_FAILED;
147 | if (msgStatus == SentStatus_SENDING) {
148 | [self.activityIndicator startAnimating];
149 | self.activityIndicator.hidden = NO;
150 | }else{
151 | [self.activityIndicator stopAnimating];
152 | self.activityIndicator.hidden = YES;
153 | }
154 | }
155 | -(void)avatarTapAction {
156 | self.clickAvatar(self.message.messageDirection);
157 | }
158 | -(void)removeAllConstraints{
159 | for (MASConstraint *constraint in self.constraints) {
160 | [constraint uninstall];
161 | }
162 | }
163 | -(void)retryBtnAction{
164 | if (self.reSendAction) self.reSendAction(self.message);
165 | }
166 | -(BOOL)canBecomeFirstResponder{
167 | return YES;
168 | }
169 | -(BOOL)canResignFirstResponder{
170 | return YES;
171 | }
172 |
173 | -(UILabel *)dateTimeLabel{
174 | if (!_dateTimeLabel) {
175 | _dateTimeLabel = [[UILabel alloc] init];
176 | _dateTimeLabel.textColor = UIColorFromRGB(0x999999);
177 | _dateTimeLabel.font = [UIFont systemFontOfSize:9];
178 | _dateTimeLabel.textAlignment = NSTextAlignmentCenter;
179 | }
180 | return _dateTimeLabel;
181 | }
182 | -(UIImageView *)avatarImageView{
183 | if (!_avatarImageView) {
184 | _avatarImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"chatfrom_doctor_icon"]];
185 | _avatarImageView.layer.cornerRadius = 15;
186 | _avatarImageView.layer.masksToBounds = YES;
187 | _avatarImageView.userInteractionEnabled = YES;
188 | }
189 | return _avatarImageView;
190 | }
191 | -(UIView *)statusView{
192 | if (!_statusView) {
193 | _statusView = [[UIView alloc] init];
194 | }
195 | return _statusView;
196 | }
197 | -(UIButton *)retryBtn{
198 | if (!_retryBtn) {
199 | _retryBtn = [UIButton buttonWithType:UIButtonTypeCustom];
200 | [_retryBtn setImage:[UIImage imageNamed:@"news_failinsend"] forState:UIControlStateNormal];
201 | [_retryBtn addTarget:self action:@selector(retryBtnAction) forControlEvents:UIControlEventTouchUpInside];
202 | }
203 | return _retryBtn;
204 | }
205 | -(UIActivityIndicatorView *)activityIndicator{
206 | if (!_activityIndicator) {
207 | _activityIndicator = [[UIActivityIndicatorView alloc] init];
208 | _activityIndicator.hidesWhenStopped = YES;
209 | _activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyleWhite;
210 | _activityIndicator.color = UIColorFromRGB(0x999999);
211 | }
212 | return _activityIndicator;
213 | }
214 | -(TLMessageBubble *)bubbleImageView{
215 | if (!_bubbleImageView) {
216 | _bubbleImageView = [[TLMessageBubble alloc] init];
217 | _bubbleImageView.userInteractionEnabled = YES;
218 | }
219 | return _bubbleImageView;
220 | }
221 | -(UIImageView *)arrowImageView{
222 | if (!_arrowImageView) {
223 | _arrowImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"sharp"]];
224 | }
225 | return _arrowImageView;
226 | }
227 | @end
228 |
229 |
230 |
231 | @implementation TLMessageBubble
232 | -(BOOL)canBecomeFirstResponder{
233 | return YES;
234 | }
235 | @end
236 |
--------------------------------------------------------------------------------
/TLMessageView/Chat/Cell/TLPhotoMessageCell.h:
--------------------------------------------------------------------------------
1 | //
2 | // TLPhotoMessageCell.h
3 | // TLMessageView
4 | //
5 | // Created by 郭锐 on 16/8/18.
6 | // Copyright © 2016年 com.garry.message. All rights reserved.
7 | //
8 |
9 | #import "TLMessageCell.h"
10 |
11 | @interface TLPhotoMessageCell : TLMessageCell
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/TLMessageView/Chat/Cell/TLPhotoMessageCell.m:
--------------------------------------------------------------------------------
1 | //
2 | // TLPhotoMessageCell.m
3 | // TLMessageView
4 | //
5 | // Created by 郭锐 on 16/8/18.
6 | // Copyright © 2016年 com.garry.message. All rights reserved.
7 | //
8 |
9 | #import "TLPhotoMessageCell.h"
10 | #import
11 | #import "TLPhotoBrowser.h"
12 |
13 | static CGFloat fitImgWidth = 150;
14 | static CGFloat fitImgHeight = 150;
15 |
16 |
17 | @interface TLPhotoMessageCell ()
18 | @property(nonatomic,strong)UIImageView *photoImageView;
19 | @property(nonatomic,strong)UIImageView *imageViewMask;
20 | @end
21 |
22 | @implementation TLPhotoMessageCell
23 | -(instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{
24 | if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
25 | [self.bubbleImageView addSubview:self.photoImageView];
26 | [self.photoImageView mas_makeConstraints:^(MASConstraintMaker *make) {
27 | make.edges.equalTo(self.bubbleImageView);
28 | }];
29 |
30 | UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didClickImage)];
31 | [self.photoImageView addGestureRecognizer:tap];
32 |
33 | }
34 | return self;
35 | }
36 | -(void)didClickImage{
37 | RCImageMessage *imgMessage = (RCImageMessage *)self.message.content;
38 |
39 | if (imgMessage.originalImage) {
40 | [TLPhotoBrowser showOriginalImage:imgMessage.originalImage];
41 | }else{
42 | [self.photoImageView sd_setImageWithURL:[NSURL URLWithString:imgMessage.imageUrl] completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
43 | [TLPhotoBrowser showOriginalImage:image];
44 | }];
45 | }
46 | }
47 |
48 | -(void)updateMessage:(RCMessage *)message showDate:(BOOL)showDate{
49 | [super updateMessage:message showDate:showDate];
50 | RCImageMessage *imgMessage = (RCImageMessage *)message.content;
51 | self.photoImageView.image = imgMessage.thumbnailImage;
52 |
53 | CGSize imgSize = imgMessage.thumbnailImage.size;
54 | CGFloat scale = imgSize.width / imgSize.height;
55 |
56 | CGFloat newWidth = MAX(MIN(imgSize.width, fitImgWidth), fitImgWidth);
57 | CGFloat newHeight = MAX(MIN(imgSize.height, fitImgHeight), fitImgHeight);
58 |
59 | CGSize newSize = scale > 1 ? CGSizeMake(newWidth, newWidth / scale) : CGSizeMake(newHeight * scale, newHeight);
60 |
61 | self.imageViewMask.image = self.bubbleImageView.image;
62 | self.imageViewMask.frame = CGRectMake(0, 0, newSize.width, newSize.height);
63 | self.photoImageView.layer.mask = self.imageViewMask.layer;
64 |
65 | [self.photoImageView mas_updateConstraints:^(MASConstraintMaker *make) {
66 | make.size.mas_offset(newSize);
67 | }];
68 | }
69 |
70 | -(UIImageView *)imageViewMask{
71 | if (!_imageViewMask) {
72 | _imageViewMask = [[UIImageView alloc] init];
73 | }
74 | return _imageViewMask;
75 | }
76 | -(UIImageView *)photoImageView{
77 | if (!_photoImageView) {
78 | _photoImageView = [[UIImageView alloc] init];
79 | _photoImageView.layer.masksToBounds = YES;
80 | _photoImageView.userInteractionEnabled = YES;
81 | _photoImageView.contentMode = UIViewContentModeScaleAspectFill;
82 | [_photoImageView setContentHuggingPriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisHorizontal];
83 | }
84 | return _photoImageView;
85 | }
86 | @end
87 |
--------------------------------------------------------------------------------
/TLMessageView/Chat/Cell/TLPhotoThumbCell.h:
--------------------------------------------------------------------------------
1 | //
2 | // TLPhotoThumbCell.h
3 | // TLMessageView
4 | //
5 | // Created by 郭锐 on 16/8/25.
6 | // Copyright © 2016年 com.garry.message. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | @interface TLPhotoThumbCell : UICollectionViewCell
13 | @property(nonatomic,strong)PHAsset *item;
14 | @property(nonatomic,copy)BOOL (^selectBlock)(PHAsset *x);
15 | @end
16 |
--------------------------------------------------------------------------------
/TLMessageView/Chat/Cell/TLPhotoThumbCell.m:
--------------------------------------------------------------------------------
1 | //
2 | // TLPhotoThumbCell.m
3 | // TLMessageView
4 | //
5 | // Created by 郭锐 on 16/8/25.
6 | // Copyright © 2016年 com.garry.message. All rights reserved.
7 | //
8 |
9 | #import "TLPhotoThumbCell.h"
10 | #import "TLProjectMacro.h"
11 | #import "TLButton.h"
12 | #import "PHAsset+Extend.h"
13 |
14 | @interface TLPhotoThumbCell ()
15 | @property(nonatomic,strong)UIImageView *imageView;
16 | @property(nonatomic,strong)TLButton *selectBtn;
17 | @property(nonatomic,strong)PHImageRequestOptions *options;
18 | @end
19 |
20 | @implementation TLPhotoThumbCell
21 | -(instancetype)initWithFrame:(CGRect)frame{
22 | if (self = [super initWithFrame:frame]) {
23 | [self setupView];
24 | }
25 | return self;
26 | }
27 | -(void)setupView{
28 | [self.contentView addSubview:self.imageView];
29 | [self.imageView mas_makeConstraints:^(MASConstraintMaker *make) {
30 | make.edges.mas_equalTo(UIEdgeInsetsMake(0, 0, 0, 0));
31 | }];
32 |
33 | [self.contentView addSubview:self.selectBtn];
34 | [self.selectBtn mas_makeConstraints:^(MASConstraintMaker *make) {
35 | make.top.equalTo(self.contentView.mas_top).offset(5);
36 | make.right.equalTo(self.contentView.mas_right).offset(-5);
37 | }];
38 | }
39 | -(void)setItem:(PHAsset *)item{
40 | _item = item;
41 | self.selectBtn.selected = item.selected;
42 | [[PHImageManager defaultManager] requestImageForAsset:item targetSize:CGSizeMake(UI_SCREEN_HEIGHT / 4, UI_SCREEN_HEIGHT / 4) contentMode:PHImageContentModeDefault options:self.options resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
43 | self.imageView.image = result;
44 | }];
45 | }
46 | -(void)selectAction:(UIButton *)sender{
47 | self.item.selected = !sender.selected;
48 | if (self.selectBlock && self.selectBlock(self.item)){
49 | sender.selected = !sender.selected;
50 | }
51 | }
52 | -(UIImageView *)imageView{
53 | if (!_imageView) {
54 | _imageView = [[UIImageView alloc] init];
55 | _imageView.contentMode = UIViewContentModeScaleAspectFill;
56 | _imageView.clipsToBounds = YES;
57 | }
58 | return _imageView;
59 | }
60 | -(TLButton *)selectBtn{
61 | if (!_selectBtn) {
62 | _selectBtn = [TLButton buttonWithType:UIButtonTypeCustom];
63 | [_selectBtn setImage:[UIImage imageNamed:@"photopicker_state_normal"] forState:UIControlStateNormal];
64 | [_selectBtn setImage:[UIImage imageNamed:@"photopicker_state_selected"] forState:UIControlStateSelected];
65 | [_selectBtn addTarget:self action:@selector(selectAction:) forControlEvents:UIControlEventTouchUpInside];
66 | }
67 | return _selectBtn;
68 | }
69 | -(PHImageRequestOptions *)options{
70 | if (!_options) {
71 | _options = [[PHImageRequestOptions alloc] init];
72 | }
73 | return _options;
74 | }
75 | @end
76 |
--------------------------------------------------------------------------------
/TLMessageView/Chat/Cell/TLTextMessageCell.h:
--------------------------------------------------------------------------------
1 | //
2 | // TLTextMessageCell.h
3 | // TLMessageView
4 | //
5 | // Created by 郭锐 on 16/8/18.
6 | // Copyright © 2016年 com.garry.message. All rights reserved.
7 | //
8 |
9 | #import "TLMessageCell.h"
10 |
11 | @interface TLTextMessageCell : TLMessageCell
12 | @end
13 |
--------------------------------------------------------------------------------
/TLMessageView/Chat/Cell/TLTextMessageCell.m:
--------------------------------------------------------------------------------
1 | //
2 | // TLTextMessageCell.m
3 | // TLMessageView
4 | //
5 | // Created by 郭锐 on 16/8/18.
6 | // Copyright © 2016年 com.garry.message. All rights reserved.
7 | //
8 |
9 | #import "TLTextMessageCell.h"
10 |
11 | @interface TLTextMessageCell ()
12 | @property(nonatomic,strong)UILabel *messageLabel;
13 | @end
14 |
15 | @implementation TLTextMessageCell
16 | -(instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{
17 | if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
18 | [self.bubbleImageView addSubview:self.messageLabel];
19 | [self.messageLabel mas_makeConstraints:^(MASConstraintMaker *make) {
20 | make.edges.mas_offset(UIEdgeInsetsMake(10, 15, 10, 15));
21 | }];
22 | }
23 | return self;
24 | }
25 | -(void)updateDirection:(RCMessageDirection)direction{
26 | [super updateDirection:direction];
27 | [self.messageLabel mas_updateConstraints:^(MASConstraintMaker *make) {
28 | if (direction == MessageDirection_RECEIVE) {
29 | make.edges.mas_offset(UIEdgeInsetsMake(10, 15, 10, 10));
30 | }else{
31 | make.edges.mas_offset(UIEdgeInsetsMake(10, 10, 10, 15));
32 | }
33 | }];
34 | }
35 | -(void)updateMessage:(RCMessage *)message showDate:(BOOL)showDate{
36 | [super updateMessage:message showDate:showDate];
37 |
38 | RCTextMessage* textMessage = (RCTextMessage*)message.content;
39 | self.messageLabel.text = textMessage.content;
40 | }
41 | -(UILabel *)messageLabel{
42 | if (!_messageLabel) {
43 | _messageLabel = [[UILabel alloc] init];
44 | _messageLabel.font = [UIFont systemFontOfSize:15];
45 | _messageLabel.textColor = UIColorFromRGB(0x333333);
46 | _messageLabel.numberOfLines = 0;
47 | }
48 | return _messageLabel;
49 | }
50 | @end
51 |
--------------------------------------------------------------------------------
/TLMessageView/Chat/Cell/TLVoiceMessageCell.h:
--------------------------------------------------------------------------------
1 | //
2 | // TLVoiceMessageCell.h
3 | // TLMessageView
4 | //
5 | // Created by 郭锐 on 16/8/18.
6 | // Copyright © 2016年 com.garry.message. All rights reserved.
7 | //
8 |
9 | #import "TLMessageCell.h"
10 |
11 | @interface TLVoiceMessageCell : TLMessageCell
12 | @property(nonatomic,assign)BOOL isPlaying;
13 | @end
14 |
--------------------------------------------------------------------------------
/TLMessageView/Chat/Cell/TLVoiceMessageCell.m:
--------------------------------------------------------------------------------
1 | //
2 | // TLVoiceMessageCell.m
3 | // TLMessageView
4 | //
5 | // Created by 郭锐 on 16/8/18.
6 | // Copyright © 2016年 com.garry.message. All rights reserved.
7 | //
8 |
9 | #import "TLVoiceMessageCell.h"
10 | #import "TLVoicePlayer.h"
11 |
12 | @interface TLVoiceMessageCell ()
13 | @property(nonatomic,strong)UIImageView *voicePlayingImageView;
14 | @property(nonatomic,strong)UILabel *voiceDurationLabel;
15 | @end
16 |
17 | @implementation TLVoiceMessageCell
18 | -(instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{
19 | if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
20 | [self.bubbleImageView mas_makeConstraints:^(MASConstraintMaker *make) {
21 | make.size.mas_offset(CGSizeMake(80, 40));
22 | }];
23 |
24 | [self.bubbleImageView addSubview:self.voicePlayingImageView];
25 | [self.voicePlayingImageView mas_makeConstraints:^(MASConstraintMaker *make) {
26 | SMAS(make.left.equalTo(self.bubbleImageView.mas_left).offset(10));
27 | make.centerY.equalTo(self.bubbleImageView.mas_centerY).offset(0);
28 | make.size.mas_offset(CGSizeMake(20, 20));
29 | }];
30 |
31 | [self.bubbleImageView addSubview:self.voiceDurationLabel];
32 | [self.voiceDurationLabel mas_makeConstraints:^(MASConstraintMaker *make) {
33 | SMAS(make.right.equalTo(self.bubbleImageView.mas_right).offset(5));
34 | make.centerY.equalTo(self.bubbleImageView.mas_centerY).offset(0);
35 | }];
36 |
37 | self.bubbleImageView.userInteractionEnabled = YES;
38 | UITapGestureRecognizer *playTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(playRecord)];
39 | [self.bubbleImageView addGestureRecognizer:playTap];
40 | }
41 | return self;
42 | }
43 | -(void)playRecord{
44 | void (^didFinishPlay)(void) = ^(){
45 | [self.voicePlayingImageView stopAnimating];
46 | self.isPlaying = NO;
47 | [[TLVoicePlayer sharePlayer] endPlay];
48 | };
49 |
50 | if (!self.isPlaying) {
51 | [self.voicePlayingImageView startAnimating];
52 | self.isPlaying = YES;
53 | [[TLVoicePlayer sharePlayer] playVoiceWithData:((RCVoiceMessage *)self.message.content).wavAudioData didFinish:didFinishPlay];
54 | }else{
55 | didFinishPlay();
56 | }
57 | }
58 | -(void)updateDirection:(RCMessageDirection)direction{
59 | [super updateDirection:direction];
60 |
61 | [self.voicePlayingImageView mas_makeConstraints:^(MASConstraintMaker *make) {
62 | if (direction == MessageDirection_RECEIVE) {
63 | SMAS(make.left.equalTo(self.bubbleImageView.mas_left).offset(10));
64 | }else{
65 | SMAS(make.right.equalTo(self.bubbleImageView.mas_right).offset(-10));
66 | }
67 | }];
68 |
69 | [self.voiceDurationLabel mas_makeConstraints:^(MASConstraintMaker *make) {
70 | if (direction == MessageDirection_RECEIVE) {
71 | SMAS(make.right.equalTo(self.bubbleImageView.mas_right).offset(-10));
72 | }else{
73 | SMAS(make.left.equalTo(self.bubbleImageView.mas_left).offset(10));
74 | }
75 | }];
76 |
77 | self.voiceDurationLabel.textColor = direction == MessageDirection_RECEIVE ? UIColorFromRGB(0xa2a2a2) : UIColorFromRGB(0x4182b5);
78 | }
79 | -(void)updateMessage:(RCMessage *)message showDate:(BOOL)showDate{
80 | [super updateMessage:message showDate:showDate];
81 |
82 | self.voicePlayingImageView.animationImages = message.messageDirection == MessageDirection_RECEIVE ? @[[UIImage imageNamed:@"from_voice_1"],[UIImage imageNamed:@"from_voice_2"],[UIImage imageNamed:@"from_voice_3"]] : @[[UIImage imageNamed:@"to_voice_1"],[UIImage imageNamed:@"to_voice_2"],[UIImage imageNamed:@"to_voice_3"]];
83 |
84 | self.voicePlayingImageView.image = message.messageDirection == MessageDirection_RECEIVE ? [UIImage imageNamed:@"from_voice"] : [UIImage imageNamed:@"to_voice"];
85 |
86 | self.voiceDurationLabel.text = [NSString stringWithFormat:@"%ld''",((RCVoiceMessage *)message.content).duration];
87 | }
88 | -(UIImageView *)voicePlayingImageView{
89 | if (!_voicePlayingImageView) {
90 | _voicePlayingImageView = [[UIImageView alloc] init];
91 | _voicePlayingImageView.userInteractionEnabled = YES;
92 | _voicePlayingImageView.animationDuration = 1;
93 | _voicePlayingImageView.animationRepeatCount = 0;
94 | }
95 | return _voicePlayingImageView;
96 | }
97 | -(UILabel *)voiceDurationLabel{
98 | if (!_voiceDurationLabel) {
99 | _voiceDurationLabel = [[UILabel alloc] init];
100 | _voiceDurationLabel.textColor = UIColorFromRGB(0x4182b5);
101 | _voiceDurationLabel.font = [UIFont systemFontOfSize:15];
102 | }
103 | return _voiceDurationLabel;
104 | }
105 | @end
106 |
--------------------------------------------------------------------------------
/TLMessageView/Chat/Controller/TLChatViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // TLChatViewController.h
3 | // TLMessageView
4 | //
5 | // Created by 郭锐 on 16/8/18.
6 | // Copyright © 2016年 com.garry.message. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface TLChatViewController : UIViewController
12 | @property(nonatomic,strong)UITableView *chatTableView;
13 | -(void)scrollToBottom;
14 | @end
15 |
--------------------------------------------------------------------------------
/TLMessageView/Chat/Controller/TLChatViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // TLChatViewController.m
3 | // TLMessageView
4 | //
5 | // Created by 郭锐 on 16/8/18.
6 | // Copyright © 2016年 com.garry.message. All rights reserved.
7 | //
8 |
9 | #import "TLChatViewController.h"
10 | #import "TLProjectMacro.h"
11 | #import "TLTextMessageCell.h"
12 | #import "TLPhotoMessageCell.h"
13 | #import "TLVoiceMessageCell.h"
14 | #import "TLLocationMessageCell.h"
15 | #import "TLChatInputView.h"
16 | #import "TLRCManager.h"
17 | #import "APIDebug.h"
18 |
19 | @interface TLChatViewController ()
20 |
21 |
24 |
25 | @property(nonatomic,strong)TLChatInputView *inputView;
26 | @property(nonatomic,strong)NSMutableArray *messages;
27 | @end
28 |
29 | @implementation TLChatViewController
30 | - (void)viewDidLoad {
31 | [super viewDidLoad];
32 |
33 | [[TLRCManager shareManager] connectWithToken:@"jqAhQVLtdDAJsN+nYuGMg+SZkEN9cVfzRVKTg8tY0IOhDwJ1Cn3qxdbPXWTk4XVCfZLmci9yJ2QWgjEOhUtgXg=="];
34 | [TLRCManager shareManager].delegate = self;
35 |
36 | self.view.backgroundColor = [UIColor whiteColor];
37 | self.navigationController.navigationBar.barTintColor = [UIColor whiteColor];
38 |
39 | [self.view addSubview:self.inputView];
40 | [self.inputView mas_makeConstraints:^(MASConstraintMaker *make) {
41 | make.left.equalTo(self.view.mas_left).offset(0);
42 | make.right.equalTo(self.view.mas_right).offset(0);
43 | make.bottom.equalTo(self.view.mas_bottom).offset(0);
44 | make.height.mas_offset(@45);
45 | }];
46 |
47 | [self.view addSubview:self.chatTableView];
48 | [self.chatTableView mas_makeConstraints:^(MASConstraintMaker *make) {
49 | make.left.equalTo(self.view.mas_left).offset(0);
50 | make.right.equalTo(self.view.mas_right).offset(0);
51 | make.top.equalTo(self.mas_topLayoutGuideBottom).offset(0);
52 | make.bottom.equalTo(self.inputView.mas_top).offset(0);
53 | }];
54 |
55 | weakifySelf;
56 | //发送消息回调
57 | self.inputView.sendMsgAction = ^(RCMessageContent *x){
58 | strongifySelf;
59 | [self sendMessage:x];
60 | };
61 |
62 | //navbar上的一个APIDebug调试,可注释掉
63 | [APIDebug configWithVC:self];
64 | }
65 |
66 | #pragma - mark tableviewDelegate
67 |
68 | -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
69 | RCMessage *msg = self.messages[indexPath.row];
70 | RCMessage *lastMsg = [self lasetMsgWithIndex:indexPath.row];
71 |
72 | TLMessageCell *cell = [tableView dequeueReusableCellWithIdentifier:[self cellIdentifierWithMsg:msg]];
73 |
74 | weakifySelf;
75 | cell.reSendAction = ^(RCMessage *msg){
76 | strongifySelf;
77 | msg.sentStatus = SentStatus_SENDING;
78 | [self retrySendMessage:msg];
79 | };
80 |
81 | cell.clickAvatar = ^(RCMessageDirection msgDirection){
82 |
83 | };
84 |
85 | [cell updateMessage:msg showDate:(msg.sentTime - lastMsg.sentTime > 60 * 5 * 1000)];
86 |
87 | return cell;
88 | }
89 | -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
90 | return self.messages.count;
91 | }
92 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
93 | RCMessage *msg = self.messages[indexPath.row];
94 | RCMessage *lastMsg = [self lasetMsgWithIndex:indexPath.row];
95 | CGFloat height = [tableView fd_heightForCellWithIdentifier:[self cellIdentifierWithMsg:msg] cacheByIndexPath:indexPath configuration:^(TLMessageCell *cell) {
96 | [cell updateMessage:msg showDate:(msg.sentTime - lastMsg.sentTime > 60 * 5 * 1000)];
97 | }];
98 | return height;
99 | }
100 |
101 | #pragma - mark RCManagerDelegate
102 |
103 | -(void)rcManagerReceiveMsg:(RCMessage *)msg{
104 | msg.sentStatus = SentStatus_RECEIVED;
105 | dispatch_async(dispatch_get_main_queue(), ^{
106 | [self insertMessage:msg];
107 | });
108 | }
109 |
110 | #pragma - mark private
111 | -(void)sendMessage:(id)message{
112 | RCMessage *msg = [[RCMessage alloc] initWithType:ConversationType_PRIVATE targetId:@"111" direction:MessageDirection_SEND messageId:0 content:message];
113 |
114 | msg.sentStatus = SentStatus_SENDING;
115 | msg.receivedStatus = ReceivedStatus_READ;
116 | [self insertMessage:msg];
117 | [self retrySendMessage:msg];
118 | }
119 | - (void)insertMessage:(RCMessage *)message{
120 | if (!message.content) {
121 | return;
122 | }
123 | [self.messages addObject:message];
124 |
125 | [self.chatTableView insertRowsAtIndexPaths:@[[self lastMessageIndexPath]] withRowAnimation:UITableViewRowAnimationFade];
126 |
127 | [self.chatTableView scrollToRowAtIndexPath:[self lastMessageIndexPath] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
128 | }
129 | - (void)retrySendMessage:(RCMessage *)message{
130 | [[TLRCManager shareManager] sendMessage:message successBlock:^(RCMessage *x){
131 | x.sentStatus = SentStatus_SENT;
132 | [self updateCellStatusWithMsg:x];
133 | } failedBlock:^(RCMessage *x){
134 | x.sentStatus = SentStatus_FAILED;
135 | [self updateCellStatusWithMsg:x];
136 | }];
137 | }
138 | -(void)updateCellStatusWithMsg:(RCMessage *)msg{
139 | NSInteger index = [self.messages indexOfObject:msg];
140 | TLMessageCell *cell = [self.chatTableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0]];
141 | [cell setMsgStatus:msg.sentStatus];
142 | }
143 | -(void)scrollToBottom{
144 | if (self.messages.count) {
145 | [self.chatTableView scrollToRowAtIndexPath:[self lastMessageIndexPath] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
146 | }
147 | }
148 | -(NSIndexPath *)lastMessageIndexPath{
149 | return [NSIndexPath indexPathForItem:self.messages.count - 1 inSection:0];
150 | }
151 | -(RCMessage *)lasetMsgWithIndex:(NSInteger)index{
152 | return index > 0 ? self.messages[index - 1] : nil;
153 | }
154 | -(NSString *)cellIdentifierWithMsg:(RCMessage *)msg{
155 | NSDictionary *dic = @{@"RCTextMessage":@"textcell",
156 | @"RCVoiceMessage":@"voicecell",
157 | @"RCImageMessage":@"photocell",
158 | @"RCLocationMessage":@"locationcell"};
159 | return dic[NSStringFromClass([msg.content class])];
160 | }
161 | #pragma - mark getter
162 | -(NSMutableArray *)messages{
163 | if (!_messages) {
164 | _messages = [NSMutableArray array];
165 | }
166 | return _messages;
167 | }
168 | -(UITableView *)chatTableView{
169 | if (!_chatTableView) {
170 | _chatTableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
171 | _chatTableView.delegate = self;
172 | _chatTableView.dataSource = self;
173 | _chatTableView.backgroundColor = UIColorFromRGB(0xebebeb);
174 | _chatTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
175 | _chatTableView.backgroundColor = UIColorFromRGB(0xf8f8f8);
176 | _chatTableView.separatorColor = UIColorFromRGB(0xeeeeee);
177 | [_chatTableView registerClass:[TLTextMessageCell class] forCellReuseIdentifier:@"textcell"];
178 | [_chatTableView registerClass:[TLPhotoMessageCell class] forCellReuseIdentifier:@"photocell"];
179 | [_chatTableView registerClass:[TLVoiceMessageCell class] forCellReuseIdentifier:@"voicecell"];
180 | [_chatTableView registerClass:[TLLocationMessageCell class] forCellReuseIdentifier:@"locationcell"];
181 | }
182 | return _chatTableView;
183 | }
184 | -(TLChatInputView *)inputView{
185 | if (!_inputView) {
186 | _inputView = [[TLChatInputView alloc] initWithChatVc:self];
187 | }
188 | return _inputView;
189 | }
190 | - (void)didReceiveMemoryWarning {
191 | [super didReceiveMemoryWarning];
192 | // Dispose of any resources that can be recreated.
193 | }
194 | @end
195 |
--------------------------------------------------------------------------------
/TLMessageView/Chat/Controller/TLLocationViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // TLLocationViewController.h
3 | // TLMessageView
4 | //
5 | // Created by 郭锐 on 16/8/23.
6 | // Copyright © 2016年 com.garry.message. All rights reserved.
7 | //
8 |
9 | #import "TLProjectMacro.h"
10 |
11 | @protocol TLLocationViewControllerDelegate
12 | -(void)locationViewControllerSendMsg:(RCLocationMessage *)msg;
13 | @end
14 |
15 | @interface TLLocationViewController : UIViewController
16 | -(instancetype)initWithDelegate:(id )delegate;
17 | @property(nonatomic,assign)id delegate;
18 | @end
19 |
--------------------------------------------------------------------------------
/TLMessageView/Chat/Controller/TLLocationViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // TLLocationViewController.m
3 | // TLMessageView
4 | //
5 | // Created by 郭锐 on 16/8/23.
6 | // Copyright © 2016年 com.garry.message. All rights reserved.
7 | //
8 |
9 | #import "TLLocationViewController.h"
10 | #import
11 |
12 | @interface TLLocationViewController ()
13 |
17 | @property(nonatomic,strong)MKMapView *mapView;
18 | @property(nonatomic,strong)CLLocationManager *locationManager;
19 | @property(nonatomic,strong)MKUserLocation *userLocation;
20 | @property(nonatomic,strong)MKPointAnnotation *annotation;
21 | @property(nonatomic,strong)UITableView *listTableView;
22 | @property(nonatomic,strong)NSMutableArray *dataSource;
23 | @end
24 |
25 | @implementation TLLocationViewController
26 | -(instancetype)initWithDelegate:(id)delegate{
27 | if (self = [super init]) {
28 | self.delegate = delegate;
29 | }
30 | return self;
31 | }
32 | - (void)viewDidLoad {
33 | [super viewDidLoad];
34 |
35 | [self configNavBar];
36 |
37 | [self.view addSubview:self.mapView];
38 | [self.mapView mas_makeConstraints:^(MASConstraintMaker *make) {
39 | make.top.equalTo(self.mas_topLayoutGuideBottom).offset(0);
40 | make.left.equalTo(self.view.mas_left).offset(0);
41 | make.right.equalTo(self.view.mas_right).offset(0);
42 | make.height.mas_offset(@300);
43 | }];
44 |
45 | [self.view addSubview:self.listTableView];
46 | [self.listTableView mas_makeConstraints:^(MASConstraintMaker *make) {
47 | make.left.equalTo(self.view.mas_left).offset(0);
48 | make.right.equalTo(self.view.mas_right).offset(0);
49 | make.top.equalTo(self.mapView.mas_bottom).offset(0);
50 | make.bottom.equalTo(self.view.mas_bottom).offset(0);
51 | }];
52 |
53 | [self.locationManager requestWhenInUseAuthorization];
54 |
55 | if([CLLocationManager locationServicesEnabled]){
56 | [self.locationManager startUpdatingLocation];
57 | }
58 | }
59 | - (void)configNavBar {
60 | self.navigationController.navigationBar.barTintColor = [UIColor whiteColor];
61 |
62 | UIBarButtonItem *backItem = [[UIBarButtonItem alloc] initWithTitle:@"返回" style:UIBarButtonItemStylePlain target:self action:@selector(dismiss)];
63 | backItem.tintColor = [UIColor grayColor];
64 | self.navigationItem.leftBarButtonItem = backItem;
65 |
66 | UIBarButtonItem *sendItem = [[UIBarButtonItem alloc] initWithTitle:@"发送" style:UIBarButtonItemStylePlain target:self action:@selector(sendLocationMsg)];
67 | sendItem.tintColor = [UIColor grayColor];
68 | self.navigationItem.rightBarButtonItem = sendItem;
69 | self.navigationController.navigationBar.tintColor = [UIColor whiteColor];
70 | }
71 | - (void)dismiss {
72 | [self.navigationController popViewControllerAnimated:YES];
73 | }
74 | - (void)sendLocationMsg{
75 | if (!self.userLocation) {
76 | return;
77 | }
78 |
79 | [self.navigationController popViewControllerAnimated:YES];
80 |
81 | UIImage *image = [self snapshot:self.mapView];
82 | RCLocationMessage *msg = [RCLocationMessage messageWithLocationImage:image location:self.userLocation.location.coordinate locationName:self.userLocation.subtitle];
83 | if ([self.delegate respondsToSelector:@selector(locationViewControllerSendMsg:)]) {
84 | [self.delegate locationViewControllerSendMsg:msg];
85 | }
86 | }
87 | - (UIImage *)snapshot:(UIView *)view{
88 | UIGraphicsBeginImageContextWithOptions(view.bounds.size, YES, 0);
89 | [view drawViewHierarchyInRect:view.bounds afterScreenUpdates:YES];
90 | UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
91 | UIGraphicsEndImageContext();
92 |
93 | return image;
94 | }
95 | #pragma - mark tableviewDelegate
96 | -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
97 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
98 | CLPlacemark *item = self.dataSource[indexPath.row];
99 | cell.textLabel.text = item.name;
100 | return cell;
101 | }
102 | -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
103 | return self.dataSource.count;
104 | }
105 | -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
106 | [tableView deselectRowAtIndexPath:indexPath animated:YES];
107 | [self sendLocationMsg];
108 | }
109 | #pragma mark – CLLocationManagerDelegate
110 | - (void)locationManager:(CLLocationManager *)manager
111 | didUpdateToLocation:(CLLocation *)newLocation
112 | fromLocation:(CLLocation *)oldLocation{
113 |
114 | MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(newLocation.coordinate, 2000 ,2000);
115 |
116 | if (!oldLocation) {
117 | MKCoordinateRegion adjustedRegion = [self.mapView regionThatFits:region];
118 | [self.mapView setRegion:adjustedRegion animated:YES];
119 |
120 | }
121 | }
122 | - (void)locationManager:(CLLocationManager *)manager
123 | didFailWithError:(NSError *)error{
124 |
125 | }
126 | #pragma - mark mapviewDelegate
127 | - (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation {
128 | CLGeocoder *geocoder = [[CLGeocoder alloc] init];
129 | [geocoder reverseGeocodeLocation:userLocation.location completionHandler:^(NSArray *placemarks, NSError *error) {
130 | if (placemarks.count == 0 || error) {
131 | NSLog(@"找不到该位置");
132 | return;
133 | }
134 | // 当前地标
135 | CLPlacemark *pm = [placemarks firstObject];
136 | // 区域名称
137 | userLocation.title = pm.locality;
138 | // 详细名称
139 | userLocation.subtitle = pm.name;
140 |
141 | self.userLocation = userLocation;
142 |
143 | [self.dataSource removeAllObjects];
144 | [self.dataSource addObject:pm];
145 | [self.listTableView reloadData];
146 | }];
147 | }
148 | -(MKMapView *)mapView{
149 | if (!_mapView) {
150 | _mapView = [[MKMapView alloc] init];
151 | _mapView.mapType = MKMapTypeStandard;
152 | _mapView.showsUserLocation = YES;
153 | _mapView.delegate = self;
154 | }
155 | return _mapView;
156 | }
157 | -(CLLocationManager *)locationManager{
158 | if (!_locationManager) {
159 | _locationManager = [[CLLocationManager alloc] init];
160 | _locationManager.distanceFilter = kCLDistanceFilterNone;//距离筛选器,单位米(移动多少米才回调更新)
161 | _locationManager.desiredAccuracy = kCLLocationAccuracyBest; //精确度
162 | _locationManager.delegate = self;
163 | }
164 | return _locationManager;
165 | }
166 | -(MKPointAnnotation *)annotation{
167 | if (!_annotation) {
168 | _annotation = [[MKPointAnnotation alloc] init];
169 | }
170 | return _annotation;
171 | }
172 | -(UITableView *)listTableView{
173 | if (!_listTableView) {
174 | _listTableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
175 | _listTableView.delegate = self;
176 | _listTableView.dataSource = self;
177 | _listTableView.backgroundColor = UIColorFromRGB(0xebebeb);
178 | _listTableView.backgroundColor = UIColorFromRGB(0xf8f8f8);
179 | _listTableView.separatorColor = UIColorFromRGB(0xeeeeee);
180 | [_listTableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"];
181 | }
182 | return _listTableView;
183 | }
184 | -(NSMutableArray *)dataSource{
185 | if (!_dataSource) {
186 | _dataSource = [NSMutableArray array];
187 | }
188 | return _dataSource;
189 | }
190 | - (void)didReceiveMemoryWarning {
191 | [super didReceiveMemoryWarning];
192 | // Dispose of any resources that can be recreated.
193 | }
194 | @end
195 |
--------------------------------------------------------------------------------
/TLMessageView/Chat/Controller/TLPhotoPickerViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // TLPhotoPickerViewController.h
3 | // TLMessageView
4 | //
5 | // Created by 郭锐 on 16/8/25.
6 | // Copyright © 2016年 com.garry.message. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | @protocol TLPhotoPickerDelegate
13 | -(void)didSendPhotos:(NSArray *)photos;
14 | @end
15 |
16 | @interface TLPhotoPickerViewController : UIViewController
17 | @property(nonatomic,assign)id delegate;
18 | -(instancetype)initWithDelegate:(id )delegate;
19 | -(void)sendPhotoAction;
20 | @end
21 |
--------------------------------------------------------------------------------
/TLMessageView/Chat/Controller/TLPhotoPickerViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // TLPhotoPickerViewController.m
3 | // TLMessageView
4 | //
5 | // Created by 郭锐 on 16/8/25.
6 | // Copyright © 2016年 com.garry.message. All rights reserved.
7 | //
8 |
9 | #import "TLPhotoPickerViewController.h"
10 | #import "TLProjectMacro.h"
11 | #import "TLPhotoThumbCell.h"
12 | #import "TLPhotoPreviewViewController.h"
13 | #import "PHAsset+Extend.h"
14 |
15 | @interface TLPhotoPickerViewController ()
16 |
17 |
20 |
21 | @property(nonatomic,strong)UICollectionView *photoCollectionView;
22 | @property(nonatomic,strong)UICollectionViewFlowLayout *colletionLayout;
23 | @property(nonatomic,strong)UIView *bottomView;
24 | @property(nonatomic,strong)UIView *line;
25 | @property(nonatomic,strong)UIButton *previewBtn;
26 | @property(nonatomic,strong)UIButton *sendBtn;
27 | @property(nonatomic,strong)TLCountLabel *countLabel;
28 | @property(nonatomic,strong)NSMutableArray *dataSource;
29 | @property(nonatomic,strong)NSMutableArray *selectPhotos;
30 | @end
31 |
32 | @implementation TLPhotoPickerViewController
33 | -(void)viewWillAppear:(BOOL)animated{
34 | [super viewWillAppear:animated];
35 | [self.photoCollectionView reloadData];
36 | }
37 | -(instancetype)initWithDelegate:(id)delegate{
38 | if (self = [super init]) {
39 | self.delegate = delegate;
40 | }
41 | return self;
42 | }
43 | - (void)viewDidLoad {
44 | [super viewDidLoad];
45 | self.title = @"照相机胶卷";
46 | self.view.backgroundColor = [UIColor whiteColor];
47 |
48 | self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(cancel)];
49 |
50 | [self.view addSubview:self.photoCollectionView];
51 | [self.photoCollectionView mas_makeConstraints:^(MASConstraintMaker *make) {
52 | make.top.equalTo(self.view.mas_top).offset(0);
53 | make.left.equalTo(self.view.mas_left).offset(0);
54 | make.right.equalTo(self.view.mas_right).offset(0);
55 | }];
56 |
57 | [self.view addSubview:self.bottomView];
58 | [self.bottomView mas_makeConstraints:^(MASConstraintMaker *make) {
59 | make.top.equalTo(self.photoCollectionView.mas_bottom).offset(0);
60 | make.left.equalTo(self.view.mas_left).offset(0);
61 | make.right.equalTo(self.view.mas_right).offset(0);
62 | make.bottom.equalTo(self.view.mas_bottom).offset(0);
63 | make.height.mas_offset(@40);
64 | }];
65 |
66 | [self.bottomView addSubview:self.line];
67 | [self.line mas_makeConstraints:^(MASConstraintMaker *make) {
68 | make.left.equalTo(self.bottomView.mas_left).offset(0);
69 | make.right.equalTo(self.bottomView.mas_right).offset(0);
70 | make.top.equalTo(self.bottomView.mas_top).offset(0);
71 | make.height.mas_offset(@0.5);
72 | }];
73 |
74 | [self.bottomView addSubview:self.previewBtn];
75 | [self.previewBtn mas_makeConstraints:^(MASConstraintMaker *make) {
76 | make.left.equalTo(self.bottomView.mas_left).offset(0);
77 | make.top.equalTo(self.bottomView.mas_top).offset(0);
78 | make.bottom.equalTo(self.bottomView.mas_bottom).offset(0);
79 | make.width.mas_offset(@50);
80 | }];
81 |
82 | [self.bottomView addSubview:self.sendBtn];
83 | [self.sendBtn mas_makeConstraints:^(MASConstraintMaker *make) {
84 | make.right.equalTo(self.bottomView.mas_right).offset(0);
85 | make.top.equalTo(self.bottomView.mas_top).offset(0);
86 | make.bottom.equalTo(self.bottomView.mas_bottom).offset(0);
87 | make.width.mas_offset(@50);
88 | }];
89 |
90 | [self.bottomView addSubview:self.countLabel];
91 | [self.countLabel mas_makeConstraints:^(MASConstraintMaker *make) {
92 | make.right.equalTo(self.sendBtn.mas_left).offset(0);
93 | make.centerY.equalTo(self.sendBtn.mas_centerY).offset(0);
94 | }];
95 |
96 | [self loadPhotos];
97 | }
98 | - (void)cancel{
99 | [self.navigationController dismissViewControllerAnimated:YES completion:nil];
100 | }
101 | - (void)loadPhotos{
102 | void (^reloadPhoto)(void) = ^{
103 | PHFetchOptions *options = [[PHFetchOptions alloc] init];
104 | options.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:NO]];
105 | PHFetchResult *assetsFetchResults = [PHAsset fetchAssetsWithOptions:options];
106 | for (NSInteger i = 0; i < assetsFetchResults.count; i++) {
107 | PHAsset *asset = assetsFetchResults[i];
108 | if (asset.mediaType == PHAssetMediaTypeImage) {
109 | [self.dataSource addObject:asset];
110 | }
111 | }
112 | dispatch_async(dispatch_get_main_queue(), ^{
113 | [self.photoCollectionView reloadData];
114 | });
115 | };
116 |
117 | if ([PHPhotoLibrary authorizationStatus] == PHAuthorizationStatusNotDetermined) {
118 | [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
119 | if (status == PHAuthorizationStatusAuthorized) {
120 | reloadPhoto();
121 | }else{
122 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil message:@"在设置中允许照片访问" preferredStyle:UIAlertControllerStyleAlert];
123 | [alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleCancel handler:nil]];
124 | [self presentViewController:alert animated:YES completion:nil];
125 | }
126 | }];
127 | }else{
128 | reloadPhoto();
129 | }
130 | }
131 | -(void)sendPhotoAction{
132 | [self dismissViewControllerAnimated:YES completion:^{
133 | if ([self.delegate respondsToSelector:@selector(didSendPhotos:)]) {
134 | [self.delegate didSendPhotos:self.selectPhotos];
135 | }
136 | }];
137 | }
138 | -(void)previewAction {
139 | [self showPreviewWithSelectedAsset:nil];
140 | }
141 | -(void)showPreviewWithSelectedAsset:(PHAsset *)asset {
142 | TLPhotoPreviewViewController *vc = [[TLPhotoPreviewViewController alloc] initWithSelectedAsset:asset assets:self.dataSource];
143 | vc.delegate = self;
144 | [self.navigationController pushViewController:vc animated:YES];
145 | }
146 | -(void)selectedPhoto:(PHAsset *)photo{
147 | if ([self.selectPhotos indexOfObject:photo] != NSIntegerMax) {
148 | return;
149 | }
150 | [self.selectPhotos addObject:photo];
151 | [self updateSelectedCount];
152 | }
153 | -(void)removePhoto:(PHAsset *)photo{
154 | if ([self.selectPhotos indexOfObject:photo] != NSIntegerMax) {
155 | [self.selectPhotos removeObject:photo];
156 | }
157 | [self updateSelectedCount];
158 | }
159 | -(void)updateSelectedCount{
160 | self.countLabel.text = @(self.selectPhotos.count).stringValue;
161 | self.countLabel.hidden = !self.selectPhotos.count;
162 | self.sendBtn.enabled = self.selectPhotos.count;
163 | self.previewBtn.enabled = self.selectPhotos.count;
164 | }
165 | -(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
166 | TLPhotoThumbCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath];
167 | cell.item = self.dataSource[indexPath.row];
168 | weakifySelf;
169 | cell.selectBlock = ^(PHAsset *x){
170 | strongifySelf;
171 | if (self.selectPhotos.count > 8 && x.selected) {
172 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil message:@"最多只能选择9张照片" preferredStyle:UIAlertControllerStyleAlert];
173 | [alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleCancel handler:nil]];
174 | [self presentViewController:alert animated:YES completion:nil];
175 | return NO;
176 | }else{
177 | x.selected ? [self.selectPhotos addObject:x] : [self.selectPhotos removeObject:x];
178 | [self updateSelectedCount];
179 | return YES;
180 | }
181 | };
182 | return cell;
183 | }
184 | -(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
185 | PHAsset *item = self.dataSource[indexPath.row];
186 | [self showPreviewWithSelectedAsset:item];
187 | }
188 | -(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
189 | return self.dataSource.count;
190 | }
191 | - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
192 | return UIEdgeInsetsMake(5, 5, 5, 5);
193 | }
194 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
195 | CGFloat width = (UI_SCREEN_WIDTH - 25) / 4;
196 | return CGSizeMake(width, width);
197 | }
198 | - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section {
199 | return 5.0;
200 | }
201 | - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section {
202 | return 5.0;
203 | }
204 | -(void)reloadPhotos{
205 | [self.photoCollectionView reloadData];
206 | }
207 | -(void)sendPhotos{
208 | [self sendPhotoAction];
209 | }
210 | -(NSMutableArray *)dataSource{
211 | if (!_dataSource) {
212 | _dataSource = [NSMutableArray array];
213 | }
214 | return _dataSource;
215 | }
216 | -(NSMutableArray *)selectPhotos{
217 | if (!_selectPhotos) {
218 | _selectPhotos = [NSMutableArray array];
219 | }
220 | return _selectPhotos;
221 | }
222 | -(UIView *)bottomView{
223 | if (!_bottomView) {
224 | _bottomView = [[UIView alloc] init];
225 |
226 | }
227 | return _bottomView;
228 | }
229 | -(UIView *)line{
230 | if (!_line) {
231 | _line = [[UIView alloc] init];
232 | _line.backgroundColor = UIColorFromRGB(0xcccccc);
233 | }
234 | return _line;
235 | }
236 | -(UIButton *)previewBtn{
237 | if (!_previewBtn) {
238 | _previewBtn = [UIButton buttonWithType:UIButtonTypeCustom];
239 | [_previewBtn setTitleColor:UIColorFromRGB(0x999999) forState:UIControlStateNormal];
240 | [_previewBtn setTitleColor:UIColorFromRGB(0xcccccc) forState:UIControlStateDisabled];
241 | [_previewBtn setTitle:@"预览" forState:UIControlStateNormal];
242 | [_previewBtn addTarget:self action:@selector(previewAction) forControlEvents:UIControlEventTouchUpInside];
243 | _previewBtn.titleLabel.font = [UIFont systemFontOfSize:15];
244 | _previewBtn.enabled = NO;
245 | }
246 | return _previewBtn;
247 | }
248 | -(UIButton *)sendBtn{
249 | if (!_sendBtn) {
250 | _sendBtn = [UIButton buttonWithType:UIButtonTypeCustom];
251 | [_sendBtn setTitleColor:UIColorFromRGB(0x007AFF) forState:UIControlStateNormal];
252 | [_sendBtn setTitleColor:UIColorFromRGB(0xa3cdff) forState:UIControlStateDisabled];
253 | [_sendBtn setTitle:@"发送" forState:UIControlStateNormal];
254 | _sendBtn.titleLabel.font = [UIFont systemFontOfSize:15];
255 | [_sendBtn addTarget:self action:@selector(sendPhotoAction) forControlEvents:UIControlEventTouchUpInside];
256 | _sendBtn.enabled = NO;
257 | }
258 | return _sendBtn;
259 | }
260 | -(TLCountLabel *)countLabel{
261 | if (!_countLabel) {
262 | _countLabel = [[TLCountLabel alloc] init];
263 | _countLabel.backgroundColor = UIColorFromRGB(0x007AFF);
264 | _countLabel.textColor = UIColorFromRGB(0xffffff);
265 | _countLabel.font = [UIFont systemFontOfSize:15];
266 | _countLabel.hidden = YES;
267 | }
268 | return _countLabel;
269 | }
270 | -(UICollectionView *)photoCollectionView{
271 | if (!_photoCollectionView) {
272 | _photoCollectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 0, UI_SCREEN_WIDTH, UI_SCREEN_HEIGHT) collectionViewLayout:self.colletionLayout];
273 | _photoCollectionView.delegate = self;
274 | _photoCollectionView.dataSource = self;
275 | _photoCollectionView.showsVerticalScrollIndicator = NO;
276 | _photoCollectionView.scrollsToTop = YES;
277 | _photoCollectionView.alwaysBounceVertical = YES;
278 | [_photoCollectionView registerClass:[TLPhotoThumbCell class] forCellWithReuseIdentifier:@"cell"];
279 | _photoCollectionView.backgroundColor = UIColorFromRGB(0xFFFAFAFA);
280 | }
281 | return _photoCollectionView;
282 | }
283 | -(UICollectionViewFlowLayout *)colletionLayout{
284 | if (!_colletionLayout) {
285 | _colletionLayout = [[UICollectionViewFlowLayout alloc]init];
286 | }
287 | return _colletionLayout;
288 | }
289 | - (void)didReceiveMemoryWarning {
290 | [super didReceiveMemoryWarning];
291 | // Dispose of any resources that can be recreated.
292 | }
293 | @end
294 |
--------------------------------------------------------------------------------
/TLMessageView/Chat/Controller/TLPhotoPreviewViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // TLPhotoPreviewViewController.h
3 | // TLMessageView
4 | //
5 | // Created by 郭锐 on 16/8/25.
6 | // Copyright © 2016年 com.garry.message. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | #import "TLPhotoBrowser.h"
12 |
13 | @protocol TLPhotoPreviewDelegate
14 | -(void)selectedPhoto:(PHAsset *)photo;
15 | -(void)removePhoto:(PHAsset *)photo;
16 | -(void)sendPhotos;
17 | @end
18 |
19 | @interface TLPhotoPreviewViewController : UIViewController
20 | @property(nonatomic,assign)id delegate;
21 | -(instancetype)initWithSelectedAsset:(PHAsset *)selectedAsset assets:(NSArray *)asstes;
22 | @end
23 |
24 |
25 | @interface TLCountLabel : UILabel
26 |
27 | @end
28 |
29 |
30 | @interface TLPhotoPreviewCell : UICollectionViewCell
31 | @property(nonatomic,strong)TLImageScrollView *imgScrollView;
32 | @property(nonatomic,strong)PHAsset *asset;
33 | @end
34 |
--------------------------------------------------------------------------------
/TLMessageView/Chat/Controller/TLPhotoPreviewViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // TLPhotoPreviewViewController.m
3 | // TLMessageView
4 | //
5 | // Created by 郭锐 on 16/8/25.
6 | // Copyright © 2016年 com.garry.message. All rights reserved.
7 | //
8 |
9 | #import "TLPhotoPreviewViewController.h"
10 | #import "TLProjectMacro.h"
11 | #import "PHAsset+Extend.h"
12 | #import "TLButton.h"
13 |
14 | @interface TLPhotoPreviewViewController ()
15 |
19 | @property(nonatomic,strong)UICollectionView *collectionView;
20 | @property(nonatomic,strong)UICollectionViewFlowLayout *colletionLayout;
21 | @property(nonatomic,strong)UIVisualEffectView *bottomView;
22 | @property(nonatomic,strong)UIVisualEffectView *navView;
23 | @property(nonatomic,strong)TLButton *sendBtn;
24 | @property(nonatomic,strong)TLCountLabel *countLabel;
25 | @property(nonatomic,strong)TLButton *selectBtn;
26 | @property(nonatomic,strong)TLButton *sendOriginalImgBtn;
27 | @property(nonatomic,strong)TLButton *backBtn;
28 | @property(nonatomic,strong)PHAsset *selectedAsset;
29 | @property(nonatomic,strong)NSArray *assets;
30 | @end
31 |
32 | @implementation TLPhotoPreviewViewController
33 | {
34 | BOOL _showNav;
35 | NSInteger _currentIndex;
36 | }
37 | -(void)viewWillAppear:(BOOL)animated{
38 | [super viewWillAppear:animated];
39 | [self.navigationController setNavigationBarHidden:YES animated:YES];
40 | }
41 | -(void)viewWillDisappear:(BOOL)animated{
42 | [super viewWillDisappear:animated];
43 | [self.navigationController setNavigationBarHidden:NO animated:YES];
44 | }
45 | -(instancetype)initWithSelectedAsset:(PHAsset *)selectedAsset assets:(NSArray *)asstes{
46 | if (self = [super init]) {
47 | if (selectedAsset) {
48 | self.selectedAsset = selectedAsset;
49 | _currentIndex = [self.assets indexOfObject:selectedAsset];
50 | self.selectBtn.selected = selectedAsset.selected;
51 | self.sendOriginalImgBtn.selected = selectedAsset.isOriginal;
52 | }
53 | self.assets = asstes;
54 | }
55 | return self;
56 | }
57 | - (void)viewDidLoad {
58 | [super viewDidLoad];
59 |
60 | [self.view addSubview:self.collectionView];
61 |
62 | self.view.backgroundColor = [UIColor blackColor];
63 |
64 | self.collectionView.contentOffset = CGPointMake(_currentIndex * self.collectionView.bounds.size.width, 0);
65 |
66 | [self.view addSubview:self.navView];
67 | [self.navView mas_makeConstraints:^(MASConstraintMaker *make) {
68 | make.top.left.right.equalTo(self.view);
69 | make.height.mas_offset(@49);
70 | }];
71 |
72 | [self.navView addSubview:self.backBtn];
73 | [self.backBtn mas_makeConstraints:^(MASConstraintMaker *make) {
74 | make.left.equalTo(self.navView.mas_left).offset(10);
75 | make.centerY.equalTo(self.navView.mas_centerY).offset(0);
76 | }];
77 |
78 | [self.navView addSubview:self.selectBtn];
79 | [self.selectBtn mas_makeConstraints:^(MASConstraintMaker *make) {
80 | make.right.equalTo(self.navView.mas_right).offset(-10);
81 | make.centerY.equalTo(self.navView.mas_centerY).offset(0);
82 | }];
83 |
84 | [self.view addSubview:self.bottomView];
85 | [self.bottomView mas_makeConstraints:^(MASConstraintMaker *make) {
86 | make.bottom.left.right.equalTo(self.view);
87 | make.height.mas_offset(@44);
88 | }];
89 |
90 | [self.bottomView addSubview:self.sendOriginalImgBtn];
91 | [self.sendOriginalImgBtn mas_makeConstraints:^(MASConstraintMaker *make) {
92 | make.left.equalTo(self.bottomView.mas_left).offset(10);
93 | make.centerY.equalTo(self.bottomView.mas_centerY).offset(0);
94 | }];
95 |
96 | [self.bottomView addSubview:self.sendBtn];
97 | [self.sendBtn mas_makeConstraints:^(MASConstraintMaker *make) {
98 | make.right.equalTo(self.bottomView.mas_right).offset(-10);
99 | make.centerY.equalTo(self.bottomView.mas_centerY).offset(0);
100 | }];
101 | }
102 | -(void)backAction{
103 | [self.navigationController popViewControllerAnimated:YES];
104 | }
105 | -(void)sendPhotoAction:(UIButton *)sender{
106 | if ([self.delegate respondsToSelector:@selector(sendPhotos)]) {
107 | [self.delegate sendPhotos];
108 | }
109 | }
110 | -(void)selectAction:(UIButton *)sender{
111 | PHAsset *currentAsset = self.assets[_currentIndex];
112 | currentAsset.selected = !currentAsset.selected;
113 | sender.selected = currentAsset.selected;
114 | if (currentAsset.selected) {
115 | [self.delegate selectedPhoto:currentAsset];
116 | }else{
117 | [self.delegate removePhoto:currentAsset];
118 | }
119 | }
120 | -(void)sendOriginalImgAction:(UIButton *)sender{
121 | PHAsset *currentAsset = self.assets[_currentIndex];
122 | currentAsset.isOriginal = !currentAsset.isOriginal;
123 | sender.selected = currentAsset.isOriginal;
124 | }
125 | -(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
126 | TLPhotoPreviewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath];
127 | cell.imgScrollView.actionDelegate = self;
128 | cell.asset = self.assets[indexPath.row];
129 | return cell;
130 | }
131 | -(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
132 | return self.assets.count;
133 | }
134 | #pragma mark - UICollectionViewDelegateFlowLayout
135 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{
136 | return CGSizeMake(self.view.bounds.size.width + 20, self.view.bounds.size.height);
137 | }
138 | - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
139 | CGFloat offsetX = scrollView.contentOffset.x;
140 | CGFloat itemWidth = self.collectionView.frame.size.width;
141 | if (offsetX >= 0){
142 | NSInteger index = offsetX / itemWidth;
143 | _currentIndex = index;
144 | PHAsset *currentAsset = self.assets[_currentIndex];
145 | self.sendOriginalImgBtn.selected = currentAsset.isOriginal;
146 | self.selectBtn.selected = currentAsset.selected;
147 | }
148 | }
149 | -(void)imageScrollViewTap:(UITapGestureRecognizer *)sender{
150 | [self.navView mas_updateConstraints:^(MASConstraintMaker *make) {
151 | make.top.equalTo(self.view.mas_top).offset(_showNav ? 0 : -69);
152 | }];
153 |
154 | [self.bottomView mas_updateConstraints:^(MASConstraintMaker *make) {
155 | make.bottom.equalTo(self.view.mas_bottom).offset(_showNav ? 0 : 44);
156 | }];
157 |
158 | [UIView animateWithDuration:0.25 animations:^{
159 | [self.view layoutIfNeeded];
160 | } completion:^(BOOL finished) {
161 | if (finished) {
162 | _showNav = !_showNav;
163 | }
164 | }];
165 | }
166 | -(UICollectionView *)collectionView{
167 | if (!_collectionView) {
168 | _collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(- 10, 0, self.view.bounds.size.width + 20, self.view.bounds.size.height + 1) collectionViewLayout:self.colletionLayout];
169 | _collectionView.backgroundColor = [UIColor blackColor];
170 | _collectionView.delegate = self;
171 | _collectionView.dataSource = self;
172 | _collectionView.pagingEnabled = YES;
173 | _collectionView.showsHorizontalScrollIndicator = YES;
174 | [_collectionView registerClass:[TLPhotoPreviewCell class] forCellWithReuseIdentifier:@"cell"];
175 | self.colletionLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
176 | }
177 | return _collectionView;
178 | }
179 | -(UICollectionViewFlowLayout *)colletionLayout{
180 | if (!_colletionLayout) {
181 | _colletionLayout = [[UICollectionViewFlowLayout alloc] init];
182 | _colletionLayout.minimumInteritemSpacing = 0;
183 | _colletionLayout.minimumLineSpacing = 0;
184 | _colletionLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
185 | }
186 | return _colletionLayout;
187 | }
188 | -(UIVisualEffectView *)navView{
189 | if (!_navView) {
190 | _navView = [[UIVisualEffectView alloc] initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleDark]];
191 | }
192 | return _navView;
193 | }
194 | -(UIVisualEffectView *)bottomView{
195 | if (!_bottomView) {
196 | _bottomView = [[UIVisualEffectView alloc] initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleDark]];
197 | }
198 | return _bottomView;
199 | }
200 | -(TLButton *)sendBtn{
201 | if (!_sendBtn) {
202 | _sendBtn = [TLButton buttonWithType:UIButtonTypeCustom];
203 | [_sendBtn setTitleColor:UIColorFromRGB(0x007AFF) forState:UIControlStateNormal];
204 | [_sendBtn setTitleColor:UIColorFromRGB(0xa3cdff) forState:UIControlStateDisabled];
205 | [_sendBtn setTitle:@"发送" forState:UIControlStateNormal];
206 | _sendBtn.titleLabel.font = [UIFont systemFontOfSize:15];
207 | [_sendBtn addTarget:self action:@selector(sendPhotoAction:) forControlEvents:UIControlEventTouchUpInside];
208 | }
209 | return _sendBtn;
210 | }
211 | -(TLButton *)backBtn{
212 | if (!_backBtn) {
213 | _backBtn = [TLButton buttonWithType:UIButtonTypeCustom];
214 | [_backBtn setImage:[UIImage imageNamed:@"back"] forState:UIControlStateNormal];
215 | [_backBtn addTarget:self action:@selector(backAction) forControlEvents:UIControlEventTouchUpInside];
216 | }
217 | return _backBtn;
218 | }
219 | -(TLButton *)selectBtn{
220 | if (!_selectBtn) {
221 | _selectBtn = [TLButton buttonWithType:UIButtonTypeCustom];
222 | [_selectBtn setImage:[UIImage imageNamed:@"photo_preview_unselected"] forState:UIControlStateNormal];
223 | [_selectBtn setImage:[UIImage imageNamed:@"photo_preview_selected"] forState:UIControlStateSelected];
224 | [_selectBtn addTarget:self action:@selector(selectAction:) forControlEvents:UIControlEventTouchUpInside];
225 | }
226 | return _selectBtn;
227 | }
228 | -(TLCountLabel *)countLabel{
229 | if (!_countLabel) {
230 | _countLabel = [[TLCountLabel alloc] init];
231 | _countLabel.backgroundColor = UIColorFromRGB(0x007AFF);
232 | _countLabel.textColor = UIColorFromRGB(0xffffff);
233 | _countLabel.font = [UIFont systemFontOfSize:15];
234 | _countLabel.hidden = YES;
235 | }
236 | return _countLabel;
237 | }
238 | -(TLButton *)sendOriginalImgBtn{
239 | if (!_sendOriginalImgBtn) {
240 | _sendOriginalImgBtn = [TLButton buttonWithType:UIButtonTypeCustom];
241 | [_sendOriginalImgBtn setImage:[UIImage imageNamed:@"unselected_full"] forState:UIControlStateNormal];
242 | [_sendOriginalImgBtn setImage:[UIImage imageNamed:@"selected_full"] forState:UIControlStateSelected];
243 | [_sendOriginalImgBtn setTitle:@"原图" forState:UIControlStateNormal];
244 | [_sendOriginalImgBtn setTitleColor:UIColorFromRGB(0xffffff) forState:UIControlStateNormal];
245 | [_sendOriginalImgBtn setTitleColor:UIColorFromRGB(0xcccccc) forState:UIControlStateSelected];
246 | _sendOriginalImgBtn.titleLabel.font = [UIFont systemFontOfSize:15];
247 | _sendOriginalImgBtn.imageEdgeInsets = UIEdgeInsetsMake(0, -10, 0, 0);
248 | [_sendOriginalImgBtn addTarget:self action:@selector(sendOriginalImgAction:) forControlEvents:UIControlEventTouchUpInside];
249 | }
250 | return _sendOriginalImgBtn;
251 | }
252 | -(BOOL)prefersStatusBarHidden{
253 | return YES;
254 | }
255 | - (void)didReceiveMemoryWarning {
256 | [super didReceiveMemoryWarning];
257 | // Dispose of any resources that can be recreated.
258 | }
259 | @end
260 |
261 |
262 |
263 |
264 | @implementation TLCountLabel
265 | -(instancetype)init{
266 | if (self = [super init]) {
267 | self.clipsToBounds = YES;
268 | self.textAlignment = NSTextAlignmentCenter;
269 | self.backgroundColor = UIColorFromRGB(0xff514e);
270 | self.font = [UIFont systemFontOfSize:15];
271 | self.textColor = UIColorFromRGB(0xffffff);
272 | }
273 | return self;
274 | }
275 | -(CGSize)intrinsicContentSize{
276 | CGSize size = [super intrinsicContentSize];
277 | return CGSizeMake(MAX(size.width + 8, size.height + 4), size.height + 4);
278 | }
279 | -(void)layoutSubviews{
280 | [super layoutSubviews];
281 | self.layer.cornerRadius = self.frame.size.height / 2.0;
282 | }
283 | @end
284 |
285 |
286 |
287 |
288 |
289 | @implementation TLPhotoPreviewCell
290 | -(instancetype)initWithFrame:(CGRect)frame{
291 | if (self = [super initWithFrame:frame]) {
292 | if (!self.imgScrollView) {
293 | self.imgScrollView = [[TLImageScrollView alloc] initWithFrame:self.bounds];
294 | [self addSubview:self.imgScrollView];
295 | }
296 | }
297 | return self;
298 | }
299 | -(void)setAsset:(PHAsset *)asset{
300 | [[PHImageManager defaultManager] requestImageForAsset:asset targetSize:CGSizeMake(asset.pixelWidth, asset.pixelHeight) contentMode:PHImageContentModeDefault options:[[PHImageRequestOptions alloc] init] resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
301 | self.imgScrollView.img = result;
302 | }];
303 | }
304 | @end
305 |
--------------------------------------------------------------------------------
/TLMessageView/Chat/Networking/APIDebug.h:
--------------------------------------------------------------------------------
1 | //
2 | // APIDebug.h
3 | // TLMessageView
4 | //
5 | // Created by 郭锐 on 16/8/24.
6 | // Copyright © 2016年 com.garry.message. All rights reserved.
7 | //
8 |
9 | #import "TLProjectMacro.h"
10 |
11 | @interface APIDebug : NSObject
12 | +(void)configWithVC:(UIViewController *)vc;
13 | +(void)sendMsgWithContent:(NSString *)content
14 | objectName:(NSString *)objectName
15 | success:(void(^)())success
16 | failure:(void(^)(NSError *error))failure;
17 | @end
18 |
19 |
20 | @interface ActionSheet : UIActionSheet
21 | -(instancetype)initWithTitle:(NSString *)title cancelButtonTitle:(NSString *)cancelButtonTitle destructiveButtonTitle:(NSString *)destructiveButtonTitle action:(void(^)(NSInteger))action otherButtonTitles:(NSArray *)otherButtonTitles;
22 | @end
--------------------------------------------------------------------------------
/TLMessageView/Chat/Networking/APIDebug.m:
--------------------------------------------------------------------------------
1 | //
2 | // APIDebug.m
3 | // TLMessageView
4 | //
5 | // Created by 郭锐 on 16/8/24.
6 | // Copyright © 2016年 com.garry.message. All rights reserved.
7 | //
8 |
9 | #import "APIDebug.h"
10 | #import
11 | #import "TLProjectMacro.h"
12 |
13 | static NSString *Host = @"https://api.cn.ronghub.com";
14 | static NSString *Url = @"/message/private/publish.json";
15 | static NSString *AppKey = @"y745wfm844a4v";
16 | static NSString *Appsecret = @"eIPrvtxgHw";
17 |
18 | static NSString *const kAFCharactersGeneralDelimitersToEncode =@":#[]@"; // does not include "?" or "/" due to RFC 3986 - Section 3.4
19 | static NSString *const kAFCharactersSubDelimitersToEncode = @"!$&'()*+,;=";
20 |
21 |
22 | @interface APIDebug ()
23 |
24 | @end
25 |
26 | @implementation APIDebug
27 | +(void)configWithVC:(UIViewController *)vc{
28 | UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithTitle:@"APIDebug" style:UIBarButtonItemStylePlain target:self action:@selector(debugAction)];
29 | item.tintColor = [UIColor grayColor];
30 | vc.navigationItem.rightBarButtonItem = item;
31 | }
32 | +(void)debugAction{
33 | ActionSheet *sheet = [[ActionSheet alloc] initWithTitle:@"模拟融云服务器向当前用户发送消息" cancelButtonTitle:@"取消" destructiveButtonTitle:nil action:^(NSInteger x) {
34 | NSString *content = nil;
35 | NSString *objName = nil;
36 |
37 | NSMutableCharacterSet * allowedCharacterSet = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy];
38 | [allowedCharacterSet removeCharactersInString:[kAFCharactersGeneralDelimitersToEncode stringByAppendingString:kAFCharactersSubDelimitersToEncode]];
39 | switch (x) {
40 | case 1:
41 | {
42 | content = @"{\"content\":\"hello\",\"extra\":\"helloExtra\"}";
43 | objName = @"RC:TxtMsg";
44 | }
45 | break;
46 | case 2:
47 | {
48 |
49 | content = @"{\"content\":\"iVBORw0KGgoAAAANSUhEUgAAAA8AAAARCAIAAACNaGH2AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA7EAAAOxAGVKw4bAAACg0lEQVQokQXBS2+UZRQA4HPe23cfOkNn2grShmmpBMMCMRoSFv4C1+re6MKFf8XEJf+jbtiTuNGFaQSkpWl1mDKl813fy3mPz4NnS1+NegdUaNlyAjQkmHrwgJKl1rbxyhQsOyExOpGNfDD5WOU9aIUkdZEKRJmMpFTnLrABkfRsK3KAoP47e2ttaPp1AJD1ek0sAtVD75v60eMvt3fujWJo2DeqaNypevbrL9F7RAGI3jnmyIAcWTC+ev3XTz/+3G1uSeuEdKt1UPP5/OTNCccoAbbu3s1kyjKqTJpI7QDnXXsr1JTNEtcebu6pL55+9eb1MwaAGB9+9vjzJwcmmbr2fVpOUhhpgA/QZeR7qWRktfvgweZs8/Ld0iP++fuLadr6fLe9XIhMOVLGDtOD+e7OtknyAQiXFP54fvTb0RFRZOBSZV4gSJR+sCyctYcP73/97Xf11ZpiJSLT/SdPhVTMLIWsqUffeTu4CMBhNKlWy0VuaTr7aDzWSsXBpOV8f358fGwA8py/+eH70WRPKefWC13eJiYSqTB+659KFCzl4Lf3trUQlqFrpKmmG0YZsZFN9ot03IY6SMx9cvVJEB+Qg6Zbn97TKBA4xPD29GWtCkE2QiJ9O8tvCxBWgbmwQusSIx3ODiAzMTIgtv92uq9tUiq0jZAWqABlADGAWF+8HISIIEZlkRZJVpjr99dqqDWI7O+FDCKhwBBrCt0dgxfXFzfTaq3y66tL3a+suVHKVbqxf746DX5jZzY2SB71DXIdEC6pEWw49p5UmuY5vDqjSXK5UOOPdQxsSuNarzOJ0rtOSDYxdizkcFI774f+ZgXVbHJHqUSnJTKRziDKDCmcw//w3WEJ8Go/SwAAAABJRU5ErkJggg==\",\"imageUri\":\"http://7i7gc6.com1.z0.glb.clouddn.com/emoji.png\",\"extra\":\"helloExtra\"}";
50 |
51 | objName = @"RC:ImgMsg";
52 | }
53 | break;
54 | case 3:
55 | {
56 | content = @"{\"content\":\"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\",\"duration\":2,\"extra\":\"helloExtra\"}";
57 |
58 |
59 | objName = @"RC:VcMsg";
60 |
61 | }
62 | break;
63 | case 4:
64 | {
65 | content = @"{\"content\":\"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\",\"latitude\":40.032149,\"longitude\":116.417613,\"poi\":\"北京经济技术研修学院(北苑路)\",\"extra\":\"helloExtra\"}";
66 |
67 | objName = @"RC:LBSMsg";
68 |
69 | }
70 | break;
71 | default:
72 | break;
73 | }
74 | content = [content stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet];
75 |
76 | [APIDebug sendMsgWithContent:content objectName:objName success:^{
77 |
78 | } failure:^(NSError *error) {
79 |
80 | }];
81 |
82 | } otherButtonTitles:@[@"发送文字消息",@"发送图片消息",@"发送语音消息",@"发送地理位置消息"]];
83 | [sheet showInView:kKeyWindow];
84 | }
85 | +(void)sendMsgWithContent:(NSString *)content
86 | objectName:(NSString *)objectName
87 | success:(void (^)())success
88 | failure:(void (^)(NSError *))failure{
89 | NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@",Host,Url]];
90 |
91 | NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
92 | [request setHTTPMethod:@"POST"];
93 | [request setTimeoutInterval:10];
94 |
95 | NSString *random = @(1000000000 + (arc4random() % (1000000000 - 1))).stringValue;
96 |
97 | NSString *appKey = AppKey;
98 |
99 | NSString *secret = Appsecret;
100 |
101 | NSString *time = [@([[NSDate date] timeIntervalSince1970]).stringValue componentsSeparatedByString:@"."][0];
102 |
103 | NSString *signature = [self sha1:[NSString stringWithFormat:@"%@%@%@",secret,random,time]];
104 |
105 | NSDictionary *header = @{@"App-Key":appKey,@"Timestamp":time,@"Nonce":random,@"Signature":signature,@"Content-Type":@"application/x-www-form-urlencoded"};
106 | [request setAllHTTPHeaderFields:header];
107 |
108 | NSString *params = [NSString stringWithFormat:@"content=%@&fromUserId=%@&toUserId=%@&objectName=%@",content,@"222",@"111",objectName];
109 |
110 | NSData *bodyData = [params dataUsingEncoding:NSUTF8StringEncoding];
111 | [request setHTTPBody:bodyData];
112 |
113 | NSURLSessionDataTask *task = [ [NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
114 | if (error) {
115 | failure(error);
116 | }else{
117 | NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
118 | NSLog(@"%@",json);
119 | }
120 | }];
121 | [task resume];
122 |
123 | }
124 | +(NSString*)dataTOjsonString:(id)object
125 | {
126 | NSString *jsonString = nil;
127 | NSError *error;
128 | NSData *jsonData = [NSJSONSerialization dataWithJSONObject:object
129 | options:NSJSONWritingPrettyPrinted // Pass 0 if you don't care about the readability of the generated string
130 | error:&error];
131 | if (! jsonData) {
132 | NSLog(@"Got an error: %@", error);
133 | } else {
134 | jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
135 | }
136 | return jsonString;
137 | }
138 | + (NSString *) sha1:(NSString *)input
139 | {
140 | const char *cstr = [input cStringUsingEncoding:NSUTF8StringEncoding];
141 | NSData *data = [NSData dataWithBytes:cstr length:input.length];
142 |
143 | uint8_t digest[CC_SHA1_DIGEST_LENGTH];
144 |
145 | CC_SHA1(data.bytes, (unsigned int)data.length, digest);
146 |
147 | NSMutableString *output = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 2];
148 |
149 | for(int i=0; i
7 |
8 | @interface LPlaceholderTextView : UITextView
9 | {
10 | UILabel *_placeholderLabel;
11 | }
12 |
13 |
14 | @property (strong, nonatomic) NSString *placeholderText;
15 | @property (strong, nonatomic) UIColor *placeholderColor;
16 | @property (assign, nonatomic) CGFloat maxTextViewHeight;
17 | @end
--------------------------------------------------------------------------------
/TLMessageView/Chat/Vendor/LPlaceholderTextView.m:
--------------------------------------------------------------------------------
1 | //
2 | // Created by Luka Gabrić.
3 | // Copyright (c) 2013 Luka Gabrić. All rights reserved.
4 | //
5 |
6 |
7 | #import "LPlaceholderTextView.h"
8 |
9 |
10 | @implementation LPlaceholderTextView
11 |
12 |
13 | #pragma mark - init & dealloc
14 |
15 |
16 | - (id)initWithCoder:(NSCoder *)aDecoder
17 | {
18 | self = [super initWithCoder:aDecoder];
19 | if (self)
20 | {
21 | [self initialize];
22 | }
23 |
24 | return self;
25 | }
26 |
27 |
28 | - (id)initWithFrame:(CGRect)frame
29 | {
30 | self = [super initWithFrame:frame];
31 | if (self)
32 | {
33 | [self initialize];
34 | }
35 |
36 | return self;
37 | }
38 |
39 |
40 | - (id)init
41 | {
42 | self = [super init];
43 | if (self)
44 | {
45 | [self initialize];
46 | }
47 |
48 | return self;
49 | }
50 |
51 |
52 | - (void)initialize
53 | {
54 | _placeholderColor = [UIColor lightGrayColor];
55 | [self layoutGUI];
56 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChanged:) name:UITextViewTextDidChangeNotification object:self];
57 | }
58 |
59 |
60 | - (void)dealloc
61 | {
62 | [[NSNotificationCenter defaultCenter] removeObserver:self];
63 | }
64 |
65 |
66 | #pragma mark - Notification center
67 |
68 |
69 | - (void)textChanged:(NSNotification *)notification
70 | {
71 | if (notification.object == self)
72 | [self layoutGUI];
73 | }
74 |
75 |
76 | #pragma mark - layoutGUI
77 |
78 |
79 | - (void)layoutGUI
80 | {
81 | _placeholderLabel.alpha = [self.text length] > 0 || [_placeholderText length] == 0 ? 0 : 1;
82 | }
83 |
84 |
85 | #pragma mark - Setters
86 |
87 |
88 | - (void)setText:(NSString *)text
89 | {
90 | [super setText:text];
91 | [self layoutGUI];
92 | }
93 | - (void)setAttributedText:(NSAttributedString *)attributedText
94 | {
95 | [super setAttributedText:attributedText];
96 | [self layoutGUI];
97 | }
98 |
99 | - (void)setPlaceholderText:(NSString*)placeholderText
100 | {
101 | _placeholderText = placeholderText;
102 | [self setNeedsDisplay];
103 | }
104 |
105 |
106 | - (void)setPlaceholderColor:(UIColor*)color
107 | {
108 | _placeholderColor = color;
109 | [self setNeedsDisplay];
110 | }
111 |
112 |
113 | #pragma mark - drawRect
114 |
115 |
116 | - (void)drawRect:(CGRect)rect
117 | {
118 | if ([_placeholderText length] > 0)
119 | {
120 | if (!_placeholderLabel)
121 | {
122 | _placeholderLabel = [[UILabel alloc] initWithFrame:CGRectMake(8, 8, self.bounds.size.width - 16, 0)];
123 | _placeholderLabel.lineBreakMode = NSLineBreakByWordWrapping;
124 | _placeholderLabel.numberOfLines = 0;
125 | _placeholderLabel.font = self.font;
126 | _placeholderLabel.backgroundColor = [UIColor clearColor];
127 | _placeholderLabel.alpha = 0;
128 | [self addSubview:_placeholderLabel];
129 | }
130 |
131 | _placeholderLabel.text = _placeholderText;
132 | _placeholderLabel.textColor = _placeholderColor;
133 | [_placeholderLabel sizeToFit];
134 | [self sendSubviewToBack:_placeholderLabel];
135 | }
136 |
137 | [self layoutGUI];
138 |
139 | [super drawRect:rect];
140 | }
141 | -(CGSize)contentSize{
142 | CGSize size = [super contentSize];
143 | CGSize maxSize = CGSizeMake(self.frame.size.width, MIN(self.maxTextViewHeight, size.height));
144 | return maxSize;
145 | }
146 |
147 | #pragma mark -
148 |
149 |
150 | @end
151 |
--------------------------------------------------------------------------------
/TLMessageView/Chat/Vendor/NSDate+Utils.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSDate+Utils.h
3 | // BloodSugar
4 | //
5 | // Created by PeterPan on 13-12-27.
6 | // Copyright (c) 2013年 shake. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | @interface NSDate (Utils)
13 |
14 | + (NSDate *)dateWithYear:(NSInteger)year
15 | month:(NSInteger)month
16 | day:(NSInteger)day
17 | hour:(NSInteger)hour
18 | minute:(NSInteger)minute
19 | second:(NSInteger)second;
20 |
21 | + (NSInteger)daysOffsetBetweenStartDate:(NSDate *)startDate endDate:(NSDate *)endDate;
22 |
23 | + (NSDate *)dateWithHour:(int)hour
24 | minute:(int)minute;
25 |
26 | #pragma mark - Getter
27 | - (NSInteger)year;
28 | - (NSInteger)month;
29 | - (NSInteger)day;
30 | - (NSInteger)hour;
31 | - (NSInteger)minute;
32 | - (NSInteger)second;
33 | - (NSString *)weekday;
34 |
35 |
36 | #pragma mark - Time string
37 | - (NSString *)timeHourMinute;
38 | - (NSString *)timeHourMinuteWithPrefix;
39 | - (NSString *)timeHourMinuteWithSuffix;
40 | - (NSString *)timeHourMinuteWithPrefix:(BOOL)enablePrefix suffix:(BOOL)enableSuffix;
41 |
42 | #pragma mark - Date String
43 | - (NSString *)stringTime;
44 | - (NSString *)stringMonthDay;
45 | - (NSString *)stringYearMonthDay;
46 | - (NSString *)stringYearMonthDayHourMinuteSecond;
47 | + (NSString *)stringYearMonthDayWithDate:(NSDate *)date; //date为空时返回的是当前年月日
48 | + (NSString *)stringLoacalDate;
49 |
50 | #pragma mark - Date formate
51 | + (NSString *)dateFormatString;
52 | + (NSString *)timeFormatString;
53 | + (NSString *)timestampFormatString;
54 | + (NSString *)timestampFormatStringSubSeconds;
55 |
56 | #pragma mark - Date adjust
57 | - (NSDate *) dateByAddingDays: (NSInteger) dDays;
58 | - (NSDate *) dateBySubtractingDays: (NSInteger) dDays;
59 |
60 | #pragma mark - Relative dates from the date
61 | + (NSDate *) dateTomorrow;
62 | + (NSDate *) dateYesterday;
63 | + (NSDate *) dateWithDaysFromNow: (NSInteger) days;
64 | + (NSDate *) dateWithDaysBeforeNow: (NSInteger) days;
65 | + (NSDate *) dateWithHoursFromNow: (NSInteger) dHours;
66 | + (NSDate *) dateWithHoursBeforeNow: (NSInteger) dHours;
67 | + (NSDate *) dateWithMinutesFromNow: (NSInteger) dMinutes;
68 | + (NSDate *) dateWithMinutesBeforeNow: (NSInteger) dMinutes;
69 | + (NSDate *) dateStandardFormatTimeZeroWithDate: (NSDate *) aDate; //标准格式的零点日期
70 | - (NSInteger) daysBetweenCurrentDateAndDate; //负数为过去,正数为未来
71 |
72 | #pragma mark - Date compare
73 | - (BOOL)isEqualToDateIgnoringTime: (NSDate *) aDate;
74 | - (NSString *)stringYearMonthDayCompareToday; //返回“今天”,“明天”,“昨天”,或年月日
75 |
76 | #pragma mark - Date and string convert
77 | + (NSDate *)dateFromString:(NSString *)string;
78 | + (NSDate *)dateFromString:(NSString *)string withFormat:(NSString *)format;
79 | - (NSString *)string;
80 | - (NSString *)stringCutSeconds;
81 |
82 | @end
83 |
--------------------------------------------------------------------------------
/TLMessageView/Chat/Vendor/NSDate+Utils.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSDate+Utils.m
3 | // BloodSugar
4 | //
5 | // Created by PeterPan on 13-12-27.
6 | // Copyright (c) 2013年 shake. All rights reserved.
7 | //
8 |
9 | #import "NSDate+Utils.h"
10 |
11 | #define DATE_COMPONENTS (NSCalendarUnitYear| NSCalendarUnitMonth | NSCalendarUnitDay | kCFCalendarUnitWeekOfMonth | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond | NSCalendarUnitWeekday | NSCalendarUnitWeekdayOrdinal)
12 | #define CURRENT_CALENDAR [NSCalendar currentCalendar]
13 |
14 | #define D_MINUTE 60
15 | #define D_HOUR 3600
16 | #define D_DAY 86400
17 | #define D_WEEK 604800
18 | #define D_YEAR 31556926
19 |
20 | @implementation NSDate (Utils)
21 |
22 | + (NSDate *)dateWithYear:(NSInteger)year
23 | month:(NSInteger)month
24 | day:(NSInteger)day
25 | hour:(NSInteger)hour
26 | minute:(NSInteger)minute
27 | second:(NSInteger)second{
28 |
29 | NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
30 |
31 | NSTimeZone *systemTimeZone = [NSTimeZone systemTimeZone];
32 |
33 | NSDateComponents *dateComps = [[NSDateComponents alloc] init];
34 | [dateComps setCalendar:gregorian];
35 | [dateComps setYear:year];
36 | [dateComps setMonth:month];
37 | [dateComps setDay:day];
38 | [dateComps setTimeZone:systemTimeZone];
39 | [dateComps setHour:hour];
40 | [dateComps setMinute:minute];
41 | [dateComps setSecond:second];
42 |
43 |
44 | return [dateComps date];
45 | }
46 |
47 | + (NSInteger)daysOffsetBetweenStartDate:(NSDate *)startDate endDate:(NSDate *)endDate
48 | {
49 | NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
50 | unsigned int unitFlags = NSCalendarUnitDay;
51 | NSDateComponents *comps = [gregorian components:unitFlags fromDate:startDate toDate:endDate options:0];
52 | NSInteger days = [comps day];
53 | return days;
54 | }
55 |
56 | + (NSDate *)dateWithHour:(int)hour
57 | minute:(int)minute
58 | {
59 | NSDate *now = [NSDate date];
60 | NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
61 | NSDateComponents *components = [calendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay fromDate:now];
62 | [components setHour:hour];
63 | [components setMinute:minute];
64 | NSDate *newDate = [calendar dateFromComponents:components];
65 | return newDate;
66 | }
67 |
68 | #pragma mark - Data component
69 | - (NSInteger)year
70 | {
71 | NSDateComponents *dateComponents = [[NSCalendar currentCalendar] components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay|NSCalendarUnitHour|NSCalendarUnitMinute fromDate:self];
72 | return [dateComponents year];
73 | }
74 |
75 | - (NSInteger)month
76 | {
77 | NSDateComponents *dateComponents = [[NSCalendar currentCalendar] components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay|NSCalendarUnitHour|NSCalendarUnitMinute fromDate:self];
78 | return [dateComponents month];
79 | }
80 |
81 | - (NSInteger)day
82 | {
83 | NSDateComponents *dateComponents = [[NSCalendar currentCalendar] components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay|NSCalendarUnitHour|NSCalendarUnitMinute fromDate:self];
84 | return [dateComponents day];
85 | }
86 |
87 | - (NSInteger)hour
88 | {
89 | NSDateComponents *dateComponents = [[NSCalendar currentCalendar] components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay|NSCalendarUnitHour|NSCalendarUnitMinute fromDate:self];
90 | return [dateComponents hour];
91 | }
92 |
93 | - (NSInteger)minute
94 | {
95 | NSDateComponents *dateComponents = [[NSCalendar currentCalendar] components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay|NSCalendarUnitHour|NSCalendarUnitMinute fromDate:self];
96 | return [dateComponents minute];
97 | }
98 |
99 | - (NSInteger)second
100 | {
101 | NSDateComponents *dateComponents = [[NSCalendar currentCalendar] components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay|NSCalendarUnitHour|NSCalendarUnitMinute fromDate:self];
102 | return [dateComponents second];
103 | }
104 |
105 | - (NSString *)weekday
106 | {
107 | NSCalendar*calendar = [NSCalendar currentCalendar];
108 | NSDateComponents*comps;
109 |
110 | NSDate *date = [NSDate date];
111 | comps =[calendar components:(NSCalendarUnitWeekOfMonth | NSCalendarUnitWeekday |NSCalendarUnitWeekdayOrdinal)
112 | fromDate:date];
113 | NSInteger weekday = [comps weekday]; // 星期几(注意,周日是“1”,周一是“2”。。。。)
114 | NSString *week = @"";
115 | switch (weekday) {
116 | case 1:
117 | week = @"星期日";
118 | break;
119 | case 2:
120 | week = @"星期一";
121 | break;
122 | case 3:
123 | week = @"星期二";
124 | break;
125 | case 4:
126 | week = @"星期三";
127 | break;
128 | case 5:
129 | week = @"星期四";
130 | break;
131 | case 6:
132 | week = @"星期五";
133 | break;
134 | case 7:
135 | week = @"星期六";
136 | break;
137 |
138 | default:
139 | break;
140 | }
141 |
142 | return week;
143 | }
144 |
145 |
146 |
147 |
148 | #pragma mark - Time string
149 | - (NSString *)timeHourMinute
150 | {
151 |
152 | return [self timeHourMinuteWithPrefix:NO suffix:NO];
153 | }
154 |
155 | - (NSString *)timeHourMinuteWithPrefix
156 | {
157 | return [self timeHourMinuteWithPrefix:YES suffix:NO];
158 | }
159 |
160 | - (NSString *)timeHourMinuteWithSuffix
161 | {
162 | return [self timeHourMinuteWithPrefix:NO suffix:YES];
163 | }
164 |
165 | - (NSString *)timeHourMinuteWithPrefix:(BOOL)enablePrefix suffix:(BOOL)enableSuffix
166 | {
167 | NSDateFormatter *formatter = [[NSDateFormatter alloc]init];
168 | [formatter setDateFormat:@"HH:mm"];
169 | NSString *timeStr = [formatter stringFromDate:self];
170 | if (enablePrefix) {
171 | timeStr = [NSString stringWithFormat:@"%@%@",([self hour] > 12 ? @"下午" : @"上午"),timeStr];
172 | }
173 | if (enableSuffix) {
174 | timeStr = [NSString stringWithFormat:@"%@%@",([self hour] > 12 ? @"pm" : @"am"),timeStr];
175 | }
176 | return timeStr;
177 | }
178 |
179 |
180 | #pragma mark - Date String
181 | - (NSString *)stringTime
182 | {
183 | NSDateFormatter *formatter = [[NSDateFormatter alloc]init];
184 | [formatter setDateFormat:@"HH:mm"];
185 | NSString *str = [formatter stringFromDate:self];
186 | return str;
187 | }
188 |
189 | - (NSString *)stringMonthDay
190 | {
191 | return [NSDate dateMonthDayWithDate:self];
192 | }
193 |
194 | - (NSString *)stringYearMonthDay
195 | {
196 | return [NSDate stringYearMonthDayWithDate:self];
197 | }
198 |
199 | - (NSString *)stringYearMonthDayHourMinuteSecond
200 | {
201 | NSDateFormatter *formatter = [[NSDateFormatter alloc]init];
202 | [formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
203 | NSString *str = [formatter stringFromDate:self];
204 | return str;
205 |
206 | }
207 |
208 | - (NSString *)stringYearMonthDayCompareToday
209 | {
210 | NSString *str;
211 | NSInteger chaDay = [self daysBetweenCurrentDateAndDate];
212 | if (chaDay == 0) {
213 | str = @"今天";
214 | }else if (chaDay == 1){
215 | str = @"明天";
216 | }else if (chaDay == -1){
217 | str = @"昨天";
218 | }else{
219 | str = [self stringYearMonthDay];
220 | }
221 |
222 | return str;
223 | }
224 |
225 | + (NSString *)stringLoacalDate
226 | {
227 | NSDateFormatter *format = [[NSDateFormatter alloc] init];
228 | [format setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]];
229 | [format setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
230 | NSDate *date = [NSDate date];
231 | NSTimeZone *zone = [NSTimeZone systemTimeZone];
232 | NSInteger interval = [zone secondsFromGMTForDate: date];
233 | NSDate *localeDate = [date dateByAddingTimeInterval: interval];
234 | NSString *dateStr = [format stringFromDate:localeDate];
235 |
236 | return dateStr;
237 | }
238 |
239 | + (NSString *)stringYearMonthDayWithDate:(NSDate *)date
240 | {
241 | if (date == nil) {
242 | date = [NSDate date];
243 | }
244 | NSDateFormatter *formatter = [[NSDateFormatter alloc]init];
245 | [formatter setDateFormat:@"yyyy-MM-dd"];
246 | NSString *str = [formatter stringFromDate:date];
247 | return str;
248 | }
249 |
250 | + (NSString *)dateMonthDayWithDate:(NSDate *)date
251 | {
252 | if (date == nil) {
253 | date = [NSDate date];
254 | }
255 |
256 | NSDateFormatter *formatter = [[NSDateFormatter alloc]init];
257 | [formatter setDateFormat:@"MM.dd"];
258 | NSString *str = [formatter stringFromDate:date];
259 | return str;
260 | }
261 |
262 |
263 | #pragma mark - Date formate
264 |
265 | + (NSString *)dateFormatString {
266 | return @"yyyy-MM-dd";
267 | }
268 |
269 | + (NSString *)timeFormatString {
270 | return @"HH:mm:ss";
271 | }
272 |
273 | + (NSString *)timestampFormatString {
274 | return @"yyyy-MM-dd HH:mm:ss";
275 | }
276 |
277 | + (NSString *)timestampFormatStringSubSeconds
278 | {
279 | return @"yyyy-MM-dd HH:mm";
280 | }
281 |
282 | #pragma mark - Date adjust
283 | - (NSDate *) dateByAddingDays: (NSInteger) dDays
284 | {
285 | NSTimeInterval aTimeInterval = [self timeIntervalSinceReferenceDate] + D_DAY * dDays;
286 | NSDate *newDate = [NSDate dateWithTimeIntervalSinceReferenceDate:aTimeInterval];
287 | return newDate;
288 | }
289 |
290 | - (NSDate *) dateBySubtractingDays: (NSInteger) dDays
291 | {
292 | return [self dateByAddingDays: (dDays * -1)];
293 | }
294 |
295 | #pragma mark - Relative Dates
296 | + (NSDate *) dateWithDaysFromNow: (NSInteger) days
297 | {
298 | // Thanks, Jim Morrison
299 | return [[NSDate date] dateByAddingDays:days];
300 | }
301 |
302 | + (NSDate *) dateWithDaysBeforeNow: (NSInteger) days
303 | {
304 | // Thanks, Jim Morrison
305 | return [[NSDate date] dateBySubtractingDays:days];
306 | }
307 |
308 | + (NSDate *) dateTomorrow
309 | {
310 | return [NSDate dateWithDaysFromNow:1];
311 | }
312 |
313 | + (NSDate *) dateYesterday
314 | {
315 | return [NSDate dateWithDaysBeforeNow:1];
316 | }
317 |
318 | + (NSDate *) dateWithHoursFromNow: (NSInteger) dHours
319 | {
320 | NSTimeInterval aTimeInterval = [[NSDate date] timeIntervalSinceReferenceDate] + D_HOUR * dHours;
321 | NSDate *newDate = [NSDate dateWithTimeIntervalSinceReferenceDate:aTimeInterval];
322 | return newDate;
323 | }
324 |
325 | + (NSDate *) dateWithHoursBeforeNow: (NSInteger) dHours
326 | {
327 | NSTimeInterval aTimeInterval = [[NSDate date] timeIntervalSinceReferenceDate] - D_HOUR * dHours;
328 | NSDate *newDate = [NSDate dateWithTimeIntervalSinceReferenceDate:aTimeInterval];
329 | return newDate;
330 | }
331 |
332 | + (NSDate *) dateWithMinutesFromNow: (NSInteger) dMinutes
333 | {
334 | NSTimeInterval aTimeInterval = [[NSDate date] timeIntervalSinceReferenceDate] + D_MINUTE * dMinutes;
335 | NSDate *newDate = [NSDate dateWithTimeIntervalSinceReferenceDate:aTimeInterval];
336 | return newDate;
337 | }
338 |
339 | + (NSDate *) dateWithMinutesBeforeNow: (NSInteger) dMinutes
340 | {
341 | NSTimeInterval aTimeInterval = [[NSDate date] timeIntervalSinceReferenceDate] - D_MINUTE * dMinutes;
342 | NSDate *newDate = [NSDate dateWithTimeIntervalSinceReferenceDate:aTimeInterval];
343 | return newDate;
344 | }
345 |
346 |
347 | - (BOOL) isEqualToDateIgnoringTime: (NSDate *) aDate
348 | {
349 | NSDateComponents *components1 = [CURRENT_CALENDAR components:DATE_COMPONENTS fromDate:self];
350 | NSDateComponents *components2 = [CURRENT_CALENDAR components:DATE_COMPONENTS fromDate:aDate];
351 | return ((components1.year == components2.year) &&
352 | (components1.month == components2.month) &&
353 | (components1.day == components2.day));
354 | }
355 |
356 |
357 | + (NSDate *) dateStandardFormatTimeZeroWithDate: (NSDate *) aDate{
358 | NSString *str = [[NSDate stringYearMonthDayWithDate:aDate]stringByAppendingString:@" 00:00:00"];
359 | NSDate *date = [NSDate dateFromString:str];
360 | return date;
361 | }
362 |
363 | - (NSInteger) daysBetweenCurrentDateAndDate
364 | {
365 | //只取年月日比较
366 | NSDate *dateSelf = [NSDate dateStandardFormatTimeZeroWithDate:self];
367 | NSTimeInterval timeInterval = [dateSelf timeIntervalSince1970];
368 | NSDate *dateNow = [NSDate dateStandardFormatTimeZeroWithDate:nil];
369 | NSTimeInterval timeIntervalNow = [dateNow timeIntervalSince1970];
370 |
371 | NSTimeInterval cha = timeInterval - timeIntervalNow;
372 | CGFloat chaDay = cha / 86400.0;
373 | NSInteger day = chaDay * 1;
374 | return day;
375 | }
376 |
377 | #pragma mark - Date and string convert
378 | + (NSDate *)dateFromString:(NSString *)string {
379 | return [NSDate dateFromString:string withFormat:[NSDate dbFormatString]];
380 | }
381 |
382 | + (NSDate *)dateFromString:(NSString *)string withFormat:(NSString *)format {
383 | NSDateFormatter *inputFormatter = [[NSDateFormatter alloc] init];
384 | [inputFormatter setDateFormat:format];
385 | NSDate *date = [inputFormatter dateFromString:string];
386 | return date;
387 | }
388 |
389 | - (NSString *)string {
390 | return [self stringWithFormat:[NSDate dbFormatString]];
391 | }
392 |
393 | - (NSString *)stringCutSeconds
394 | {
395 | return [self stringWithFormat:[NSDate timestampFormatStringSubSeconds]];
396 | }
397 |
398 | - (NSString *)stringWithFormat:(NSString *)format {
399 | NSDateFormatter *outputFormatter = [[NSDateFormatter alloc] init];
400 | [outputFormatter setDateFormat:format];
401 | NSString *timestamp_str = [outputFormatter stringFromDate:self];
402 | return timestamp_str;
403 | }
404 |
405 | + (NSString *)dbFormatString {
406 | return [NSDate timestampFormatString];
407 | }
408 | @end
409 |
--------------------------------------------------------------------------------
/TLMessageView/Chat/Vendor/PHAsset+Extend.h:
--------------------------------------------------------------------------------
1 | //
2 | // PHAsset+Extend.h
3 | // TLMessageView
4 | //
5 | // Created by 郭锐 on 2016/11/20.
6 | // Copyright © 2016年 com.garry.message. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface PHAsset (Extend)
12 | //选中状态
13 | @property(nonatomic,assign)BOOL selected;
14 | //原图
15 | @property(nonatomic,assign)BOOL isOriginal;
16 | @end
17 |
--------------------------------------------------------------------------------
/TLMessageView/Chat/Vendor/PHAsset+Extend.m:
--------------------------------------------------------------------------------
1 | //
2 | // PHAsset+Extend.m
3 | // TLMessageView
4 | //
5 | // Created by 郭锐 on 2016/11/20.
6 | // Copyright © 2016年 com.garry.message. All rights reserved.
7 | //
8 |
9 | #import "PHAsset+Extend.h"
10 | #import
11 |
12 | static char selectedKey;
13 | static char originalKey;
14 |
15 | @implementation PHAsset (Extend)
16 | -(void)setSelected:(BOOL)selected{
17 | objc_setAssociatedObject(self, &selectedKey, @(selected), OBJC_ASSOCIATION_ASSIGN);
18 | }
19 | -(BOOL)selected{
20 | return [objc_getAssociatedObject(self, &selectedKey) boolValue];
21 | }
22 | -(void)setIsOriginal:(BOOL)isOriginal{
23 | objc_setAssociatedObject(self, &originalKey, @(isOriginal), OBJC_ASSOCIATION_ASSIGN);
24 | }
25 | -(BOOL)isOriginal{
26 | return [objc_getAssociatedObject(self, &originalKey) boolValue];
27 | }
28 | @end
29 |
--------------------------------------------------------------------------------
/TLMessageView/Chat/Vendor/TLRCManager.h:
--------------------------------------------------------------------------------
1 | //
2 | // TLRCManager.h
3 | // TLMessageView
4 | //
5 | // Created by 郭锐 on 16/8/20.
6 | // Copyright © 2016年 com.garry.message. All rights reserved.
7 | //
8 |
9 | #import "TLProjectMacro.h"
10 |
11 | @protocol TLRCManagerDelegate
12 | -(void)rcManagerReceiveMsg:(RCMessage *)msg;
13 | @end
14 |
15 | @interface TLRCManager : NSObject
16 | @property(nonatomic,assign)id delegate;
17 | +(TLRCManager *)shareManager;
18 | - (void)initEnv;
19 | - (void)connectWithToken:(NSString*)token;
20 | - (void)sendMessage:(RCMessage *)message
21 | successBlock:(void(^)(RCMessage *))successBlock
22 | failedBlock:(void(^)(RCMessage *))failedBlock;
23 | @end
24 |
--------------------------------------------------------------------------------
/TLMessageView/Chat/Vendor/TLRCManager.m:
--------------------------------------------------------------------------------
1 | //
2 | // TLRCManager.m
3 | // TLMessageView
4 | //
5 | // Created by 郭锐 on 16/8/20.
6 | // Copyright © 2016年 com.garry.message. All rights reserved.
7 | //
8 |
9 | #import "TLRCManager.h"
10 |
11 | static NSString *RongCloundKey = @"y745wfm844a4v";
12 |
13 | @interface TLRCManager ()
14 |
15 | @end
16 |
17 | @implementation TLRCManager
18 | +(TLRCManager *)shareManager{
19 | static dispatch_once_t onceToken;
20 | static TLRCManager *player;
21 | dispatch_once(&onceToken, ^{
22 | player = [[TLRCManager alloc] init];
23 | });
24 | return player;
25 | }
26 | - (instancetype) init{
27 | if (self = [super init]) {
28 | [[RCIMClient sharedRCIMClient] setReceiveMessageDelegate:self object:nil];
29 | [[RCIMClient sharedRCIMClient] setRCConnectionStatusChangeDelegate:self];
30 | }
31 | return self;
32 | }
33 |
34 | - (void) initEnv{
35 | [[RCIMClient sharedRCIMClient] initWithAppKey:RongCloundKey];
36 | }
37 |
38 | - (void) disconnect{
39 | [[RCIMClient sharedRCIMClient] logout];
40 | }
41 |
42 | - (void) connectWithToken:(NSString*)token{
43 | if (!token) return;
44 | [[RCIMClient sharedRCIMClient] connectWithToken:token success:^(NSString *userId) {
45 | NSLog(@"****************************************");
46 | NSLog(@"Rong connectWithToken success");
47 | NSLog(@"Rong uid is:%@", userId);
48 | NSLog(@"****************************************");
49 | } error:^(RCConnectErrorCode status) {
50 | NSLog(@"💥Rong connectWithToken error:%tu", status);
51 | } tokenIncorrect:^{
52 | NSLog(@"💥Rong connectWithToken incorrect");
53 | }];
54 | }
55 | - (void) sendMessage:(RCMessage *)message
56 | successBlock:(void(^)(RCMessage *))successBlock
57 | failedBlock:(void(^)(RCMessage *))failedBlock
58 | {
59 | [[RCIMClient sharedRCIMClient] sendMessage:message.conversationType targetId:message.targetId content:message.content pushContent:nil pushData:nil success:^(long messageId) {
60 | dispatch_async(dispatch_get_main_queue(), ^{
61 | if (successBlock) {
62 | successBlock(message);
63 | }
64 | });
65 | } error:^(RCErrorCode nErrorCode, long messageId) {
66 | dispatch_async(dispatch_get_main_queue(), ^{
67 | if (failedBlock) {
68 | failedBlock(message);
69 | }
70 | });
71 | }];
72 | }
73 | - (void)onReceived:(RCMessage *)message
74 | left:(int)nLeft
75 | object:(id)object{
76 | if (message.conversationType == ConversationType_PRIVATE) {
77 | if ([self.delegate respondsToSelector:@selector(rcManagerReceiveMsg:)]) {
78 | [self.delegate rcManagerReceiveMsg:message];
79 | }
80 | }
81 | }
82 |
83 | - (void)onConnectionStatusChanged:(RCConnectionStatus)status
84 | {
85 | NSLog(@"Rong cloud status:%tu", status);
86 | }
87 | @end
88 |
--------------------------------------------------------------------------------
/TLMessageView/Chat/Vendor/TLRecordVoice.h:
--------------------------------------------------------------------------------
1 | //
2 | // TLRecordVoice.h
3 | // TLMessageView
4 | //
5 | // Created by 郭锐 on 16/8/19.
6 | // Copyright © 2016年 com.garry.message. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @protocol TLRecorderVoiceDelegate
12 | -(void)recorderVoiceFailure;
13 | -(void)recorderVoiceSuccessWithVoiceData:(NSData *)voiceData duration:(long)duration;
14 | @end
15 |
16 | @interface TLRecordVoice : NSObject
17 | @property(nonatomic,assign)id delegate;
18 | -(instancetype)initWithDelegate:(id )delegate;
19 | -(void)startRecord;
20 | -(void)completeRecord;
21 | -(void)cancelRecord;
22 | @end
23 |
--------------------------------------------------------------------------------
/TLMessageView/Chat/Vendor/TLRecordVoice.m:
--------------------------------------------------------------------------------
1 | //
2 | // TLRecordVoice.m
3 | // TLMessageView
4 | //
5 | // Created by 郭锐 on 16/8/19.
6 | // Copyright © 2016年 com.garry.message. All rights reserved.
7 | //
8 |
9 | #import "TLRecordVoice.h"
10 | #import
11 | #import "TLRecordVoiceHUD.h"
12 |
13 | @interface TLRecordVoice ()
14 | @property(nonatomic,strong)AVAudioSession *audioSession;
15 | @property(nonatomic,strong)AVAudioRecorder *audioRecorder;
16 | @property(nonatomic,strong)NSTimer *timer;
17 | @end
18 |
19 | @implementation TLRecordVoice
20 | -(instancetype)initWithDelegate:(id)delegate{
21 | if (self = [super init]) {
22 | self.delegate = delegate;
23 | }
24 | return self;
25 | }
26 | -(void)startRecord{
27 | [self configSession];
28 | [self configRecord];
29 | [self.audioRecorder record];
30 |
31 | [TLRecordVoiceHUD showRecording];
32 | self.timer = [NSTimer scheduledTimerWithTimeInterval:0 target:self selector:@selector(observerVoiceVolume) userInfo:nil repeats:YES];
33 | }
34 | -(void)completeRecord{
35 | if (self.audioRecorder.currentTime > 2) {
36 | [TLRecordVoiceHUD dismiss];
37 | NSData *data = [[NSData alloc] initWithContentsOfURL:[self recordFilePath]];
38 | if ([self.delegate respondsToSelector:@selector(recorderVoiceSuccessWithVoiceData:duration:)]) {
39 | [self.delegate recorderVoiceSuccessWithVoiceData:data duration:self.audioRecorder.currentTime];
40 | }
41 | }else {
42 | [TLRecordVoiceHUD dismissWithRecordShort];
43 | }
44 | [self endRecord];
45 | }
46 | -(void)cancelRecord{
47 | [self endRecord];
48 | [TLRecordVoiceHUD dismiss];
49 | }
50 | - (void)endRecord{
51 | [self.audioRecorder deleteRecording];
52 | [self.audioRecorder stop];
53 | [self.timer invalidate];
54 | }
55 | - (void)observerVoiceVolume{
56 | [self.audioRecorder updateMeters];
57 | CGFloat lowPassResults = pow(10, (0.05 * [self.audioRecorder peakPowerForChannel:0]));
58 | [TLRecordVoiceHUD updatePeakPower:lowPassResults];
59 | }
60 |
61 | -(void)configSession{
62 | self.audioSession = [AVAudioSession sharedInstance];
63 |
64 | NSError *error;
65 | [self.audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:&error];
66 |
67 | if (self.audioSession) {
68 | [self.audioSession setActive:YES error:nil];
69 | }
70 | }
71 | -(void)configRecord{
72 | NSDictionary *settings = @{AVFormatIDKey : @(kAudioFormatLinearPCM),
73 | AVSampleRateKey : @8000.00f,
74 | AVNumberOfChannelsKey : @1,
75 | AVLinearPCMBitDepthKey : @16,
76 | AVLinearPCMIsNonInterleaved : @NO,
77 | AVLinearPCMIsFloatKey : @NO,
78 | AVLinearPCMIsBigEndianKey : @NO};
79 |
80 | NSURL *tmpUrl = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:@"tmp"]];
81 |
82 | NSError *error;
83 | self.audioRecorder = [[AVAudioRecorder alloc] initWithURL:tmpUrl
84 | settings:settings
85 | error:&error];
86 | if (error) NSLog(@"%@",error);
87 |
88 | self.audioRecorder.meteringEnabled = YES;
89 | self.audioRecorder.delegate = self;
90 | [self.audioRecorder prepareToRecord];
91 | }
92 | -(NSURL *)recordFilePath{
93 | return [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:@"tmp"]];
94 | }
95 | @end
96 |
--------------------------------------------------------------------------------
/TLMessageView/Chat/Vendor/TLVoicePlayer.h:
--------------------------------------------------------------------------------
1 | //
2 | // TLVoicePlayer.h
3 | // TLMessageView
4 | //
5 | // Created by 郭锐 on 16/8/19.
6 | // Copyright © 2016年 com.garry.message. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | @interface TLVoicePlayer : NSObject
13 | +(TLVoicePlayer *)sharePlayer;
14 | -(void)playVoiceWithData:(NSData *)data didFinish:(void (^)(void))didFinish;
15 | -(void)endPlay;
16 | @end
17 |
--------------------------------------------------------------------------------
/TLMessageView/Chat/Vendor/TLVoicePlayer.m:
--------------------------------------------------------------------------------
1 | //
2 | // TLVoicePlayer.m
3 | // TLMessageView
4 | //
5 | // Created by 郭锐 on 16/8/19.
6 | // Copyright © 2016年 com.garry.message. All rights reserved.
7 | //
8 |
9 | #import "TLVoicePlayer.h"
10 | #import
11 |
12 | @interface TLVoicePlayer ()
13 | @property (nonatomic,strong)AVAudioPlayer *player;
14 | @property (nonatomic,copy)void (^didFinishBlock)(void);
15 | @end
16 |
17 | @implementation TLVoicePlayer
18 | +(TLVoicePlayer *)sharePlayer{
19 | static dispatch_once_t onceToken;
20 | static TLVoicePlayer *player;
21 | dispatch_once(&onceToken, ^{
22 | player = [[TLVoicePlayer alloc] init];
23 | });
24 | return player;
25 | }
26 | -(instancetype)init{
27 | if (self = [super init]) {
28 | [[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayback error: nil];
29 | }
30 | return self;
31 | }
32 | -(void)playVoiceWithData:(NSData *)data didFinish:(void (^)(void))didFinish{
33 | if (self.player) {
34 | [self.player stop];
35 | self.player.delegate = nil;
36 | self.player = nil;
37 | }
38 | self.didFinishBlock = didFinish;
39 | NSError *error;
40 | self.player = [[AVAudioPlayer alloc] initWithData:data error:&error];
41 | self.player.volume = 1.0f;
42 | if (error) NSLog(@"%@", error);
43 | self.player.delegate = self;
44 | [self.player play];
45 | }
46 | -(void)endPlay{
47 | if (self.player && self.player.isPlaying) {
48 | [self.player stop];
49 | }
50 | }
51 | - (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{
52 | if (self.didFinishBlock) self.didFinishBlock();
53 | }
54 | @end
55 |
--------------------------------------------------------------------------------
/TLMessageView/Chat/View/TLButton.h:
--------------------------------------------------------------------------------
1 | //
2 | // TLButton.h
3 | // TLMessageView
4 | //
5 | // Created by 郭锐 on 16/8/22.
6 | // Copyright © 2016年 com.garry.message. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface TLButton : UIButton
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/TLMessageView/Chat/View/TLButton.m:
--------------------------------------------------------------------------------
1 | //
2 | // TLButton.m
3 | // TLMessageView
4 | //
5 | // Created by 郭锐 on 16/8/22.
6 | // Copyright © 2016年 com.garry.message. All rights reserved.
7 | //
8 |
9 | #import "TLButton.h"
10 |
11 | @implementation TLButton
12 | /**
13 | * 放大热区
14 | *
15 | */
16 | - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event{
17 | CGRect bounds = self.bounds;
18 | CGFloat widthDelta = MAX(44.0 - bounds.size.width, 0);
19 | CGFloat heightDelta = MAX(44.0 - bounds.size.height, 0);
20 | bounds = CGRectInset(bounds, -0.5 * widthDelta, -0.5 * heightDelta);
21 | return CGRectContainsPoint(bounds, point);
22 | }
23 | @end
24 |
--------------------------------------------------------------------------------
/TLMessageView/Chat/View/TLChatEmojiBoard.h:
--------------------------------------------------------------------------------
1 | //
2 | // TLChatEmojiBoard.h
3 | // TLMessageView
4 | //
5 | // Created by 郭锐 on 2016/8/18.
6 | // Copyright © 2016年 com.garry.message. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | static NSInteger emojiBoardHeight = 223;
12 |
13 | @protocol TLChatEmojiBoardDelegate
14 | -(void)chatEmojiBoarDidSelectEmoji:(NSString *)emoji;
15 | -(void)chatEmojiBoarDidClickBackspace;
16 | -(void)chatEmojiBoarDidClickSend;
17 | @end
18 |
19 | @interface TLChatEmojiBoard : UIView
20 | @property(nonatomic,assign)BOOL show;
21 | @property(nonatomic,assign)id delegate;
22 | @end
23 |
--------------------------------------------------------------------------------
/TLMessageView/Chat/View/TLChatEmojiBoard.m:
--------------------------------------------------------------------------------
1 | //
2 | // TLChatEmojiBoard.m
3 | // TLMessageView
4 | //
5 | // Created by 郭锐 on 2016/8/18.
6 | // Copyright © 2016年 com.garry.message. All rights reserved.
7 | //
8 |
9 | #import "TLChatEmojiBoard.h"
10 | #import "TLProjectMacro.h"
11 | #import "TLButton.h"
12 |
13 | @interface TLChatEmojiBoard ()
14 | @property(nonatomic,strong)UIScrollView *pageScrollView;
15 | @property(nonatomic,strong)UIView *bottomView;
16 | @property(nonatomic,strong)UIButton *emojiSwitchBtn;
17 | @property(nonatomic,strong)UIButton *sendBtn;
18 | @property(nonatomic,strong)UIPageControl *pageControl;
19 | @property(nonatomic,strong)NSMutableArray *pageViews;
20 | @property(nonatomic,strong)NSArray *emojis;
21 | @end
22 |
23 | @implementation TLChatEmojiBoard
24 | {
25 | BOOL hasLayout;
26 | }
27 | -(instancetype)init{
28 | if (self = [super init]) {
29 | [self addSubview:self.pageScrollView];
30 | [self.pageScrollView mas_makeConstraints:^(MASConstraintMaker *make) {
31 | make.left.equalTo(self.mas_left).offset(0);
32 | make.right.equalTo(self.mas_right).offset(0);
33 | make.top.equalTo(self.mas_top).offset(0);
34 | }];
35 |
36 | [self addSubview:self.bottomView];
37 | [self.bottomView mas_makeConstraints:^(MASConstraintMaker *make) {
38 | make.left.equalTo(self.mas_left).offset(0);
39 | make.right.equalTo(self.mas_right).offset(0);
40 | make.top.equalTo(self.pageScrollView.mas_bottom).offset(0);
41 | make.bottom.equalTo(self.mas_bottom).offset(0);
42 | make.height.mas_offset(@37);
43 | }];
44 |
45 | [self.bottomView addSubview:self.emojiSwitchBtn];
46 | [self.emojiSwitchBtn mas_makeConstraints:^(MASConstraintMaker *make) {
47 | make.left.equalTo(self.bottomView.mas_left).offset(0);
48 | make.top.equalTo(self.bottomView.mas_top).offset(0);
49 | make.bottom.equalTo(self.bottomView.mas_bottom).offset(0);
50 | make.width.equalTo(self.emojiSwitchBtn.mas_height);
51 | }];
52 |
53 | [self.bottomView addSubview:self.sendBtn];
54 | [self.sendBtn mas_makeConstraints:^(MASConstraintMaker *make) {
55 | make.right.equalTo(self.bottomView.mas_right).offset(0);
56 | make.top.equalTo(self.bottomView.mas_top).offset(0);
57 | make.bottom.equalTo(self.bottomView.mas_bottom).offset(0);
58 | make.width.mas_offset(@52);
59 | }];
60 |
61 | self.pageViews = [NSMutableArray array];
62 | for (int i = 0; i < self.emojis.count; i ++ ) {
63 | NSArray *emojiArray = self.emojis[i];
64 | UIView *pageView = [[UIView alloc] init];
65 | [self.pageScrollView addSubview:pageView];
66 | [self.pageViews addObject:pageView];
67 |
68 | for (int j = 0; j < emojiArray.count; j ++ ) {
69 | TLButton *emojiBtn = [TLButton buttonWithType:UIButtonTypeCustom];
70 | [emojiBtn setTitle:emojiArray[j] forState:UIControlStateNormal];
71 | [pageView addSubview:emojiBtn];
72 | [emojiBtn addTarget:self action:@selector(didClickEmoji:) forControlEvents:UIControlEventTouchUpInside];
73 | if (j == emojiArray.count - 1) {
74 | [emojiBtn setImage:[UIImage imageNamed:@"emoji_btn_delete"] forState:UIControlStateNormal];
75 | emojiBtn.tag = 99;
76 | }
77 | }
78 | }
79 |
80 | self.pageControl.numberOfPages = self.emojis.count;
81 | [self addSubview:self.pageControl];
82 | [self.pageControl mas_makeConstraints:^(MASConstraintMaker *make) {
83 | make.centerX.equalTo(self.mas_centerX).offset(0);
84 | make.bottom.equalTo(self.pageScrollView.mas_bottom).offset(0);
85 | }];
86 | }
87 | return self;
88 | }
89 | -(void)didClickEmoji:(UIButton *)sender{
90 | if (sender.tag == 99) {
91 | if ([self.delegate respondsToSelector:@selector(chatEmojiBoarDidClickBackspace)]) {
92 | [self.delegate chatEmojiBoarDidClickBackspace];
93 | }
94 | }else{
95 | NSString *emoji = sender.titleLabel.text;
96 | if ([self.delegate respondsToSelector:@selector(chatEmojiBoarDidSelectEmoji:)]) {
97 | [self.delegate chatEmojiBoarDidSelectEmoji:emoji];
98 | }
99 | }
100 | }
101 | -(void)didClickSend:(UIButton *)sender{
102 | if ([self.delegate respondsToSelector:@selector(chatEmojiBoarDidClickSend)]) {
103 | [self.delegate chatEmojiBoarDidClickSend];
104 | }
105 | }
106 | -(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
107 | self.pageControl.currentPage = scrollView.contentOffset.x / scrollView.frame.size.width;
108 | }
109 | -(void)layoutSubviews{
110 | [super layoutSubviews];
111 |
112 | if (hasLayout) {
113 | return;
114 | }
115 | hasLayout = YES;
116 |
117 | CGSize emojiBtnSize = CGSizeMake(30, 34);
118 |
119 | //横向间距
120 | CGFloat emojiHorizontalSpacing = (self.frame.size.width - 50 - emojiBtnSize.width * 8) / 7.0;
121 | //纵向间距
122 | CGFloat emojiVerticalSpacing = (self.pageScrollView.frame.size.height - 25 - self.pageControl.frame.size.height - emojiBtnSize.height * 3) / 2.0;
123 |
124 | UIView *lastView = nil;
125 | for (int i = 0; i < self.pageViews.count; i ++ ) {
126 | UIView *pageView = self.pageViews[i];
127 |
128 | if (!lastView) {
129 | [pageView mas_makeConstraints:^(MASConstraintMaker *make) {
130 | make.left.equalTo(self.pageScrollView.mas_left).offset(0);
131 | }];
132 | }else{
133 | [pageView mas_makeConstraints:^(MASConstraintMaker *make) {
134 | make.left.equalTo(lastView.mas_right).offset(0);
135 | }];
136 | if (i == self.pageViews.count - 1) {
137 | [pageView mas_makeConstraints:^(MASConstraintMaker *make) {
138 | make.right.equalTo(self.pageScrollView.mas_right).offset(0);
139 | }];
140 | }
141 | }
142 |
143 | [pageView mas_makeConstraints:^(MASConstraintMaker *make) {
144 | make.top.equalTo(self.pageScrollView.mas_top).offset(0);
145 | make.bottom.equalTo(self.pageScrollView.mas_bottom).offset(0);
146 | make.height.equalTo(self.pageScrollView.mas_height);
147 | make.width.equalTo(self.mas_width);
148 | }];
149 |
150 | UIView *lastBtn = nil;
151 | for (int j = 0; j < pageView.subviews.count; j ++ ) {
152 | UIButton *emojiBtn = pageView.subviews[j];
153 | if (j % 8 == 0) {
154 | [emojiBtn mas_makeConstraints:^(MASConstraintMaker *make) {
155 | make.left.equalTo(pageView.mas_left).offset(25);
156 | if (lastBtn) {
157 | make.top.equalTo(lastBtn.mas_bottom).offset(emojiVerticalSpacing);
158 | }else{
159 | make.top.equalTo(pageView.mas_top).offset(25);
160 | }
161 | }];
162 |
163 | }else{
164 | [emojiBtn mas_makeConstraints:^(MASConstraintMaker *make) {
165 | make.left.equalTo(lastBtn.mas_right).offset(emojiHorizontalSpacing);
166 | make.top.equalTo(lastBtn.mas_top).offset(0);
167 | }];
168 | }
169 | [emojiBtn mas_makeConstraints:^(MASConstraintMaker *make) {
170 | make.size.mas_offset(emojiBtnSize);
171 | }];
172 | lastBtn = emojiBtn;
173 | }
174 | lastView = pageView;
175 | }
176 | }
177 | -(NSArray *)emojis{
178 | if (!_emojis) {
179 | _emojis = @[
180 | @[@"😊",@"😨",@"😍",@"😳",@"😎",@"😭",@"😌",@"😵",@"😴",@"😢",@"😅",@"😡",@"😜",@"😀",@"😲",@"😟",@"😤",@"😞",@"😫",@"😣",@"😈",@"😉",@"😯",@""],
181 | @[@"😕",@"😰",@"😋",@"😝",@"😓",@"😀",@"😂",@"😘",@"😒",@"😏",@"😶",@"😱",@"😖",@"😩",@"😔",@"😑",@"😚",@"😪",@"😇",@"🙊",@"👊",@"👎",@"☝️",@""],
182 | @[@"✌️",@"😬",@"😷",@"🙈",@"👌",@"👋",@"✊",@"💪",@"😆",@"☺️",@"🙉",@"👍",@"🙏",@"✋",@"☀️",@"☕️",@"⛄️",@"📚",@"🎁",@"🎉",@"🍦",@"☁️",@"❄️",@""],
183 | @[@"⚡️",@"💰",@"🎂",@"🎓",@"🍖",@"☔️",@"⛅️",@"✏️",@"💩",@"🎄",@"🍷",@"🎤",@"🏀",@"🀄️",@"💣",@"📢",@"🌍",@"🍫",@"🎲",@"🏂",@"💡",@"💤",@"🚫",@""],
184 | @[@"🌻",@"🍻",@"🎵",@"🏡",@"💢",@"📞",@"🚿",@"🍚",@"👪",@"👼",@"💊",@"🔫",@"🌹",@"🐶",@"💄",@"👫",@"👽",@"💋",@"🌙",@"🍉",@"🐷",@"💔",@"👻",@""],
185 | @[@"😈",@"💍",@"🌲",@"🐴",@"👑",@"🔥",@"⭐️",@"⚽️",@"🕖",@"⏰",@"😁",@"🚀",@"⏳",@""]
186 | ];
187 | }
188 | return _emojis;
189 | }
190 | -(UIScrollView *)pageScrollView{
191 | if (!_pageScrollView) {
192 | _pageScrollView = [[UIScrollView alloc] init];
193 | _pageScrollView.pagingEnabled = YES;
194 | _pageScrollView.backgroundColor = UIColorFromRGB(0xf8f8f8);
195 | _pageScrollView.showsHorizontalScrollIndicator = NO;
196 | _pageScrollView.delegate = self;
197 | }
198 | return _pageScrollView;
199 | }
200 | -(UIPageControl *)pageControl{
201 | if (!_pageControl) {
202 | _pageControl = [[UIPageControl alloc] init];
203 | _pageControl.currentPageIndicatorTintColor = UIColorFromRGB(0x888888);
204 | _pageControl.pageIndicatorTintColor = UIColorFromRGB(0xb9b9b9);
205 | }
206 | return _pageControl;
207 | }
208 | -(UIView *)bottomView{
209 | if (!_bottomView) {
210 | _bottomView = [[UIView alloc] init];
211 | _bottomView.backgroundColor = UIColorFromRGB(0xffffff);
212 | }
213 | return _bottomView;
214 | }
215 | -(UIButton *)emojiSwitchBtn{
216 | if (!_emojiSwitchBtn) {
217 | _emojiSwitchBtn = [UIButton buttonWithType:UIButtonTypeCustom];
218 | [_emojiSwitchBtn setImage:[UIImage imageNamed:@"emoji_btn_normal"] forState:UIControlStateNormal];
219 | _emojiSwitchBtn.backgroundColor = UIColorFromRGB(0xf8f8f8);
220 | }
221 | return _emojiSwitchBtn;
222 | }
223 | -(UIButton *)sendBtn{
224 | if (!_sendBtn) {
225 | _sendBtn = [UIButton buttonWithType:UIButtonTypeCustom];
226 | [_sendBtn setTitle:@"发送" forState:UIControlStateNormal];
227 | [_sendBtn setTitleColor:UIColorFromRGB(0x9d9d9d) forState:UIControlStateNormal];
228 | _sendBtn.titleLabel.font = [UIFont systemFontOfSize:13];
229 | _sendBtn.backgroundColor = UIColorFromRGB(0xf8f8f8);
230 | [_sendBtn addTarget:self action:@selector(didClickSend:) forControlEvents:UIControlEventTouchUpInside];
231 | }
232 | return _sendBtn;
233 | }
234 | @end
235 |
--------------------------------------------------------------------------------
/TLMessageView/Chat/View/TLChatInputView.h:
--------------------------------------------------------------------------------
1 | //
2 | // TLChatInputView.h
3 | // TLMessageView
4 | //
5 | // Created by 郭锐 on 16/8/18.
6 | // Copyright © 2016年 com.garry.message. All rights reserved.
7 | //
8 |
9 | #import "TLChatViewController.h"
10 | #import "TLProjectMacro.h"
11 |
12 | @interface TLChatInputView : UIView
13 | -(instancetype)initWithChatVc:(TLChatViewController *)vc;
14 | @property(nonatomic,copy)void (^sendMsgAction)(RCMessageContent *x);
15 | @end
16 |
--------------------------------------------------------------------------------
/TLMessageView/Chat/View/TLPhotoBrowser.h:
--------------------------------------------------------------------------------
1 | //
2 | // TLPhotoBrowser.h
3 | // TLMessageView
4 | //
5 | // Created by 郭锐 on 16/8/21.
6 | // Copyright © 2016年 com.garry.message. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @class TLImageScrollView;
12 |
13 | @protocol TLImageScrollViewDelegate
14 | @optional
15 | -(void)imageScrollViewLongTap:(UILongPressGestureRecognizer *)sender;
16 | -(void)imageScrollViewTap:(UITapGestureRecognizer *)sender;
17 | @end
18 |
19 | @interface TLPhotoBrowser : UIView
20 | @property(nonatomic,strong)TLImageScrollView *imageScrollView;
21 | +(void)showOriginalImage:(UIImage *)originalImage;
22 | @end
23 |
24 |
25 | @interface TLImageScrollView : UIScrollView
26 | @property(nonatomic,assign)id actionDelegate;
27 | @property(nonatomic,strong)UIImage *img;
28 | @end
29 |
--------------------------------------------------------------------------------
/TLMessageView/Chat/View/TLPhotoBrowser.m:
--------------------------------------------------------------------------------
1 | //
2 | // TLPhotoBrowser.m
3 | // TLMessageView
4 | //
5 | // Created by 郭锐 on 16/8/21.
6 | // Copyright © 2016年 com.garry.message. All rights reserved.
7 | //
8 |
9 | #import "TLPhotoBrowser.h"
10 | #import "TLProjectMacro.h"
11 |
12 | @interface TLPhotoBrowser ()
13 | @end
14 |
15 | @implementation TLPhotoBrowser
16 | -(instancetype)initWithFrame:(CGRect)frame{
17 | if (self = [super initWithFrame:frame]) {
18 | self.userInteractionEnabled = YES;
19 | self.backgroundColor = UIColorFromRGB(0x000000);
20 |
21 | [self addSubview:self.imageScrollView];
22 | }
23 | return self;
24 | }
25 | +(void)showOriginalImage:(UIImage *)originalImage{
26 | TLPhotoBrowser *browser = [[TLPhotoBrowser alloc] initWithFrame:kKeyWindow.bounds];
27 | browser.imageScrollView.img = originalImage;
28 | [browser show];
29 | }
30 | -(void)show{
31 | self.alpha = 0;
32 | [kKeyWindow addSubview:self];
33 | self.frame = kKeyWindow.bounds;
34 |
35 | [UIView animateWithDuration:0.25 animations:^{
36 | self.alpha = 1;
37 | }];
38 | }
39 | -(void)imageScrollViewLongTap:(UILongPressGestureRecognizer *)sender{
40 | if (sender.state != UIGestureRecognizerStateBegan) {
41 | return;
42 | }
43 | UIActionSheet *sheet = [[UIActionSheet alloc] initWithTitle:nil delegate:self cancelButtonTitle:@"取消" destructiveButtonTitle:@"保存" otherButtonTitles:nil];
44 | [sheet showInView:self];
45 | }
46 | -(void)imageScrollViewTap:(UITapGestureRecognizer *)sender{
47 | [UIView animateWithDuration:0.25 animations:^{
48 | self.alpha = 0;
49 | } completion:^(BOOL finished) {
50 | [self removeFromSuperview];
51 | }];
52 | }
53 | -(TLImageScrollView *)imageScrollView{
54 | if (!_imageScrollView) {
55 | _imageScrollView = [[TLImageScrollView alloc] initWithFrame:self.bounds];
56 | _imageScrollView.actionDelegate = self;
57 | }
58 | return _imageScrollView;
59 | }
60 | @end
61 |
62 |
63 |
64 |
65 | @interface TLImageScrollView ()
66 | @property(nonatomic,strong)UIImageView *photoView;
67 | @end
68 |
69 | @implementation TLImageScrollView
70 | -(instancetype)initWithFrame:(CGRect)frame{
71 | if (self = [super initWithFrame:frame]) {
72 | self.delegate = self;
73 | self.minimumZoomScale = 0.8;
74 | self.maximumZoomScale = 2.0;
75 |
76 | [self addSubview:self.photoView];
77 |
78 | UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressAction:)];
79 | longPress.minimumPressDuration = 0.5;
80 | [self addGestureRecognizer:longPress];
81 |
82 | UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(doubleTapAction:)];
83 | [doubleTap setNumberOfTapsRequired:2];
84 | [self addGestureRecognizer:doubleTap];
85 |
86 | UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapAction:)];
87 | [tap setNumberOfTapsRequired:1];
88 | [self addGestureRecognizer:tap];
89 |
90 | [tap requireGestureRecognizerToFail:doubleTap];
91 |
92 | }
93 | return self;
94 | }
95 | -(void)doubleTapAction:(UITapGestureRecognizer *)sender{
96 | [UIView animateWithDuration:0.25 animations:^{
97 | if (self.zoomScale < 1) {
98 | self.zoomScale = 1.0f;
99 | }else if (self.zoomScale == 1){
100 | self.zoomScale = 2.0f;
101 | }else{
102 | self.zoomScale = 1.0f;
103 | }
104 | }];
105 | }
106 | -(void)longPressAction:(UILongPressGestureRecognizer *)sender{
107 | if ([self.actionDelegate respondsToSelector:@selector(imageScrollViewLongTap:)]) {
108 | [self.actionDelegate imageScrollViewLongTap:sender];
109 | }
110 | }
111 | -(void)tapAction:(UITapGestureRecognizer *)sender{
112 | if ([self.actionDelegate respondsToSelector:@selector(imageScrollViewTap:)]) {
113 | [self.actionDelegate imageScrollViewTap:sender];
114 | }
115 | }
116 | -(void)setScrollZoom:(CGFloat)zoom{
117 | self.zoomScale = 1;
118 | }
119 | - (void)scrollViewDidZoom:(UIScrollView *)scrollView{
120 | CGFloat offsetX = (scrollView.bounds.size.width > scrollView.contentSize.width)?
121 | (scrollView.bounds.size.width - scrollView.contentSize.width) * 0.5 : 0.0;
122 |
123 | CGFloat offsetY = (scrollView.bounds.size.height > scrollView.contentSize.height)?
124 | (scrollView.bounds.size.height - scrollView.contentSize.height) * 0.5 : 0.0;
125 |
126 | self.photoView.center = CGPointMake(scrollView.contentSize.width * 0.5 + offsetX,
127 | scrollView.contentSize.height * 0.5 + offsetY);
128 | }
129 | - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView{
130 | return self.photoView;
131 | }
132 | -(void)setImg:(UIImage *)img{
133 | if (_img != img) {
134 | _img = img;
135 | self.photoView.image = img;
136 | }
137 | }
138 | -(UIImageView *)photoView{
139 | if (!_photoView) {
140 | _photoView = [[UIImageView alloc] initWithFrame:self.bounds];
141 | _photoView.userInteractionEnabled = YES;
142 | _photoView.contentMode = UIViewContentModeScaleAspectFit;
143 | }
144 | return _photoView;
145 | }
146 | @end
147 |
--------------------------------------------------------------------------------
/TLMessageView/Chat/View/TLPluginBoardView.h:
--------------------------------------------------------------------------------
1 | //
2 | // TLPluginBoardView.h
3 | // TLMessageView
4 | //
5 | // Created by 郭锐 on 16/8/19.
6 | // Copyright © 2016年 com.garry.message. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | static NSInteger pluginBoardHeight = 223;
12 |
13 | @protocol TLPluginBoardViewDelegate
14 | -(NSArray *)pluginBoardItems;
15 | -(void)pluginBoardDidClickItemIndex:(NSInteger)itemIndex;
16 | @end
17 |
18 | @interface TLPluginBoardView : UIView
19 | @property(nonatomic,assign)BOOL show;
20 | @property(nonatomic,assign)id delegate;
21 | -(instancetype)initWithDelegate:(id )delegate;
22 | @end
23 |
24 | @interface TLPluginBoardItem : NSObject
25 | -(instancetype)initWithIcoNamed:(NSString *)icoNamed title:(NSString *)title;
26 | @property(nonatomic,copy)NSString *icoNamed;
27 | @property(nonatomic,copy)NSString *title;
28 | @end
29 |
30 | @interface TLPluginBoardButton : UIButton
31 | -(instancetype)initWithItem:(TLPluginBoardItem *)item;
32 | @end
--------------------------------------------------------------------------------
/TLMessageView/Chat/View/TLPluginBoardView.m:
--------------------------------------------------------------------------------
1 | //
2 | // TLPluginBoardView.m
3 | // TLMessageView
4 | //
5 | // Created by 郭锐 on 16/8/19.
6 | // Copyright © 2016年 com.garry.message. All rights reserved.
7 | //
8 |
9 | #import "TLPluginBoardView.h"
10 | #import "TLProjectMacro.h"
11 |
12 | @interface TLPluginBoardView ()
13 | @property(nonatomic,strong)NSMutableArray *btns;
14 | @end
15 |
16 | @implementation TLPluginBoardView
17 | {
18 | BOOL hasLayout;
19 | }
20 | -(instancetype)initWithDelegate:(id)delegate{
21 | if (self = [super init]) {
22 | self.delegate = delegate;
23 | self.backgroundColor = UIColorFromRGB(0xF8F8F8);
24 | self.btns = [NSMutableArray array];
25 | NSArray *items = [self.delegate pluginBoardItems];
26 | for (int i = 0; i < items.count; i ++ ) {
27 | TLPluginBoardButton *btn = [[TLPluginBoardButton alloc] initWithItem:items[i]];
28 | btn.tag = 100 + i;
29 | [self addSubview:btn];
30 | [self.btns addObject:btn];
31 | [btn addTarget:self action:@selector(didClickItem:) forControlEvents:UIControlEventTouchUpInside];
32 | }
33 | }
34 | return self;
35 | }
36 | -(void)didClickItem:(TLPluginBoardButton *)sender{
37 | if ([self.delegate respondsToSelector:@selector(pluginBoardDidClickItemIndex:)]) {
38 | [self.delegate pluginBoardDidClickItemIndex:sender.tag - 100];
39 | }
40 | }
41 | -(void)layoutSubviews{
42 | [super layoutSubviews];
43 |
44 | if (hasLayout) {
45 | return;
46 | }
47 | hasLayout = YES;
48 |
49 | CGFloat icoSpacingWidth = (self.frame.size.width - 60 * 4) / 5;
50 | CGFloat icoSpacingHeight = (self.frame.size.height - 90 * 2) / 3;
51 |
52 | for (int i = 0; i < self.btns.count; i ++ ) {
53 | TLPluginBoardButton *btn = self.btns[i];
54 | int k = i < 4 ? i : i - 4;
55 | [btn mas_makeConstraints:^(MASConstraintMaker *make) {
56 | make.left.equalTo(self.mas_left).offset((k + 1) * icoSpacingWidth + 60 * k);
57 | make.top.equalTo(self.mas_top).offset(i < 4 ? icoSpacingHeight : icoSpacingHeight * 2 + 90);
58 | make.size.mas_offset(CGSizeMake(60, 90));
59 | }];
60 | }
61 | }
62 | @end
63 |
64 | @implementation TLPluginBoardItem
65 | -(instancetype)initWithIcoNamed:(NSString *)icoNamed title:(NSString *)title{
66 | if (self = [super init]) {
67 | self.icoNamed = icoNamed;
68 | self.title = title;
69 | }
70 | return self;
71 | }
72 | @end
73 |
74 | @implementation TLPluginBoardButton
75 | -(instancetype)initWithItem:(TLPluginBoardItem *)item{
76 | if (self = [super init]) {
77 | [self setImage:[UIImage imageNamed:item.icoNamed] forState:UIControlStateNormal];
78 | [self setTitle:item.title forState:UIControlStateNormal];
79 | [self setTitleColor:UIColorFromRGB(0x6F7277) forState:UIControlStateNormal];
80 | self.backgroundColor = [UIColor clearColor];
81 | self.titleLabel.font = [UIFont systemFontOfSize:12];
82 |
83 | }
84 | return self;
85 | }
86 | -(void)layoutSubviews{
87 | [super layoutSubviews];
88 | CGPoint center = self.imageView.center;
89 | center.x = self.frame.size.width / 2;
90 | center.y = self.imageView.frame.size.height / 2 + 5;
91 | self.imageView.center = center;
92 |
93 | CGRect newFrame = [self titleLabel].frame;
94 | newFrame.origin.x = 0;
95 | newFrame.origin.y = self.imageView.frame.size.height + 10;
96 | newFrame.size.width = self.frame.size.width;
97 |
98 | self.titleLabel.frame = newFrame;
99 | self.titleLabel.textAlignment = NSTextAlignmentCenter;
100 | }
101 |
102 | @end
--------------------------------------------------------------------------------
/TLMessageView/Chat/View/TLRecordVoiceHUD.h:
--------------------------------------------------------------------------------
1 | //
2 | // TLRecordVoiceHUD.h
3 | // TLMessageView
4 | //
5 | // Created by 郭锐 on 16/8/19.
6 | // Copyright © 2016年 com.garry.message. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface TLRecordVoiceHUD : UIView
12 | @property(nonatomic,strong)UIButton *statusBtn;
13 | @property(nonatomic,strong)UIImageView *recordTipImage;
14 |
15 | +(void)updatePeakPower:(CGFloat)peakPower;
16 | +(void)showRecording;
17 | +(void)showWCancel;
18 | +(void)dismissWithRecordShort;
19 | +(void)dismiss;
20 | @end
21 |
--------------------------------------------------------------------------------
/TLMessageView/Chat/View/TLRecordVoiceHUD.m:
--------------------------------------------------------------------------------
1 | //
2 | // TLRecordVoiceHUD.m
3 | // TLMessageView
4 | //
5 | // Created by 郭锐 on 16/8/19.
6 | // Copyright © 2016年 com.garry.message. All rights reserved.
7 | //
8 |
9 | #import "TLRecordVoiceHUD.h"
10 | #import "TLProjectMacro.h"
11 |
12 | @interface TLRecordVoiceHUD ()
13 | @end
14 |
15 | @implementation TLRecordVoiceHUD
16 | -(instancetype)initWithFrame:(CGRect)frame{
17 | if (self = [super initWithFrame:frame]) {
18 | self.backgroundColor = UIColorFromRGBA(0x000000, 0.5);
19 | self.layer.cornerRadius = 4.0f;
20 |
21 | [self addSubview:self.recordTipImage];
22 | [self.recordTipImage mas_makeConstraints:^(MASConstraintMaker *make) {
23 | make.top.equalTo(self.mas_top).offset(20);
24 | make.centerX.equalTo(self.mas_centerX).offset(0);
25 | make.size.mas_offset(CGSizeMake(90, 90));
26 | }];
27 |
28 | [self addSubview:self.statusBtn];
29 | [self.statusBtn mas_makeConstraints:^(MASConstraintMaker *make) {
30 | make.left.equalTo(self.mas_left).offset(7);
31 | make.right.equalTo(self.mas_right).offset(-7);
32 | make.bottom.equalTo(self.mas_bottom).offset(-7);
33 | make.height.mas_offset(22);
34 | }];
35 |
36 | self.tag = 999;
37 | }
38 | return self;
39 | }
40 | +(void)updatePeakPower:(CGFloat)peakPower{
41 | TLRecordVoiceHUD *hud = [kKeyWindow viewWithTag:999];
42 | if (!hud){
43 | hud = [[TLRecordVoiceHUD alloc] initWithFrame:CGRectMake(0, 0, 150, 150)];
44 | hud.center = kKeyWindow.center;
45 | [kKeyWindow addSubview:hud];
46 | };
47 |
48 | NSDictionary *dic = @{@[@0,@0.125]:@"voice_1",@[@0.126,@0.250]:@"voice_2",@[@0.251,@0.375]:@"voice_3",@[@0.376,@0.500]:@"voice_4",@[@0.501,@0.625]:@"voice_5",@[@0.626,@0.750]:@"voice_6",@[@0.751,@0.875]:@"voice_7",@[@0.876,@1]:@"voice_8"};
49 |
50 | NSString *value;
51 | for (NSArray *key in dic) {
52 | if (peakPower < [key[1] floatValue] && peakPower > [key[0] floatValue]) {
53 | value = dic[key];
54 | break;
55 | }
56 | }
57 | if (!value) {
58 | return;
59 | }
60 |
61 | hud.recordTipImage.image = [UIImage imageNamed:value];
62 | }
63 | +(void)showRecording{
64 | TLRecordVoiceHUD *hud = [kKeyWindow viewWithTag:999];
65 | if (!hud){
66 | hud = [[TLRecordVoiceHUD alloc] initWithFrame:CGRectMake(0, 0, 150, 150)];
67 | hud.center = kKeyWindow.center;
68 | [kKeyWindow addSubview:hud];
69 |
70 | hud.alpha = 0;
71 | [UIView animateWithDuration:0.1 animations:^{
72 | hud.alpha = 1;
73 | }];
74 | };
75 |
76 | hud.statusBtn.selected = NO;
77 | [hud.statusBtn setTitle:@"手指上划,取消发送" forState:UIControlStateNormal];
78 | }
79 | +(void)showWCancel{
80 | TLRecordVoiceHUD *hud = [kKeyWindow viewWithTag:999];
81 | hud.recordTipImage.image = [UIImage imageNamed:@"return"];
82 | hud.statusBtn.selected = YES;
83 | [hud.statusBtn setTitle:@"松开手指,取消发送" forState:UIControlStateNormal];
84 | }
85 | +(void)dismiss{
86 | TLRecordVoiceHUD *hud = [kKeyWindow viewWithTag:999];
87 | [UIView animateWithDuration:0.1 animations:^{
88 | hud.alpha = 0;
89 | } completion:^(BOOL finished) {
90 | [hud removeFromSuperview];
91 | }];
92 | }
93 | +(void)dismissWithRecordShort{
94 | TLRecordVoiceHUD *hud = [kKeyWindow viewWithTag:999];
95 | hud.recordTipImage.image = [UIImage imageNamed:@"return"];
96 | [hud.statusBtn setTitle:@"录音时间短" forState:UIControlStateNormal];
97 | hud.statusBtn.selected = NO;
98 |
99 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
100 | [UIView animateWithDuration:0.3 animations:^{
101 | hud.alpha = 0;
102 | } completion:^(BOOL finished) {
103 | [hud removeFromSuperview];
104 | }];
105 | });
106 | }
107 | -(UIImageView *)recordTipImage{
108 | if (!_recordTipImage) {
109 | _recordTipImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"voice_1"]];
110 | }
111 | return _recordTipImage;
112 | }
113 | -(UIButton *)statusBtn{
114 | if (!_statusBtn) {
115 | _statusBtn = [UIButton buttonWithType:UIButtonTypeCustom];
116 | _statusBtn.userInteractionEnabled = NO;
117 | _statusBtn.titleLabel.font = [UIFont systemFontOfSize:13.5];
118 | [_statusBtn setBackgroundImage:[UIImage imageNamed:@"red_background"] forState:UIControlStateSelected];
119 | }
120 | return _statusBtn;
121 | }
122 | @end
123 |
--------------------------------------------------------------------------------
/TLMessageView/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | NSCameraUsageDescription
6 | 此 App 需要您的同意才能使用相机
7 | NSMicrophoneUsageDescription
8 | 此 App 需要您的同意才能使用麦克风
9 | NSPhotoLibraryUsageDescription
10 | 此 App 需要您的同意才能读取媒体资料库
11 | NSLocationAlwaysUsageDescription
12 | 允许获取位置信息
13 | NSLocationWhenInUseUsageDescription
14 | 允许获取位置信息
15 | CFBundleDevelopmentRegion
16 | zh_CN
17 | CFBundleExecutable
18 | $(EXECUTABLE_NAME)
19 | CFBundleIdentifier
20 | $(PRODUCT_BUNDLE_IDENTIFIER)
21 | CFBundleInfoDictionaryVersion
22 | 6.0
23 | CFBundleName
24 | $(PRODUCT_NAME)
25 | CFBundlePackageType
26 | APPL
27 | CFBundleShortVersionString
28 | 1.0
29 | CFBundleSignature
30 | ????
31 | CFBundleVersion
32 | 1
33 | LSRequiresIPhoneOS
34 |
35 | NSAppTransportSecurity
36 |
37 | NSAllowsArbitraryLoads
38 |
39 |
40 | UILaunchStoryboardName
41 | LaunchScreen
42 | UIRequiredDeviceCapabilities
43 |
44 | armv7
45 |
46 | UISupportedInterfaceOrientations
47 |
48 | UIInterfaceOrientationPortrait
49 |
50 | UISupportedInterfaceOrientations~ipad
51 |
52 | UIInterfaceOrientationPortrait
53 | UIInterfaceOrientationPortraitUpsideDown
54 | UIInterfaceOrientationLandscapeLeft
55 | UIInterfaceOrientationLandscapeRight
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/TLMessageView/TLProjectMacro.h:
--------------------------------------------------------------------------------
1 | //
2 | // Header.h
3 | // TLMessageView
4 | //
5 | // Created by 郭锐 on 16/8/18.
6 | // Copyright © 2016年 com.garry.message. All rights reserved.
7 | //
8 |
9 | #ifndef Header_h
10 | #define Header_h
11 |
12 | #import
13 | #import
14 | #import
15 |
16 | /**
17 | * Weaky self
18 | */
19 | #define weakifySelf \
20 | __weak __typeof(&*self)weakSelf = self;
21 |
22 | #define strongifySelf \
23 | __strong __typeof(&*weakSelf)self = weakSelf;
24 |
25 | #define kKeyWindow [UIApplication sharedApplication].keyWindow
26 |
27 | /**
28 | * Color
29 | */
30 | #define ColorWithRGBA(r, g, b, a) [UIColor colorWithRed:(r) / 255.0f green:(g) / 255.0f blue:(b) / 255.0f alpha:(a)]
31 |
32 | #define UIColorFromRGB(rgbValue) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]
33 |
34 | #define UIColorFromRGBA(rgbValue, a) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 blue:((float)(rgbValue & 0xFF))/255.0 alpha:a]
35 |
36 | #define UI_SCREEN_WIDTH ([[UIScreen mainScreen] bounds].size.width)
37 | #define UI_SCREEN_HEIGHT ([[UIScreen mainScreen] bounds].size.height)
38 | #define UI_SCREEN_BOUNDS ([UIScreen mainScreen].bounds)
39 |
40 | #endif /* Header_h */
41 |
--------------------------------------------------------------------------------
/TLMessageView/account.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 0001
6 | XazzEyo80CdGQT0ixnyRcpp+gB9O3Igt57fet/zpar+Mvfmwptdl0to2K5YBcxcCBOmM2CJONymZaaNbhcd8yA==
7 | 0002
8 | 7Shy0aKksceAEqUpX0xUa0xpZ1Jg7vO5lNpY4hvHdVrHW6Ulb2kykOebNwPDLayFWQJ0rg0l3+lBJO1Gpvm3cw==
9 | 0003
10 | FI8WkREyWcSM//7QcJJDbUxpZ1Jg7vO5lNpY4hvHdVrHW6Ulb2kykE8HFc0iqv8aWQJ0rg0l3+kgZJLu6vmX1w==
11 | 0004
12 | F3fH0ar+RQDpd1KR1kYEcpp+gB9O3Igt57fet/zpar+Mvfmwptdl0rjOu1XS89iO/1qQdTSocbhLMJ4ui1RA/Q==
13 | 0005
14 | jED3KUMVIkJGPRY16y3otpp+gB9O3Igt57fet/zpar+Mvfmwptdl0kQRZzs+t2vn/1qQdTSocbitvzrbMsmLQg==
15 | 0006
16 | QWf/T/ykIryE68yrgR9NQkxpZ1Jg7vO5lNpY4hvHdVrHW6Ulb2kykFNw0mLFyYquJYKPHr0CS2hBJO1Gpvm3cw==
17 | 0007
18 | 7Pj+49opgqiXnB8zBJ7iVJp+gB9O3Igt57fet/zpar+Mvfmwptdl0oGFMFvHzLa5BOmM2CJONyl2gWGIv369NA==
19 | 0008
20 | Pjsy4MDskV13FgnWNP1JOeSZkEN9cVfzRVKTg8tY0IOhDwJ1Cn3qxVgN5+mt2W4a2vylG+P0/9ipZCgp45dC1Q==
21 | 0009
22 | 06pyYHUDLPadREnKdilFg5p+gB9O3Igt57fet/zpar+Mvfmwptdl0p1Ns7ESnQi9oiyf2BfJawKvWqlA8EwXAw==
23 | 0010
24 | DM+6m33yv15sOdkiCdtgr0xpZ1Jg7vO5lNpY4hvHdVrHW6Ulb2kykMgLiBiSW/0FkK1CZPbBalJrh1SYaXka8g==
25 |
26 |
27 |
--------------------------------------------------------------------------------
/TLMessageView/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // TLMessageView
4 | //
5 | // Created by 郭锐 on 16/8/18.
6 | // Copyright © 2016年 com.garry.message. 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 |
--------------------------------------------------------------------------------