├── README.md
├── YTChatDemo.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ ├── xcshareddata
│ │ └── YTChatDemo.xccheckout
│ └── xcuserdata
│ │ └── Ycc.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
└── xcuserdata
│ └── Ycc.xcuserdatad
│ ├── xcdebugger
│ └── Breakpoints_v2.xcbkptlist
│ └── xcschemes
│ ├── YTChatDemo.xcscheme
│ └── xcschememanagement.plist
├── YTChatDemo
├── HUD
│ ├── MBProgressHUD.h
│ └── MBProgressHUD.m
├── ImgBrower
│ ├── YTImgBrowerController.h
│ ├── YTImgBrowerController.m
│ ├── YTImgInfo.h
│ ├── YTImgInfo.m
│ ├── YTImgScroll.h
│ └── YTImgScroll.m
├── KeyBoard
│ ├── Emoji
│ │ ├── EmojiModel
│ │ │ ├── Resource
│ │ │ │ ├── EmojiFile.plist
│ │ │ │ ├── EmojiGirl.plist
│ │ │ │ ├── EmojiPanda.plist
│ │ │ │ ├── EmojiSutra.plist
│ │ │ │ ├── girl_beg@2x.gif
│ │ │ │ ├── grinning@2x.png
│ │ │ │ └── smile@2x.png
│ │ │ ├── YTEmoji.h
│ │ │ ├── YTEmoji.m
│ │ │ ├── YTEmojiChartletM.h
│ │ │ ├── YTEmojiChartletM.m
│ │ │ ├── YTEmojiFile.h
│ │ │ ├── YTEmojiFile.m
│ │ │ ├── YTEmojiIconM.h
│ │ │ ├── YTEmojiIconM.m
│ │ │ ├── YTEmojiM.h
│ │ │ └── YTEmojiM.m
│ │ └── EmojiView
│ │ │ ├── YTEmojiBottom.h
│ │ │ ├── YTEmojiBottom.m
│ │ │ ├── YTEmojiButton.h
│ │ │ ├── YTEmojiButton.m
│ │ │ ├── YTEmojiPage.h
│ │ │ ├── YTEmojiPage.m
│ │ │ ├── YTEmojiView.h
│ │ │ └── YTEmojiView.m
│ ├── More
│ │ ├── YTMoreView.h
│ │ └── YTMoreView.m
│ ├── Other
│ │ ├── SLGrowingTextView.h
│ │ ├── SLGrowingTextView.m
│ │ ├── YTTextAttachment.h
│ │ ├── YTTextAttachment.m
│ │ ├── YTTextView.h
│ │ └── YTTextView.m
│ ├── YTKeyBoardView.h
│ └── YTKeyBoardView.m
├── Main
│ ├── AppDelegate.h
│ ├── AppDelegate.m
│ ├── StartVC.h
│ └── StartVC.m
├── Supporting Files
│ ├── Base.lproj
│ │ └── Main.storyboard
│ ├── Defines.h
│ ├── Images.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ ├── Contents.json
│ │ │ └── YT@2x.png
│ │ ├── btn_del.imageset
│ │ │ ├── Contents.json
│ │ │ └── btn_facecancel@2x.png
│ │ ├── btn_face.imageset
│ │ │ ├── Contents.json
│ │ │ ├── btn_face@2x.png
│ │ │ └── btn_face@3x.png
│ │ ├── btn_key.imageset
│ │ │ ├── Contents.json
│ │ │ ├── btn_key@2x.png
│ │ │ └── btn_key@3x.png
│ │ ├── btn_more.imageset
│ │ │ ├── Contents.json
│ │ │ ├── btn_chatmore@2x.png
│ │ │ └── btn_chatmore@3x.png
│ │ ├── btn_photo.imageset
│ │ │ ├── Contents.json
│ │ │ ├── btn_photo@2x.png
│ │ │ └── btn_photo@3x.png
│ │ ├── btn_pic.imageset
│ │ │ ├── Contents.json
│ │ │ ├── btn_pic@2x.png
│ │ │ └── btn_pic@3x.png
│ │ ├── btn_say.imageset
│ │ │ ├── Contents.json
│ │ │ ├── btn_say@2x.png
│ │ │ └── btn_say@3x.png
│ │ ├── camera_down.imageset
│ │ │ ├── Contents.json
│ │ │ └── camera_down@2x.png
│ │ ├── camera_nomal.imageset
│ │ │ ├── Contents.json
│ │ │ └── camera@2x.png
│ │ ├── default_img.imageset
│ │ │ ├── Contents.json
│ │ │ └── default_img@2x.png
│ │ ├── photo_down.imageset
│ │ │ ├── Contents.json
│ │ │ └── img_down@2x.png
│ │ └── photo_nomal.imageset
│ │ │ ├── Contents.json
│ │ │ └── img@2x.png
│ ├── Info.plist
│ ├── Message.h
│ ├── Message.m
│ ├── YTChatDemo.xcdatamodeld
│ │ ├── .xccurrentversion
│ │ └── YTChatDemo.xcdatamodel
│ │ │ └── contents
│ └── main.m
└── Tools
│ ├── Catgray
│ ├── NSDictionary+YTSafe.h
│ ├── NSDictionary+YTSafe.m
│ ├── UIColor+YTBacal.h
│ ├── UIColor+YTBacal.m
│ ├── UIColor+YTKeyBoard.h
│ ├── UIColor+YTKeyBoard.m
│ ├── UIImage+YTGif.h
│ ├── UIImage+YTGif.m
│ ├── UIView+YTLayer.h
│ └── UIView+YTLayer.m
│ ├── coreData
│ ├── NSManagedObject+YTMoreThread.h
│ ├── NSManagedObject+YTMoreThread.m
│ ├── YTCoreData.h
│ └── YTCoreData.m
│ └── other
│ ├── YTDeviceTest.h
│ ├── YTDeviceTest.m
│ ├── YTNetworkLoad.h
│ └── YTNetworkLoad.m
└── YTChatDemoTests
├── Info.plist
└── YTChatDemoTests.m
/README.md:
--------------------------------------------------------------------------------
1 | # KeyBoard
2 |
3 |
--------------------------------------------------------------------------------
/YTChatDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
7 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/YTChatDemo.xcodeproj/project.xcworkspace/xcshareddata/YTChatDemo.xccheckout:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDESourceControlProjectFavoriteDictionaryKey
6 |
7 | IDESourceControlProjectIdentifier
8 | FE6A357C-A451-4510-804D-CB75BC0DED4D
9 | IDESourceControlProjectName
10 | YTChatDemo
11 | IDESourceControlProjectOriginsDictionary
12 |
13 | 903E540FEA9652A10B393974C6BBBEC6EB5C8454
14 | https://github.com/TiYcc/KeyBoard.git
15 |
16 | IDESourceControlProjectPath
17 | YTChatDemo.xcodeproj
18 | IDESourceControlProjectRelativeInstallPathDictionary
19 |
20 | 903E540FEA9652A10B393974C6BBBEC6EB5C8454
21 | ../..
22 |
23 | IDESourceControlProjectURL
24 | https://github.com/TiYcc/KeyBoard.git
25 | IDESourceControlProjectVersion
26 | 111
27 | IDESourceControlProjectWCCIdentifier
28 | 903E540FEA9652A10B393974C6BBBEC6EB5C8454
29 | IDESourceControlProjectWCConfigurations
30 |
31 |
32 | IDESourceControlRepositoryExtensionIdentifierKey
33 | public.vcs.git
34 | IDESourceControlWCCIdentifierKey
35 | 903E540FEA9652A10B393974C6BBBEC6EB5C8454
36 | IDESourceControlWCCName
37 | KeyBaoard
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/YTChatDemo.xcodeproj/project.xcworkspace/xcuserdata/Ycc.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TiYcc/KeyBoard/dfcec9a85580fcff301d4fa832c1de1c74566320/YTChatDemo.xcodeproj/project.xcworkspace/xcuserdata/Ycc.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/YTChatDemo.xcodeproj/xcuserdata/Ycc.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/YTChatDemo.xcodeproj/xcuserdata/Ycc.xcuserdatad/xcschemes/YTChatDemo.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
38 |
39 |
44 |
45 |
47 |
53 |
54 |
55 |
56 |
57 |
63 |
64 |
65 |
66 |
75 |
77 |
83 |
84 |
85 |
86 |
87 |
88 |
94 |
96 |
102 |
103 |
104 |
105 |
107 |
108 |
111 |
112 |
113 |
--------------------------------------------------------------------------------
/YTChatDemo.xcodeproj/xcuserdata/Ycc.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | YTChatDemo.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | 50724F581B8B0EDB00EA425A
16 |
17 | primary
18 |
19 |
20 | 50724F741B8B0EDB00EA425A
21 |
22 | primary
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/YTChatDemo/HUD/MBProgressHUD.h:
--------------------------------------------------------------------------------
1 | //
2 | // MBProgressHUD.h
3 | // Version 0.9.1
4 | // Created by Matej Bukovinski on 2.4.09.
5 | //
6 |
7 | // This code is distributed under the terms and conditions of the MIT license.
8 |
9 | // Copyright (c) 2009-2015 Matej Bukovinski
10 | //
11 | // Permission is hereby granted, free of charge, to any person obtaining a copy
12 | // of this software and associated documentation files (the "Software"), to deal
13 | // in the Software without restriction, including without limitation the rights
14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 | // copies of the Software, and to permit persons to whom the Software is
16 | // furnished to do so, subject to the following conditions:
17 | //
18 | // The above copyright notice and this permission notice shall be included in
19 | // all copies or substantial portions of the Software.
20 | //
21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 | // THE SOFTWARE.
28 |
29 | #import
30 | #import
31 | #import
32 |
33 | @protocol MBProgressHUDDelegate;
34 |
35 |
36 | typedef NS_ENUM(NSInteger, MBProgressHUDMode) {
37 | /** Progress is shown using an UIActivityIndicatorView. This is the default. */
38 | MBProgressHUDModeIndeterminate,
39 | /** Progress is shown using a round, pie-chart like, progress view. */
40 | MBProgressHUDModeDeterminate,
41 | /** Progress is shown using a horizontal progress bar */
42 | MBProgressHUDModeDeterminateHorizontalBar,
43 | /** Progress is shown using a ring-shaped progress view. */
44 | MBProgressHUDModeAnnularDeterminate,
45 | /** Shows a custom view */
46 | MBProgressHUDModeCustomView,
47 | /** Shows only labels */
48 | MBProgressHUDModeText
49 | };
50 |
51 | typedef NS_ENUM(NSInteger, MBProgressHUDAnimation) {
52 | /** Opacity animation */
53 | MBProgressHUDAnimationFade,
54 | /** Opacity + scale animation */
55 | MBProgressHUDAnimationZoom,
56 | MBProgressHUDAnimationZoomOut = MBProgressHUDAnimationZoom,
57 | MBProgressHUDAnimationZoomIn
58 | };
59 |
60 |
61 | #ifndef MB_INSTANCETYPE
62 | #if __has_feature(objc_instancetype)
63 | #define MB_INSTANCETYPE instancetype
64 | #else
65 | #define MB_INSTANCETYPE id
66 | #endif
67 | #endif
68 |
69 | #ifndef MB_STRONG
70 | #if __has_feature(objc_arc)
71 | #define MB_STRONG strong
72 | #else
73 | #define MB_STRONG retain
74 | #endif
75 | #endif
76 |
77 | #ifndef MB_WEAK
78 | #if __has_feature(objc_arc_weak)
79 | #define MB_WEAK weak
80 | #elif __has_feature(objc_arc)
81 | #define MB_WEAK unsafe_unretained
82 | #else
83 | #define MB_WEAK assign
84 | #endif
85 | #endif
86 |
87 | #if NS_BLOCKS_AVAILABLE
88 | typedef void (^MBProgressHUDCompletionBlock)();
89 | #endif
90 |
91 |
92 | /**
93 | * Displays a simple HUD window containing a progress indicator and two optional labels for short messages.
94 | *
95 | * This is a simple drop-in class for displaying a progress HUD view similar to Apple's private UIProgressHUD class.
96 | * The MBProgressHUD window spans over the entire space given to it by the initWithFrame constructor and catches all
97 | * user input on this region, thereby preventing the user operations on components below the view. The HUD itself is
98 | * drawn centered as a rounded semi-transparent view which resizes depending on the user specified content.
99 | *
100 | * This view supports four modes of operation:
101 | * - MBProgressHUDModeIndeterminate - shows a UIActivityIndicatorView
102 | * - MBProgressHUDModeDeterminate - shows a custom round progress indicator
103 | * - MBProgressHUDModeAnnularDeterminate - shows a custom annular progress indicator
104 | * - MBProgressHUDModeCustomView - shows an arbitrary, user specified view (see `customView`)
105 | *
106 | * All three modes can have optional labels assigned:
107 | * - If the labelText property is set and non-empty then a label containing the provided content is placed below the
108 | * indicator view.
109 | * - If also the detailsLabelText property is set then another label is placed below the first label.
110 | */
111 | @interface MBProgressHUD : UIView
112 |
113 | /**
114 | * Creates a new HUD, adds it to provided view and shows it. The counterpart to this method is hideHUDForView:animated:.
115 | *
116 | * @note This method sets `removeFromSuperViewOnHide`. The HUD will automatically be removed from the view hierarchy when hidden.
117 | *
118 | * @param view The view that the HUD will be added to
119 | * @param animated If set to YES the HUD will appear using the current animationType. If set to NO the HUD will not use
120 | * animations while appearing.
121 | * @return A reference to the created HUD.
122 | *
123 | * @see hideHUDForView:animated:
124 | * @see animationType
125 | */
126 | + (MB_INSTANCETYPE)showHUDAddedTo:(UIView *)view animated:(BOOL)animated;
127 |
128 | /**
129 | * Finds the top-most HUD subview and hides it. The counterpart to this method is showHUDAddedTo:animated:.
130 | *
131 | * @note This method sets `removeFromSuperViewOnHide`. The HUD will automatically be removed from the view hierarchy when hidden.
132 | *
133 | * @param view The view that is going to be searched for a HUD subview.
134 | * @param animated If set to YES the HUD will disappear using the current animationType. If set to NO the HUD will not use
135 | * animations while disappearing.
136 | * @return YES if a HUD was found and removed, NO otherwise.
137 | *
138 | * @see showHUDAddedTo:animated:
139 | * @see animationType
140 | */
141 | + (BOOL)hideHUDForView:(UIView *)view animated:(BOOL)animated;
142 |
143 | /**
144 | * Finds all the HUD subviews and hides them.
145 | *
146 | * @note This method sets `removeFromSuperViewOnHide`. The HUDs will automatically be removed from the view hierarchy when hidden.
147 | *
148 | * @param view The view that is going to be searched for HUD subviews.
149 | * @param animated If set to YES the HUDs will disappear using the current animationType. If set to NO the HUDs will not use
150 | * animations while disappearing.
151 | * @return the number of HUDs found and removed.
152 | *
153 | * @see hideHUDForView:animated:
154 | * @see animationType
155 | */
156 | + (NSUInteger)hideAllHUDsForView:(UIView *)view animated:(BOOL)animated;
157 |
158 | /**
159 | * Finds the top-most HUD subview and returns it.
160 | *
161 | * @param view The view that is going to be searched.
162 | * @return A reference to the last HUD subview discovered.
163 | */
164 | + (MB_INSTANCETYPE)HUDForView:(UIView *)view;
165 |
166 | /**
167 | * Finds all HUD subviews and returns them.
168 | *
169 | * @param view The view that is going to be searched.
170 | * @return All found HUD views (array of MBProgressHUD objects).
171 | */
172 | + (NSArray *)allHUDsForView:(UIView *)view;
173 |
174 | /**
175 | * A convenience constructor that initializes the HUD with the window's bounds. Calls the designated constructor with
176 | * window.bounds as the parameter.
177 | *
178 | * @param window The window instance that will provide the bounds for the HUD. Should be the same instance as
179 | * the HUD's superview (i.e., the window that the HUD will be added to).
180 | */
181 | - (id)initWithWindow:(UIWindow *)window;
182 |
183 | /**
184 | * A convenience constructor that initializes the HUD with the view's bounds. Calls the designated constructor with
185 | * view.bounds as the parameter
186 | *
187 | * @param view The view instance that will provide the bounds for the HUD. Should be the same instance as
188 | * the HUD's superview (i.e., the view that the HUD will be added to).
189 | */
190 | - (id)initWithView:(UIView *)view;
191 |
192 | /**
193 | * Display the HUD. You need to make sure that the main thread completes its run loop soon after this method call so
194 | * the user interface can be updated. Call this method when your task is already set-up to be executed in a new thread
195 | * (e.g., when using something like NSOperation or calling an asynchronous call like NSURLRequest).
196 | *
197 | * @param animated If set to YES the HUD will appear using the current animationType. If set to NO the HUD will not use
198 | * animations while appearing.
199 | *
200 | * @see animationType
201 | */
202 | - (void)show:(BOOL)animated;
203 |
204 | /**
205 | * Hide the HUD. This still calls the hudWasHidden: delegate. This is the counterpart of the show: method. Use it to
206 | * hide the HUD when your task completes.
207 | *
208 | * @param animated If set to YES the HUD will disappear using the current animationType. If set to NO the HUD will not use
209 | * animations while disappearing.
210 | *
211 | * @see animationType
212 | */
213 | - (void)hide:(BOOL)animated;
214 |
215 | /**
216 | * Hide the HUD after a delay. This still calls the hudWasHidden: delegate. This is the counterpart of the show: method. Use it to
217 | * hide the HUD when your task completes.
218 | *
219 | * @param animated If set to YES the HUD will disappear using the current animationType. If set to NO the HUD will not use
220 | * animations while disappearing.
221 | * @param delay Delay in seconds until the HUD is hidden.
222 | *
223 | * @see animationType
224 | */
225 | - (void)hide:(BOOL)animated afterDelay:(NSTimeInterval)delay;
226 |
227 | /**
228 | * Shows the HUD while a background task is executing in a new thread, then hides the HUD.
229 | *
230 | * This method also takes care of autorelease pools so your method does not have to be concerned with setting up a
231 | * pool.
232 | *
233 | * @param method The method to be executed while the HUD is shown. This method will be executed in a new thread.
234 | * @param target The object that the target method belongs to.
235 | * @param object An optional object to be passed to the method.
236 | * @param animated If set to YES the HUD will (dis)appear using the current animationType. If set to NO the HUD will not use
237 | * animations while (dis)appearing.
238 | */
239 | - (void)showWhileExecuting:(SEL)method onTarget:(id)target withObject:(id)object animated:(BOOL)animated;
240 |
241 | #if NS_BLOCKS_AVAILABLE
242 |
243 | /**
244 | * Shows the HUD while a block is executing on a background queue, then hides the HUD.
245 | *
246 | * @see showAnimated:whileExecutingBlock:onQueue:completionBlock:
247 | */
248 | - (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block;
249 |
250 | /**
251 | * Shows the HUD while a block is executing on a background queue, then hides the HUD.
252 | *
253 | * @see showAnimated:whileExecutingBlock:onQueue:completionBlock:
254 | */
255 | - (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block completionBlock:(MBProgressHUDCompletionBlock)completion;
256 |
257 | /**
258 | * Shows the HUD while a block is executing on the specified dispatch queue, then hides the HUD.
259 | *
260 | * @see showAnimated:whileExecutingBlock:onQueue:completionBlock:
261 | */
262 | - (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block onQueue:(dispatch_queue_t)queue;
263 |
264 | /**
265 | * Shows the HUD while a block is executing on the specified dispatch queue, executes completion block on the main queue, and then hides the HUD.
266 | *
267 | * @param animated If set to YES the HUD will (dis)appear using the current animationType. If set to NO the HUD will
268 | * not use animations while (dis)appearing.
269 | * @param block The block to be executed while the HUD is shown.
270 | * @param queue The dispatch queue on which the block should be executed.
271 | * @param completion The block to be executed on completion.
272 | *
273 | * @see completionBlock
274 | */
275 | - (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block onQueue:(dispatch_queue_t)queue
276 | completionBlock:(MBProgressHUDCompletionBlock)completion;
277 |
278 | /**
279 | * A block that gets called after the HUD was completely hidden.
280 | */
281 | @property (copy) MBProgressHUDCompletionBlock completionBlock;
282 |
283 | #endif
284 |
285 | /**
286 | * MBProgressHUD operation mode. The default is MBProgressHUDModeIndeterminate.
287 | *
288 | * @see MBProgressHUDMode
289 | */
290 | @property (assign) MBProgressHUDMode mode;
291 |
292 | /**
293 | * The animation type that should be used when the HUD is shown and hidden.
294 | *
295 | * @see MBProgressHUDAnimation
296 | */
297 | @property (assign) MBProgressHUDAnimation animationType;
298 |
299 | /**
300 | * The UIView (e.g., a UIImageView) to be shown when the HUD is in MBProgressHUDModeCustomView.
301 | * For best results use a 37 by 37 pixel view (so the bounds match the built in indicator bounds).
302 | */
303 | @property (MB_STRONG) UIView *customView;
304 |
305 | /**
306 | * The HUD delegate object.
307 | *
308 | * @see MBProgressHUDDelegate
309 | */
310 | @property (MB_WEAK) id delegate;
311 |
312 | /**
313 | * An optional short message to be displayed below the activity indicator. The HUD is automatically resized to fit
314 | * the entire text. If the text is too long it will get clipped by displaying "..." at the end. If left unchanged or
315 | * set to @"", then no message is displayed.
316 | */
317 | @property (copy) NSString *labelText;
318 |
319 | /**
320 | * An optional details message displayed below the labelText message. This message is displayed only if the labelText
321 | * property is also set and is different from an empty string (@""). The details text can span multiple lines.
322 | */
323 | @property (copy) NSString *detailsLabelText;
324 |
325 | /**
326 | * The opacity of the HUD window. Defaults to 0.8 (80% opacity).
327 | */
328 | @property (assign) float opacity;
329 |
330 | /**
331 | * The color of the HUD window. Defaults to black. If this property is set, color is set using
332 | * this UIColor and the opacity property is not used. using retain because performing copy on
333 | * UIColor base colors (like [UIColor greenColor]) cause problems with the copyZone.
334 | */
335 | @property (MB_STRONG) UIColor *color;
336 |
337 | /**
338 | * The x-axis offset of the HUD relative to the centre of the superview.
339 | */
340 | @property (assign) float xOffset;
341 |
342 | /**
343 | * The y-axis offset of the HUD relative to the centre of the superview.
344 | */
345 | @property (assign) float yOffset;
346 |
347 | /**
348 | * The amount of space between the HUD edge and the HUD elements (labels, indicators or custom views).
349 | * Defaults to 20.0
350 | */
351 | @property (assign) float margin;
352 |
353 | /**
354 | * The corner radius for the HUD
355 | * Defaults to 10.0
356 | */
357 | @property (assign) float cornerRadius;
358 |
359 | /**
360 | * Cover the HUD background view with a radial gradient.
361 | */
362 | @property (assign) BOOL dimBackground;
363 |
364 | /*
365 | * Grace period is the time (in seconds) that the invoked method may be run without
366 | * showing the HUD. If the task finishes before the grace time runs out, the HUD will
367 | * not be shown at all.
368 | * This may be used to prevent HUD display for very short tasks.
369 | * Defaults to 0 (no grace time).
370 | * Grace time functionality is only supported when the task status is known!
371 | * @see taskInProgress
372 | */
373 | @property (assign) float graceTime;
374 |
375 | /**
376 | * The minimum time (in seconds) that the HUD is shown.
377 | * This avoids the problem of the HUD being shown and than instantly hidden.
378 | * Defaults to 0 (no minimum show time).
379 | */
380 | @property (assign) float minShowTime;
381 |
382 | /**
383 | * Indicates that the executed operation is in progress. Needed for correct graceTime operation.
384 | * If you don't set a graceTime (different than 0.0) this does nothing.
385 | * This property is automatically set when using showWhileExecuting:onTarget:withObject:animated:.
386 | * When threading is done outside of the HUD (i.e., when the show: and hide: methods are used directly),
387 | * you need to set this property when your task starts and completes in order to have normal graceTime
388 | * functionality.
389 | */
390 | @property (assign) BOOL taskInProgress;
391 |
392 | /**
393 | * Removes the HUD from its parent view when hidden.
394 | * Defaults to NO.
395 | */
396 | @property (assign) BOOL removeFromSuperViewOnHide;
397 |
398 | /**
399 | * Font to be used for the main label. Set this property if the default is not adequate.
400 | */
401 | @property (MB_STRONG) UIFont* labelFont;
402 |
403 | /**
404 | * Color to be used for the main label. Set this property if the default is not adequate.
405 | */
406 | @property (MB_STRONG) UIColor* labelColor;
407 |
408 | /**
409 | * Font to be used for the details label. Set this property if the default is not adequate.
410 | */
411 | @property (MB_STRONG) UIFont* detailsLabelFont;
412 |
413 | /**
414 | * Color to be used for the details label. Set this property if the default is not adequate.
415 | */
416 | @property (MB_STRONG) UIColor* detailsLabelColor;
417 |
418 | /**
419 | * The color of the activity indicator. Defaults to [UIColor whiteColor]
420 | * Does nothing on pre iOS 5.
421 | */
422 | @property (MB_STRONG) UIColor *activityIndicatorColor;
423 |
424 | /**
425 | * The progress of the progress indicator, from 0.0 to 1.0. Defaults to 0.0.
426 | */
427 | @property (assign) float progress;
428 |
429 | /**
430 | * The minimum size of the HUD bezel. Defaults to CGSizeZero (no minimum size).
431 | */
432 | @property (assign) CGSize minSize;
433 |
434 |
435 | /**
436 | * The actual size of the HUD bezel.
437 | * You can use this to limit touch handling on the bezel aria only.
438 | * @see https://github.com/jdg/MBProgressHUD/pull/200
439 | */
440 | @property (atomic, assign, readonly) CGSize size;
441 |
442 |
443 | /**
444 | * Force the HUD dimensions to be equal if possible.
445 | */
446 | @property (assign, getter = isSquare) BOOL square;
447 |
448 | @end
449 |
450 |
451 | @protocol MBProgressHUDDelegate
452 |
453 | @optional
454 |
455 | /**
456 | * Called after the HUD was fully hidden from the screen.
457 | */
458 | - (void)hudWasHidden:(MBProgressHUD *)hud;
459 |
460 | @end
461 |
462 |
463 | /**
464 | * A progress view for showing definite progress by filling up a circle (pie chart).
465 | */
466 | @interface MBRoundProgressView : UIView
467 |
468 | /**
469 | * Progress (0.0 to 1.0)
470 | */
471 | @property (nonatomic, assign) float progress;
472 |
473 | /**
474 | * Indicator progress color.
475 | * Defaults to white [UIColor whiteColor]
476 | */
477 | @property (nonatomic, MB_STRONG) UIColor *progressTintColor;
478 |
479 | /**
480 | * Indicator background (non-progress) color.
481 | * Defaults to translucent white (alpha 0.1)
482 | */
483 | @property (nonatomic, MB_STRONG) UIColor *backgroundTintColor;
484 |
485 | /*
486 | * Display mode - NO = round or YES = annular. Defaults to round.
487 | */
488 | @property (nonatomic, assign, getter = isAnnular) BOOL annular;
489 |
490 | @end
491 |
492 |
493 | /**
494 | * A flat bar progress view.
495 | */
496 | @interface MBBarProgressView : UIView
497 |
498 | /**
499 | * Progress (0.0 to 1.0)
500 | */
501 | @property (nonatomic, assign) float progress;
502 |
503 | /**
504 | * Bar border line color.
505 | * Defaults to white [UIColor whiteColor].
506 | */
507 | @property (nonatomic, MB_STRONG) UIColor *lineColor;
508 |
509 | /**
510 | * Bar background color.
511 | * Defaults to clear [UIColor clearColor];
512 | */
513 | @property (nonatomic, MB_STRONG) UIColor *progressRemainingColor;
514 |
515 | /**
516 | * Bar progress color.
517 | * Defaults to white [UIColor whiteColor].
518 | */
519 | @property (nonatomic, MB_STRONG) UIColor *progressColor;
520 |
521 | @end
522 |
--------------------------------------------------------------------------------
/YTChatDemo/ImgBrower/YTImgBrowerController.h:
--------------------------------------------------------------------------------
1 | //
2 | // YTImgBrowerController.h
3 | // YTImgBrower
4 | //
5 | // Created by TI on 15/10/26.
6 | // Copyright © 2015年 ycctime.com. All rights reserved.
7 | //
8 |
9 | #import
10 | @class YTImgInfo;
11 | typedef NS_ENUM(NSInteger, YTImgBrowerModel) {
12 | YTImgBrowerModelNone = 0,
13 | };
14 |
15 | @protocol YTImgBrowerControllerDelegate
16 | @optional
17 | /** 图片信息初始化完成触发 */
18 | - (void)imgBrowerControllerInitEnd:(YTImgInfo *)imgInfo;
19 | /** 在控制器消失之前触发 */
20 | - (void)imgBrowerControllerWillDismiss:(YTImgInfo *)imgInfo;
21 | @end
22 |
23 | @interface YTImgBrowerController : UIViewController
24 |
25 | @property(nonatomic, assign) YTImgBrowerModel *model;
26 | /*
27 | * delegate 代理
28 | * imgInfos 里面是YTImgInfo对象
29 | * index 开始定位到第几张图片 默认是0 代表第一张
30 | */
31 | - (instancetype)initWithDelegate:(id)delegate imgInfos:(NSArray *)imgInfos index:(NSInteger)index;
32 | @end
33 |
--------------------------------------------------------------------------------
/YTChatDemo/ImgBrower/YTImgBrowerController.m:
--------------------------------------------------------------------------------
1 | //
2 | // YTImgBrowerController.m
3 | // YTImgBrower
4 | //
5 | // Created by TI on 15/10/26.
6 | // Copyright © 2015年 ycctime.com. All rights reserved.
7 | //
8 |
9 | #import "YTImgBrowerController.h"
10 | #import "YTImgScroll.h"
11 |
12 | #define Page_Lab_H 14.0f
13 | #define Page_Scale (4.9f/5.0f)
14 |
15 | @interface YTImgBrowerController (){
16 | NSInteger beginIndex, currentIndex;//当前显示图片下标
17 | YTImgScroll *currentScroll;//当前ImgScroll
18 | CGFloat frontPointX;
19 | }
20 | @property(nonatomic, assign) id delegate;
21 | @property(nonatomic, strong) NSArray *imgInfos;
22 | @property(nonatomic, strong) UIScrollView *scrollView;
23 | @property(nonatomic, strong) UILabel *pageNumber;
24 |
25 | @property(nonatomic, strong) NSMutableArray *imgScrolls;
26 | @end
27 |
28 | @implementation YTImgBrowerController
29 |
30 | - (UIStatusBarStyle)preferredStatusBarStyle{
31 | return UIStatusBarStyleLightContent;
32 | }
33 |
34 | - (NSMutableArray *)imgScrolls{
35 | if (!_imgScrolls) {
36 | _imgScrolls = [NSMutableArray arrayWithCapacity:2];
37 | }
38 | return _imgScrolls;
39 | }
40 |
41 | - (void)viewDidLoad {
42 | [super viewDidLoad];
43 | [self showImgBrower];
44 | [self addGestureRecognizers];
45 | }
46 |
47 | - (void)viewWillDisappear:(BOOL)animated{
48 | [super viewWillDisappear:animated];
49 | [self delegateWillDismiss];
50 | }
51 |
52 | - (void)viewWillAppear:(BOOL)animated{
53 | [super viewWillAppear:animated];
54 | }
55 |
56 | - (instancetype)initWithDelegate:(id)delegate imgInfos:(NSArray *)imgInfos index:(NSInteger)index{
57 | if (self = [super init]) {
58 | self.delegate = delegate;
59 | self.imgInfos = [imgInfos copy];
60 | beginIndex = index;
61 | currentIndex = -1;
62 | [self performSelector:@selector(initEnd) withObject:nil afterDelay:0.1];
63 | }
64 | return self;
65 | }
66 |
67 | - (UIScrollView *)scrollView{
68 | if (!_scrollView) {
69 | UIScrollView *scrollView = [[UIScrollView alloc]initWithFrame:self.view.bounds];
70 | scrollView.delegate = self;
71 | scrollView.pagingEnabled = YES;
72 | scrollView.alwaysBounceVertical = NO;
73 | scrollView.showsVerticalScrollIndicator = NO;
74 | scrollView.showsHorizontalScrollIndicator = NO;
75 | scrollView.backgroundColor = [UIColor blackColor];
76 | scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
77 | [self.view addSubview:_scrollView = scrollView];
78 | }
79 | return _scrollView;
80 | }
81 |
82 | - (UILabel *)pageNumber{
83 | if (!_pageNumber) {
84 | UILabel *pageNumber = [[UILabel alloc]init];
85 | pageNumber.textColor = [UIColor whiteColor];
86 | pageNumber.textAlignment = NSTextAlignmentCenter;
87 | pageNumber.font = [UIFont systemFontOfSize:12.0f];
88 | pageNumber.autoresizingMask = UIViewAutoresizingFlexibleWidth;
89 | [self.view addSubview:_pageNumber = pageNumber];
90 | }
91 | return _pageNumber;
92 | }
93 |
94 | - (void)showImgBrower{
95 | [self setingScrollViewView];
96 | self.pageNumber.text = [self getPabeNumberText];
97 | }
98 |
99 | - (void)addGestureRecognizers{//添加单击返回,双击缩放手势
100 | UITapGestureRecognizer *tap1 = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapAction:)];
101 | [self.view addGestureRecognizer:tap1];
102 |
103 | UITapGestureRecognizer *tap2 = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapAction:)];
104 | [tap2 setNumberOfTapsRequired:2];
105 | [self.view addGestureRecognizer:tap2];
106 |
107 | [tap1 requireGestureRecognizerToFail:tap2];//单击优先级滞后于双击
108 | }
109 |
110 | - (void)setingScrollViewView{
111 | /* 提示:当图片数大于等于2张时,目前设计为2个ImgScroll进行重用,修改需谨慎 */
112 | for (int i = 0; i < MIN(2, self.imgInfos.count); i++) {
113 | YTImgScroll *imgScroll = [[YTImgScroll alloc]initWithFrame:self.scrollView.bounds];
114 | imgScroll.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
115 | [self.scrollView addSubview:imgScroll];
116 | [self.imgScrolls addObject:imgScroll];
117 | }
118 | [self imgScrollToIndex:beginIndex];
119 | }
120 |
121 | - (NSString*)getPabeNumberText{
122 | return [NSString stringWithFormat:@"%i/%i",(int)(currentIndex+1),(int)self.imgInfos.count];
123 | }
124 |
125 |
126 | - (void)tapAction:(UITapGestureRecognizer *)ges{
127 | if (ges.numberOfTapsRequired == 1) { // 单击
128 | [self dismissViewControllerAnimated:NO completion:nil];
129 | }else { //双击
130 | [currentScroll doubleTapAction];
131 | }
132 | }
133 |
134 | #pragma mark - layout Sub Views (横竖屏切换重新布局)
135 | - (void)scrollViewLayoutSubViews{//重新布局scrollView内部的控件
136 | for (YTImgScroll *imgScroll in self.imgScrolls) {
137 | if (![imgScroll isKindOfClass:[YTImgScroll class]]) return;
138 | CGRect frame = self.scrollView.bounds;
139 | frame.origin.x = (imgScroll.imgInfo.index) * (frame.size.width);
140 | imgScroll.frame = frame;
141 | [imgScroll layoutSubview];
142 | }
143 |
144 | [currentScroll replyStatuseAnimated:YES];
145 | [self.scrollView setContentOffset:currentScroll.frame.origin];
146 | }
147 |
148 | - (void)viewWillLayoutSubviews{
149 | [super viewWillLayoutSubviews];
150 |
151 | CGSize size = self.scrollView.bounds.size;
152 | size.width *= self.imgInfos.count;
153 | self.scrollView.contentSize = size;
154 |
155 | CGRect frame = self.view.bounds;
156 | frame.origin.y = (frame.size.height*Page_Scale) - Page_Lab_H;
157 | frame.size.height = Page_Lab_H;
158 | self.pageNumber.frame = frame;
159 |
160 | [self scrollViewLayoutSubViews];
161 | }
162 |
163 |
164 | #pragma mark - Scroll View Deledate (scroll 代理)
165 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView{
166 | if (!scrollView.isDragging) return;
167 | [self scrollViewDragging:scrollView];
168 | }
169 |
170 | - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
171 |
172 | [self scrollViewDragging:scrollView];
173 |
174 | for (YTImgScroll *imgScroll in self.imgScrolls) {
175 | if (imgScroll != currentScroll) {
176 | [imgScroll replyStatuseAnimated:NO];
177 | }
178 | }
179 | }
180 |
181 | #pragma mark - Image Scroll Reuse (imageScroll复用)
182 | - (void)scrollViewDragging:(UIScrollView *)scrollView{
183 | CGFloat pointx = (scrollView.contentOffset.x)/(scrollView.bounds.size.width);
184 |
185 | if (scrollView.contentOffset.x > frontPointX) {
186 | [self imgScrollToIndex:ceilf(pointx)];//取上整
187 | }else{
188 | [self imgScrollToIndex:floorf(pointx)];//取下整
189 | }
190 |
191 | NSInteger integer = pointx+0.5;
192 | if (integer != currentIndex) {
193 | //currentIndex = integer;
194 | self.pageNumber.text = [self getPabeNumberText];
195 | }
196 | frontPointX = scrollView.contentOffset.x;
197 | }
198 |
199 | - (void)imgScrollToIndex:(NSInteger)index{
200 | BOOL back = currentIndex > index;
201 | if ((currentIndex == index)||(index >= self.imgInfos.count)) {
202 | return;
203 | }
204 | currentIndex = index;
205 | YTImgScroll *imgScroll = [self getFollowScroll];
206 | currentScroll = imgScroll;
207 | if (imgScroll.tag == index) return;
208 |
209 | [currentScroll replyStatuseAnimated:NO];
210 | CGRect frame = self.scrollView.bounds;
211 | frame.origin.x = index*(frame.size.width);
212 | imgScroll.frame = frame;
213 | imgScroll.tag = index;
214 | imgScroll.imgInfo = self.imgInfos[index];
215 | if (back) { //预加载
216 | if (currentIndex > 0) {
217 | [(YTImgInfo *)self.imgInfos[index-1] httpRequest];
218 | }
219 | }else{
220 | if (currentIndex < self.imgInfos.count-1) {
221 | [(YTImgInfo *)self.imgInfos[index+1] httpRequest];
222 | }
223 | }
224 | }
225 |
226 | - (YTImgScroll *)getFollowScroll{
227 | YTImgScroll *imgScroll = self.imgScrolls.lastObject;
228 | [self.imgScrolls removeLastObject];
229 | [self.imgScrolls insertObject:imgScroll atIndex:0];
230 | return imgScroll;
231 | }
232 |
233 | - (void)initEnd{
234 | if (self.delegate && [self.delegate respondsToSelector:@selector(imgBrowerControllerInitEnd:)]) {
235 | YTImgInfo *imgInfo = self.imgInfos[beginIndex];
236 | [self.delegate imgBrowerControllerInitEnd:imgInfo];
237 | }
238 | }
239 |
240 | - (void)delegateWillDismiss{
241 | if (self.delegate && [self.delegate respondsToSelector:@selector(imgBrowerControllerWillDismiss:)]) {
242 | [self.delegate imgBrowerControllerWillDismiss:currentScroll.imgInfo];
243 | }
244 | }
245 |
246 | @end
247 |
--------------------------------------------------------------------------------
/YTChatDemo/ImgBrower/YTImgInfo.h:
--------------------------------------------------------------------------------
1 | //
2 | // YTImgInfo.h
3 | // YTImgBrower
4 | //
5 | // Created by TI on 15/10/26.
6 | // Copyright © 2015年 ycctime.com. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | typedef NS_ENUM(NSInteger, YTImgInfoFromeType) {
12 | YTImgInfoFromeTypePhoto = 0, //来之本地相册
13 | YTImgInfoFromeTypeRemote //来之远程网络
14 | };
15 |
16 | @interface YTImgInfo : NSObject
17 | /** image, url 有可能为空 引用时要判断 */
18 | @property(nonatomic, strong) UIImage *image; //图片
19 | @property(nonatomic, strong, readonly) NSString *explin; //图片信息
20 | @property(nonatomic, strong, readonly) NSURL *url; //图片地址
21 | @property(nonatomic, assign, readonly) NSInteger index; //位置索引
22 | @property(nonatomic, assign, readonly) CGSize size; //图片适配后大小
23 | @property(nonatomic, assign, readonly) YTImgInfoFromeType fromeType; //来之哪里
24 | @property (nonatomic, assign, getter=ishttp) BOOL http; //网络请求成功与否
25 |
26 | + (NSArray *)imgInfosWithImgs:(NSArray *)imgs urls:(NSArray *)urls type:(YTImgInfoFromeType)type;
27 |
28 | - (void)httpRequest; // 网络请求数据
29 | @end
30 |
--------------------------------------------------------------------------------
/YTChatDemo/ImgBrower/YTImgInfo.m:
--------------------------------------------------------------------------------
1 | //
2 | // YTImgInfo.m
3 | // YTImgBrower
4 | //
5 | // Created by TI on 15/10/26.
6 | // Copyright © 2015年 ycctime.com. All rights reserved.
7 | //
8 |
9 | @import AssetsLibrary;
10 | #import
11 | #import "YTNetworkLoad.h"
12 | #import "YTImgInfo.h"
13 |
14 | #define Max_Count_Obj 5000 //它具体是多少随便你
15 | #define Device_Size ([UIScreen mainScreen].bounds.size)
16 |
17 | @interface YTImgInfo(){
18 | YTNetworkLoad *netLoad;
19 | }
20 | @property(nonatomic, strong) NSString *explin;//图片信息
21 | @property(nonatomic, strong) NSURL *url; //图片地址
22 | @property(nonatomic, assign) NSInteger index; //位置索引
23 | @property(nonatomic, assign) CGSize size; //图片适配后大小
24 | @property(nonatomic, assign) YTImgInfoFromeType fromeType;
25 | @end
26 |
27 | @implementation YTImgInfo
28 | + (NSArray *)imgInfosWithImgs:(NSArray *)imgs urls:(NSArray *)urls type:(YTImgInfoFromeType)type{
29 | //根据图片及图片网址来创建一组该对象 最多为(Max_Count_Obj)个
30 | NSInteger max = MAX(imgs.count, urls.count);
31 | NSInteger maxInt = MIN(Max_Count_Obj, max);
32 | NSMutableArray *imgInfos = [NSMutableArray arrayWithCapacity:maxInt];
33 | for (int index = 0; index < maxInt; index++) {
34 | YTImgInfo *imgInfo = [[YTImgInfo alloc]init];
35 |
36 | id objUrl = index < urls.count?urls[index]:nil;
37 | //url
38 | switch (type) {
39 | case YTImgInfoFromeTypePhoto:
40 | {
41 | if (objUrl) [imgInfo setingPhotoWith:objUrl];
42 | break;
43 | }
44 | case YTImgInfoFromeTypeRemote:
45 | {
46 | //图片
47 | UIImage *image = index < imgs.count?imgs[index]:nil;
48 | if (!image||(image.size.width < 1.0f)) {
49 | image = [UIImage imageNamed:@"default_img"];
50 | }
51 | imgInfo.image = image;
52 | if (objUrl) [imgInfo setingRemoteWith:objUrl];
53 | break;
54 | }
55 | default:
56 | break;
57 | }
58 | imgInfo.index = index;
59 | imgInfo.fromeType = type;
60 | [imgInfos addObject:imgInfo];
61 | }
62 | return imgInfos;
63 | }
64 |
65 |
66 | - (void)setingPhotoWith:(id)objUrl{
67 | if ([objUrl isKindOfClass:[ALAsset class]]) {
68 | UIImage *image = [UIImage imageWithCGImage:[[objUrl defaultRepresentation] fullScreenImage]];
69 | self.image = image;
70 | self.url = [objUrl defaultRepresentation].url;
71 | self.http = YES;
72 | }else if([objUrl isKindOfClass:[NSURL class]]){
73 | ALAssetsLibrary *libary = [[ALAssetsLibrary alloc]init];
74 | [libary assetForURL:objUrl resultBlock:^(ALAsset *asset) {
75 | UIImage *image = [UIImage imageWithCGImage:[[asset defaultRepresentation] fullScreenImage]];
76 | self.image = image;
77 | self.url = objUrl;
78 | self.http = YES;
79 | } failureBlock:^(NSError *error) {
80 | self.url = objUrl;
81 | self.http = NO;
82 | }];
83 | }else{
84 | self.url = nil;
85 | self.image = [UIImage imageNamed:@"default_img"];
86 | }
87 | }
88 |
89 | - (void)setingRemoteWith:(id)objUrl{
90 | if ([objUrl isKindOfClass:[NSURL class]]) {
91 | self.url = objUrl;
92 | }else if([objUrl isKindOfClass:[NSString class]]){
93 | self.url = [NSURL URLWithString:objUrl];
94 | }else{
95 | self.url = nil;
96 | }
97 | self.http = NO;
98 | }
99 |
100 |
101 | #pragma mark - Network loag (图片网络请求)
102 | - (void)httpRequest{
103 | if ((self.url)&&(!self.ishttp)) {
104 | switch (self.fromeType) {
105 | case YTImgInfoFromeTypePhoto:
106 | [self photoRequest];
107 | break;
108 | case YTImgInfoFromeTypeRemote:
109 | [self remoteRequest];
110 | break;
111 | default:
112 | break;
113 | }
114 | }
115 | }
116 |
117 | - (void)photoRequest{
118 | ALAssetsLibrary *libary = [[ALAssetsLibrary alloc]init];
119 | __weak __typeof (self)weekSelf = self;
120 | [libary assetForURL:self.url resultBlock:^(ALAsset *asset) {
121 | UIImage *image = [UIImage imageWithCGImage:[[asset defaultRepresentation] fullScreenImage]];
122 | __strong __typeof (weekSelf)strongSelf = weekSelf;
123 | strongSelf.image = image;
124 | strongSelf.http = YES;
125 | } failureBlock:^(NSError *error) {
126 |
127 | }];
128 | }
129 |
130 | - (void)remoteRequest{
131 | __weak __typeof (self)weekSelf = self;
132 | [netLoad cancel];
133 | netLoad = [[YTNetworkLoad alloc]initWithUrl:self.url success:^(NSURLRequest *request, id data) {
134 | __strong __typeof (weekSelf)strongSelf = weekSelf;
135 | strongSelf.image = [UIImage imageWithData:data];
136 | strongSelf.http = YES;
137 | netLoad = nil;
138 | } failure:^(NSError *error) {
139 | netLoad = nil;
140 | }];
141 | }
142 |
143 | - (void)setImage:(UIImage *)image{
144 | if (image) {
145 | _image = image;
146 | self.size = [self imageSize];
147 | }
148 | }
149 |
150 | - (CGSize)imageSize{//图片根据屏幕大小来调整size,保证与屏幕比例适配
151 | CGFloat wid = _image.size.width;
152 | CGFloat heig = _image.size.height;
153 | if ((wid <= Device_Size.width) && (heig <= Device_Size.height)) {
154 | return _image.size;
155 | }
156 |
157 | CGFloat scale_poor = (wid/Device_Size.width)-( heig/Device_Size.height);
158 | CGSize endSize = CGSizeZero;
159 |
160 | if (scale_poor > 0) {
161 | CGFloat height_now = heig*(Device_Size.width/wid);
162 | endSize = CGSizeMake(Device_Size.width, height_now);
163 | }else{
164 | CGFloat width_now = wid*(Device_Size.height/heig);
165 | endSize = CGSizeMake(width_now, Device_Size.height);
166 | }
167 | return endSize;
168 | }
169 |
170 | @end
171 |
--------------------------------------------------------------------------------
/YTChatDemo/ImgBrower/YTImgScroll.h:
--------------------------------------------------------------------------------
1 | //
2 | // YTImgScroll.h
3 | // YTImgBrower
4 | //
5 | // Created by TI on 15/10/26.
6 | // Copyright © 2015年 ycctime.com. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "YTImgInfo.h"
11 |
12 | @interface YTImgScroll : UIScrollView
13 | @property(nonatomic, strong) YTImgInfo *imgInfo;
14 | /**恢复图片原始状态 animated->是否需要动画*/
15 | - (void)replyStatuseAnimated:(BOOL)animated;
16 |
17 | /**双击事件(父视触发,放大或缩小)*/
18 | - (void)doubleTapAction;
19 |
20 | /**在横竖屏变化是调用 重新布局*/
21 | - (void)layoutSubview;
22 | @end
23 |
--------------------------------------------------------------------------------
/YTChatDemo/ImgBrower/YTImgScroll.m:
--------------------------------------------------------------------------------
1 | //
2 | // YTImgScroll.m
3 | // YTImgBrower
4 | //
5 | // Created by TI on 15/10/26.
6 | // Copyright © 2015年 ycctime.com. All rights reserved.
7 | //
8 |
9 | @import AssetsLibrary;
10 | #import "YTImgScroll.h"
11 | #import "MBProgressHUD.h"
12 | #import "YTNetworkLoad.h"
13 |
14 | #define MaxZoomScale 2.5f
15 | #define MinZoomScale 1.0f
16 |
17 | @interface YTImgScroll(){
18 | MBProgressHUD *HUD;
19 | YTNetworkLoad *netLoad;
20 | BOOL needAnimal;
21 | }
22 | @property (nonatomic, strong) UIImageView *imgView;
23 | @end
24 |
25 | @implementation YTImgScroll
26 |
27 | - (UIImageView *)imgView{
28 | if (!_imgView) {
29 | UIImageView *imgView = [[UIImageView alloc]init];
30 | imgView.userInteractionEnabled = YES;
31 | [imgView setContentMode:UIViewContentModeScaleAspectFit];
32 | [imgView setClipsToBounds:YES];
33 | [self addSubview:_imgView = imgView];
34 | // 添加HUD和手势
35 | [self addHUD];
36 | [self addGestureRecognizers];
37 | }
38 | return _imgView;
39 | }
40 |
41 | - (instancetype)initWithFrame:(CGRect)frame{
42 | if (self = [super initWithFrame:frame]) {
43 | self.delegate = self;
44 | self.alwaysBounceVertical = NO;
45 | self.contentSize = self.bounds.size;
46 | self.maximumZoomScale = MaxZoomScale;
47 | self.minimumZoomScale = MinZoomScale;
48 | self.showsVerticalScrollIndicator = NO;
49 | self.showsHorizontalScrollIndicator = NO;
50 | self.tag = -1; /* 注明:后面以tag来判断当前页以免造成混乱 */
51 | }
52 | return self;
53 | }
54 |
55 | - (void)addGestureRecognizers{
56 | //长按手势 保存图片
57 | UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(longPressAction:)];
58 | [self addGestureRecognizer:longPress];
59 | }
60 |
61 | - (void)addHUD{
62 | HUD = [[MBProgressHUD alloc]initWithView:self];
63 | HUD.mode = MBProgressHUDModeIndeterminate;
64 | HUD.color = [UIColor clearColor];
65 | HUD.delegate = self;
66 | [self addSubview:HUD];
67 | }
68 |
69 | #pragma mark 横竖屏切换布局
70 | - (void)layoutSubview{
71 | [_imgInfo setImage:_imgInfo.image];
72 | [self setImgInfo:_imgInfo];
73 | }
74 |
75 | -(void)setImgInfo:(YTImgInfo *)imgInfo{
76 | if (!imgInfo) return;
77 | BOOL BUG = (_imgInfo.index == imgInfo.index);
78 | _imgInfo = imgInfo;
79 | self.imgView.image = imgInfo.image;
80 | CGRect frame = self.bounds;
81 | frame.size = imgInfo.size;
82 | if ((needAnimal == YES) && (BUG == YES)) {
83 | needAnimal = NO;
84 | [UIView animateWithDuration:0.2 animations:^{
85 | self.imgView.frame = frame;
86 | [self scrollViewDidZoom:self];
87 | }];
88 | }else{
89 | needAnimal = NO;
90 | self.imgView.frame = frame;
91 | [self scrollViewDidZoom:self];
92 | [self loadHttpImg];
93 | }
94 | }
95 |
96 | #pragma mark - Network loag (图片网络请求)
97 | - (void)loadHttpImg{
98 | //自己写的网络请求加载
99 | if ((_imgInfo.url)&&(!_imgInfo.ishttp)) {
100 | [HUD show:YES];
101 | __weak __typeof (self)weekSelf = self;
102 | switch (_imgInfo.fromeType) {
103 | case YTImgInfoFromeTypePhoto:
104 | {
105 | ALAssetsLibrary *libary = [[ALAssetsLibrary alloc]init];
106 | [libary assetForURL:_imgInfo.url resultBlock:^(ALAsset *asset) {
107 | UIImage *image = [UIImage imageWithCGImage:[[asset defaultRepresentation] fullScreenImage]];
108 | __strong __typeof (weekSelf)strongSelf = weekSelf;
109 | [strongSelf requestResult:image];
110 | } failureBlock:^(NSError *error) {
111 | __strong __typeof (weekSelf)strongSelf = weekSelf;
112 | [strongSelf requestResult:nil];
113 | }];
114 | }
115 | break;
116 | case YTImgInfoFromeTypeRemote:
117 | {
118 | [netLoad cancel];
119 | netLoad = [[YTNetworkLoad alloc]initWithUrl:_imgInfo.url success:^(NSURLRequest *request, id data) {
120 | __strong __typeof (weekSelf)strongSelf = weekSelf;
121 | [strongSelf requestResult:[UIImage imageWithData:data]];
122 | } failure:^(NSError *error) {
123 | __strong __typeof (weekSelf)strongSelf = weekSelf;
124 | [strongSelf requestResult:nil];
125 | }];
126 |
127 | netLoad.updataBlock = ^(float progress){
128 | __strong __typeof (weekSelf)strongSelf = weekSelf;
129 | strongSelf->HUD.mode = MBProgressHUDModeDeterminate;
130 | strongSelf->HUD.progress = progress;
131 | };
132 | }
133 | default:
134 | break;
135 | }
136 | }else{
137 | [HUD hide:YES];
138 | }
139 | }
140 |
141 | - (void)requestResult:(UIImage*)image{
142 | if (HUD.isHidden) return;
143 | if (image) {
144 | needAnimal = YES;
145 | [_imgInfo setImage:image];
146 | _imgInfo.http = YES;
147 | [self setImgInfo:_imgInfo];
148 | }
149 | [HUD hide:YES];
150 | }
151 |
152 |
153 | #pragma mark - Public Action
154 | #pragma mark 恢复图片原始状态
155 | - (void)replyStatuseAnimated:(BOOL)animated{
156 | [self setZoomScale:1.0f animated:animated];
157 | }
158 |
159 | #pragma mark 双击事件
160 | - (void)doubleTapAction{//图片变大或变小
161 | if ((self.minimumZoomScale<=self.zoomScale)&&(self.maximumZoomScale>self.zoomScale)) {
162 | [self setZoomScale:self.maximumZoomScale animated:YES];
163 | }else {
164 | [self setZoomScale:self.minimumZoomScale animated:YES];
165 | }
166 | }
167 |
168 | #pragma mark - Scroll View Deledate (不断适配图片大小)
169 | - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView{
170 | return _imgView;
171 | }
172 |
173 | - (void)scrollViewDidZoom:(UIScrollView *)scrollView{
174 | //放大或缩小时图片位置(frame)调整,保证居中
175 | CGFloat Wo = self.frame.size.width - self.contentInset.left - self.contentInset.right;
176 | CGFloat Ho = self.frame.size.height - self.contentInset.top - self.contentInset.bottom;
177 | UIImageView *imgView = _imgView;
178 | CGFloat W = imgView.frame.size.width;
179 | CGFloat H = imgView.frame.size.height;
180 | CGRect rct = imgView.frame;
181 | rct.origin.x = MAX((Wo-W)*0.5, 0);
182 | rct.origin.y = MAX((Ho-H)*0.5, 0);
183 | imgView.frame = rct;
184 | }
185 |
186 | #pragma mark - Image Save (图像保存)
187 | #pragma mark 长按手势
188 | -(void)longPressAction:(UILongPressGestureRecognizer*)longPress{
189 | if (longPress.state == UIGestureRecognizerStateBegan) {
190 | if (YES) {//[YTDeviceTest userAuthorizationPhotoStatus]
191 | UIActionSheet *actionSheet = [[UIActionSheet alloc]initWithTitle:nil delegate:self cancelButtonTitle:@"取消" destructiveButtonTitle:nil otherButtonTitles:@"保存到手机", nil];
192 | [actionSheet showInView:self];
193 | }
194 | }
195 | }
196 |
197 | #pragma mark Action Sheet Delegate (actionSheet代理)
198 | - (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex{
199 | if (buttonIndex == 0) {
200 | if (self.imgView.image) {//保存图片
201 | UIImageWriteToSavedPhotosAlbum(self.imgView.image, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
202 | }
203 | }
204 | }
205 |
206 | #pragma mark 图片保存结果回调
207 | - (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo{//保存图片回调
208 | if (error != NULL){ //失败
209 | NSLog(@"图片保存失败->%@",error);
210 | }else{//成功
211 | NSLog(@"图片保存成功");
212 | }
213 | }
214 |
215 | #pragma mark - HUD delegate
216 | - (void)hudWasHidden:(MBProgressHUD *)hud{
217 | HUD.progress = 0.0f;
218 | }
219 |
220 | @end
221 |
--------------------------------------------------------------------------------
/YTChatDemo/KeyBoard/Emoji/EmojiModel/Resource/EmojiFile.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Id
7 | local
8 | path
9 | EmojiSutra
10 | type
11 | 0
12 | disable
13 |
14 |
15 |
16 | Id
17 | local
18 | path
19 | EmojiPanda
20 | type
21 | 0
22 | disable
23 |
24 |
25 |
26 | Id
27 | local
28 | path
29 | EmojiGirl
30 | type
31 | 1
32 | disable
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/YTChatDemo/KeyBoard/Emoji/EmojiModel/Resource/EmojiGirl.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | image
6 | girl_beg
7 | name
8 | 矮矮女
9 | Id
10 | local
11 | icons
12 |
13 |
14 | emojiName
15 | 求包养
16 | emojiImage
17 | girl_beg
18 | emojiCode
19 |
20 |
21 |
22 | emojiName
23 | 求包养
24 | emojiImage
25 | girl_beg
26 | emojiCode
27 |
28 |
29 |
30 | emojiName
31 | 求包养
32 | emojiImage
33 | girl_beg
34 | emojiCode
35 |
36 |
37 |
38 | emojiName
39 | 求包养
40 | emojiImage
41 | girl_beg
42 | emojiCode
43 |
44 |
45 |
46 | emojiName
47 | 求包养
48 | emojiImage
49 | girl_beg
50 | emojiCode
51 |
52 |
53 |
54 | emojiName
55 | 求包养
56 | emojiImage
57 | girl_beg
58 | emojiCode
59 |
60 |
61 |
62 | emojiName
63 | 求包养
64 | emojiImage
65 | girl_beg
66 | emojiCode
67 |
68 |
69 |
70 | emojiName
71 | 求包养
72 | emojiImage
73 | girl_beg
74 | emojiCode
75 |
76 |
77 |
78 | emojiName
79 | 求包养
80 | emojiImage
81 | girl_beg
82 | emojiCode
83 |
84 |
85 |
86 | emojiName
87 | 求包养
88 | emojiImage
89 | girl_beg
90 | emojiCode
91 |
92 |
93 |
94 | emojiName
95 | 求包养
96 | emojiImage
97 | girl_beg
98 | emojiCode
99 |
100 |
101 |
102 | emojiName
103 | 求包养
104 | emojiImage
105 | girl_beg
106 | emojiCode
107 |
108 |
109 |
110 | emojiName
111 | 求包养
112 | emojiImage
113 | girl_beg
114 | emojiCode
115 |
116 |
117 |
118 | emojiName
119 | 求包养
120 | emojiImage
121 | girl_beg
122 | emojiCode
123 |
124 |
125 |
126 | emojiName
127 | 求包养
128 | emojiImage
129 | girl_beg
130 | emojiCode
131 |
132 |
133 |
134 | emojiName
135 | 求包养
136 | emojiImage
137 | girl_beg
138 | emojiCode
139 |
140 |
141 |
142 | emojiName
143 | 求包养
144 | emojiImage
145 | girl_beg
146 | emojiCode
147 |
148 |
149 |
150 | emojiName
151 | 求包养
152 | emojiImage
153 | girl_beg
154 | emojiCode
155 |
156 |
157 |
158 | emojiName
159 | 求包养
160 | emojiImage
161 | girl_beg
162 | emojiCode
163 |
164 |
165 |
166 | emojiName
167 | 求包养
168 | emojiImage
169 | girl_beg
170 | emojiCode
171 |
172 |
173 |
174 | emojiName
175 | 求包养
176 | emojiImage
177 | girl_beg
178 | emojiCode
179 |
180 |
181 |
182 |
183 |
184 |
--------------------------------------------------------------------------------
/YTChatDemo/KeyBoard/Emoji/EmojiModel/Resource/EmojiPanda.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | image
6 | smile
7 | name
8 | 熊猫
9 | Id
10 | local
11 | icons
12 |
13 |
14 | emojiName
15 | 微笑
16 | emojiImage
17 | smile
18 | emojiCode
19 | [smile]
20 |
21 |
22 | emojiName
23 | 微笑
24 | emojiImage
25 | smile
26 | emojiCode
27 | [smile]
28 |
29 |
30 | emojiName
31 | 微笑
32 | emojiImage
33 | smile
34 | emojiCode
35 | [smile]
36 |
37 |
38 | emojiName
39 | 微笑
40 | emojiImage
41 | smile
42 | emojiCode
43 | [smile]
44 |
45 |
46 | emojiName
47 | 微笑
48 | emojiImage
49 | smile
50 | emojiCode
51 | [smile]
52 |
53 |
54 | emojiName
55 | 微笑
56 | emojiImage
57 | smile
58 | emojiCode
59 | [smile]
60 |
61 |
62 | emojiName
63 | 微笑
64 | emojiImage
65 | smile
66 | emojiCode
67 | [smile]
68 |
69 |
70 | emojiName
71 | 微笑
72 | emojiImage
73 | smile
74 | emojiCode
75 | [smile]
76 |
77 |
78 | emojiName
79 | 微笑
80 | emojiImage
81 | smile
82 | emojiCode
83 | [smile]
84 |
85 |
86 | emojiName
87 | 微笑
88 | emojiImage
89 | smile
90 | emojiCode
91 | [smile]
92 |
93 |
94 | emojiName
95 | 微笑
96 | emojiImage
97 | smile
98 | emojiCode
99 | [smile]
100 |
101 |
102 | emojiName
103 | 微笑
104 | emojiImage
105 | smile
106 | emojiCode
107 | [smile]
108 |
109 |
110 | emojiName
111 | 微笑
112 | emojiImage
113 | smile
114 | emojiCode
115 | [smile]
116 |
117 |
118 | emojiName
119 | 微笑
120 | emojiImage
121 | smile
122 | emojiCode
123 | [smile]
124 |
125 |
126 | emojiName
127 | 微笑
128 | emojiImage
129 | smile
130 | emojiCode
131 | [smile]
132 |
133 |
134 | emojiName
135 | 微笑
136 | emojiImage
137 | smile
138 | emojiCode
139 | [smile]
140 |
141 |
142 | emojiName
143 | 微笑
144 | emojiImage
145 | smile
146 | emojiCode
147 | [smile]
148 |
149 |
150 | emojiName
151 | 微笑
152 | emojiImage
153 | smile
154 | emojiCode
155 | [smile]
156 |
157 |
158 | emojiName
159 | 微笑
160 | emojiImage
161 | smile
162 | emojiCode
163 | [smile]
164 |
165 |
166 | emojiName
167 | 微笑
168 | emojiImage
169 | smile
170 | emojiCode
171 | [smile]
172 |
173 |
174 | emojiName
175 | 微笑
176 | emojiImage
177 | smile
178 | emojiCode
179 | [smile]
180 |
181 |
182 | emojiName
183 | 微笑
184 | emojiImage
185 | smile
186 | emojiCode
187 | [smile]
188 |
189 |
190 |
191 |
192 |
--------------------------------------------------------------------------------
/YTChatDemo/KeyBoard/Emoji/EmojiModel/Resource/EmojiSutra.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | image
6 | grinning
7 | name
8 | 经典
9 | Id
10 | local
11 | icons
12 |
13 |
14 | emojiName
15 | 哈哈
16 | emojiImage
17 | grinning
18 | emojiCode
19 | [grinning]
20 |
21 |
22 | emojiName
23 | 哈哈
24 | emojiImage
25 | grinning
26 | emojiCode
27 | [grinning]
28 |
29 |
30 | emojiName
31 | 哈哈
32 | emojiImage
33 | grinning
34 | emojiCode
35 | [grinning]
36 |
37 |
38 | emojiName
39 | 哈哈
40 | emojiImage
41 | grinning
42 | emojiCode
43 | [grinning]
44 |
45 |
46 | emojiName
47 | 哈哈
48 | emojiImage
49 | grinning
50 | emojiCode
51 | [grinning]
52 |
53 |
54 | emojiName
55 | 哈哈
56 | emojiImage
57 | grinning
58 | emojiCode
59 | [grinning]
60 |
61 |
62 | emojiName
63 | 哈哈
64 | emojiImage
65 | grinning
66 | emojiCode
67 | [grinning]
68 |
69 |
70 | emojiName
71 | 哈哈
72 | emojiImage
73 | grinning
74 | emojiCode
75 | [grinning]
76 |
77 |
78 | emojiName
79 | 哈哈
80 | emojiImage
81 | grinning
82 | emojiCode
83 | [grinning]
84 |
85 |
86 | emojiName
87 | 哈哈
88 | emojiImage
89 | grinning
90 | emojiCode
91 | [grinning]
92 |
93 |
94 | emojiName
95 | 哈哈
96 | emojiImage
97 | grinning
98 | emojiCode
99 | [grinning]
100 |
101 |
102 | emojiName
103 | 哈哈
104 | emojiImage
105 | grinning
106 | emojiCode
107 | [grinning]
108 |
109 |
110 | emojiName
111 | 哈哈
112 | emojiImage
113 | grinning
114 | emojiCode
115 | [grinning]
116 |
117 |
118 | emojiName
119 | 哈哈
120 | emojiImage
121 | grinning
122 | emojiCode
123 | [grinning]
124 |
125 |
126 | emojiName
127 | 哈哈
128 | emojiImage
129 | grinning
130 | emojiCode
131 | [grinning]
132 |
133 |
134 | emojiName
135 | 哈哈
136 | emojiImage
137 | grinning
138 | emojiCode
139 | [grinning]
140 |
141 |
142 | emojiName
143 | 哈哈
144 | emojiImage
145 | grinning
146 | emojiCode
147 | [grinning]
148 |
149 |
150 | emojiName
151 | 哈哈
152 | emojiImage
153 | grinning
154 | emojiCode
155 | [grinning]
156 |
157 |
158 | emojiName
159 | 哈哈
160 | emojiImage
161 | grinning
162 | emojiCode
163 | [grinning]
164 |
165 |
166 | emojiName
167 | 哈哈
168 | emojiImage
169 | grinning
170 | emojiCode
171 | [grinning]
172 |
173 |
174 | emojiName
175 | 哈哈
176 | emojiImage
177 | grinning
178 | emojiCode
179 | [grinning]
180 |
181 |
182 | emojiName
183 | 哈哈
184 | emojiImage
185 | grinning
186 | emojiCode
187 | [grinning]
188 |
189 |
190 | emojiName
191 | 哈哈
192 | emojiImage
193 | grinning
194 | emojiCode
195 | [grinning]
196 |
197 |
198 |
199 |
200 |
--------------------------------------------------------------------------------
/YTChatDemo/KeyBoard/Emoji/EmojiModel/Resource/girl_beg@2x.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TiYcc/KeyBoard/dfcec9a85580fcff301d4fa832c1de1c74566320/YTChatDemo/KeyBoard/Emoji/EmojiModel/Resource/girl_beg@2x.gif
--------------------------------------------------------------------------------
/YTChatDemo/KeyBoard/Emoji/EmojiModel/Resource/grinning@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TiYcc/KeyBoard/dfcec9a85580fcff301d4fa832c1de1c74566320/YTChatDemo/KeyBoard/Emoji/EmojiModel/Resource/grinning@2x.png
--------------------------------------------------------------------------------
/YTChatDemo/KeyBoard/Emoji/EmojiModel/Resource/smile@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TiYcc/KeyBoard/dfcec9a85580fcff301d4fa832c1de1c74566320/YTChatDemo/KeyBoard/Emoji/EmojiModel/Resource/smile@2x.png
--------------------------------------------------------------------------------
/YTChatDemo/KeyBoard/Emoji/EmojiModel/YTEmoji.h:
--------------------------------------------------------------------------------
1 | //
2 | // YTEmoji.h
3 | // YTChatDemo
4 | //
5 | // Created by TI on 15/8/25.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface YTEmoji : NSObject
12 |
13 | @property (nonatomic, strong) NSString *emojiName; //名字(中文)
14 | @property (nonatomic, strong) NSString *emojiImage; //图片名字
15 | @property (nonatomic, strong) NSString *emojiCode; //表情码(要和“某卓”沟通)
16 | @end
17 |
--------------------------------------------------------------------------------
/YTChatDemo/KeyBoard/Emoji/EmojiModel/YTEmoji.m:
--------------------------------------------------------------------------------
1 | //
2 | // YTEmoji.m
3 | // YTChatDemo
4 | //
5 | // Created by TI on 15/8/25.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import "YTEmoji.h"
10 |
11 | @implementation YTEmoji
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/YTChatDemo/KeyBoard/Emoji/EmojiModel/YTEmojiChartletM.h:
--------------------------------------------------------------------------------
1 | //
2 | // YTEmojiChartletM.h
3 | // YTChatDemo
4 | //
5 | // Created by TI on 15/8/25.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import "YTEmojiM.h"
10 |
11 | @interface YTEmojiChartletM : YTEmojiM
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/YTChatDemo/KeyBoard/Emoji/EmojiModel/YTEmojiChartletM.m:
--------------------------------------------------------------------------------
1 | //
2 | // YTEmojiChartletM.m
3 | // YTChatDemo
4 | //
5 | // Created by TI on 15/8/25.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import "YTEmojiChartletM.h"
10 |
11 | @implementation YTEmojiChartletM
12 |
13 | -(void)setNorms:(YTEmojiNorms)norms{
14 | norms.lines = 2;
15 | norms.boardWH = 54.5f;
16 | norms.spaceBoard = 12.0f;
17 | norms.spaceHorizontalMIN = 20.0f;
18 | norms.spaceVerticalityMIN = 15.0f;
19 | [super setNorms:norms];
20 | }
21 |
22 | @end
23 |
--------------------------------------------------------------------------------
/YTChatDemo/KeyBoard/Emoji/EmojiModel/YTEmojiFile.h:
--------------------------------------------------------------------------------
1 | //
2 | // YTEmojiFile.h
3 | // YTChatDemo
4 | //
5 | // Created by TI on 15/8/25.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "YTEmojiChartletM.h"
11 | #import "YTEmojiIconM.h"
12 |
13 | @interface YTEmojiFile : NSObject
14 |
15 | /* 包含YTEmojiIconM子类对象 */
16 | + (NSArray *)emojiModelS;
17 | @end
18 |
--------------------------------------------------------------------------------
/YTChatDemo/KeyBoard/Emoji/EmojiModel/YTEmojiFile.m:
--------------------------------------------------------------------------------
1 | //
2 | // YTEmojiFile.m
3 | // YTChatDemo
4 | //
5 | // Created by TI on 15/8/25.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import "YTEmojiFile.h"
10 | #import "NSDictionary+YTSafe.h"
11 |
12 | @interface YTEmojiFileM : NSObject
13 |
14 | @property (nonatomic, strong) NSString * path; //表情路径
15 | @property (nonatomic, strong) NSString * Id; //标示(来自网络还是本地)
16 | @property (nonatomic, assign) YTEmojiType type; //表情类型
17 | @property (nonatomic, assign, getter=isDisable) BOOL disable; //判断可用
18 | @end
19 |
20 | @implementation YTEmojiFileM
21 | @end
22 |
23 | @implementation YTEmojiFile
24 |
25 | + (NSArray *)emojiModelS{
26 | return [self emojiDataSource];
27 | }
28 |
29 | + (NSArray *)emojiDataSource{
30 | NSMutableArray *emojiMS = [NSMutableArray array];
31 | NSArray *emojiFS = [[self class] emojiFiles];
32 | for (YTEmojiFileM *ef in emojiFS) {
33 | NSString *emojiPath = [[NSBundle mainBundle]pathForResource:ef.path ofType:@"plist"];
34 | NSDictionary *emojiDic = [NSDictionary dictionaryWithContentsOfFile:emojiPath];
35 | YTEmojiM *em = nil;
36 | switch (ef.type) {
37 | case YTEmojiTypeIcon:
38 | em = [[YTEmojiIconM alloc]initWithDic:emojiDic];
39 | break;
40 | case YTEmojiTypeChartlet:
41 | em = [[YTEmojiChartletM alloc]initWithDic:emojiDic];
42 | break;
43 | default:
44 | break;
45 | }
46 | if (em) {
47 | em.type = ef.type;
48 | [emojiMS addObject:em];
49 | }
50 | }
51 |
52 | return [emojiMS copy];
53 | }
54 |
55 | + (NSArray *)emojiFiles{
56 | NSMutableArray *emojiFS = [NSMutableArray array];
57 | NSString *filePath = [[NSBundle mainBundle]pathForResource:@"EmojiFile" ofType:@"plist"];
58 |
59 |
60 | NSArray *files = [NSArray arrayWithContentsOfFile:filePath];
61 | for (NSDictionary *dic in files) {
62 | YTEmojiFileM *ef = [[YTEmojiFileM alloc]init];
63 | ef.path = [dic safeStringValueForKey:@"path"];
64 | ef.Id = [dic safeStringValueForKey:@"Id"];
65 | ef.disable = [dic safeBoolValueForKey:@"disable"];
66 | ef.type = (YTEmojiType)[dic safeIntegerValueForKey:@"type"];
67 | if ((!ef.isDisable) && (ef.path.length > 0)) {
68 | [emojiFS addObject:ef];
69 | }
70 | }
71 |
72 | return emojiFS;
73 | }
74 |
75 | @end
76 |
--------------------------------------------------------------------------------
/YTChatDemo/KeyBoard/Emoji/EmojiModel/YTEmojiIconM.h:
--------------------------------------------------------------------------------
1 | //
2 | // YTEmojiIconM.h
3 | // YTChatDemo
4 | //
5 | // Created by TI on 15/8/25.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import "YTEmojiM.h"
10 |
11 | @interface YTEmojiIconM : YTEmojiM
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/YTChatDemo/KeyBoard/Emoji/EmojiModel/YTEmojiIconM.m:
--------------------------------------------------------------------------------
1 | //
2 | // YTEmojiIconM.m
3 | // YTChatDemo
4 | //
5 | // Created by TI on 15/8/25.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import "YTEmojiIconM.h"
10 |
11 | @implementation YTEmojiIconM
12 |
13 | -(void)setNorms:(YTEmojiNorms)norms{
14 | norms.lines = 3;
15 | norms.boardWH = 32.0f;
16 | norms.spaceBoard = 12.0f;
17 | norms.spaceHorizontalMIN = 20.0f;
18 | norms.spaceVerticalityMIN = 15.0f;
19 | [super setNorms:norms];
20 | }
21 |
22 | -(NSInteger)countOnePage{
23 | return ([super countOnePage] - 1);//最后一个是删除键
24 | }
25 |
26 | @end
27 |
--------------------------------------------------------------------------------
/YTChatDemo/KeyBoard/Emoji/EmojiModel/YTEmojiM.h:
--------------------------------------------------------------------------------
1 | //
2 | // YTEmojiM.h
3 | // YTChatDemo
4 | //
5 | // Created by TI on 15/8/25.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "YTEmoji.h"
11 | @import UIKit;
12 |
13 | #define WIDTH ([UIScreen mainScreen].bounds.size.width)
14 |
15 | #if !defined(YT_INLINE)
16 | # if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
17 | # define YT_INLINE static inline
18 | # elif defined(__cplusplus)
19 | # define YT_INLINE static inline
20 | # elif defined(__GNUC__)
21 | # define YT_INLINE static __inline__
22 | # else
23 | # define YT_INLINE static
24 | # endif
25 | #endif
26 |
27 | typedef NS_ENUM(NSInteger, YTEmojiType){
28 | YTEmojiTypeIcon = 0, //一般表情图片
29 | YTEmojiTypeChartlet //贴图
30 | };
31 |
32 | struct YTEmojiNorms{
33 | NSUInteger lines; //行数
34 | CGFloat boardWH; //宽高
35 | CGFloat spaceBoard; //距离边框距离(水平=垂直)
36 | CGFloat spaceHorizontalMIN; //水平间距(min)
37 | CGFloat spaceVerticalityMIN; //垂直间距(min)
38 | };
39 |
40 | typedef struct YTEmojiNorms YTEmojiNorms;
41 |
42 | YT_INLINE YTEmojiNorms YTEmojiNormsMake(NSUInteger lines, CGFloat boardWH, CGFloat spaceBoard, CGFloat spaceHorizontalMIN, CGFloat spaceVerticalityMIN);
43 |
44 | @interface YTEmojiM : NSObject
45 |
46 | @property (nonatomic, strong) NSString *name; //这一类表情总名称
47 | @property (nonatomic, strong) NSString *image; //这一类表情代表图标
48 | @property (nonatomic, assign) YTEmojiType type; //类型
49 |
50 | @property (nonatomic, strong, readonly) NSString *Id; //标示(来自网络http还是本地local)
51 | @property (nonatomic, strong, readonly) NSArray *icons; //表情集合
52 |
53 | @property (nonatomic, assign) YTEmojiNorms norms; //一般交于子类赋值
54 |
55 | /* 通过字典初始化 */
56 | - (instancetype)initWithDic:(NSDictionary *)dic;
57 |
58 | /* 一行可显示多少个表情 */
59 | - (NSInteger)countOneLine;
60 |
61 | /* 一页多少个表情 */
62 | - (NSInteger)countOnePage;
63 |
64 | /* 一共多少页 */
65 | - (NSInteger)countPageAll;
66 |
67 | /* 第index页包含多少个表情 */
68 | - (NSInteger)countPageIndex:(NSInteger)index;
69 | @end
70 |
--------------------------------------------------------------------------------
/YTChatDemo/KeyBoard/Emoji/EmojiModel/YTEmojiM.m:
--------------------------------------------------------------------------------
1 | //
2 | // YTEmojiM.m
3 | // YTChatDemo
4 | //
5 | // Created by TI on 15/8/25.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import "NSDictionary+YTSafe.h"
10 | #import "YTEmojiM.h"
11 |
12 | NSString *const local = @"local";
13 | NSString *const network = @"http";
14 |
15 | YTEmojiNorms YTEmojiNormsMake(NSUInteger lines, CGFloat boardWH, CGFloat spaceBoard, CGFloat spaceHorizontalMIN, CGFloat spaceVerticalityMIN){
16 | YTEmojiNorms norms;
17 | norms.lines = lines;
18 | norms.boardWH = boardWH;
19 | norms.spaceBoard = spaceBoard;
20 | norms.spaceHorizontalMIN = spaceHorizontalMIN;
21 | norms.spaceVerticalityMIN = spaceVerticalityMIN;
22 | return norms;
23 | }
24 |
25 | @interface YTEmojiM()
26 |
27 | @property (nonatomic, strong) NSString *Id; //标示(来自网络还是本地)
28 | @property (nonatomic, strong) NSArray *icons; //表情集合
29 | @end
30 |
31 | @implementation YTEmojiM
32 |
33 | #pragma mark - public api
34 | - (instancetype)initWithDic:(NSDictionary *)dic{
35 | if (self = [super init]) {
36 | self.image = [dic safeStringValueForKey:@"image"];
37 | self.name = [dic safeStringValueForKey:@"name"];
38 | self.Id = [dic safeStringValueForKey:@"Id"];
39 | self.icons = [dic safeArrayForKey:@"icons"];
40 | self.norms = YTEmojiNormsMake(0, 0, 0, 0, 0);
41 | }
42 | return self;
43 | }
44 |
45 | - (NSInteger)countOneLine{
46 | YTEmojiNorms norms = self.norms;
47 | return floorf((WIDTH-norms.spaceBoard*2+norms.spaceHorizontalMIN)/(norms.boardWH + norms.spaceHorizontalMIN));
48 | }
49 |
50 | - (NSInteger)countOnePage{
51 | NSInteger countLine = [self countOneLine];
52 | return (countLine*self.norms.lines);
53 | }
54 |
55 | - (NSInteger)countPageAll{
56 | NSInteger countPage = [self countOnePage];
57 | return ceilf(self.icons.count/((float)countPage));
58 | }
59 |
60 | - (NSInteger)countPageIndex:(NSInteger)index{
61 | NSInteger countPage = [self countOnePage];
62 | NSInteger count = countPage*(index+1);
63 | if (count <= self.icons.count) {
64 | return countPage;
65 | }else{
66 | return (self.icons.count-(countPage*MAX(index, 0)));
67 | }
68 | }
69 |
70 | #pragma mark - self action
71 | - (void)setIcons:(NSArray *)icons{
72 | if (!icons) {
73 | _icons = @[];
74 | }else{
75 | NSMutableArray *iconArray = [NSMutableArray arrayWithCapacity:icons.count];
76 | for (NSDictionary * dic in icons) {
77 | YTEmoji *ej = [YTEmoji new];
78 | ej.emojiName = [dic safeStringValueForKey:@"emojiName"];
79 | ej.emojiImage = [dic safeStringValueForKey:@"emojiImage"];
80 | ej.emojiCode = [dic safeStringValueForKey:@"emojiCode"];
81 | [iconArray addObject:ej];
82 | }
83 | _icons = [iconArray copy];
84 | }
85 | }
86 |
87 | @end
88 |
--------------------------------------------------------------------------------
/YTChatDemo/KeyBoard/Emoji/EmojiView/YTEmojiBottom.h:
--------------------------------------------------------------------------------
1 | //
2 | // YTEmojiBottom.h
3 | // YTChatDemo
4 | //
5 | // Created by TI on 15/8/26.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 | // 如果你不喜欢这个效果可以自己写个bottom或者对它加以修改,来吻合你的想法。
9 |
10 | #import
11 |
12 | #define EMOJI_BOTTOM_HEIGHT 35.0f
13 | #define EMOJI_BOTTOM_BUTTON_WIDTH 40.0f
14 |
15 | @interface YTEmojiBottom : UIView
16 |
17 | @property (nonatomic, strong) UIButton *rightButton; //发送
18 | @property (nonatomic, strong) UIButton *liftButton; //添加
19 | @property (nonatomic, strong) UIScrollView *scrollView; //表情栏
20 |
21 | //发送按钮-隐藏与显示
22 | - (void)rightButtonAnimationHiden:(BOOL)hiden;
23 |
24 | @end
25 |
--------------------------------------------------------------------------------
/YTChatDemo/KeyBoard/Emoji/EmojiView/YTEmojiBottom.m:
--------------------------------------------------------------------------------
1 | //
2 | // YTEmojiBottom.m
3 | // YTChatDemo
4 | //
5 | // Created by TI on 15/8/26.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import "UIView+YTLayer.h"
10 | #import "YTEmojiBottom.h"
11 |
12 | @interface YTLine : UIView
13 |
14 | @end
15 |
16 | @implementation YTLine
17 |
18 | - (void)addToSuperView:(UIView *)superView{
19 | if (superView) {
20 | CGSize size = superView.bounds.size;
21 | CGRect frame = CGRectMake(size.width-0.5f, 5.0f, 0.5f, size.height-10.0f);
22 | self.frame = frame;
23 | self.backgroundColor = [UIColor grayColor];
24 | [superView addSubview:self];
25 | }
26 | }
27 |
28 | @end
29 |
30 | @interface YTScrollView : UIScrollView
31 |
32 | @end
33 |
34 | @implementation YTScrollView
35 |
36 | - (instancetype)initWithFrame:(CGRect)frame{
37 | if (self = [super initWithFrame:frame]) {
38 | self.showsHorizontalScrollIndicator = NO;
39 | self.showsVerticalScrollIndicator = NO;
40 | self.alwaysBounceVertical = NO;
41 | }
42 | return self;
43 | }
44 |
45 | - (void)addSubview:(UIView *)view{
46 | if (view) {
47 | YTLine *line = [[YTLine alloc]init];
48 | [line addToSuperView:view];
49 | [super addSubview:view];
50 | }
51 | }
52 |
53 | - (void)setContentSize:(CGSize)contentSize{
54 | contentSize.width = MAX(contentSize.width+(EMOJI_BOTTOM_BUTTON_WIDTH*1.5f), self.bounds.size.width+1);
55 | [super setContentSize:contentSize];
56 | }
57 |
58 | @end
59 |
60 | @implementation YTEmojiBottom
61 |
62 | - (instancetype)initWithFrame:(CGRect)frame{
63 | if (self = [super initWithFrame:frame]) {
64 | [self borderWithColor:[UIColor lightGrayColor] borderWidth:0.5];
65 | [self initUI];
66 | }
67 | return self;
68 | }
69 |
70 | - (void)initUI{
71 | [self addScrollView];
72 | [self addBts];
73 | }
74 |
75 | - (void)addBts{
76 | UIColor *viewBGColor = [UIColor whiteColor];
77 | UIColor *btTitleColor = [UIColor grayColor];
78 | //lift button
79 | CGRect frame = CGRectMake(0, 0, EMOJI_BOTTOM_BUTTON_WIDTH, self.frame.size.height);
80 | UIView *liftView = [[UIView alloc]initWithFrame:frame];
81 | liftView.backgroundColor = viewBGColor;
82 | UIButton *lift = [[UIButton alloc]initWithFrame:liftView.bounds];
83 | [lift setTitle:@"+" forState:UIControlStateNormal];
84 | [lift setTitleColor:btTitleColor forState:UIControlStateNormal];
85 | YTLine *line = [[YTLine alloc]init];
86 | [line addToSuperView:lift];
87 | [liftView addSubview:lift];
88 | self.liftButton = lift;
89 | [self addSubview:liftView];
90 | //right button
91 | frame.origin.x = self.bounds.size.width - EMOJI_BOTTOM_BUTTON_WIDTH*1.5;
92 | frame.size.width *= 1.5;
93 | UIView *rightView = [[UIView alloc]initWithFrame:frame];
94 | rightView.backgroundColor = viewBGColor;
95 | frame = rightView.bounds;
96 | frame.size.width*= 0.75f;
97 | frame.size.height*= 0.75f;
98 | UIButton *right = [[UIButton alloc]initWithFrame:frame];
99 | CGSize size = rightView.bounds.size;
100 | right.center = CGPointMake(size.width*0.5f, size.height*0.5f);
101 | [right cornerRadius:5.0f borderColor:btTitleColor borderWidth:1.0f];
102 | right.titleLabel.font = [UIFont systemFontOfSize:13];
103 | [right setTitle:@"发送" forState:UIControlStateNormal];
104 | [right setTitleColor:btTitleColor forState:UIControlStateNormal];
105 | [rightView addSubview:right];
106 | self.rightButton = right;
107 | [self addSubview:rightView];
108 | }
109 |
110 | - (void)addScrollView{
111 | CGRect frame = self.bounds;
112 | frame.origin.x = EMOJI_BOTTOM_BUTTON_WIDTH;
113 | frame.size.width -= EMOJI_BOTTOM_BUTTON_WIDTH;
114 | YTScrollView *scroll = [[YTScrollView alloc]initWithFrame:frame];
115 | [self addSubview:scroll];
116 | self.scrollView = scroll;
117 | }
118 |
119 | - (void)rightButtonAnimationHiden:(BOOL)hiden{
120 | CGAffineTransform Transform;
121 | CGRect frame = self.rightButton.superview.frame;
122 | if (hiden) {
123 | if (CGRectGetMinX(frame) >= self.bounds.size.width ) {
124 | return;
125 | }
126 | Transform = CGAffineTransformMakeTranslation(CGRectGetWidth(frame), 0);
127 | }else{
128 | if (CGRectGetMinX(frame) < self.bounds.size.width ){
129 | return;
130 | }
131 | Transform = CGAffineTransformIdentity;
132 | }
133 | [UIView animateWithDuration:0.5f animations:^{
134 | self.rightButton.superview.transform = Transform;
135 | }];
136 | }
137 |
138 | @end
139 |
--------------------------------------------------------------------------------
/YTChatDemo/KeyBoard/Emoji/EmojiView/YTEmojiButton.h:
--------------------------------------------------------------------------------
1 | //
2 | // YTEmojiButton.h
3 | // YTChatDemo
4 | //
5 | // Created by TI on 15/8/26.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import
10 | @class YTEmoji;
11 |
12 | @interface YTEmojiButton : UIButton
13 | @property (nonatomic, strong) YTEmoji *emoji;
14 | @end
15 |
--------------------------------------------------------------------------------
/YTChatDemo/KeyBoard/Emoji/EmojiView/YTEmojiButton.m:
--------------------------------------------------------------------------------
1 | //
2 | // YTEmojiButton.m
3 | // YTChatDemo
4 | //
5 | // Created by TI on 15/8/26.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import "YTEmojiButton.h"
10 | #import "YTEmoji.h"
11 |
12 | @implementation YTEmojiButton
13 |
14 | - (void)setEmoji:(YTEmoji *)emoji{
15 | _emoji = emoji;
16 | [self setImage:[UIImage imageNamed:emoji.emojiImage] forState:UIControlStateNormal];
17 | }
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/YTChatDemo/KeyBoard/Emoji/EmojiView/YTEmojiPage.h:
--------------------------------------------------------------------------------
1 | //
2 | // YTEmojiPage.h
3 | // YTChatDemo
4 | //
5 | // Created by TI on 15/8/26.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import
10 | @class YTEmojiM;
11 |
12 | #define EMOJI_IMG_WH 20.0f
13 |
14 | @interface YTEmojiPage : UIButton
15 | @property (nonatomic, strong) YTEmojiM *emojiM; //包含此类表情全部信息
16 | @property (nonatomic, assign) CGPoint offsetStart; //表情开始位置
17 | @property (nonatomic, assign) CGPoint offsetEnd; //表情结束位置
18 | @end
19 |
--------------------------------------------------------------------------------
/YTChatDemo/KeyBoard/Emoji/EmojiView/YTEmojiPage.m:
--------------------------------------------------------------------------------
1 | //
2 | // YTEmojiPage.m
3 | // YTChatDemo
4 | //
5 | // Created by TI on 15/8/26.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import "YTEmojiPage.h"
10 |
11 | @implementation YTEmojiPage
12 |
13 | - (CGRect)imageRectForContentRect:(CGRect)contentRect{
14 | CGRect rect = [super imageRectForContentRect:contentRect];
15 | rect.size= CGSizeMake(EMOJI_IMG_WH, EMOJI_IMG_WH);
16 | CGFloat W = self.bounds.size.width;
17 | CGFloat H = self.bounds.size.height;
18 | rect.origin = CGPointMake((W-EMOJI_IMG_WH)*0.5f, (H-EMOJI_IMG_WH)*0.5f);
19 | return rect;
20 | }
21 |
22 | @end
23 |
--------------------------------------------------------------------------------
/YTChatDemo/KeyBoard/Emoji/EmojiView/YTEmojiView.h:
--------------------------------------------------------------------------------
1 | //
2 | // YTEmojiView.h
3 | // YTChatDemo
4 | //
5 | // Created by TI on 15/8/26.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import
10 | @class YTEmoji;
11 |
12 | #define EMOJI_VIEW_HEIGHT 205.0f
13 | #define EMOJI_SCROLL_HEIGHT 170.0f
14 |
15 | @protocol YTEmojiViewDelegate
16 | @optional
17 | /* 点击某个表情,将会显示在输入框内 */
18 | - (void)emojiViewEmoji:(YTEmoji *)emoji;
19 |
20 | /* 点击“删除”按钮 */
21 | - (void)emojiViewDelete;
22 |
23 | /* 点击“发送”按钮 */
24 | - (void)emojiViewSend;
25 | @end
26 |
27 | @interface YTEmojiView : UIView
28 |
29 | - (instancetype)initWithDelegate:(id)delegate;
30 |
31 | @end
32 |
--------------------------------------------------------------------------------
/YTChatDemo/KeyBoard/Emoji/EmojiView/YTEmojiView.m:
--------------------------------------------------------------------------------
1 | //
2 | // YTEmojiView.m
3 | // YTChatDemo
4 | //
5 | // Created by TI on 15/8/26.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import "YTEmojiBottom.h"
10 | #import "YTEmojiButton.h"
11 | #import "YTEmojiView.h"
12 | #import "YTEmojiFile.h"
13 | #import "YTEmojiPage.h"
14 |
15 | #define EMOJI_PAGECONTROL_HEIGHT 24.0f
16 |
17 | @interface YTEmojiView(){
18 | YTEmojiPage *currentPageBT; //当前表情页
19 | CGFloat priopPointX; // scrollView 划过的前一个点x坐标
20 | BOOL fromeScroll; // 标记切换表情方法来源
21 | }
22 | @property (nonatomic, assign) id delegate; //代理
23 | @property (nonatomic, strong) UIScrollView *scrollView; //显示表情图片
24 | @property (nonatomic, strong) UIPageControl *pageControl; //图片翻页标示
25 | @property (nonatomic, strong) NSMutableArray *pageBts; //底部切换表情按钮
26 | @property (nonatomic, strong) YTEmojiBottom *bottomView; //底部空间
27 | @end
28 |
29 | @implementation YTEmojiView
30 |
31 | #pragma mark - 初始化
32 |
33 | - (NSMutableArray *)pageBts{
34 | if (!_pageBts) {
35 | _pageBts = [NSMutableArray array];
36 | }
37 | return _pageBts;
38 | }
39 |
40 | - (instancetype)initWithDelegate:(id)delegate{
41 | CGRect frame = CGRectMake(0, 0, WIDTH, EMOJI_VIEW_HEIGHT);
42 | if (self = [super initWithFrame:frame]) {
43 | self.delegate = delegate;
44 | [self initUI];
45 | }
46 | return self;
47 | }
48 |
49 | - (void)setFrame:(CGRect)frame{
50 | frame.size = CGSizeMake(WIDTH, EMOJI_VIEW_HEIGHT);
51 | [super setFrame:frame];
52 | }
53 |
54 | - (void)initUI{
55 | CGRect frame = self.bounds;
56 | frame.size.height = EMOJI_SCROLL_HEIGHT;
57 | [self addScroll:frame]; /*scroll*/
58 |
59 | frame.origin.y = CGRectGetHeight(frame)- EMOJI_PAGECONTROL_HEIGHT;
60 | frame.size.height = EMOJI_PAGECONTROL_HEIGHT;
61 | [self addPageControl:frame]; /*pagecontrol*/
62 |
63 | frame.origin.y = CGRectGetHeight(self.bounds) - EMOJI_BOTTOM_HEIGHT;
64 | frame.size.height = EMOJI_BOTTOM_HEIGHT;
65 | [self addBottom:frame];/*bottom*/
66 |
67 | [self showIcons];//展示表情图标
68 | }
69 |
70 | #pragma mark 添加控件
71 | -(void)addScroll:(CGRect)frame{
72 | UIScrollView *scroll = [[UIScrollView alloc]initWithFrame:frame];
73 | scroll.delegate = self;
74 | scroll.pagingEnabled = YES;
75 | scroll.showsHorizontalScrollIndicator = NO;
76 | [self addSubview:scroll];
77 | self.scrollView = scroll;
78 | }
79 |
80 | -(void)addPageControl:(CGRect)frame{
81 | UIPageControl *page = [[UIPageControl alloc]initWithFrame:frame];
82 | page.currentPageIndicatorTintColor = [UIColor lightGrayColor];
83 | page.pageIndicatorTintColor = [UIColor blackColor];
84 | page.userInteractionEnabled = NO;
85 | [self addSubview:page];
86 | self.pageControl = page;
87 | }
88 |
89 | -(void)addBottom:(CGRect)frame{
90 | YTEmojiBottom *bottom = [[YTEmojiBottom alloc]initWithFrame:frame];
91 | //page
92 | NSArray *emojiMS = [YTEmojiFile emojiModelS];
93 | for (int i = 0; i < emojiMS.count; i++) {
94 | CGRect rect = CGRectMake(EMOJI_BOTTOM_BUTTON_WIDTH*i, 0, EMOJI_BOTTOM_BUTTON_WIDTH, EMOJI_BOTTOM_HEIGHT);
95 | YTEmojiPage *pageBt = [[YTEmojiPage alloc]initWithFrame:rect];
96 | YTEmojiM *em = emojiMS[i];
97 | pageBt.emojiM = em;
98 | pageBt.tag = i;
99 | [pageBt setImage:[UIImage imageNamed:em.image] forState:UIControlStateNormal];
100 | [pageBt addTarget:self action:@selector(pageChange:) forControlEvents:UIControlEventTouchUpInside];
101 | [bottom.scrollView addSubview:pageBt];
102 | [self.pageBts addObject:pageBt];
103 | }
104 | //scroll
105 | bottom.scrollView.contentSize = CGSizeMake(emojiMS.count*EMOJI_BOTTOM_BUTTON_WIDTH, 0);
106 | //send
107 | [bottom.rightButton addTarget:self action:@selector(sendButtonAction) forControlEvents:UIControlEventTouchUpInside];
108 | //+"添加"
109 | //........//
110 | [self addSubview:bottom];
111 | self.bottomView = bottom;
112 | }
113 |
114 | #pragma mark 表情展示
115 | - (void)showIcons{
116 | if (self.pageBts.count == 0) return;
117 | for (int i = 0; i < self.pageBts.count; i++) {
118 | YTEmojiPage *pageBt = self.pageBts[i];
119 | if (i == 0) {
120 | currentPageBT = pageBt;
121 | pageBt.offsetStart = CGPointZero;
122 | pageBt.backgroundColor = [UIColor grayColor];
123 | self.pageControl.numberOfPages = [pageBt.emojiM countPageAll];
124 | }else{
125 | YTEmojiPage *p = self.pageBts[(int)MAX(i-1, 0)];
126 | pageBt.offsetStart = p.offsetEnd;
127 | }
128 | [self addIconsWithPage:pageBt];
129 | }
130 | CGFloat widthX = ((YTEmojiPage *)[self.pageBts lastObject]).offsetEnd.x;
131 | self.scrollView.contentSize = CGSizeMake( widthX, 0);
132 | }
133 |
134 | - (void)addIconsWithPage:(YTEmojiPage *)pageBt{
135 | YTEmojiM *em = pageBt.emojiM;
136 | YTEmojiNorms norms = em.norms;
137 | CGFloat WH = norms.boardWH;
138 | CGFloat spaceB = norms.spaceBoard;
139 | CGFloat spaceV = norms.spaceVerticalityMIN;
140 | CGFloat initX = pageBt.offsetStart.x;
141 | NSInteger countInLine = [em countOneLine];
142 | NSInteger countInPage = [em countOnePage];
143 | NSInteger countAllPage = [em countPageAll];
144 | CGFloat spaceH = (WIDTH-spaceB*2-WH*countInLine)/(float)(countInLine-1);
145 | pageBt.offsetEnd = CGPointMake(pageBt.offsetStart.x+countAllPage*WIDTH, 0);
146 | for (int page = 0; page < countAllPage; page++) {//多少页
147 | NSInteger countIndex = [em countPageIndex:page];
148 | for (int count = 0; count < countIndex; count++) {//一页多少个
149 | // X:行中第几个,Y:第几行
150 | NSInteger X = count%countInLine;
151 | NSInteger Y = count/countInLine;
152 | CGRect rect = CGRectZero;
153 | rect.size = CGSizeMake( WH, WH);
154 | rect.origin.x = spaceB+(spaceH+WH)*X+page*WIDTH+initX;
155 | rect.origin.y = spaceB+(spaceV+WH)*Y;
156 |
157 | YTEmojiButton *eb = [[YTEmojiButton alloc]initWithFrame:rect];
158 | eb.emoji = em.icons[(page*countInPage+count)];
159 | [eb addTarget:self action:@selector(selectorThisIcon:) forControlEvents:UIControlEventTouchUpInside];
160 | [self.scrollView addSubview:eb];
161 | UILongPressGestureRecognizer *longTap = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(longTapAction:)];
162 | [eb addGestureRecognizer:longTap];
163 | }
164 | if (em.type == YTEmojiTypeIcon) {
165 | CGRect frame = CGRectZero;
166 | frame.size = CGSizeMake( WH, WH);
167 | frame.origin.x = spaceB+(spaceH+WH)*(countInLine-1)+page*WIDTH+initX;
168 | frame.origin.y = spaceB+(spaceV+WH)*(em.norms.lines-1);
169 | YTEmojiButton *del = [[YTEmojiButton alloc]initWithFrame:frame];
170 | [del addTarget:self action:@selector(deleteIcons) forControlEvents:UIControlEventTouchUpInside];
171 | [del setImage:[UIImage imageNamed:@"btn_del"] forState:UIControlStateNormal];
172 | [self.scrollView addSubview:del];
173 | }
174 | }
175 | }
176 |
177 | #pragma mark - 表情栏切换
178 | - (void)pageChange:(YTEmojiPage *)pageBt{
179 | if (pageBt.tag == currentPageBT.tag) {
180 | fromeScroll = NO;
181 | return;
182 | }
183 | YTEmojiM *emojiM = pageBt.emojiM;
184 | switch (emojiM.type) {
185 | case YTEmojiTypeChartlet:
186 | [self.bottomView rightButtonAnimationHiden:YES];
187 | break;
188 | case YTEmojiTypeIcon:
189 | [self.bottomView rightButtonAnimationHiden:NO];
190 | break;
191 | default:
192 | break;
193 | }
194 | pageBt.backgroundColor = [UIColor grayColor];
195 | _pageControl.numberOfPages = [emojiM countPageAll];
196 | if (!fromeScroll) {
197 | [self.scrollView setContentOffset:pageBt.offsetStart];
198 | priopPointX = pageBt.offsetStart.x;
199 | _pageControl.currentPage = 0;
200 | }else{
201 | fromeScroll = NO;
202 | }
203 | currentPageBT.backgroundColor = [UIColor clearColor];
204 | currentPageBT = pageBt;
205 | }
206 |
207 | #pragma mark - Scroll View Delegate
208 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView{
209 | if ((scrollView != self.scrollView)||(!scrollView.isDragging)) return;
210 | [self scrollViewDidDragging:scrollView];
211 | }
212 |
213 | - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
214 | [self scrollViewDidDragging:scrollView];
215 | }
216 |
217 | - (void)scrollViewDidDragging:(UIScrollView*)scrollView{
218 | CGFloat offsetX = scrollView.contentOffset.x;
219 | NSInteger tag = currentPageBT.tag;
220 | YTEmojiPage *pageBt = nil;
221 | if (offsetX > priopPointX) { //右划
222 | tag = MIN(tag+1, self.pageBts.count-1);
223 | pageBt = self.pageBts[tag];
224 | if (fabs(pageBt.offsetStart.x-offsetX)<10) {
225 | fromeScroll = YES;
226 | [self pageChange:pageBt];
227 | }
228 | }else{ //左划
229 | tag = MAX(0, tag-1);
230 | pageBt = self.pageBts[tag];
231 | if (fabs(pageBt.offsetEnd.x-offsetX-WIDTH)<10) {
232 | fromeScroll = YES;
233 | [self pageChange:pageBt];
234 | }
235 | }
236 | self.pageControl.currentPage =((scrollView.contentOffset.x - currentPageBT.offsetStart.x)/scrollView.bounds.size.width)+0.5;
237 | priopPointX = offsetX;
238 | }
239 |
240 | #pragma mark - 长按表情动画
241 | - (void)longTapAction:(UILongPressGestureRecognizer*)longTap{
242 | YTEmojiButton *eb = (YTEmojiButton*)longTap.view;
243 | switch (longTap.state) {
244 | case UIGestureRecognizerStateBegan:
245 | [self showEmotionName:eb.emoji.emojiName];
246 | break;
247 | case UIGestureRecognizerStateEnded:
248 | [self selectorThisIcon:eb];
249 | break;
250 | default:
251 | break;
252 | }
253 |
254 | }
255 |
256 | - (void)showEmotionName:(NSString *)name {
257 | #define W 80.0f
258 | #define H 40.0f
259 | CGFloat x = (self.superview.frame.size.width-80.0f)/2.0f;
260 | UILabel *lab = [[UILabel alloc] initWithFrame:CGRectMake(x, self.superview.frame.origin.y, W, H)];
261 |
262 | lab.text = name;
263 | lab.alpha = 0.1f;
264 | lab.layer.masksToBounds = YES;
265 | lab.layer.cornerRadius = 10.0f;
266 | lab.textColor = [UIColor whiteColor];
267 | lab.backgroundColor = [UIColor grayColor];
268 | lab.textAlignment = NSTextAlignmentCenter;
269 | lab.font = [UIFont systemFontOfSize:15.0f];
270 |
271 | [self.superview.superview addSubview:lab];
272 | [UIView animateWithDuration:0.4f animations:^{
273 | lab.frame = CGRectMake(x, -H, W, H);
274 | lab.alpha = 1.0f;
275 | } completion:^(BOOL finished) {
276 | if (finished) {
277 | [UIView animateWithDuration:0.3f delay:0.5f options:UIViewAnimationOptionCurveEaseOut animations:^{
278 | lab.alpha = 0.0f;
279 | } completion:^(BOOL finished) {
280 | if (finished) [lab removeFromSuperview];
281 | }];
282 | }
283 | }];
284 | }
285 |
286 | #pragma mark - self delegate action
287 | -(void)selectorThisIcon:(YTEmojiButton*)sender{
288 | if ([self.delegate respondsToSelector:@selector(emojiViewEmoji:)]) {
289 | [self.delegate emojiViewEmoji:sender.emoji];
290 | }
291 | }
292 |
293 | -(void)deleteIcons{
294 | if ([self.delegate respondsToSelector:@selector(emojiViewDelete)]) {
295 | [self.delegate emojiViewDelete];
296 | }
297 | }
298 |
299 | -(void)sendButtonAction{
300 | if ([self.delegate respondsToSelector:@selector(emojiViewSend)]) {
301 | [self.delegate emojiViewSend];
302 | }
303 | }
304 |
305 | @end
306 |
--------------------------------------------------------------------------------
/YTChatDemo/KeyBoard/More/YTMoreView.h:
--------------------------------------------------------------------------------
1 | //
2 | // YTMoreView.h
3 | // YTChatDemo
4 | //
5 | // Created by TI on 15/9/1.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import
10 | typedef NS_ENUM(NSInteger, YTMoreViewTypeAction){
11 | YTMoreViewTypeActionNon = 0, //
12 | YTMoreViewTypeActionPhoto,
13 | YTMoreViewTypeActionCamera
14 | };
15 | @protocol YTMoreViewDelegate
16 |
17 | /**moewView包含控件事件,有可能是后期扩展*/
18 | - (void)moreViewType:(YTMoreViewTypeAction)type;
19 | @end
20 |
21 | @interface YTMoreView : UIView
22 |
23 | @property (nonatomic, strong) id delegate; //代理
24 |
25 | @end
26 |
--------------------------------------------------------------------------------
/YTChatDemo/KeyBoard/More/YTMoreView.m:
--------------------------------------------------------------------------------
1 | //
2 | // YTMoreView.m
3 | // YTChatDemo
4 | //
5 | // Created by TI on 15/9/1.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import "YTDeviceTest.h"
10 | #import "YTMoreView.h"
11 |
12 | @interface YTIcons : UIButton
13 |
14 | @end
15 |
16 | @implementation YTIcons
17 | #define BT_IMG_WH 50.0f //自身宽和内部image宽高
18 | #define BT_H 70.0f //自身高
19 |
20 | - (instancetype)initWithFrame:(CGRect)frame{
21 | if (self = [super initWithFrame:frame]) {
22 | self.titleLabel.font = [UIFont systemFontOfSize:14];
23 | self.titleLabel.textAlignment = NSTextAlignmentCenter;
24 | [self setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
25 | }
26 | return self;
27 | }
28 |
29 | - (CGRect)imageRectForContentRect:(CGRect)contentRect{
30 | contentRect = CGRectMake(0, 0, BT_IMG_WH, BT_IMG_WH);
31 | return contentRect;
32 | }
33 |
34 | - (CGRect)titleRectForContentRect:(CGRect)contentRect{
35 | contentRect = CGRectMake(0, BT_IMG_WH, BT_IMG_WH, BT_H-BT_IMG_WH);
36 | return contentRect;
37 | }
38 |
39 | @end
40 |
41 | @interface YTMoreView(){
42 | NSArray *images;
43 | NSArray *hightImages;
44 | NSArray *titles;
45 | NSArray *types;
46 | }
47 | @property (nonatomic, strong) UIScrollView * scrollView;
48 | @end
49 |
50 | @implementation YTMoreView
51 |
52 | - (instancetype)initWithFrame:(CGRect)frame{
53 | if (self = [super initWithFrame:frame]) {
54 | self.backgroundColor = [UIColor whiteColor];
55 | [self initUI];
56 | }
57 | return self;
58 | }
59 |
60 | - (void)initUI{
61 | [self addResource];
62 | [self addScrollView];
63 | [self addIcons];
64 | }
65 |
66 | - (void)addResource{
67 | images = @[@"photo_nomal",@"camera_nomal"];
68 | hightImages = @[@"photo_down",@"camera_down"];
69 | titles = @[@"图片",@"照相"];
70 | types = @[@(YTMoreViewTypeActionPhoto),@(YTMoreViewTypeActionCamera)];
71 | }
72 |
73 | - (void)addScrollView{
74 | UIScrollView * scroll = [[UIScrollView alloc]initWithFrame:self.bounds];
75 | scroll.bounces = NO;
76 | scroll.pagingEnabled = YES;
77 | scroll.showsVerticalScrollIndicator = NO;
78 | scroll.showsHorizontalScrollIndicator = NO;
79 | [self addSubview:scroll];
80 | self.scrollView = scroll;
81 | }
82 |
83 | - (void)addIcons{
84 | CGSize size = self.bounds.size;
85 | CGFloat spaceV = (size.width-BT_IMG_WH*4.0f)/5.0f;
86 | CGFloat spaceH = (size.height-BT_H*2.0f)/3.0f;
87 |
88 | CGRect rect = CGRectMake(0, 0, BT_IMG_WH, BT_H);
89 | for (int i =0; i < images.count; i++) {//暂时不考虑第二页
90 | NSInteger V = i%4;
91 | NSInteger H = i/4;
92 | rect.origin.x = spaceV+V*(spaceV+BT_IMG_WH);
93 | rect.origin.y = spaceH+H*(spaceH+BT_H);
94 | YTIcons * icon = [[YTIcons alloc]initWithFrame:rect];
95 | icon.tag = ((NSNumber *)types[i]).integerValue;
96 | [icon setImage:[UIImage imageNamed:images[i]] forState:UIControlStateNormal];
97 | [icon setImage:[UIImage imageNamed:hightImages[i]] forState:UIControlStateHighlighted];
98 | [icon setTitle:titles[i] forState:UIControlStateNormal];
99 | [icon addTarget:self action:@selector(iconsAction:) forControlEvents:UIControlEventTouchUpInside];
100 | [self.scrollView addSubview:icon];
101 |
102 | }
103 | }
104 |
105 | - (void)iconsAction:(UIButton *)icon{
106 | switch ((YTMoreViewTypeAction)icon.tag) {
107 | case YTMoreViewTypeActionPhoto:
108 | if ([YTDeviceTest userAuthorizationPhotoStatus]) {
109 | [self sourceType:YTMoreViewTypeActionPhoto];
110 | }
111 | break;
112 | case YTMoreViewTypeActionCamera:
113 | if ([YTDeviceTest userAuthorizationCameraStatus]) {
114 | [self sourceType:YTMoreViewTypeActionCamera];
115 | }
116 | break;
117 | default:
118 | break;
119 | }
120 | }
121 |
122 | #pragma mark - delegate
123 | - (void)sourceType:(YTMoreViewTypeAction)type{
124 | if ([self.delegate respondsToSelector:@selector(moreViewType:)]) {
125 | [self.delegate moreViewType:type];
126 | }
127 | }
128 |
129 | @end
130 |
--------------------------------------------------------------------------------
/YTChatDemo/KeyBoard/Other/SLGrowingTextView.h:
--------------------------------------------------------------------------------
1 | //
2 | // SLGrowingTextView.h
3 | //
4 | // Created by Kent on 29-06-2014
5 | //
6 |
7 | @import UIKit;
8 |
9 |
10 | extern const NSInteger SLMinNumOfLines;
11 | extern const NSInteger SLMaxNumOfLines;
12 | extern const CGFloat SLFrameChangeDuration;
13 |
14 |
15 | @class SLGrowingTextView;
16 |
17 |
18 | @protocol SLGrowingTextViewDelegate
19 |
20 | @optional
21 | - (BOOL)growingTextViewShouldBeginEditing:(SLGrowingTextView *)growingTextView;
22 | - (BOOL)growingTextViewShouldEndEditing:(SLGrowingTextView *)growingTextView;
23 |
24 | - (void)growingTextViewDidBeginEditing:(SLGrowingTextView *)growingTextView;
25 | - (void)growingTextViewDidEndEditing:(SLGrowingTextView *)growingTextView;
26 |
27 | - (BOOL)growingTextView:(SLGrowingTextView *)growingTextView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text;
28 | - (void)growingTextViewDidChange:(SLGrowingTextView *)growingTextView;
29 |
30 | - (void)growingTextView:(SLGrowingTextView *)growingTextView shouldChangeHeight:(CGFloat)height;
31 |
32 | - (void)growingTextViewDidChangeSelection:(SLGrowingTextView *)growingTextView;
33 | - (BOOL)growingTextViewShouldReturn:(SLGrowingTextView *)growingTextView;
34 | @end
35 |
36 |
37 | /**
38 | * \class
39 | * \brief Frame自适应的textView。当用户输入时,textView会根据font和其他一些因素计算最佳的高度,
40 | * 通过shouldChangeHeight通知代理对象,代理对象可以修改textView的frame,由于不知道
41 | * 上下文,textView并不会自己修改自己的frame。代理对象可以用SLFrameChangeDuration用动画
42 | * 的形式修改frame。
43 | * 第一次创建textView的时候,用sizeThatFits计算最佳高度,并设置textView的frame。
44 | * 此外,textView用户可以设置最小和最大行数,行数会影响textView的最大和最小高度。
45 | */
46 | #import "YTTextView.h"
47 | @interface SLGrowingTextView : UIView
48 |
49 | @property (nonatomic, weak) id delegate;
50 |
51 | /**
52 | * default nil, it will be created when accessed
53 | */
54 | @property (nonatomic, strong) UIView *backgroundView;
55 | @property (nonatomic, strong, readonly) YTTextView *internalTextView;
56 |
57 | // 设置最大和最小行数
58 | @property (nonatomic, assign) int maxNumberOfLines; // default 1
59 | @property (nonatomic, assign) int minNumberOfLines; // default 5
60 |
61 | @property (nonatomic, copy) NSString *placeholder; // default nil
62 |
63 | /**
64 | * 内部textView的insets
65 | */
66 | @property (nonatomic, assign) UIEdgeInsets contentInset;
67 |
68 | // UITextView属性
69 | @property (nonatomic, strong) NSString *text;
70 | @property (nonatomic, strong) UIFont *font;
71 | @property (nonatomic, strong) UIColor *textColor;
72 | @property (nonatomic, assign) NSTextAlignment textAlignment; // default is UITextAlignmentLeft
73 | @property (nonatomic, assign) NSRange selectedRange; // only ranges of length 0 are supported
74 | @property (nonatomic, getter=isEditable) BOOL editable;
75 | @property (nonatomic, assign) UIDataDetectorTypes dataDetectorTypes;
76 | @property (nonatomic, assign) UIReturnKeyType returnKeyType;
77 | @property (nonatomic, assign) UIKeyboardAppearance keyboardAppearance;
78 | @property (nonatomic, assign) BOOL enablesReturnKeyAutomatically;
79 |
80 | - (BOOL)canBecomeFirstResponder;
81 | - (BOOL)becomeFirstResponder;
82 | - (BOOL)canResignFirstResponder;
83 | - (BOOL)resignFirstResponder;
84 | - (BOOL)isFirstResponder;
85 |
86 | - (BOOL)hasText;
87 | - (void)scrollRangeToVisible:(NSRange)range;
88 |
89 | @end
90 |
--------------------------------------------------------------------------------
/YTChatDemo/KeyBoard/Other/SLGrowingTextView.m:
--------------------------------------------------------------------------------
1 | //
2 | // SLGrowingTextView.m
3 | //
4 | // Created by Kent on 29-06-2014
5 | //
6 |
7 | #import "SLGrowingTextView.h"
8 |
9 |
10 | const NSInteger SLMinNumOfLines = 1;
11 | const NSInteger SLMaxNumOfLines = 5;
12 | const CGFloat SLFrameChangeDuration = 0.1;
13 |
14 |
15 | @interface SLGrowingTextView ()
16 |
17 | @property (nonatomic, assign) CGFloat internalTextViewSizeDelta;
18 |
19 | // 最大和最小高度,根据行数、contentInset计算而来
20 | @property (nonatomic, assign) int minHeight;
21 | @property (nonatomic, assign) int maxHeight;
22 |
23 | @property (nonatomic, strong) UILabel *placeholderLabel;
24 |
25 | @end
26 |
27 | @implementation SLGrowingTextView
28 |
29 | @synthesize backgroundView = _backgroundView;
30 |
31 | @synthesize minNumberOfLines = _minNumberOfLines;
32 | @synthesize maxNumberOfLines = _maxNumberOfLines;
33 |
34 | @synthesize font = _font;
35 | @synthesize textColor = _textColor;
36 | @synthesize textAlignment = _textAlignment;
37 | @synthesize selectedRange = _selectedRange;
38 | @synthesize editable = _editable;
39 | @synthesize dataDetectorTypes = _dataDetectorTypes;
40 | @synthesize returnKeyType = _returnKeyType;
41 | @synthesize contentInset = _contentInset;
42 |
43 |
44 | - (id)initWithCoder:(NSCoder *)aDecoder {
45 | if ((self = [super initWithCoder:aDecoder])) {
46 | [self commonInitialiser];
47 | }
48 | return self;
49 | }
50 |
51 | - (id)initWithFrame:(CGRect)frame {
52 | if ((self = [super initWithFrame:frame])) {
53 | [self commonInitialiser];
54 | }
55 | return self;
56 | }
57 |
58 | - (void)commonInitialiser {
59 | self.backgroundColor = [UIColor whiteColor];
60 |
61 | CGRect frame = UIEdgeInsetsInsetRect(self.bounds, _contentInset);
62 | _internalTextView = [[YTTextView alloc] initWithFrame:frame];
63 | _internalTextView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
64 | _internalTextView.delegate = self;
65 |
66 | _internalTextView.font = [UIFont systemFontOfSize:15.0];
67 | _internalTextView.backgroundColor = [UIColor clearColor];
68 |
69 | _internalTextView.showsHorizontalScrollIndicator = NO;
70 | _internalTextView.showsVerticalScrollIndicator = YES;
71 |
72 | _internalTextView.scrollEnabled = YES;
73 |
74 | if ([_internalTextView respondsToSelector:@selector(textContainerInset)]) {
75 | _internalTextView.textContainerInset = UIEdgeInsetsMake(0.0, 0.0, 0.0, 0.0);
76 | _internalTextViewSizeDelta = 0.0;
77 | } else {
78 | // iOS7之前,textView.contentInset == UIEdgeZero,但绘制文本时,
79 | // 却缩进了,缩进值大概为{7.0, 0.0, 7.0, 0.0},这样文本的上下有空白,不会紧贴边框。
80 | // 这里取消掉,但用sizeThatFits计算size时,系统还是考虑了这个缩进,我们用sizeThatFits计算时,
81 | // 要去除缩进,即_internalTextViewSizeDelta的值
82 | // 在不同fontSize下,这个缩进值不变
83 | _internalTextView.contentInset = UIEdgeInsetsMake(-7.0, 0.0, -7.0, 0.0);
84 | _internalTextViewSizeDelta = -7.0 * 2.0;
85 | }
86 |
87 | [self addSubview:_internalTextView];
88 |
89 | [self setMinNumberOfLines:SLMinNumOfLines];
90 | [self setMaxNumberOfLines:SLMaxNumOfLines];
91 | }
92 |
93 | - (CGSize)sizeThatFits:(CGSize)size {
94 | CGFloat estimateWidth = self.bounds.size.width - _contentInset.left - _contentInset.right;
95 | CGFloat internalTextViewHeight = [self internalTextViewSizeThatFits:CGSizeMake(estimateWidth, CGFLOAT_MAX)].height;
96 | internalTextViewHeight = MAX(MIN(internalTextViewHeight, _maxHeight), _minHeight);
97 |
98 | return CGSizeMake(self.bounds.size.width,
99 | _contentInset.top + _contentInset.bottom + internalTextViewHeight);
100 | }
101 |
102 | #pragma mark - Setters & getters
103 |
104 | - (void)setContentInset:(UIEdgeInsets)inset {
105 | _contentInset = inset;
106 |
107 | _internalTextView.frame = UIEdgeInsetsInsetRect(self.bounds, self.contentInset);
108 |
109 | [self performSelector:@selector(textViewDidChange:) withObject:_internalTextView];
110 | }
111 |
112 | - (UIEdgeInsets)contentInset {
113 | return _contentInset;
114 | }
115 |
116 | - (UIView *)backgroundView {
117 | if (!_backgroundView) {
118 | _backgroundView = [[UIImageView alloc] initWithFrame:self.bounds];
119 | _backgroundView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
120 | [self insertSubview:_backgroundView atIndex:0];
121 | }
122 | return _backgroundView;
123 | }
124 |
125 | - (void)setBackgroundView:(UIView *)backgroundView {
126 | if (backgroundView != _backgroundView &&
127 | backgroundView != nil) {
128 |
129 | if ([backgroundView superview]) {
130 | [backgroundView removeFromSuperview];
131 | }
132 |
133 | if ([_backgroundView superview]) {
134 | [_backgroundView removeFromSuperview];
135 | }
136 | _backgroundView = nil;
137 |
138 | _backgroundView = backgroundView;
139 | _backgroundView.frame = self.bounds;
140 | _backgroundView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
141 | [self insertSubview:backgroundView atIndex:0];
142 | }
143 | }
144 |
145 | - (void)setPlaceholder:(NSString *)placeholder {
146 | _placeholder = [placeholder copy];
147 | if (placeholder.length) {
148 | self.placeholderLabel.text = placeholder;
149 | } else {
150 | self.placeholderLabel = nil;
151 | }
152 |
153 | [self showOrHidePlaceholder];
154 | }
155 |
156 | - (UILabel *)placeholderLabel {
157 | if (!_placeholderLabel) {
158 | _placeholderLabel = [[UILabel alloc] initWithFrame:CGRectInset(self.bounds, 5.0, 0.0)];
159 | _placeholderLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
160 | _placeholderLabel.font = _internalTextView.font;
161 | _placeholderLabel.textColor = [UIColor lightGrayColor];
162 | _placeholderLabel.backgroundColor = [UIColor clearColor];
163 | [self insertSubview:_placeholderLabel belowSubview:_internalTextView];
164 | }
165 | return _placeholderLabel;
166 | }
167 |
168 | - (void)setMaxNumberOfLines:(int)n {
169 | if (_maxNumberOfLines != n) {
170 | _maxHeight = [self estimateIntervalTextViewHeightWithLines:n];
171 |
172 | _maxNumberOfLines = n;
173 | }
174 | }
175 |
176 | - (void)setMinNumberOfLines:(int)m {
177 | if (_minNumberOfLines != m) {
178 | _minHeight = [self estimateIntervalTextViewHeightWithLines:m];
179 |
180 | _minNumberOfLines = m;
181 | }
182 | }
183 |
184 | #pragma mark - Instance methods
185 |
186 | - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
187 | [_internalTextView becomeFirstResponder];
188 | }
189 |
190 | - (BOOL)canBecomeFirstResponder {
191 | return [_internalTextView canBecomeFirstResponder];
192 | }
193 |
194 | - (BOOL)becomeFirstResponder {
195 | return [_internalTextView becomeFirstResponder];
196 | }
197 |
198 | - (BOOL)canResignFirstResponder {
199 | return [_internalTextView canResignFirstResponder];
200 | }
201 |
202 | - (BOOL)resignFirstResponder {
203 | return [_internalTextView resignFirstResponder];
204 | }
205 |
206 | - (BOOL)isFirstResponder {
207 | return [_internalTextView isFirstResponder];
208 | }
209 |
210 | - (BOOL)hasText{
211 | return [_internalTextView hasText];
212 | }
213 |
214 | - (void)scrollRangeToVisible:(NSRange)range {
215 | [_internalTextView scrollRangeToVisible:range];
216 | }
217 |
218 | ///////////////////////////////////////////////////////////////////////////////////////////////////
219 | #pragma mark - UITextView properties
220 | ///////////////////////////////////////////////////////////////////////////////////////////////////
221 |
222 | - (void)setText:(NSString *)newText {
223 | _internalTextView.text = newText;
224 |
225 | [self performSelector:@selector(textViewDidChange:) withObject:_internalTextView];
226 | }
227 |
228 | - (NSString *)text {
229 | return _internalTextView.text;
230 | }
231 |
232 | ///////////////////////////////////////////////////////////////////////////////////////////////////
233 |
234 | - (void)setFont:(UIFont *)afont {
235 | _internalTextView.font = afont;
236 | _placeholderLabel.font = afont;
237 |
238 | _maxHeight = [self estimateIntervalTextViewHeightWithLines:_maxNumberOfLines];
239 | _minHeight = [self estimateIntervalTextViewHeightWithLines:_minNumberOfLines];
240 |
241 | [self performSelector:@selector(textViewDidChange:) withObject:_internalTextView];
242 | }
243 |
244 | - (UIFont *)font {
245 | return _internalTextView.font;
246 | }
247 |
248 | ///////////////////////////////////////////////////////////////////////////////////////////////////
249 |
250 | -(void)setTextColor:(UIColor *)color {
251 | _internalTextView.textColor = color;
252 | }
253 |
254 | -(UIColor*)textColor{
255 | return _internalTextView.textColor;
256 | }
257 |
258 | ///////////////////////////////////////////////////////////////////////////////////////////////////
259 |
260 | - (void)setBackgroundColor:(UIColor *)backgroundColor {
261 | [super setBackgroundColor:backgroundColor];
262 | _internalTextView.backgroundColor = backgroundColor;
263 | }
264 |
265 | - (UIColor*)backgroundColor {
266 | return _internalTextView.backgroundColor;
267 | }
268 |
269 | ///////////////////////////////////////////////////////////////////////////////////////////////////
270 |
271 | -(void)setTextAlignment:(NSTextAlignment)aligment {
272 | _internalTextView.textAlignment = aligment;
273 | }
274 |
275 | -(NSTextAlignment)textAlignment {
276 | return _internalTextView.textAlignment;
277 | }
278 |
279 | ///////////////////////////////////////////////////////////////////////////////////////////////////
280 |
281 | - (void)setSelectedRange:(NSRange)range {
282 | _internalTextView.selectedRange = range;
283 | }
284 |
285 | - (NSRange)selectedRange {
286 | return _internalTextView.selectedRange;
287 | }
288 |
289 | ///////////////////////////////////////////////////////////////////////////////////////////////////
290 |
291 | - (void)setEditable:(BOOL)beditable {
292 | _internalTextView.editable = beditable;
293 | }
294 |
295 | - (BOOL)isEditable {
296 | return _internalTextView.editable;
297 | }
298 |
299 | ///////////////////////////////////////////////////////////////////////////////////////////////////
300 |
301 | - (void)setReturnKeyType:(UIReturnKeyType)keyType {
302 | _internalTextView.returnKeyType = keyType;
303 | }
304 |
305 | - (UIReturnKeyType)returnKeyType {
306 | return _internalTextView.returnKeyType;
307 | }
308 |
309 | - (void)setKeyboardAppearance:(UIKeyboardAppearance)keyboardAppearance {
310 | _internalTextView.keyboardAppearance = keyboardAppearance;
311 | }
312 |
313 | - (UIKeyboardAppearance)keyboardAppearance {
314 | return _internalTextView.keyboardAppearance;
315 | }
316 |
317 | ///////////////////////////////////////////////////////////////////////////////////////////////////
318 |
319 | - (void)setEnablesReturnKeyAutomatically:(BOOL)enablesReturnKeyAutomatically {
320 | _internalTextView.enablesReturnKeyAutomatically = enablesReturnKeyAutomatically;
321 | }
322 |
323 | - (BOOL)enablesReturnKeyAutomatically {
324 | return _internalTextView.enablesReturnKeyAutomatically;
325 | }
326 |
327 | ///////////////////////////////////////////////////////////////////////////////////////////////////
328 |
329 | -(void)setDataDetectorTypes:(UIDataDetectorTypes)datadetector {
330 | _internalTextView.dataDetectorTypes = datadetector;
331 | }
332 |
333 | -(UIDataDetectorTypes)dataDetectorTypes {
334 | return _internalTextView.dataDetectorTypes;
335 | }
336 |
337 | /////////////////////////////////////////////////////////////////////////////////////////////////////
338 | /////////////////////////////////////////////////////////////////////////////////////////////////////
339 | #pragma mark - UITextViewDelegate
340 |
341 |
342 | ///////////////////////////////////////////////////////////////////////////////////////////////////
343 | - (BOOL)textViewShouldBeginEditing:(UITextView *)textView {
344 | if ([self.delegate respondsToSelector:@selector(growingTextViewShouldBeginEditing:)]) {
345 | return [self.delegate growingTextViewShouldBeginEditing:self];
346 |
347 | } else {
348 | return YES;
349 | }
350 | }
351 |
352 |
353 | ///////////////////////////////////////////////////////////////////////////////////////////////////
354 | - (BOOL)textViewShouldEndEditing:(UITextView *)textView {
355 | if ([self.delegate respondsToSelector:@selector(growingTextViewShouldEndEditing:)]) {
356 | return [self.delegate growingTextViewShouldEndEditing:self];
357 |
358 | } else {
359 | return YES;
360 | }
361 | }
362 |
363 |
364 | ///////////////////////////////////////////////////////////////////////////////////////////////////
365 | - (void)textViewDidBeginEditing:(UITextView *)textView {
366 | if ([self.delegate respondsToSelector:@selector(growingTextViewDidBeginEditing:)]) {
367 | [self.delegate growingTextViewDidBeginEditing:self];
368 | }
369 | }
370 |
371 |
372 | ///////////////////////////////////////////////////////////////////////////////////////////////////
373 | - (void)textViewDidEndEditing:(UITextView *)textView {
374 | [self showOrHidePlaceholder];
375 |
376 | if ([self.delegate respondsToSelector:@selector(growingTextViewDidEndEditing:)]) {
377 | [self.delegate growingTextViewDidEndEditing:self];
378 | }
379 | }
380 |
381 | ///////////////////////////////////////////////////////////////////////////////////////////////////
382 | - (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range
383 | replacementText:(NSString *)atext {
384 |
385 | //weird 1 pixel bug when clicking backspace when textView is empty
386 | if (![textView hasText] && [atext isEqualToString:@""]) {
387 | return NO;
388 | }
389 |
390 | //Added by bretdabaker: sometimes we want to handle this ourselves
391 | if ([self.delegate respondsToSelector:@selector(growingTextView:shouldChangeTextInRange:replacementText:)]) {
392 | return [self.delegate growingTextView:self shouldChangeTextInRange:range replacementText:atext];
393 | }
394 |
395 | if ([atext isEqualToString:@"\n"]) {
396 | if ([self.delegate respondsToSelector:@selector(growingTextViewShouldReturn:)]) {
397 | if (![self.delegate performSelector:@selector(growingTextViewShouldReturn:) withObject:self]) {
398 | return YES;
399 | } else {
400 | [textView resignFirstResponder];
401 | return NO;
402 | }
403 | }
404 | }
405 |
406 | return YES;
407 | }
408 |
409 | ///////////////////////////////////////////////////////////////////////////////////////////////////
410 | - (void)textViewDidChange:(UITextView *)textView {
411 | // placeholder text label
412 | [self showOrHidePlaceholder];
413 |
414 | CGFloat needHeight = [self internalTextViewSizeThatFits:CGSizeMake(CGRectGetWidth(_internalTextView.bounds), CGFLOAT_MAX)].height;
415 | CGFloat internalViewNewHeight = MIN(MAX(needHeight, _minHeight), _maxHeight);
416 |
417 | CGFloat selfNewHeight = internalViewNewHeight + _contentInset.top + _contentInset.bottom;
418 | CGFloat currentHeight = CGRectGetHeight(self.bounds);
419 | if (ceilf(currentHeight) != ceilf(selfNewHeight)) {
420 |
421 | // iOS6,iOS7,iOS8下:
422 | // 当前文本行数小于最大行数时,如果scrollEnabled为YES,调整self的frame,
423 | // 文本的显示异常,插入符不在最后一行,插入符下面还有一个空白行。
424 | BOOL shouldUseScrollIndicators = NO;
425 | if (internalViewNewHeight >= _maxHeight) {
426 | if(!_internalTextView.scrollEnabled){
427 | _internalTextView.scrollEnabled = YES;
428 | shouldUseScrollIndicators = YES;
429 | }
430 | } else {
431 | if (_internalTextView.scrollEnabled) {
432 | _internalTextView.scrollEnabled = NO;
433 | }
434 | }
435 |
436 | if ([self.delegate respondsToSelector:@selector(growingTextView:shouldChangeHeight:)]) {
437 | [self.delegate growingTextView:self shouldChangeHeight:selfNewHeight];
438 | }
439 |
440 | if (internalViewNewHeight >= _maxHeight) {
441 | // ios7下,文本高度过高时,需要设置textContainer.size,否则不能scroll,且有些文本没有绘制。
442 | if ([_internalTextView respondsToSelector:@selector(textContainer)]) {
443 | // 需要post消息调用让插入符可见。(可能是_internalTextView.scrollEnabled为NO导致)
444 | [self performSelector:@selector(scrollToCaretAnimated:) withObject:@YES afterDelay:SLFrameChangeDuration];
445 | } else {
446 | // 自动滑动使插入符可见。(可能是_internalTextView.scrollEnabled为NO导致)
447 | // iOS7前,粘贴超过maxLine的文本时,使插入符可见
448 | [self scrollToCaretAnimated:YES];
449 | }
450 |
451 | [_internalTextView flashScrollIndicators];
452 | }
453 | }
454 |
455 | if ([self.delegate respondsToSelector:@selector(growingTextViewDidChange:)]) {
456 | [self.delegate growingTextViewDidChange:self];
457 | }
458 | }
459 |
460 | ///////////////////////////////////////////////////////////////////////////////////////////////////
461 | - (void)textViewDidChangeSelection:(UITextView *)textView {
462 | if ([self.delegate respondsToSelector:@selector(growingTextViewDidChangeSelection:)]) {
463 | [self.delegate growingTextViewDidChangeSelection:self];
464 | }
465 | }
466 |
467 | #pragma mark - Private
468 |
469 | - (void)showOrHidePlaceholder {
470 | self.placeholderLabel.hidden = (_internalTextView.text.length > 0);
471 | }
472 |
473 | - (void)scrollToCaretAnimated:(BOOL)animated {
474 | CGRect rect = [_internalTextView caretRectForPosition:_internalTextView.selectedTextRange.end];
475 | [_internalTextView scrollRectToVisible:rect animated:YES];
476 | }
477 |
478 | - (CGFloat)estimateIntervalTextViewHeightWithLines:(NSInteger)lineCount {
479 | NSString *saveText = _internalTextView.text;
480 | NSString *newText = @"|W|";
481 |
482 | _internalTextView.delegate = nil;
483 | _internalTextView.hidden = YES;
484 |
485 | for (int i = 1; i < lineCount; ++i) {
486 | newText = [newText stringByAppendingString:@"\n|W|"];
487 | }
488 |
489 | _internalTextView.text = newText;
490 |
491 | CGFloat height = [_internalTextView sizeThatFits:CGSizeMake(self.bounds.size.width, CGFLOAT_MAX)].height;
492 |
493 | _internalTextView.text = saveText;
494 | _internalTextView.hidden = NO;
495 | _internalTextView.delegate = self;
496 |
497 | return height;
498 | }
499 |
500 | - (CGSize)internalTextViewSizeThatFits:(CGSize)constraintSize {
501 | CGSize size = [_internalTextView sizeThatFits:constraintSize];
502 | size.height += _internalTextViewSizeDelta;
503 | return size;
504 | }
505 |
506 | @end
507 |
--------------------------------------------------------------------------------
/YTChatDemo/KeyBoard/Other/YTTextAttachment.h:
--------------------------------------------------------------------------------
1 | //
2 | // YTTextAttachment.h
3 | // YTChatDemo
4 | //
5 | // Created by TI on 15/8/31.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface YTTextAttachment : NSTextAttachment
12 |
13 | @property (nonatomic, strong) NSString *emojiCode; //编码字符
14 |
15 | /** 插入表情到attri中 */
16 | - (void)insertAttri:(NSMutableAttributedString *)attri font:(UIFont *)font;
17 | @end
18 |
--------------------------------------------------------------------------------
/YTChatDemo/KeyBoard/Other/YTTextAttachment.m:
--------------------------------------------------------------------------------
1 | //
2 | // YTTextAttachment.m
3 | // YTChatDemo
4 | //
5 | // Created by TI on 15/8/31.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import "YTTextAttachment.h"
10 |
11 | @implementation YTTextAttachment
12 |
13 | - (instancetype)initWithData:(NSData *)contentData ofType:(NSString *)uti{
14 | if (self = [super initWithData:contentData ofType:uti]) {
15 | if (!self.image) {
16 | self.image = [UIImage imageWithData:contentData];
17 | }
18 | }
19 | return self;
20 | }
21 |
22 | - (void)insertAttri:(NSMutableAttributedString *)attri font:(UIFont *)font{
23 |
24 | [attri appendAttributedString:[NSAttributedString attributedStringWithAttachment:self]];
25 | NSRange range = NSMakeRange(attri.length-1, 1);
26 | NSParagraphStyle * paragraph = [NSParagraphStyle defaultParagraphStyle];
27 | NSDictionary * attrs = @{NSAttachmentAttributeName:self,
28 | NSFontAttributeName:font,
29 | NSParagraphStyleAttributeName:paragraph};
30 | [attri setAttributes:attrs range:range];
31 | }
32 |
33 | // 重写表情加入字符串位置具体信息 可根据具体情况设置不同位置
34 | - (CGRect)attachmentBoundsForTextContainer:(NSTextContainer *)textContainer proposedLineFragment:(CGRect)lineFrag glyphPosition:(CGPoint)position characterIndex:(NSUInteger)charIndex{
35 |
36 | return CGRectMake(0, -2, lineFrag.size.height-2, lineFrag.size.height-2);
37 | }
38 |
39 | @end
40 |
--------------------------------------------------------------------------------
/YTChatDemo/KeyBoard/Other/YTTextView.h:
--------------------------------------------------------------------------------
1 | //
2 | // YTTextView.h
3 | // YTChatDemo
4 | //
5 | // Created by TI on 15/8/31.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 |
12 | @interface YTTextView : UITextView
13 | /* 返回一个干净的字符串,主要去掉编码和属性字符 */
14 | @property (nonatomic, strong, readonly) NSString *clearText;
15 |
16 | /*
17 | ** 通过传入source 返回一个有可能带属性和表情编码的字符串
18 | ** fout color 字体大小和颜色 默认15,黑色
19 | ** offset 设置基线偏移值,取值为 NSNumber(float),正值上偏,负值下偏
20 | */
21 | + (NSAttributedString *)getAttributedText:(NSString *)source Font:(UIFont *)font Color:(UIColor *)color Offset:(CGFloat)offset;
22 |
23 | // 快速插入一个表情
24 | + (BOOL)insertAttri:(NSMutableAttributedString *)attri imageName:(NSString *)imageName font:(UIFont *)font;
25 |
26 | @end
27 |
--------------------------------------------------------------------------------
/YTChatDemo/KeyBoard/Other/YTTextView.m:
--------------------------------------------------------------------------------
1 | //
2 | // YTTextView.m
3 | // YTChatDemo
4 | //
5 | // Created by TI on 15/8/31.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import "YTTextAttachment.h"
10 | #import "UIImage+YTGif.h"
11 | #import "YTTextView.h"
12 |
13 | @implementation YTTextView
14 |
15 | - (NSString *)clearText{
16 | NSMutableAttributedString *result = [self.attributedText mutableCopy];
17 | NSRange range = NSMakeRange(0, self.attributedText.length);
18 | [result enumerateAttribute:NSAttachmentAttributeName inRange:range options:0 usingBlock:^(id value, NSRange range, BOOL *stop) {
19 | if (value && [value isKindOfClass:[YTTextAttachment class]]) {
20 | YTTextAttachment *attach = (YTTextAttachment *)value;
21 | [result deleteCharactersInRange:range];
22 | [result insertAttributedString:[[NSAttributedString alloc] initWithString:attach.emojiCode] atIndex:range.location];
23 | }
24 | }];
25 | return result.string;
26 | }
27 |
28 | + (NSAttributedString *)getAttributedText:(NSString *)source Font:(UIFont *)font Color:(UIColor *)color Offset:(CGFloat)offset{
29 | if (!color) color = [UIColor blackColor];
30 | if (!font) font = [UIFont systemFontOfSize:15];
31 |
32 | NSRange prange = [source rangeOfString:@"["];
33 | NSRange hrange = [source rangeOfString:@"]"];
34 | if ((prange.location==NSNotFound)||(hrange.location==NSNotFound)) { // 没有自定义表情
35 | NSAttributedString * attributeString = [[NSAttributedString alloc] initWithString:source attributes:@{NSFontAttributeName:font,NSForegroundColorAttributeName:color}];
36 | return attributeString;
37 | }
38 |
39 | // 有可能包含自定义表情
40 | NSMutableAttributedString *attributeString = [[NSMutableAttributedString alloc] init];
41 | NSScanner *theScanner = [NSScanner scannerWithString:source];
42 | NSString *text = nil;
43 | while (![theScanner isAtEnd]) {
44 | //截取"["之前字符
45 | [theScanner scanUpToString:@"[" intoString:&text];
46 | if (text&&(text.length > 0)) {
47 | NSAttributedString *str = [[NSAttributedString alloc] initWithString:text attributes:@{NSFontAttributeName:font,NSForegroundColorAttributeName:color}];
48 | [attributeString appendAttributedString:str];
49 | }//end
50 |
51 | text = nil;
52 | //取得"[]"之间字符
53 | [theScanner scanUpToString:@"]" intoString:&text];
54 | if (text && ![text isEqualToString:@"]"] && (text.length > 0)) {
55 | NSString *imageName = [text substringFromIndex:1];
56 | BOOL insert= [[self class]insertAttri:attributeString imageName:imageName font:font];
57 | if (!insert) {
58 | NSString *txt = [NSString stringWithFormat:@"%@]", text];
59 | NSDictionary *attrs = nil;
60 | // 识别是不是草稿
61 | if ([txt isEqualToString:@"[草稿]"]) {
62 | attrs = @{NSFontAttributeName:font, NSForegroundColorAttributeName:[UIColor redColor]};
63 | } else {
64 | attrs = @{NSFontAttributeName:font, NSForegroundColorAttributeName:color};
65 | }
66 |
67 | NSAttributedString * str = [[NSAttributedString alloc] initWithString:txt attributes:attrs];
68 | if (str) {
69 | [attributeString appendAttributedString:str];
70 | }
71 |
72 | }
73 | }
74 | text = nil;
75 | [theScanner scanString:@"]" intoString:NULL];
76 | }
77 | return attributeString;
78 | }
79 |
80 | + (BOOL)insertAttri:(NSMutableAttributedString *)attri imageName:(NSString *)imageName font:(UIFont *)font{
81 | NSData *data = UIImagePNGRepresentation([UIImage imageNamed:imageName]);
82 | YTTextAttachment *attach = [[YTTextAttachment alloc] initWithData:data ofType:nil];
83 | if (attach.image && attach.image.size.width > 1.0f) {
84 | // 表情插入strat
85 | attach.emojiCode = [NSString stringWithFormat:@"[%@]", imageName];
86 | [attach insertAttri:attri font:font];
87 | return YES;
88 | // 表情图片插入end
89 | }else{
90 | return NO;
91 | }
92 | }
93 |
94 | @end
95 |
--------------------------------------------------------------------------------
/YTChatDemo/KeyBoard/YTKeyBoardView.h:
--------------------------------------------------------------------------------
1 | //
2 | // YTKeyBoardView.h
3 | // YTChatDemo
4 | //
5 | // Created by TI on 15/8/31.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "YTMoreView.h"
11 | #import "YTTextView.h"
12 |
13 | @class YTKeyBoardView;
14 | @protocol YTKeyBoardDelegate
15 | @optional
16 | // 当键盘位置发生变化是调用,durtaion时长
17 | - (void)keyBoardView:(YTKeyBoardView *)keyBoard ChangeDuration:(CGFloat)durtaion;
18 |
19 | // 发送事件 文字,图片都有可能
20 | - (void)keyBoardView:(YTKeyBoardView *)keyBoard sendResous:(id)resous;
21 |
22 | // 音频事件
23 | - (void)keyBoardView:(YTKeyBoardView *)keyBoard audioRuning:(UILongPressGestureRecognizer *)longPress;
24 |
25 | // 其他事件,有可能是后期扩展
26 | - (void)keyBoardView:(YTKeyBoardView *)keyBoard otherType:(YTMoreViewTypeAction)type;
27 | @end
28 |
29 | @interface YTKeyBoardView : UIView
30 | /* 键盘顶部view 控制键盘各种事件 */
31 | @property (nonatomic,strong) UIView *topView;
32 |
33 | // 创建键盘快捷方法
34 | - (instancetype)initDelegate:(id)delegate superView:(UIView *)superView;
35 |
36 | // 点击,键盘回到底部
37 | - (void)tapAction;
38 | @end
39 |
--------------------------------------------------------------------------------
/YTChatDemo/KeyBoard/YTKeyBoardView.m:
--------------------------------------------------------------------------------
1 | //
2 | // YTKeyBoardView.m
3 | // YTChatDemo
4 | //
5 | // Created by TI on 15/8/31.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import "SLGrowingTextView.h"
10 | #import "YTKeyBoardView.h"
11 | #import "UIView+YTLayer.h"
12 | #import "UIImage+YTGif.h"
13 | #import "YTDeviceTest.h"
14 | #import "YTEmojiView.h"
15 | #import "YTEmoji.h"
16 |
17 | @interface YTSwitcherView : UIView
18 |
19 | @end
20 |
21 | @implementation YTSwitcherView
22 | #define DURTAION 0.25f
23 | - (void)addSubview:(UIView *)view{
24 | // 动画添加子view 并且改控件只包含一个子view
25 | CGRect rect = self.frame;
26 | CGRect frame = view.frame;
27 | rect.size.height = CGRectGetHeight(frame);
28 | self.frame = rect;
29 | for (UIView * v in self.subviews) {// 移除前一个view
30 | [v removeFromSuperview];
31 | }
32 | [super addSubview:view];// 添加一个view
33 | frame.origin.y = CGRectGetHeight(self.frame);
34 | view.frame = frame;
35 | frame.origin.y = 0;
36 | [UIView animateWithDuration:DURTAION animations:^{// 动画显示
37 | view.frame = frame;
38 | }];
39 | }
40 |
41 | @end
42 |
43 | @interface YTKeyBoardView(){
44 | /* topView 一些设置全局参数 */
45 | CGFloat top_end_h; // textView 隐藏前的高度
46 |
47 | /* textView 一些设置全局参数 */
48 | //CGFloat text_one_hight; // 一行文字高度
49 | NSUInteger text_location; // 将要插入表情时 记录最后光标位置
50 | BOOL text_beInsert; // 记录光标位置后 是否允许插入表情
51 |
52 | /* keyBoard 一些设置全局参数 */
53 | BOOL kb_resign; //系统键盘已响应 响应为YES:且每次响应其值仅用一次
54 | BOOL kb_visiable;
55 |
56 | /* audio(音频) 一些设置全局参数 */
57 | BOOL audio_beTap; //音频状态按钮是否已被点击
58 | }
59 |
60 | @property (nonatomic, strong) UIButton *audio; //录音图标
61 | @property (nonatomic, strong) UIButton *emoji; //表情图标
62 | @property (nonatomic, strong) UIButton *more; //更多图标“+”
63 | @property (nonatomic, strong) NSArray *icons; //图标集合
64 |
65 | @property (nonatomic, assign) id delegate; //代理
66 | @property (nonatomic, strong) SLGrowingTextView *textView; //输入框
67 | @property (nonatomic, strong) UIButton *audioBt; //音频录制开关
68 | @property (nonatomic, strong) UIView *bottomView; //底部各种切换控件
69 |
70 | @property (nonatomic, strong) YTEmojiView *emojiView; //表情控制器
71 | @property (nonatomic, strong) UIView *audioView; //录音控制器
72 | @property (nonatomic, strong) YTMoreView *moreView;
73 |
74 | @end
75 |
76 | @implementation YTKeyBoardView
77 |
78 | #define KB_WIDTH ([UIScreen mainScreen].bounds.size.width)
79 | /* 小图标(录音,表情,更多)位置参数 */
80 | #define ICON_LR 12.0f //左右边距
81 | #define ICON_TOP 8.0f //顶端边距
82 | #define ICON_WH 28.0f //宽高
83 | /* textView 高度默认补充 为了显示更好看 */
84 | #define TOP_H 44.0f
85 | #define TEXT_FIT 10.0f
86 |
87 | #pragma mark - init 初始化
88 | - (instancetype)initDelegate:(id)delegate superView:(UIView *)superView{
89 | if (self = [super init]) {
90 | [self initUI];
91 | [self addNotifations];
92 | [self addToSuperView:superView];
93 | self.delegate = delegate;
94 | }
95 | return self;
96 | }
97 |
98 | - (void)initUI{
99 | [self addTopAndBottom];
100 | [self addIcons];
101 | [self addTextView];
102 | //[self addAudionButton];
103 | [self addContentView];
104 | }
105 |
106 | - (void)addTopAndBottom{
107 | UIView * top = [[UIView alloc]initWithFrame:CGRectMake( -1, 0,KB_WIDTH+2, TOP_H)];
108 | top.backgroundColor = [UIColor whiteColor];
109 | [top borderWithColor:[UIColor grayColor] borderWidth:0.5f];
110 | [self addSubview:top];
111 | self.topView = top;
112 |
113 | YTSwitcherView * bottom = [[YTSwitcherView alloc]initWithFrame:CGRectMake(0, CGRectGetHeight(top.frame), KB_WIDTH, EMOJI_VIEW_HEIGHT)];
114 | bottom.backgroundColor = [UIColor clearColor];
115 | [self addSubview:bottom];
116 | self.bottomView = bottom;
117 | }
118 |
119 | - (void)addIcons{
120 | UIButton *audio = [[UIButton alloc]initWithFrame:CGRectMake(ICON_LR, ICON_TOP, ICON_WH, ICON_WH)];
121 | audio.tag = 1;
122 | UIButton *emoji = [[UIButton alloc]initWithFrame:CGRectMake(KB_WIDTH-(ICON_LR+ICON_WH)*2.0f, ICON_TOP, ICON_WH, ICON_WH)];
123 | emoji.tag = 2;
124 | UIButton *more = [[UIButton alloc]initWithFrame:CGRectMake(KB_WIDTH-ICON_LR-ICON_WH, ICON_TOP, ICON_WH, ICON_WH)];
125 | more.tag = 3;
126 |
127 | [audio setImage:[UIImage imageNamed:@"btn_say"] forState:UIControlStateNormal];
128 | [emoji setImage:[UIImage imageNamed:@"btn_face"] forState:UIControlStateNormal];
129 | [more setImage:[UIImage imageNamed:@"btn_more"] forState:UIControlStateNormal];
130 |
131 | self.icons = @[emoji,more];// audio
132 | for (UIButton * bt in self.icons) {
133 | [bt setImage:[UIImage imageNamed:@"btn_key"] forState:UIControlStateSelected];
134 | [bt addTarget:self action:@selector(iconsAction:) forControlEvents:UIControlEventTouchUpInside];
135 | [self.topView addSubview:bt];
136 | }
137 | //self.audio = audio;
138 | self.emoji = emoji;
139 | self.more = more;
140 | }
141 |
142 | - (void)addTextView{
143 | SLGrowingTextView * text = [[SLGrowingTextView alloc]init];
144 | text.delegate = self;
145 | text.returnKeyType = UIReturnKeySend;
146 | text.enablesReturnKeyAutomatically = YES;
147 | text.font = [UIFont systemFontOfSize:16.0f];
148 | text.minNumberOfLines = 1;
149 | text.maxNumberOfLines = 5;
150 | text.backgroundColor = [UIColor whiteColor];
151 | [text cornerRadius:5.0f borderColor:[UIColor grayColor] borderWidth:0.5f];
152 | CGFloat hight = [text sizeThatFits:CGSizeMake(KB_WIDTH-ICON_WH*2.0f-ICON_LR*4.0f, CGFLOAT_MAX)].height;
153 | text.frame = CGRectMake(CGRectGetMaxX(self.audio.frame)+ICON_LR, ICON_TOP, KB_WIDTH-ICON_WH*2.0f-ICON_LR*4.0f, hight);
154 | CGFloat insetsTB = (TOP_H - ICON_TOP*2 - hight)*0.5;
155 | text.contentInset = UIEdgeInsetsMake(insetsTB, 2, insetsTB, 2);
156 | [text sizeToFit];
157 | [self.topView addSubview:text];
158 | self.textView = text;
159 | }
160 |
161 | - (void)addAudionButton{
162 | UIButton *audioBt = [[UIButton alloc]initWithFrame:self.textView.frame];
163 | audioBt.hidden = YES;
164 | audioBt.backgroundColor = [UIColor whiteColor];
165 | [audioBt setTitle:@"按住说话" forState:UIControlStateNormal];
166 | [audioBt setTitle:@"松开结束" forState:UIControlStateSelected];
167 | [audioBt setTitleColor:[UIColor grayColor] forState:UIControlStateNormal];
168 | [audioBt cornerRadius:5.0f borderColor:[UIColor grayColor] borderWidth:0.5f];
169 | UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(audionRecord:)];
170 | [audioBt addGestureRecognizer:longPress];
171 | [self.topView addSubview:audioBt];
172 | self.audioBt = audioBt;
173 | }
174 |
175 | - (void)addContentView{
176 | //表情
177 | self.emojiView = [[YTEmojiView alloc]initWithDelegate:self];
178 | //音频
179 | self.audioView = [[UIView alloc]init];
180 | //更多
181 | self.moreView = [[YTMoreView alloc]initWithFrame:self.emojiView.bounds];
182 | self.moreView.delegate = self;
183 | }
184 |
185 | - (void)addNotifations{
186 | [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(keyBoardHiden:) name:UIKeyboardWillHideNotification object:nil];
187 | [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(keyBoardShow:) name:UIKeyboardWillShowNotification object:nil];
188 | }
189 |
190 | - (void)addToSuperView:(UIView *)superView{
191 | CGFloat H = CGRectGetHeight(superView.bounds);
192 | CGFloat topH = self.topView.bounds.size.height;
193 | CGRect frame = CGRectMake(0,H-topH,KB_WIDTH, H);
194 | self.frame = frame;
195 | [superView addSubview:self];
196 | }
197 |
198 | #pragma mark - Key Board icons action
199 | - (void)iconsAction:(UIButton*)sender{
200 | [self audionDispose:NO];
201 | if (sender.selected) {
202 | [self.textView becomeFirstResponder];
203 | return;
204 | }else{
205 | kb_resign = YES;
206 | [self.textView resignFirstResponder];
207 |
208 | for (UIButton * b in self.icons) {
209 | if ([b isEqual:sender]) {
210 | sender.selected = !sender.selected;
211 | }else{
212 | b.selected = NO;
213 | }
214 | }
215 | UIView * visiableView = nil;
216 | switch (sender.tag) {
217 | case 1://录音
218 | {
219 | visiableView = self.audioView;
220 | [self audionDispose:YES];
221 | break;
222 | }
223 | case 2://表情
224 | {
225 | visiableView = self.emojiView;
226 | text_location = self.textView.selectedRange.location;
227 | text_beInsert = YES;
228 | break;
229 | }
230 | case 3://其他+
231 | {
232 | visiableView = self.moreView;
233 | break;
234 | }
235 | default:
236 | visiableView = [[UIView alloc]init];
237 | break;
238 | }
239 | [self.bottomView addSubview:visiableView];
240 | CGRect fram = self.frame;
241 | fram.origin.y =[UIScreen mainScreen].bounds.size.height- (CGRectGetHeight(visiableView.frame) + self.topView.bounds.size.height);
242 | [self duration:DURTAION EndF:fram Options:UIViewAnimationOptionCurveLinear];
243 | }
244 | }
245 |
246 | - (void)audionDispose:(BOOL)tap{
247 | if (tap) {
248 | audio_beTap = YES;
249 | self.textView.hidden = YES;
250 | self.audioBt.hidden = NO;
251 | CGRect frame = self.textView.frame;
252 | frame.size.height = 27.0f;
253 | self.audioBt.frame = frame;
254 | frame = self.topView.frame;
255 | top_end_h = frame.size.height;
256 | frame.size.height = TOP_H;
257 | self.topView.frame = frame;
258 | frame = self.frame;
259 | frame.origin.y += (top_end_h - TOP_H);
260 | [self duration:0 EndF:frame Options:UIViewAnimationOptionCurveLinear];
261 | }else{
262 | if (audio_beTap==NO) return;
263 | audio_beTap = NO;
264 | self.textView.hidden = NO;
265 | self.audioBt.hidden = YES;
266 | CGRect frame = self.topView.frame;
267 | frame.size.height = top_end_h;
268 | self.topView.frame = frame;
269 | frame = self.frame;
270 | frame.origin.y -= (top_end_h - TOP_H);
271 | [self duration:0 EndF:frame Options:UIViewAnimationOptionCurveLinear];
272 | }
273 |
274 | }
275 |
276 | #pragma mark - 系统键盘通知事件
277 | - (void)keyBoardHiden:(NSNotification*)noti{
278 | if (kb_resign==NO) {
279 | CGRect endF = [[noti.userInfo objectForKey:UIKeyboardFrameEndUserInfoKey]CGRectValue];
280 | CGFloat duration = [[noti.userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] floatValue];
281 | UIViewAnimationOptions options = [[noti.userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] integerValue];
282 | CGRect fram = self.frame;
283 | fram.origin.y = (endF.origin.y - _topView.frame.size.height);
284 | [self duration:duration EndF:fram Options:options];
285 | }else{
286 | kb_resign = NO;
287 | }
288 | }
289 |
290 | - (void)keyBoardShow:(NSNotification*)noti{
291 | CGRect endF = [[noti.userInfo objectForKey:UIKeyboardFrameEndUserInfoKey]CGRectValue];
292 | if (kb_resign==NO) {
293 | for (UIButton * b in self.icons) {
294 | b.selected = NO;
295 | }
296 | [self.bottomView addSubview:[UIView new]];
297 |
298 | NSTimeInterval duration = [[noti.userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] floatValue];
299 | UIViewAnimationOptions options = [[noti.userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] integerValue];
300 | CGRect fram = self.frame;
301 | fram.origin.y = (endF.origin.y - _topView.frame.size.height);
302 | [self duration:duration EndF:fram Options:options];
303 | }else{
304 | kb_resign = NO;
305 | }
306 | }
307 |
308 | #pragma mark - chat Emoji View Delegate
309 | - (void)emojiViewEmoji:(YTEmoji *)emoji{
310 | if (!emoji.emojiCode||(emoji.emojiCode.length == 0)) {
311 | UIImage *image = [UIImage gifImageNamed:emoji.emojiImage];
312 | [self sendResous:image];
313 | }else{
314 | NSMutableAttributedString *attributeString = nil;
315 | NSAttributedString *attribu = nil;
316 | if (text_beInsert) {
317 | NSRange range = NSMakeRange(0, text_location);
318 | attributeString = [[NSMutableAttributedString alloc]initWithAttributedString:[self.textView.internalTextView.attributedText attributedSubstringFromRange:range]];
319 | range = NSMakeRange(text_location, self.textView.internalTextView.attributedText.length-text_location);
320 | attribu = [[self.textView.internalTextView.attributedText attributedSubstringFromRange:range] mutableCopy];
321 | text_beInsert = NO;
322 | }else{
323 | attributeString = [[NSMutableAttributedString alloc]initWithAttributedString:self.textView.internalTextView.attributedText];
324 | }
325 |
326 | BOOL insert = [YTTextView insertAttri:attributeString imageName:emoji.emojiImage font:self.textView.font];
327 | if (attribu) {
328 | [attributeString appendAttributedString:attribu];
329 | }
330 | if (insert) {
331 | self.textView.internalTextView.attributedText = attributeString;
332 | }
333 | }
334 | }
335 |
336 | - (void)emojiViewDelete{
337 | NSRange range = self.textView.selectedRange;
338 | NSInteger location = (NSInteger)range.location;
339 | if (location == 0) {
340 | return;
341 | }
342 | range.location = location-1;
343 | range.length = 1;
344 |
345 | NSMutableAttributedString *attStr = [self.textView.internalTextView.attributedText mutableCopy];
346 | [attStr deleteCharactersInRange:range];
347 | self.textView.internalTextView.attributedText = attStr;
348 | self.textView.selectedRange = range;
349 | }
350 |
351 | - (void)emojiViewSend{
352 | [self sendMessage];
353 | }
354 |
355 | #pragma mark - text View Delegate
356 | - (BOOL)growingTextView:(SLGrowingTextView *)growingTextView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text{
357 | if(![growingTextView hasText] && [text isEqualToString:@""]) {
358 | return NO;
359 | }
360 | if ([text isEqualToString:@"\n"]) {
361 | [self sendMessage];
362 | return NO;
363 | }
364 | return YES;
365 | }
366 |
367 | - (void)growingTextView:(SLGrowingTextView *)growingTextView shouldChangeHeight:(CGFloat)height{
368 | CGRect frame = self.textView.frame;
369 | frame.size.height = height;
370 | [UIView animateWithDuration:DURTAION animations:^{
371 | self.textView.frame = frame;
372 | [self topLayoutSubViewWithH:(frame.size.height+ICON_TOP*2)];
373 | }];
374 | }
375 |
376 | -(void)audionRecord:(UILongPressGestureRecognizer*)longPress{
377 | switch (longPress.state) {
378 | case UIGestureRecognizerStateBegan:
379 | {
380 | if ([YTDeviceTest userAuthorizationAudioStatus]) {
381 | self.audioBt.selected = YES;
382 | [self.audioBt setBackgroundColor:[UIColor lightGrayColor]];
383 | for (UIButton * b in self.icons) {
384 | b.enabled = NO;
385 | }
386 | }
387 | break;
388 | }
389 | case UIGestureRecognizerStateEnded:
390 | case UIGestureRecognizerStateCancelled:
391 | {
392 | self.audioBt.selected = NO;
393 | [self.audioBt setBackgroundColor:[UIColor whiteColor]];
394 | for (UIButton * b in self.icons) {
395 | b.enabled = YES;
396 | }
397 | break;
398 | }
399 | default:
400 | break;
401 | }
402 | [self audioRuning:longPress];
403 | }
404 |
405 | #pragma mark - other logic
406 | - (void)topLayoutSubViewWithH:(CGFloat)hight{
407 | CGRect frame = self.topView.frame;
408 | CGFloat diff = hight - frame.size.height;
409 | frame.size.height = hight;
410 | self.topView.frame = frame;
411 |
412 | frame = self.bottomView.frame;
413 | frame.origin.y = CGRectGetHeight(self.topView.bounds);
414 | self.bottomView.frame = frame;
415 |
416 | frame = self.frame;
417 | frame.origin.y -= diff;
418 |
419 | [self duration:DURTAION EndF:frame Options:UIViewAnimationOptionCurveLinear];
420 | }
421 |
422 | - (void)duration:(CGFloat)duration EndF:(CGRect)endF Options:(UIViewAnimationOptions)options{
423 |
424 | [UIView animateWithDuration:duration delay:0.0f options:options animations:^{
425 | kb_resign = NO;
426 | self.frame = endF;
427 | } completion:^(BOOL finished) {
428 |
429 | }];
430 | [self changeDuration:duration];
431 | }
432 |
433 | - (void)sendMessage{
434 | if (![self.textView hasText]&&(self.textView.text.length==0)) {
435 | return;
436 | }
437 | NSString *plainText = self.textView.internalTextView.clearText;
438 | //空格处理
439 | plainText = [plainText stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
440 |
441 | if (plainText.length > 0) {
442 | [self sendResous:plainText];
443 | self.textView.text = @"";
444 | }
445 | }
446 |
447 | #pragma mark - self public api action
448 | - (void)tapAction{
449 | if (![self.textView isFirstResponder]) {
450 | UIButton * b = [[UIButton alloc]init];
451 | b.selected = NO;
452 | [self iconsAction:b];
453 | }else{
454 | [self.textView resignFirstResponder];
455 | }
456 | }
457 |
458 | #pragma mark - self delegate action
459 | - (void)changeDuration:(CGFloat)duration{
460 | if ([self.delegate respondsToSelector:@selector(keyBoardView:ChangeDuration:)]) {
461 | [self.delegate keyBoardView:self ChangeDuration:duration];
462 | }
463 | }
464 |
465 | - (void)sendResous:(id)resous{
466 | if ([self.delegate respondsToSelector:@selector(keyBoardView:sendResous:)]) {
467 | [self.delegate keyBoardView:self sendResous:resous];
468 | }
469 | }
470 |
471 | - (void)audioRuning:(UILongPressGestureRecognizer*)longPress{
472 | if ([self.delegate respondsToSelector:@selector(keyBoardView:audioRuning:)]) {
473 | [self.delegate keyBoardView:self audioRuning:longPress];
474 | }
475 | }
476 |
477 | - (void)moreViewType:(YTMoreViewTypeAction)type{
478 | if ([self.delegate respondsToSelector:@selector(keyBoardView:otherType:)]) {
479 | [self.delegate keyBoardView:self otherType:type];
480 | }
481 | }
482 |
483 | - (void)dealloc{
484 | [[NSNotificationCenter defaultCenter]removeObserver:self];
485 | }
486 |
487 | @end
488 |
--------------------------------------------------------------------------------
/YTChatDemo/Main/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // YTChatDemo
4 | //
5 | // Created by TI on 15/8/24.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | @interface AppDelegate : UIResponder
13 |
14 | @property (strong, nonatomic) UIWindow *window;
15 |
16 | @property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
17 | @property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
18 | @property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
19 |
20 | - (void)saveContext;
21 | - (NSURL *)applicationDocumentsDirectory;
22 |
23 |
24 | @end
25 |
26 |
--------------------------------------------------------------------------------
/YTChatDemo/Main/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // YTChatDemo
4 | //
5 | // Created by TI on 15/8/24.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import "AppDelegate.h"
10 | #import "YTCoreData.h"
11 |
12 | @interface AppDelegate ()
13 |
14 | @end
15 |
16 | @implementation AppDelegate
17 |
18 |
19 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
20 |
21 | [YTCoreData instance];//初始化coreData 建立数据库
22 | return YES;
23 | }
24 |
25 | /*
26 | - (void)applicationWillResignActive:(UIApplication *)application {
27 |
28 | }
29 |
30 | - (void)applicationDidEnterBackground:(UIApplication *)application {
31 |
32 | }
33 |
34 | - (void)applicationWillEnterForeground:(UIApplication *)application {
35 |
36 | }
37 |
38 | - (void)applicationDidBecomeActive:(UIApplication *)application {
39 |
40 | }
41 |
42 |
43 | - (void)applicationWillTerminate:(UIApplication *)application {
44 | [self saveContext];
45 | }
46 | */
47 | - (void)applicationDidReceiveMemoryWarning:(UIApplication *)application{
48 | /* 内存警告 */
49 | [[NSURLCache sharedURLCache] removeAllCachedResponses];
50 | }
51 |
52 | #pragma mark - Core Data stack & Saving support
53 | - (NSURL *)applicationDocumentsDirectory {
54 | return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
55 | }
56 |
57 | - (void)saveContext {
58 | NSError * error;
59 | if ([[YTCoreData instance].privateObjectContext hasChanges]) {
60 | [[YTCoreData instance].privateObjectContext save:&error];
61 | }
62 | }
63 |
64 |
65 |
66 | @end
67 |
--------------------------------------------------------------------------------
/YTChatDemo/Main/StartVC.h:
--------------------------------------------------------------------------------
1 | //
2 | // StartVC.h
3 | // ChatDemo
4 | //
5 | // Created by TI on 15/6/5.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface StartVC : UIViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/YTChatDemo/Main/StartVC.m:
--------------------------------------------------------------------------------
1 | //
2 | // StartVC.m
3 | // ChatDemo
4 | //
5 | // Created by TI on 15/6/5.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import "NSManagedObject+YTMoreThread.h"
10 | #import "YTImgBrowerController.h"
11 | #import "YTKeyBoardView.h"
12 | #import "YTDeviceTest.h"
13 | #import "YTImgInfo.h"
14 | #import "Defines.h"
15 | #import "StartVC.h"
16 | #import "Message.h"
17 | @import AssetsLibrary;
18 |
19 | @interface StartVC ()
20 | {
21 | BOOL typeChange;
22 | }
23 | @property (nonatomic, strong) UITableView *tableView;
24 | @property (nonatomic, strong) YTKeyBoardView *keyBoard;
25 | @property (nonatomic, strong) NSMutableArray *infoArray;
26 |
27 | @property (nonatomic, strong) YTImgBrowerController *ImgBrower;
28 | @end
29 |
30 | @implementation StartVC
31 |
32 | - (NSMutableArray *)infoArray{
33 | if (!_infoArray) {
34 | _infoArray = [[Message filter:nil orderby:@[@"objID"] offset:0 limit:0] mutableCopy];
35 | }
36 | return _infoArray;
37 | }
38 |
39 | - (void)viewDidAppear:(BOOL)animated{
40 | [super viewDidAppear:animated];
41 | if (self.infoArray.count == 0) return;
42 | [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:self.infoArray.count-1 inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:NO];
43 | typeChange = ((Message *)self.infoArray.lastObject).type.boolValue;
44 | }
45 |
46 | - (void)viewDidLoad {
47 | [super viewDidLoad];
48 | self.keyBoard = [[YTKeyBoardView alloc]
49 | initDelegate:self
50 | superView:self.view];
51 | [self initUI];
52 | }
53 |
54 | - (void)initUI{
55 | [self addTableView];
56 | UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc]
57 | initWithTarget:self
58 | action:@selector(tapAction)];
59 | [self.tableView addGestureRecognizer:tap];
60 |
61 | self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:@"清空" style:UIBarButtonItemStyleDone target:self action:@selector(clearDataSource)];
62 |
63 | self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:@"浏览相册" style:UIBarButtonItemStyleDone target:self action:@selector(seePhoto)];
64 | }
65 |
66 | - (void)addTableView{
67 | UITableView *tableView = [[UITableView alloc]
68 | initWithFrame:[self tableViewFrame]];
69 | tableView.delegate = self;
70 | tableView.dataSource = self;
71 | tableView.tableFooterView = [UIView new];
72 | tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
73 | [self.view insertSubview:(self.tableView = tableView) belowSubview:self.keyBoard];
74 | }
75 |
76 | #pragma mark - key board delegate
77 | - (void)keyBoardView:(YTKeyBoardView *)keyBoard ChangeDuration:(CGFloat)durtaion{
78 | if (self.infoArray.count == 0) return;
79 | [UIView animateWithDuration:durtaion animations:^{
80 | self.tableView.frame = [self tableViewFrame];
81 | [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:self.infoArray.count-1 inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:NO];
82 | }];
83 | }
84 |
85 | - (void)keyBoardView:(YTKeyBoardView *)keyBoard audioRuning:(UILongPressGestureRecognizer *)longPress{
86 | LOG(@"录音 -> 在此处理");
87 | if (longPress.state == UIGestureRecognizerStateEnded) {
88 | [self keyBoardView:keyBoard sendResous:@"录音"];
89 | }
90 | }
91 |
92 | - (void)keyBoardView:(YTKeyBoardView *)keyBoard otherType:(YTMoreViewTypeAction)type{
93 | LOG(@"相机 相册 -> 在此处理");
94 | NSString * m;
95 | if (type == YTMoreViewTypeActionCamera) {
96 | m = @"相机";
97 | }else{
98 | m = @"相册";
99 | }
100 | [self keyBoardView:keyBoard sendResous:m];
101 | }
102 |
103 | - (void)keyBoardView:(YTKeyBoardView *)keyBoard sendResous:(id)resous{
104 | if (!resous) return;
105 | NSString *string = @"";
106 | if ([resous isKindOfClass:[NSString class]]) {
107 | string = resous;
108 | }else if ([resous isKindOfClass:[UIImage class]]){
109 | UIImage *image = resous;
110 | CGRect rect = CGRectZero;
111 | rect.size = image.size;
112 | UIView *view = [[UIView alloc]initWithFrame:rect];
113 | rect.origin.x = (self.view.bounds.size.width - rect.size.width)*0.5f;
114 | UIImageView *imageView = [[UIImageView alloc]initWithFrame:rect];
115 | imageView.image = image;
116 | [view addSubview:imageView];
117 | self.tableView.tableFooterView = view;
118 | string = [NSString stringWithFormat:@"image:size->%@ ⬇️ 有图片展示",NSStringFromCGSize(image.size)];
119 | }else{
120 | return;
121 | }
122 |
123 | Message * m = [Message createNew];
124 | m.content = string;
125 | m.type = (typeChange = !typeChange)?@(MessageTypeMe):@(MessageTypeOther);
126 | if (self.infoArray.count > 0) {
127 | m.objID = @(((Message*)[self.infoArray lastObject]).objID.intValue);
128 | }else{
129 | m.objID = @(0);
130 | }
131 |
132 | [Message save:^(NSError *error) {
133 | [self endSend:m];
134 | }];
135 |
136 | }
137 |
138 | #pragma mark - tableView delegate & dataSource
139 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
140 | return self.infoArray.count;
141 | }
142 |
143 | static NSString * cellID = @"cellID";
144 | - (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
145 | UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:cellID];
146 | if (!cell) {
147 | cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID];
148 | }
149 | Message * m = (Message*)self.infoArray[indexPath.row];
150 | NSString * string = [NSString stringWithFormat:@"%@: %@",m.type.intValue == MessageTypeMe?@"大师兄":@"小师妹" , m.content];
151 | UIColor *color = m.type.intValue == MessageTypeMe?[UIColor redColor]:[UIColor blackColor];
152 | cell.textLabel.attributedText = [YTTextView getAttributedText:string Font:nil Color:color Offset:-3];
153 | return cell;
154 | }
155 |
156 | #pragma mark - self action
157 | - (void)tapAction{
158 | [self.keyBoard tapAction];
159 | }
160 |
161 | - (CGRect)tableViewFrame{
162 | CGRect frame = self.view.bounds;
163 | if (!self.navigationController) {
164 | frame.origin.y = [UIApplication sharedApplication].statusBarFrame.size.height;
165 | }
166 | frame.size.height = self.keyBoard.frame.origin.y-frame.origin.y;
167 | return frame;
168 | }
169 |
170 | - (void)endSend:(Message*)message{
171 | [self.infoArray addObject:message];
172 | [self.tableView beginUpdates];
173 | NSIndexPath * path = [NSIndexPath indexPathForRow:(self.infoArray.count - 1) inSection:0];
174 | [self.tableView insertRowsAtIndexPaths:@[path] withRowAnimation:UITableViewRowAnimationFade];
175 | [self.tableView endUpdates];
176 |
177 | [self.tableView scrollToRowAtIndexPath:path atScrollPosition:UITableViewScrollPositionBottom animated:YES];
178 | }
179 |
180 | - (void)clearDataSource{
181 | if (self.infoArray.count == 0) return;
182 | for (Message * m in self.infoArray) {
183 | [Message delobject:m];
184 | }
185 | [Message save:^(NSError *error) {
186 | [self.infoArray removeAllObjects];
187 | dispatch_async(dispatch_get_main_queue(), ^{
188 | [self.tableView reloadData];
189 | });
190 | }];
191 | }
192 |
193 | //-------------------------------------------------------------
194 | /*
195 | 在这里仅仅是微弱的展示ACDsee的用法,时间原因,后面打算写一个类似微信的相册发
196 | 图片功能,不过核心功能已经完成,只是一些零碎的搭建和对ACDsee二次封装还没进行
197 | 有兴趣的朋友也可看下这个https://github.com/TiYcc/ImageBrower.git是我
198 | 写的,仅仅作为了解,还是值得一看的。
199 | */
200 | - (void)seePhoto{
201 | self.navigationItem.leftBarButtonItem.enabled = NO;
202 | NSMutableArray *assets = [NSMutableArray array];
203 | ALAssetsLibrary *assetsLibrary = [[ALAssetsLibrary alloc]init];
204 | [assetsLibrary enumerateGroupsWithTypes:ALAssetsGroupAll usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
205 | if (group) {
206 | [group enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
207 | if (result) {
208 | [assets addObject:result];
209 | }
210 | }];
211 | }else{
212 | if (self.ImgBrower) {
213 | self.ImgBrower = nil;
214 | }
215 | NSArray *imgInfos = [YTImgInfo imgInfosWithImgs:nil urls:assets type:YTImgInfoFromeTypePhoto];
216 | self.ImgBrower = [[YTImgBrowerController alloc]initWithDelegate:self imgInfos:imgInfos index:0];
217 | return ;
218 | }
219 | } failureBlock:^(NSError *error) {
220 |
221 | }];
222 | }
223 |
224 | - (void)imgBrowerControllerInitEnd:(YTImgInfo *)imgInfo{
225 | [self presentViewController:self.ImgBrower animated:YES completion:^{
226 | self.navigationItem.leftBarButtonItem.enabled = YES;
227 | }];
228 | }
229 |
230 | - (void)imgBrowerControllerWillDismiss:(YTImgInfo *)imgInfo{
231 | self.tableView.backgroundColor = [UIColor whiteColor];
232 | }
233 |
234 | @end
235 |
--------------------------------------------------------------------------------
/YTChatDemo/Supporting Files/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/YTChatDemo/Supporting Files/Defines.h:
--------------------------------------------------------------------------------
1 | //
2 | // Defines.h
3 | // ChatDemo
4 | //
5 | // Created by TI on 15/6/8.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #ifdef DEBUG
10 |
11 | #define log(format,...) NSLog(format, ## __VA_ARGS__)
12 | #define LOG(format) log(@"%@",(format))
13 |
14 | #else
15 |
16 | #define log(format,...)
17 | #define LOG(format)
18 |
19 | #endif
20 |
--------------------------------------------------------------------------------
/YTChatDemo/Supporting Files/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "size" : "40x40",
15 | "idiom" : "iphone",
16 | "filename" : "YT@2x.png",
17 | "scale" : "2x"
18 | },
19 | {
20 | "idiom" : "iphone",
21 | "size" : "40x40",
22 | "scale" : "3x"
23 | },
24 | {
25 | "idiom" : "iphone",
26 | "size" : "60x60",
27 | "scale" : "2x"
28 | },
29 | {
30 | "idiom" : "iphone",
31 | "size" : "60x60",
32 | "scale" : "3x"
33 | }
34 | ],
35 | "info" : {
36 | "version" : 1,
37 | "author" : "xcode"
38 | }
39 | }
--------------------------------------------------------------------------------
/YTChatDemo/Supporting Files/Images.xcassets/AppIcon.appiconset/YT@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TiYcc/KeyBoard/dfcec9a85580fcff301d4fa832c1de1c74566320/YTChatDemo/Supporting Files/Images.xcassets/AppIcon.appiconset/YT@2x.png
--------------------------------------------------------------------------------
/YTChatDemo/Supporting Files/Images.xcassets/btn_del.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x",
10 | "filename" : "btn_facecancel@2x.png"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/YTChatDemo/Supporting Files/Images.xcassets/btn_del.imageset/btn_facecancel@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TiYcc/KeyBoard/dfcec9a85580fcff301d4fa832c1de1c74566320/YTChatDemo/Supporting Files/Images.xcassets/btn_del.imageset/btn_facecancel@2x.png
--------------------------------------------------------------------------------
/YTChatDemo/Supporting Files/Images.xcassets/btn_face.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x",
10 | "filename" : "btn_face@2x.png"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x",
15 | "filename" : "btn_face@3x.png"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/YTChatDemo/Supporting Files/Images.xcassets/btn_face.imageset/btn_face@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TiYcc/KeyBoard/dfcec9a85580fcff301d4fa832c1de1c74566320/YTChatDemo/Supporting Files/Images.xcassets/btn_face.imageset/btn_face@2x.png
--------------------------------------------------------------------------------
/YTChatDemo/Supporting Files/Images.xcassets/btn_face.imageset/btn_face@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TiYcc/KeyBoard/dfcec9a85580fcff301d4fa832c1de1c74566320/YTChatDemo/Supporting Files/Images.xcassets/btn_face.imageset/btn_face@3x.png
--------------------------------------------------------------------------------
/YTChatDemo/Supporting Files/Images.xcassets/btn_key.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x",
10 | "filename" : "btn_key@2x.png"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x",
15 | "filename" : "btn_key@3x.png"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/YTChatDemo/Supporting Files/Images.xcassets/btn_key.imageset/btn_key@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TiYcc/KeyBoard/dfcec9a85580fcff301d4fa832c1de1c74566320/YTChatDemo/Supporting Files/Images.xcassets/btn_key.imageset/btn_key@2x.png
--------------------------------------------------------------------------------
/YTChatDemo/Supporting Files/Images.xcassets/btn_key.imageset/btn_key@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TiYcc/KeyBoard/dfcec9a85580fcff301d4fa832c1de1c74566320/YTChatDemo/Supporting Files/Images.xcassets/btn_key.imageset/btn_key@3x.png
--------------------------------------------------------------------------------
/YTChatDemo/Supporting Files/Images.xcassets/btn_more.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x",
10 | "filename" : "btn_chatmore@2x.png"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x",
15 | "filename" : "btn_chatmore@3x.png"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/YTChatDemo/Supporting Files/Images.xcassets/btn_more.imageset/btn_chatmore@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TiYcc/KeyBoard/dfcec9a85580fcff301d4fa832c1de1c74566320/YTChatDemo/Supporting Files/Images.xcassets/btn_more.imageset/btn_chatmore@2x.png
--------------------------------------------------------------------------------
/YTChatDemo/Supporting Files/Images.xcassets/btn_more.imageset/btn_chatmore@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TiYcc/KeyBoard/dfcec9a85580fcff301d4fa832c1de1c74566320/YTChatDemo/Supporting Files/Images.xcassets/btn_more.imageset/btn_chatmore@3x.png
--------------------------------------------------------------------------------
/YTChatDemo/Supporting Files/Images.xcassets/btn_photo.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x",
10 | "filename" : "btn_photo@2x.png"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x",
15 | "filename" : "btn_photo@3x.png"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/YTChatDemo/Supporting Files/Images.xcassets/btn_photo.imageset/btn_photo@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TiYcc/KeyBoard/dfcec9a85580fcff301d4fa832c1de1c74566320/YTChatDemo/Supporting Files/Images.xcassets/btn_photo.imageset/btn_photo@2x.png
--------------------------------------------------------------------------------
/YTChatDemo/Supporting Files/Images.xcassets/btn_photo.imageset/btn_photo@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TiYcc/KeyBoard/dfcec9a85580fcff301d4fa832c1de1c74566320/YTChatDemo/Supporting Files/Images.xcassets/btn_photo.imageset/btn_photo@3x.png
--------------------------------------------------------------------------------
/YTChatDemo/Supporting Files/Images.xcassets/btn_pic.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x",
10 | "filename" : "btn_pic@2x.png"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x",
15 | "filename" : "btn_pic@3x.png"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/YTChatDemo/Supporting Files/Images.xcassets/btn_pic.imageset/btn_pic@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TiYcc/KeyBoard/dfcec9a85580fcff301d4fa832c1de1c74566320/YTChatDemo/Supporting Files/Images.xcassets/btn_pic.imageset/btn_pic@2x.png
--------------------------------------------------------------------------------
/YTChatDemo/Supporting Files/Images.xcassets/btn_pic.imageset/btn_pic@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TiYcc/KeyBoard/dfcec9a85580fcff301d4fa832c1de1c74566320/YTChatDemo/Supporting Files/Images.xcassets/btn_pic.imageset/btn_pic@3x.png
--------------------------------------------------------------------------------
/YTChatDemo/Supporting Files/Images.xcassets/btn_say.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x",
10 | "filename" : "btn_say@2x.png"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x",
15 | "filename" : "btn_say@3x.png"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/YTChatDemo/Supporting Files/Images.xcassets/btn_say.imageset/btn_say@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TiYcc/KeyBoard/dfcec9a85580fcff301d4fa832c1de1c74566320/YTChatDemo/Supporting Files/Images.xcassets/btn_say.imageset/btn_say@2x.png
--------------------------------------------------------------------------------
/YTChatDemo/Supporting Files/Images.xcassets/btn_say.imageset/btn_say@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TiYcc/KeyBoard/dfcec9a85580fcff301d4fa832c1de1c74566320/YTChatDemo/Supporting Files/Images.xcassets/btn_say.imageset/btn_say@3x.png
--------------------------------------------------------------------------------
/YTChatDemo/Supporting Files/Images.xcassets/camera_down.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x",
10 | "filename" : "camera_down@2x.png"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/YTChatDemo/Supporting Files/Images.xcassets/camera_down.imageset/camera_down@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TiYcc/KeyBoard/dfcec9a85580fcff301d4fa832c1de1c74566320/YTChatDemo/Supporting Files/Images.xcassets/camera_down.imageset/camera_down@2x.png
--------------------------------------------------------------------------------
/YTChatDemo/Supporting Files/Images.xcassets/camera_nomal.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x",
10 | "filename" : "camera@2x.png"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/YTChatDemo/Supporting Files/Images.xcassets/camera_nomal.imageset/camera@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TiYcc/KeyBoard/dfcec9a85580fcff301d4fa832c1de1c74566320/YTChatDemo/Supporting Files/Images.xcassets/camera_nomal.imageset/camera@2x.png
--------------------------------------------------------------------------------
/YTChatDemo/Supporting Files/Images.xcassets/default_img.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x",
10 | "filename" : "default_img@2x.png"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/YTChatDemo/Supporting Files/Images.xcassets/default_img.imageset/default_img@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TiYcc/KeyBoard/dfcec9a85580fcff301d4fa832c1de1c74566320/YTChatDemo/Supporting Files/Images.xcassets/default_img.imageset/default_img@2x.png
--------------------------------------------------------------------------------
/YTChatDemo/Supporting Files/Images.xcassets/photo_down.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x",
10 | "filename" : "img_down@2x.png"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/YTChatDemo/Supporting Files/Images.xcassets/photo_down.imageset/img_down@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TiYcc/KeyBoard/dfcec9a85580fcff301d4fa832c1de1c74566320/YTChatDemo/Supporting Files/Images.xcassets/photo_down.imageset/img_down@2x.png
--------------------------------------------------------------------------------
/YTChatDemo/Supporting Files/Images.xcassets/photo_nomal.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x",
10 | "filename" : "img@2x.png"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/YTChatDemo/Supporting Files/Images.xcassets/photo_nomal.imageset/img@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TiYcc/KeyBoard/dfcec9a85580fcff301d4fa832c1de1c74566320/YTChatDemo/Supporting Files/Images.xcassets/photo_nomal.imageset/img@2x.png
--------------------------------------------------------------------------------
/YTChatDemo/Supporting Files/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | zh_CN
7 | CFBundleDisplayName
8 | YT
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIcons
12 |
13 | CFBundleIcons~ipad
14 |
15 | CFBundleIdentifier
16 | com.YccTime.app.$(PRODUCT_NAME:rfc1034identifier)
17 | CFBundleInfoDictionaryVersion
18 | 6.0
19 | CFBundleName
20 | $(PRODUCT_NAME)
21 | CFBundlePackageType
22 | APPL
23 | CFBundleShortVersionString
24 | 1.1.0
25 | CFBundleSignature
26 | ????
27 | CFBundleVersion
28 | 1.1.0
29 | LSRequiresIPhoneOS
30 |
31 | UILaunchStoryboardName
32 | Main
33 | UIMainStoryboardFile
34 | Main
35 | UIRequiredDeviceCapabilities
36 |
37 | armv7
38 |
39 | UISupportedInterfaceOrientations
40 |
41 | UIInterfaceOrientationPortrait
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/YTChatDemo/Supporting Files/Message.h:
--------------------------------------------------------------------------------
1 | //
2 | // Message.h
3 | // YTChatDemo
4 | //
5 | // Created by TI on 15/9/2.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | typedef NS_ENUM(NSInteger, MessageType) {
13 | MessageTypeMe = 0,
14 | MessageTypeOther
15 | };
16 |
17 | @interface Message : NSManagedObject
18 |
19 | @property (nonatomic, retain) NSString * content;
20 | @property (nonatomic, retain) NSNumber * objID;
21 | @property (nonatomic, retain) NSNumber * type;
22 |
23 | @end
24 |
--------------------------------------------------------------------------------
/YTChatDemo/Supporting Files/Message.m:
--------------------------------------------------------------------------------
1 | //
2 | // Message.m
3 | // YTChatDemo
4 | //
5 | // Created by TI on 15/9/2.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import "Message.h"
10 |
11 |
12 | @implementation Message
13 |
14 | @dynamic content;
15 | @dynamic objID;
16 | @dynamic type;
17 |
18 | @end
19 |
--------------------------------------------------------------------------------
/YTChatDemo/Supporting Files/YTChatDemo.xcdatamodeld/.xccurrentversion:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | _XCCurrentVersionName
6 | YTChatDemo.xcdatamodel
7 |
8 |
9 |
--------------------------------------------------------------------------------
/YTChatDemo/Supporting Files/YTChatDemo.xcdatamodeld/YTChatDemo.xcdatamodel/contents:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/YTChatDemo/Supporting Files/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // YTChatDemo
4 | //
5 | // Created by TI on 15/8/24.
6 | // Copyright (c) 2015年 YccTime. 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 |
--------------------------------------------------------------------------------
/YTChatDemo/Tools/Catgray/NSDictionary+YTSafe.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSDictionary+YTSafe.h
3 | // YTChatDemo
4 | //
5 | // Created by TI on 15/8/25.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface NSDictionary (YTSafe)
12 | /* 顾名思义,不解释 */
13 | - (id)safeValueForKey:(NSString *)key;
14 | - (NSDictionary*)safeDictForKey:(NSString *)key;
15 | - (NSArray*)safeArrayForKey:(NSString *)key;
16 | - (NSInteger)safeIntegerValueForKey:(NSString *)key;
17 | - (float)safeFloatValueForKey:(NSString *)key;
18 | - (NSString*)safeStringValueForKey:(NSString *)key;
19 | - (BOOL)safeBoolValueForKey:(NSString*)key;
20 | @end
21 |
--------------------------------------------------------------------------------
/YTChatDemo/Tools/Catgray/NSDictionary+YTSafe.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSDictionary+YTSafe.m
3 | // YTChatDemo
4 | //
5 | // Created by TI on 15/8/25.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import "NSDictionary+YTSafe.h"
10 |
11 | @implementation NSDictionary (YTSafe)
12 |
13 | - (id)safeValueForKey:(NSString *)key {
14 | id obj = [self objectForKey:key];
15 | if([self isEmpty:obj]) {
16 | return @"";
17 | } else if([obj isKindOfClass:[NSNumber class]]) {
18 | return [(NSNumber *)obj stringValue];
19 | }
20 | return obj;
21 | }
22 |
23 | - (NSDictionary *)safeDictForKey:(NSString *)key {
24 | id obj = [self objectForKey:key];
25 | if([self isEmpty:obj]) {
26 | return @{};
27 | } else if(![obj isKindOfClass:[NSDictionary class]]) {
28 | return @{};
29 | }
30 | return obj;
31 | }
32 |
33 | - (NSArray *)safeArrayForKey:(NSString *)key {
34 | id obj = [self objectForKey:key];
35 | if([self isEmpty:obj]) {
36 | return @[];
37 | }else if(![obj isKindOfClass:[NSArray class]]) {
38 | return @[];
39 | }
40 | return obj;
41 | }
42 |
43 | - (NSInteger)safeIntegerValueForKey:(NSString *)key {
44 | id obj = [self objectForKey:key];
45 | if ([self isEmpty:obj]) {
46 | return 0;
47 | }else if (![obj isKindOfClass:[NSNumber class]]) {
48 | if ([obj isKindOfClass:[NSString class]]) {
49 | return [obj integerValue];
50 | } else {
51 | return 0;
52 | }
53 | }
54 | return [obj integerValue];
55 | }
56 |
57 | - (float)safeFloatValueForKey:(NSString *)key {
58 | id obj = [self objectForKey:key];
59 | if ([self isEmpty:obj]) {
60 | return 0.0f;
61 | }else if (![obj isKindOfClass:[NSNumber class]]) {
62 | if ([obj isKindOfClass:[NSString class]]) {
63 | return [obj floatValue];
64 | } else {
65 | return 0.0f;
66 | }
67 | }
68 | return [obj floatValue];
69 | }
70 |
71 | - (NSString *)safeStringValueForKey:(NSString *)key {
72 | id obj = [self objectForKey:key];
73 | if ([self isEmpty:obj]) {
74 | return @"";
75 | }
76 | if ([obj isKindOfClass:[NSNumber class]]) {
77 | return [obj stringValue];
78 | }
79 | if (![obj isKindOfClass:[NSString class]]) {
80 | return @"";
81 | }
82 | if ([@"null" isEqualToString:obj]) {
83 | return @"";
84 | }
85 | return obj;
86 | }
87 |
88 | - (BOOL)safeBoolValueForKey:(NSString *)key{
89 | id obj = [self objectForKey:key];
90 | if ([self isEmpty:obj]) {
91 | return NO;
92 | }
93 | if (![obj isKindOfClass:[NSNumber class]]) {
94 | if ([obj isKindOfClass:[NSString class]]) {
95 | return [obj boolValue];
96 | } else {
97 | return NO;
98 | }
99 | }
100 | return [obj boolValue];
101 | }
102 |
103 | - (BOOL)isEmpty:(id)obj{
104 | return ((!obj)||([obj isKindOfClass:[NSNull class]]));
105 | }
106 |
107 | @end
108 |
--------------------------------------------------------------------------------
/YTChatDemo/Tools/Catgray/UIColor+YTBacal.h:
--------------------------------------------------------------------------------
1 | //
2 | // UIColor+YTBacal.h
3 | // YTChatDemo
4 | //
5 | // Created by TI on 15/8/31.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface UIColor (YTBacal)
12 |
13 | + (UIColor*)colorR:(CGFloat)r G:(CGFloat)g B:(CGFloat)b A:(CGFloat)a;
14 |
15 | + (UIColor*)colorR:(CGFloat)r G:(CGFloat)g B:(CGFloat)b;
16 |
17 | //#95a5a6灰
18 | + (UIColor *)C_95A5A6;
19 |
20 | //#ecf0f1白雾色
21 | + (UIColor *)C_ECF0F1;
22 |
23 | //#dddddd浅灰
24 | + (UIColor *)C_DDDDDD;
25 |
26 | //#a9a9a9深灰
27 | + (UIColor *)C_A9A9A9;
28 |
29 | //#fcfcfc浅白
30 | + (UIColor *)C_FCFCFC;
31 |
32 | //#f9f7f7灰白
33 | + (UIColor *)C_F9F7F9;
34 |
35 | @end
36 |
--------------------------------------------------------------------------------
/YTChatDemo/Tools/Catgray/UIColor+YTBacal.m:
--------------------------------------------------------------------------------
1 | //
2 | // UIColor+YTBacal.m
3 | // YTChatDemo
4 | //
5 | // Created by TI on 15/8/31.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import "UIColor+YTBacal.h"
10 |
11 | @implementation UIColor (YTBacal)
12 |
13 | + (UIColor *)colorR:(CGFloat)r G:(CGFloat)g B:(CGFloat)b A:(CGFloat)a{
14 | return [self colorWithRed:r/255.0f green:g/255.0f blue:b/255.0f alpha:a];
15 | }
16 |
17 | + (UIColor *)colorR:(CGFloat)r G:(CGFloat)g B:(CGFloat)b{
18 | return [self colorR:r G:g B:b A:1.0f];
19 | }
20 |
21 | + (UIColor *)C_95A5A6{//灰
22 | return [self colorR:149 G:165 B:166];
23 | }
24 |
25 | + (UIColor *)C_ECF0F1{//白雾色
26 | return [self colorR:236 G:240 B:241];
27 | }
28 |
29 | + (UIColor *)C_DDDDDD{//浅灰
30 | return [self colorR:221 G:221 B:221];
31 | }
32 |
33 | + (UIColor *)C_A9A9A9{//深灰
34 | return [self colorR:169 G:169 B:169];
35 | }
36 |
37 | + (UIColor *)C_FCFCFC{//浅白
38 | return [self colorR:252 G:252 B:252];
39 | }
40 |
41 | + (UIColor *)C_F9F7F9{//灰白
42 | return [self colorR:249 G:247 B:247];
43 | }
44 |
45 | @end
46 |
--------------------------------------------------------------------------------
/YTChatDemo/Tools/Catgray/UIColor+YTKeyBoard.h:
--------------------------------------------------------------------------------
1 | //
2 | // UIColor+YTKeyBoard.h
3 | // YTChatDemo
4 | //
5 | // Created by TI on 15/8/31.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "UIColor+YTBacal.h"
11 |
12 | @interface UIColor (YTKeyBoard)
13 |
14 | @end
15 |
--------------------------------------------------------------------------------
/YTChatDemo/Tools/Catgray/UIColor+YTKeyBoard.m:
--------------------------------------------------------------------------------
1 | //
2 | // UIColor+YTKeyBoard.m
3 | // YTChatDemo
4 | //
5 | // Created by TI on 15/8/31.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import "UIColor+YTKeyBoard.h"
10 |
11 | @implementation UIColor (YTKeyBoard)
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/YTChatDemo/Tools/Catgray/UIImage+YTGif.h:
--------------------------------------------------------------------------------
1 | //
2 | // UIImage+YTGif.h
3 | // YTChatDemo
4 | //
5 | // Created by TI on 15/9/6.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface UIImage (YTGif)
12 | /** 根据名字去取gif图片,有可能返回PNG类型image及nil */
13 | + (UIImage *)gifImageNamed:(NSString *)name;
14 |
15 | /** 根据data去取gif图片,有可能返回PNG类型image及nil */
16 | + (UIImage *)gifImageWithData:(NSData *)data;
17 | @end
18 |
--------------------------------------------------------------------------------
/YTChatDemo/Tools/Catgray/UIImage+YTGif.m:
--------------------------------------------------------------------------------
1 | //
2 | // UIImage+YTGif.m
3 | // YTChatDemo
4 | //
5 | // Created by TI on 15/9/6.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import "UIImage+YTGif.h"
10 | @import ImageIO;
11 |
12 | @implementation UIImage (YTGif)
13 |
14 | + (UIImage *)gifImageNamed:(NSString *)name{
15 | NSString *gifPath = [[NSBundle mainBundle] pathForResource:[name stringByAppendingString:@"@2x"] ofType:@"gif"];
16 | if (!gifPath) {
17 | gifPath = [[NSBundle mainBundle] pathForResource:name ofType:@"gif"];
18 | }
19 | NSData *gifData = [NSData dataWithContentsOfFile:gifPath];
20 | if (gifData) {
21 | return [self gifImageWithData:gifData];
22 | }else {
23 | return [self imageNamed:name];
24 | }
25 | }
26 |
27 | + (UIImage *)gifImageWithData:(NSData *)data{
28 | if (!data) return nil;
29 | CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
30 | size_t count = CGImageSourceGetCount(source);
31 | UIImage *gifImage = nil;
32 | if (count <= 1) {
33 | gifImage = [self imageWithData:data];
34 | }else{
35 | NSMutableArray *images = [NSMutableArray arrayWithCapacity:count];
36 | for (size_t index = 0; index < count; index++) {
37 | CGImageRef imageRef = CGImageSourceCreateImageAtIndex(source, index, NULL);
38 | [images addObject:[UIImage imageWithCGImage:imageRef]];
39 | CGImageRelease(imageRef);
40 | }
41 | CGFloat delayTime = [self durtaionAtIndex:0 source:source];
42 | NSTimeInterval duration = delayTime*count;
43 | gifImage = [self animatedImageWithImages:images duration:duration];
44 | }
45 | CFRelease(source);
46 | return gifImage;
47 | }
48 |
49 | // 取出指定位置图片播放时长
50 | + (CGFloat)durtaionAtIndex:(size_t)index source:(CGImageSourceRef)source{
51 | CGFloat dealyTime = 0.0f;
52 | CFDictionaryRef sourceProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil);
53 | NSDictionary *dicProperties = (__bridge NSDictionary *)sourceProperties;
54 | NSDictionary *gifProperties = dicProperties[(NSString *)kCGImagePropertyGIFDictionary];
55 | NSNumber *delayTimeUnclamped = gifProperties[(NSString *)kCGImagePropertyGIFUnclampedDelayTime];
56 |
57 | if (delayTimeUnclamped) {
58 | dealyTime = [delayTimeUnclamped floatValue];
59 | }else {
60 | NSNumber *delayTimeProp = gifProperties[(NSString *)kCGImagePropertyGIFDelayTime];
61 | if (dealyTime) {
62 | dealyTime = [delayTimeProp floatValue];
63 | }
64 | }
65 | if (dealyTime <= 0.01) dealyTime = 0.1f;
66 | CFRelease(sourceProperties);
67 | return dealyTime;
68 | }
69 |
70 | @end
71 |
--------------------------------------------------------------------------------
/YTChatDemo/Tools/Catgray/UIView+YTLayer.h:
--------------------------------------------------------------------------------
1 | //
2 | // UIView+YTLayer.h
3 | // YTChatDemo
4 | //
5 | // Created by TI on 15/8/26.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface UIView (YTLayer)
12 |
13 | /* 指定圆角大小处理 */
14 | - (void)cornerRadius:(CGFloat)radius;
15 |
16 | /* 添加border */
17 | - (void)borderWithColor:(UIColor *)borderColor borderWidth:(CGFloat)borderWidth;
18 |
19 | /* 指定圆角大小,且带border */
20 | - (void)cornerRadius:(CGFloat)radius borderColor:(UIColor*)borderColor borderWidth:(CGFloat)borderWidth;
21 |
22 | /* 对UIView的四个角进行选择性的圆角处理 */
23 | - (void)makeRoundedCorner:(UIRectCorner)byRoundingCorners cornerRadii:(CGSize)cornerRadii;
24 | @end
25 |
--------------------------------------------------------------------------------
/YTChatDemo/Tools/Catgray/UIView+YTLayer.m:
--------------------------------------------------------------------------------
1 | //
2 | // UIView+YTLayer.m
3 | // YTChatDemo
4 | //
5 | // Created by TI on 15/8/26.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import "UIView+YTLayer.h"
10 |
11 | @implementation UIView (YTLayer)
12 |
13 | - (void)cornerRadius:(CGFloat)radius {
14 | self.layer.masksToBounds = YES;
15 | self.layer.cornerRadius = radius;
16 | }
17 |
18 | - (void)borderWithColor:(UIColor *)borderColor borderWidth:(CGFloat)borderWidth {
19 | self.layer.borderWidth = borderWidth;
20 | self.layer.borderColor = borderColor.CGColor;
21 | }
22 |
23 | - (void)cornerRadius:(CGFloat)radius borderColor:(UIColor *)borderColor borderWidth:(CGFloat)borderWidth {
24 | self.layer.masksToBounds = YES;
25 | self.layer.cornerRadius = radius;
26 | self.layer.borderWidth = borderWidth;
27 | self.layer.borderColor = borderColor.CGColor;
28 | }
29 |
30 | - (void)makeRoundedCorner:(UIRectCorner)byRoundingCorners cornerRadii:(CGSize)cornerRadii {
31 | UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:byRoundingCorners cornerRadii:cornerRadii];
32 | CAShapeLayer * shape = [CAShapeLayer layer];
33 | shape.path = path.CGPath;
34 | self.layer.mask = shape;
35 | }
36 |
37 | @end
38 |
--------------------------------------------------------------------------------
/YTChatDemo/Tools/coreData/NSManagedObject+YTMoreThread.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSManagedObject+YTMoreThread.h
3 | // YTChatDemo
4 | //
5 | // Created by TI on 15/9/2.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 | // 该类与YTCoreData.h结合使用,很好地解决啦多线程访问coredata问题。
9 |
10 | #import
11 | #import "YTCoreData.h"
12 |
13 | #define YTDBA [YTCoreData instance]
14 |
15 | typedef void(^Finished)(BOOL finished);
16 | typedef void(^Request)(NSFetchRequest *request);
17 | typedef void(^ListResult)(NSArray* result, NSError *error);
18 | typedef void(^ObjectResult)(id result, NSError *error);
19 | typedef id(^AsyncProcess)(NSManagedObjectContext *ctx, NSFetchRequest *request);
20 |
21 | @interface NSManagedObject (YTMoreThread)
22 | /**创建一个当前对象*/
23 | + (id)createNew;
24 |
25 | /**保存同步*/
26 | + (void)save:(OperationResult)handler;
27 |
28 | /*
29 | ** predicate -> NSPredicate 字符串标示 查询条件
30 | ** orders -> 存放多个NSString 如果需要降序:字符串前加"-"
31 | ** offset -> 数据偏移量 位置标示
32 | ** limit -> 取出多少条数据
33 | ** filter -> 数据(多个“NSManagedObject”类对象)
34 | */
35 | + (NSArray *)filter:(NSString *)predicate orderby:(NSArray *)orders offset:(int)offset limit:(int)limit;
36 |
37 | /**
38 | ** 工能同上 -> 采用回调方法 handler 参数result = filter(up)
39 | */
40 | + (void)filter:(NSString *)predicate orderby:(NSArray *)orders offset:(int)offset limit:(int)limit on:(ListResult)handler;
41 |
42 | /**根据查询条件取出一个"NSManagedObject"对象*/
43 | + (id)one:(NSString *)predicate;
44 |
45 | /**根据查询条件取出一个"NSManagedObject"对象*/
46 | + (void)one:(NSString *)predicate on:(ObjectResult)handler;
47 |
48 | /**根据查询条件取所有符合条件的"NSManagedObject"对象*/
49 | + (void)removeAllObj:(NSString *)predicate finished:(Finished)finished;
50 |
51 | /**删除一个"NSManagedObject"类对象*/
52 | + (void)delobject:(id)object;
53 |
54 | /**自定义条件取出多个“NSManagedObject”类对象 - 回调方法*/
55 | + (void)async:(AsyncProcess)processBlock result:(ListResult)resultBlock;
56 |
57 | /**谓语查询数据库包含此对象数量*/
58 | + (NSUInteger)count:(NSString *)predicate;
59 |
60 | /**ios8新方法*/
61 | + (void)asyncRequest:(Request)requestBlock result:(ListResult)resultBlock;
62 | @end
63 |
--------------------------------------------------------------------------------
/YTChatDemo/Tools/coreData/NSManagedObject+YTMoreThread.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSManagedObject+YTMoreThread.m
3 | // YTChatDemo
4 | //
5 | // Created by TI on 15/9/2.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import "NSManagedObject+YTMoreThread.h"
10 |
11 | typedef void (^ObjectForId)(NSArray * result);
12 |
13 | @implementation NSManagedObject (YTMoreThread)
14 |
15 | + (id)createNew{
16 | NSString *className = [NSString stringWithUTF8String:object_getClassName(self)];
17 | return [NSEntityDescription insertNewObjectForEntityForName:className inManagedObjectContext:YTDBA.mainObjectContext];
18 | }
19 |
20 | + (void)save:(OperationResult)handler{
21 | [YTDBA save:handler];
22 | }
23 |
24 | + (NSArray *)filter:(NSString *)predicate orderby:(NSArray *)orders offset:(int)offset limit:(int)limit{
25 | NSManagedObjectContext *ctx = YTDBA.mainObjectContext;
26 | NSFetchRequest *fetchRequest = [self makeRequest:ctx predicate:predicate orderby:orders offset:offset limit:limit];
27 |
28 | NSError* error = nil;
29 | NSArray* results = [ctx executeFetchRequest:fetchRequest error:&error];
30 | if (error) {
31 | NSLog(@"error: %@", error);
32 | return @[];
33 | }
34 | return results;
35 | }
36 |
37 | + (NSFetchRequest *)makeRequest:(NSManagedObjectContext *)ctx predicate:(NSString *)predicate orderby:(NSArray *)orders offset:(int)offset limit:(int)limit{
38 | NSString *className = [NSString stringWithUTF8String:object_getClassName(self)];
39 | NSFetchRequest *fetchRequest = [[NSFetchRequest alloc]init];
40 | [fetchRequest setEntity:[NSEntityDescription entityForName:className inManagedObjectContext:ctx]];
41 |
42 | if (predicate) {
43 | [fetchRequest setPredicate:[NSPredicate predicateWithFormat:predicate]];
44 | }
45 |
46 | NSMutableArray *orderArray = [[NSMutableArray alloc] init];
47 | if (orders&&orders.count>0) {
48 | for (NSString *order in orders) {
49 | NSSortDescriptor *orderDesc = nil;
50 | if ([[order substringToIndex:1] isEqualToString:@"-"]) {
51 | orderDesc = [[NSSortDescriptor alloc] initWithKey:[order substringFromIndex:1]
52 | ascending:NO];
53 | }else{
54 | orderDesc = [[NSSortDescriptor alloc] initWithKey:order
55 | ascending:YES];
56 | }
57 | [orderArray addObject:orderDesc];
58 | }
59 | [fetchRequest setSortDescriptors:orderArray];
60 | }
61 |
62 | if (offset>0) {
63 | [fetchRequest setFetchOffset:offset];
64 | }
65 | if (limit>0) {
66 | [fetchRequest setFetchLimit:limit];
67 | }
68 | return fetchRequest;
69 | }
70 |
71 | + (void)filter:(NSString *)predicate orderby:(NSArray *)orders offset:(int)offset limit:(int)limit on:(ListResult)handler{
72 | NSManagedObjectContext *ctx = [YTDBA createPrivateObjectContext];
73 |
74 | [ctx performBlock:^{
75 | NSFetchRequest *fetchRequest = [self makeRequest:ctx predicate:predicate orderby:orders offset:offset limit:limit];
76 | NSError *error = nil;
77 | NSArray *results = [ctx executeFetchRequest:fetchRequest error:&error];
78 | if (results&&results.count>0) {
79 | [[self class]objIds:results result:^(NSArray *result) {
80 | if (handler) handler(results, nil);
81 | }];
82 | }else{
83 | [YTDBA.mainObjectContext performBlock:^{
84 | if (handler) handler(@[], error);
85 | }];
86 | }
87 | }];
88 | }
89 |
90 |
91 | + (id)one:(NSString *)predicate{
92 | NSManagedObjectContext *ctx = YTDBA.mainObjectContext;
93 | NSFetchRequest *fetchRequest = [self makeRequest:ctx predicate:predicate orderby:nil offset:0 limit:1];
94 |
95 | NSError *error = nil;
96 | NSArray *results = [ctx executeFetchRequest:fetchRequest error:&error];
97 | if ([results count]!=1) {
98 | return nil;
99 | //raise(1);
100 | }
101 | return results[0];
102 | }
103 |
104 | + (void)one:(NSString *)predicate on:(ObjectResult)handler{
105 | NSManagedObjectContext *ctx = [YTDBA createPrivateObjectContext];
106 |
107 | [ctx performBlock:^{
108 | NSFetchRequest *fetchRequest = [self makeRequest:ctx predicate:predicate orderby:nil offset:0 limit:1];
109 | NSError *error = nil;
110 | NSArray *results = [ctx executeFetchRequest:fetchRequest error:&error];
111 | id obj = nil;
112 | if (results&&results.count>0) {
113 | NSManagedObjectID *objId = ((NSManagedObject*)results[0]).objectID;
114 | obj = [YTDBA.mainObjectContext objectWithID:objId];
115 | }
116 | [YTDBA.mainObjectContext performBlock:^{
117 | if (handler) handler(obj, error);
118 | }];
119 | }];
120 | }
121 |
122 | + (void)removeAllObj:(NSString *)predicate finished:(Finished)finished{
123 | [self filter:predicate orderby:nil offset:0 limit:0 on:^(NSArray *result, NSError *error) {
124 |
125 | for (NSManagedObject *obj in result) {
126 | NSManagedObject *safeobject = [YTDBA.mainObjectContext objectWithID:obj.objectID];
127 | [self delobject:safeobject];
128 | }
129 |
130 | dispatch_async(dispatch_get_main_queue(), ^{
131 | [YTDBA save:^(NSError *error) {}];
132 | finished(YES);
133 |
134 | });
135 | }];
136 | }
137 |
138 | + (void)delobject:(id)object{
139 | [YTDBA.mainObjectContext deleteObject:object];
140 | }
141 |
142 | + (void)async:(AsyncProcess)processBlock result:(ListResult)resultBlock{
143 | if (!processBlock) return ;
144 | NSString *className = [NSString stringWithUTF8String:object_getClassName(self)];
145 | NSFetchRequest *request = [[NSFetchRequest alloc]initWithEntityName:className];
146 | NSManagedObjectContext *ctx = [YTDBA createPrivateObjectContext];
147 |
148 | [ctx performBlock:^{
149 | id resultList = processBlock(ctx, request);
150 | if (resultList&&[resultList isKindOfClass:[NSArray class]]) {
151 | [[self class]objIds:resultList result:^(NSArray *result) {
152 | if (resultBlock) resultBlock(result, nil);
153 | }];
154 | }else{
155 | if (resultBlock) resultBlock(@[], nil);
156 | }
157 | }];
158 | }
159 |
160 | + (void)objIds:(NSArray *)Ids result:(ObjectForId)objHander{
161 | NSMutableArray *idArray = [NSMutableArray array];
162 | for (NSManagedObject *obj in Ids) {
163 | [idArray addObject:obj.objectID];
164 | }
165 |
166 | [YTDBA.mainObjectContext performBlock:^{
167 | NSMutableArray *objArray = [NSMutableArray array];
168 | for (NSManagedObjectID *robjId in idArray) {
169 | [objArray addObject:[YTDBA.mainObjectContext objectWithID:robjId]];
170 | }
171 | if (objHander) objHander([objArray copy]);
172 | }];
173 | }
174 |
175 | + (NSUInteger)count:(NSString *)predicate{
176 | NSString *className = [NSString stringWithUTF8String:object_getClassName(self)];
177 | NSFetchRequest *request = [[NSFetchRequest alloc]initWithEntityName:className];
178 | [request setPredicate:[NSPredicate predicateWithFormat:predicate]];
179 | return [YTDBA.mainObjectContext countForFetchRequest:request error:nil];
180 | }
181 |
182 | + (void)asyncRequest:(Request)request result:(ListResult)resultBlock{
183 | [YTDBA.createPrivateObjectContext performBlock:^{
184 | NSString *className = [NSString stringWithUTF8String:object_getClassName(self)];
185 | NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:className];
186 | if (request) request(fetchRequest);
187 | NSAsynchronousFetchRequest *requ = [[NSAsynchronousFetchRequest alloc]initWithFetchRequest:fetchRequest completionBlock:^(NSAsynchronousFetchResult *result) {
188 | [[self class] objIds:result.finalResult result:^(NSArray *result) {
189 | if (resultBlock) resultBlock(result,nil);
190 | }];
191 | }];
192 | [YTDBA.mainObjectContext executeRequest:requ error:nil];
193 | }];
194 | }
195 |
196 | @end
197 |
--------------------------------------------------------------------------------
/YTChatDemo/Tools/coreData/YTCoreData.h:
--------------------------------------------------------------------------------
1 | //
2 | // YTCoreData.h
3 | // YTChatDemo
4 | //
5 | // Created by TI on 15/9/2.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | typedef void(^OperationResult)(NSError* error);
13 |
14 | @interface YTCoreData : NSObject
15 | @property (readonly, strong, nonatomic) NSOperationQueue *queue;
16 | @property (readonly ,strong, nonatomic) NSManagedObjectContext *privateObjectContext;
17 | @property (readonly, strong, nonatomic) NSManagedObjectContext *mainObjectContext;
18 |
19 | /**单例模式*/
20 | + (YTCoreData *)instance;
21 |
22 | /**创建一个队列context 做异步处理 -> 主要怎对线程和大量数据*/
23 | - (NSManagedObjectContext *)createPrivateObjectContext;
24 |
25 | /**保存同步coreData*/
26 | - (void)save:(OperationResult)handler;
27 | @end
28 |
--------------------------------------------------------------------------------
/YTChatDemo/Tools/coreData/YTCoreData.m:
--------------------------------------------------------------------------------
1 | //
2 | // YTCoreData.m
3 | // YTChatDemo
4 | //
5 | // Created by TI on 15/9/2.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import "YTCoreData.h"
10 |
11 |
12 | @interface YTCoreData()
13 | {
14 | NSString *_modelName;
15 | NSString *_fileName;
16 | NSManagedObjectModel *managedObjectModel;
17 | NSPersistentStoreCoordinator *persistentStoreCoordinator;
18 | }
19 | @end
20 |
21 | @implementation YTCoreData
22 |
23 | static YTCoreData * singleModel;
24 | + (YTCoreData *)instance{
25 | static dispatch_once_t onceToken;
26 | dispatch_once(&onceToken, ^{
27 | singleModel = [[YTCoreData alloc]init];
28 | });
29 | return singleModel;
30 | }
31 |
32 | - (instancetype)init{
33 | if (self = [super init]) {
34 | NSDictionary *infodic =[[NSBundle mainBundle]infoDictionary];
35 | _modelName = [infodic valueForKey:(NSString*)kCFBundleNameKey];
36 | _fileName = [_modelName stringByAppendingString:@".sqlite"];
37 | [self initCoreData];
38 | }
39 | return self;
40 | }
41 |
42 | - (void)initCoreData{
43 | _privateObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
44 | [_privateObjectContext setPersistentStoreCoordinator:[self persistentStoreCoordinator]];
45 | _mainObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
46 | [_mainObjectContext setParentContext:_privateObjectContext];
47 | }
48 |
49 | - (NSManagedObjectContext *)createPrivateObjectContext
50 | {
51 | NSManagedObjectContext *ctx = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
52 | [ctx setParentContext:_mainObjectContext];
53 | return ctx;
54 | }
55 |
56 | - (NSPersistentStoreCoordinator*)persistentStoreCoordinator{
57 | if (persistentStoreCoordinator != nil) {
58 | return persistentStoreCoordinator;
59 | }
60 | NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:_fileName];
61 | NSError *error = nil;
62 | persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
63 | if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
64 | NSLog(@"Unresolved error %@", error);
65 | //abort();
66 | }
67 | return persistentStoreCoordinator;
68 | }
69 |
70 | - (NSURL *)applicationDocumentsDirectory
71 | {
72 | return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
73 | }
74 |
75 | - (NSManagedObjectModel *)managedObjectModel
76 | {
77 | if (managedObjectModel != nil) {
78 | return managedObjectModel;
79 | }
80 | NSURL *modelURL = [[NSBundle mainBundle] URLForResource:_modelName withExtension:@"momd"];
81 | managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
82 | return managedObjectModel;
83 | }
84 |
85 | - (void)save:(OperationResult)handler{
86 | NSError *error;
87 | if ([_mainObjectContext hasChanges]) {
88 | [_mainObjectContext save:&error];
89 | [_privateObjectContext performBlock:^{
90 | __block NSError *inner_error = nil;
91 | [_privateObjectContext save:&inner_error];
92 | if (handler){
93 | [_mainObjectContext performBlock:^{
94 | handler(inner_error);
95 | }];
96 | }
97 | }];
98 | }
99 | if (error) {
100 | NSLog(@" save: error %@, %@",error,[error userInfo]);
101 | }
102 | }
103 |
104 | @end
105 |
--------------------------------------------------------------------------------
/YTChatDemo/Tools/other/YTDeviceTest.h:
--------------------------------------------------------------------------------
1 | //
2 | // YTDeviceTest.h
3 | // YTImageBrowser
4 | //
5 | // Created by TI on 15/8/24.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 | // 这是一个访问手机部分硬件是否别允许的简单封装
9 |
10 | #import
11 |
12 |
13 | @interface YTDeviceTest : NSObject
14 |
15 | /**判断相册是否被允许访问 返回YES为允许访问*/
16 | + (BOOL)userAuthorizationPhotoStatus;
17 |
18 | /**判断相机是否被允许访问 返回YES为允许访问*/
19 | + (BOOL)userAuthorizationCameraStatus;
20 |
21 | /**判断麦克风是否被允许访问 返回YES为允许访问*/
22 | + (BOOL)userAuthorizationAudioStatus;
23 |
24 | /***/
25 |
26 | @end
27 |
--------------------------------------------------------------------------------
/YTChatDemo/Tools/other/YTDeviceTest.m:
--------------------------------------------------------------------------------
1 | //
2 | // YTDeviceTest.m
3 | // YTImageBrowser
4 | //
5 | // Created by TI on 15/8/24.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import "YTDeviceTest.h"
10 | @import AssetsLibrary;
11 | @import AVFoundation;
12 | @import UIKit;
13 |
14 | @implementation YTDeviceTest
15 |
16 | + (BOOL)userAuthorizationPhotoStatus{//相册
17 | ALAuthorizationStatus statu = [ALAssetsLibrary authorizationStatus];
18 | switch (statu) {
19 | case ALAuthorizationStatusAuthorized:
20 | case ALAuthorizationStatusDenied:
21 | return YES;
22 | default:
23 | {
24 | NSString *title = @"无法访问相册";
25 | NSString *message = @"请在iPhone的“设置-隐私-照片”中允许访问您的照片";
26 | [[self class]showTitle:title message:message];
27 | return NO;
28 | }
29 | }
30 | }
31 |
32 | + (BOOL)userAuthorizationCameraStatus{//相机
33 | UIImagePickerControllerSourceType sourceType = UIImagePickerControllerSourceTypeCamera;
34 | AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
35 | switch (status) {
36 | case AVAuthorizationStatusNotDetermined:
37 | case AVAuthorizationStatusAuthorized:
38 | if ([UIImagePickerController isSourceTypeAvailable:sourceType]){
39 | return YES;
40 | }else{
41 | return NO;
42 | }
43 | default:
44 | {
45 | NSString *title = @"无法访问相机";
46 | NSString *message = @"请在iPhone的“设置-隐私-相机”中允许访问您的相机";
47 | [[self class]showTitle:title message:message];
48 | return NO;
49 | }
50 | }
51 | }
52 |
53 | + (BOOL)userAuthorizationAudioStatus{
54 | AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];
55 | switch (status) {
56 | case AVAuthorizationStatusAuthorized:
57 | return YES;
58 | case AVAuthorizationStatusDenied:
59 | case AVAuthorizationStatusRestricted:
60 | default:
61 | {
62 | NSString *title = @"无法访问麦克风";
63 | NSString *message = @"请在iPhone的“设置-隐私-麦克风”中允许访问您的麦克风";
64 | [[self class]showTitle:title message:message];
65 | }
66 | case AVAuthorizationStatusNotDetermined:
67 | return NO;
68 | }
69 | }
70 |
71 | + (void)showTitle:(NSString *)title message:(NSString *)message{
72 | UIAlertView * alertView = [[UIAlertView alloc]initWithTitle:title message:message delegate:nil cancelButtonTitle:nil otherButtonTitles:@"确定", nil];
73 | [alertView show];
74 | }
75 |
76 | @end
77 |
--------------------------------------------------------------------------------
/YTChatDemo/Tools/other/YTNetworkLoad.h:
--------------------------------------------------------------------------------
1 | //
2 | // YTNetworkLoad.h
3 | // YTImageBrowser
4 | //
5 | // Created by TI on 15/8/24.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 | // 轻量级网络数据请求,没法和那些第三方比。若不放心,建议替换。
9 |
10 | #import
11 |
12 | typedef void (^progressBlock)(float progress);
13 |
14 | @interface YTNetworkLoad : NSOperation
15 |
16 | @property (nonatomic, copy) progressBlock updataBlock;
17 |
18 | - (instancetype)initWithUrl:(NSURL*)url success:(void (^)(NSURLRequest *request, id data))success failure:(void (^)(NSError *error))failure;
19 |
20 | - (void)cancel;
21 |
22 | @end
23 |
--------------------------------------------------------------------------------
/YTChatDemo/Tools/other/YTNetworkLoad.m:
--------------------------------------------------------------------------------
1 | //
2 | // YTNetworkLoad.m
3 | // YTImageBrowser
4 | //
5 | // Created by TI on 15/8/24.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import "YTNetworkLoad.h"
10 | @import UIKit;
11 |
12 | typedef void (^block)();
13 |
14 | @interface YTNetworkLoad(){
15 | float totalLength;
16 | BOOL isSuccess;
17 | }
18 |
19 | @property (nonatomic, strong) NSURLConnection *connection;
20 | @property (nonatomic, strong) NSMutableData *dataSource;
21 | @property (nonatomic, strong) NSError *error;
22 | @property (nonatomic, copy) block result_block;
23 | @property (nonatomic, assign) float changeLength;
24 | @end
25 |
26 | @implementation YTNetworkLoad
27 |
28 | - (instancetype)initWithUrl:(NSURL *)url success:(void (^)(NSURLRequest *, id))success failure:(void (^)(NSError *))failure{
29 | if (self = [super init]) {
30 | [self beginLoadUrl:url success:success failure:failure];
31 | }
32 | return self;
33 | }
34 |
35 | -(void)dealloc{
36 | [self cancel];
37 | }
38 |
39 | - (void)beginLoadUrl:(NSURL *)url success:(void (^)(NSURLRequest *,id))success failure:(void (^)(NSError *))failure{
40 | NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
41 | request.networkServiceType = NSURLNetworkServiceTypeBackground;
42 | self.connection = [[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:NO];
43 | [self.connection setDelegateQueue:[NSOperationQueue currentQueue]];
44 |
45 | __weak __typeof (self)weekself = self;
46 | _result_block = ^(){
47 | if (isSuccess == YES) {
48 | success(weekself.connection.currentRequest,weekself.dataSource);
49 | }else{
50 | failure(weekself.error);
51 | }
52 | };
53 | [self.connection start];
54 | }
55 |
56 | - (void)cancel{
57 | [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
58 | [self.connection setDelegateQueue:nil];
59 | [self.connection cancel];
60 | self.connection = nil;
61 | }
62 |
63 | #define mark - Connection Delegate
64 | - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
65 | [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
66 | totalLength = MAX([response expectedContentLength], 1);
67 | self.dataSource = [NSMutableData data];
68 | }
69 |
70 | - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
71 | _changeLength += [data length];
72 | [self.dataSource appendData:data];
73 | if ((_changeLength < totalLength)&&_updataBlock) {
74 | _updataBlock(_changeLength/totalLength);
75 | }
76 | }
77 |
78 | - (void)connectionDidFinishLoading:(NSURLConnection *)connection{
79 | isSuccess = YES;
80 | [self resultBlock];
81 | }
82 |
83 | - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
84 | _error = error;
85 | [self resultBlock];
86 | }
87 |
88 | - (void)resultBlock{
89 | if (_result_block) {
90 | _result_block();
91 | }
92 | [self cancel];
93 | }
94 |
95 | @end
96 |
--------------------------------------------------------------------------------
/YTChatDemoTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | com.YccTime.app.$(PRODUCT_NAME:rfc1034identifier)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/YTChatDemoTests/YTChatDemoTests.m:
--------------------------------------------------------------------------------
1 | //
2 | // YTChatDemoTests.m
3 | // YTChatDemoTests
4 | //
5 | // Created by TI on 15/8/24.
6 | // Copyright (c) 2015年 YccTime. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | @interface YTChatDemoTests : XCTestCase
13 |
14 | @end
15 |
16 | @implementation YTChatDemoTests
17 |
18 | - (void)setUp {
19 | [super setUp];
20 | // Put setup code here. This method is called before the invocation of each test method in the class.
21 | }
22 |
23 | - (void)tearDown {
24 | // Put teardown code here. This method is called after the invocation of each test method in the class.
25 | [super tearDown];
26 | }
27 |
28 | - (void)testExample {
29 | // This is an example of a functional test case.
30 | XCTAssert(YES, @"Pass");
31 | }
32 |
33 | - (void)testPerformanceExample {
34 | // This is an example of a performance test case.
35 | [self measureBlock:^{
36 | // Put the code you want to measure the time of here.
37 | }];
38 | }
39 |
40 | @end
41 |
--------------------------------------------------------------------------------