Compare commits

...

771 Commits

Author SHA1 Message Date
Christine Corbett
26fb5cbcf4 groups and calls have optional avatar; consumer must specify that it should be used and provide images in project of naming convention. otherwise no image will be used 2015-01-27 22:53:25 -10:00
Christine Corbett
c8262eee36 calls and group update messages now blue styled in correct font 2015-01-25 19:10:42 -10:00
Joyce Yan
184f53b07b Design tweaks based on @abolishme's work. 2015-01-24 08:31:23 -10:00
dtsbourg
ed9858bd6d revert b73b5d 2014-12-29 23:48:01 +01:00
dtsbourg
9dffc05aa7 Merge branch 'FredericJacobs-JSignalQ' into JSignalQ 2014-12-29 22:07:24 +01:00
Frederic Jacobs
cb5c4facbc Removing unused properties + bug fix call label 2014-12-29 21:56:58 +01:00
dtsbourg
84d1b659f3 Typo fix 2014-12-29 21:21:19 +01:00
dtsbourg
b73b5d9983 Performance enhancement for collection view reloading 2014-12-29 19:25:29 +01:00
dtsbourg
6a30411791 Merge remote-tracking branch 'jsq/master' into JSignalQ-6.1.1 2014-12-23 18:04:39 +01:00
Jesse Squires
395b1342a9 unit tests and refinements 2014-12-19 17:41:19 -05:00
Jesse Squires
ea3b8cc6fc bump version numbers 2014-12-19 16:44:57 -05:00
Jesse Squires
697c839c80 clean up from PR #644 code review 2014-12-19 16:39:26 -05:00
Jesse Squires
d6c9dd0ce9 Merge pull request #644 from evilstudios/develop
fix #480: scrollToBottomAnimated: fix long message handling
2014-12-19 16:13:44 -05:00
Jesse Squires
16c42a11e5 fix #648. fix #646. close #649. 2014-12-19 16:03:56 -05:00
Jesse Squires
9980266b62 address code review comments from PR #646, ref #645, #314, #640 2014-12-19 15:42:21 -05:00
Jesse Squires
38c14c19e7 Merge pull request #646 from evilstudios/keyboard-notification-fix
Fix #645: add 'keyboardDidHide' notification handling
2014-12-19 15:33:04 -05:00
Jesse Squires
736d110eba Update README.md 2014-12-14 18:16:49 -05:00
Jesse Squires
7c57677738 fix keyboard dismissal and firstResponder bugs. close #640. close #510. 2014-12-14 16:12:07 -05:00
Jesse Squires
69e2518345 add assert to didPressAccessoryButton: 2014-12-13 17:35:05 -05:00
Jesse Squires
3a50f7f9c2 clean up from PR #651. update docs. 2014-12-13 17:34:03 -05:00
Jesse Squires
6e4857ab8d Merge pull request #651 from deepakraj89/Fix_ScrollAnimation
Fix for Scroll Animation (close #515)
2014-12-13 17:13:19 -05:00
Jesse Squires
260e51ce2c update README for customizing toolbar buttons. close #669 2014-12-13 17:10:54 -05:00
Jesse Squires
a81bfa2277 Merge branch 'develop' of https://github.com/jessesquires/JSQMessagesViewController into develop 2014-12-13 16:57:02 -05:00
Jesse Squires
847f49e6e7 add button customization comments to demo. #669 2014-12-13 16:56:53 -05:00
dtsbourg
8703ce020c JSQDisplayedMessage: implement tap delegate 2014-12-10 21:45:29 +01:00
Jesse Squires
7d8cd7b867 Merge pull request #673 from ranunez/develop
Updated link in README for updated iOS 6 adoption stats
2014-12-09 15:37:59 -08:00
Ricardo Nunez
8d9f241038 Updated link in README for updated iOS 6 adoption stats 2014-12-09 15:16:40 -08:00
dtsbourg
fcbbfdc43f Fix crash on long tap on error and info messages 2014-12-09 22:37:56 +01:00
Jesse Squires
66fb14161d Update README.md 2014-12-08 00:29:00 -08:00
dtsbourg
bc976a04d9 Cells: change isKindOfClass to messageType check 2014-12-07 14:42:27 +01:00
Jesse Squires
4ab13ad7ac Merge branch 'develop' of https://github.com/jessesquires/JSQMessagesViewController into develop 2014-12-07 01:58:34 -08:00
Jesse Squires
d5f8d4291b proj settings 2014-12-06 17:09:40 -08:00
dtsbourg
43f6e75bbf Set adapter type as property 2014-12-06 18:23:03 +01:00
dtsbourg
59ea2b6920 Revert to TSMessageAdapterType for naming to avoid conflicts 2014-12-06 16:57:31 +01:00
dtsbourg
dee758a3ff TSMessageType added to JSQMessageData 2014-12-06 16:52:16 +01:00
Jesse Squires
17153bdd38 add migration section to README. close #663 2014-12-04 22:19:25 -08:00
dtsbourg
82482a1fc5 Remove call button on toolbar 2014-12-04 16:11:34 +01:00
Deepak
442b597c95 More refactoring to the newly added methods 2014-12-01 22:32:58 -08:00
Jesse Squires
1bb258691b Merge pull request #655 from evermeer/patch-1
Update README. Added EVCloudKitDao as app using this library.
2014-12-01 00:45:54 -08:00
dtsbourg
7c2cec63f9 Remove unnecessary res 2014-11-30 19:55:56 +01:00
dtsbourg
91fbb4dce7 JSQDisplayedMessage: Update UI 2014-11-30 19:55:27 +01:00
Edwin Vermeer
681b6d329b Added EVCloudKitDao as app using this library.
EVCloudKitDao is a library for simplified access to CloudKit. As a sample there is a fully functional chat demo included which is using this library
2014-11-30 17:28:26 +01:00
dtsbourg
0d8fabe142 Calls: Update UI 2014-11-30 16:51:24 +01:00
dtsbourg
4049f54789 JSQDisplayedMessage: Link to UI 2014-11-29 15:30:53 +01:00
dtsbourg
54ae40b07a JSQDisplayedMessageCollectionViewCell: Base class 2014-11-29 15:06:43 +01:00
dtsbourg
bcf0ac6e1f JSQErrorMessage: Model 2014-11-29 14:48:59 +01:00
dtsbourg
e70cec697f JSQDisplayedMessage: Model + JSQInfoMessage: Model 2014-11-29 14:39:59 +01:00
Deepak
7234f10f81 Added two public methods to control collectionview scroll animation 2014-11-28 13:13:26 -08:00
Deepak
fd7f27b74b Fix for Scroll Animation 2014-11-27 11:57:04 -08:00
dtsbourg
b22b7a4caf JSQMessagesBubbleImageFactory: Add a failed message bubble 2014-11-27 12:43:24 +01:00
dtsbourg
b8b3fb9122 JSQMessageData: add message status field 2014-11-27 12:29:49 +01:00
dtsbourg
5fc12f5cba Merge branch 'master' of https://github.com/jessesquires/JSQMessagesViewController into JSignalQ 2014-11-26 18:04:32 +01:00
dtsbourg
4cad607ae2 none 2014-11-26 18:01:19 +01:00
Sean Conrad
276e9f94a6 add 'keyboardDidHide' notification handling
:: JSQMessagesKeyboardControllerDelegate protocol adds options `keyboardControllerDidReceiveKeyboardHideNotification:` method
:: this method is called within the completion block of the typical keyboard frame change notification handler, ensuring that the 'hide' message is received last
:: `JSQMessagesViewController` uses this delegate callback to call `jsq_setToolbarBottomLayoutGuideConstant:` with a value of `0.0f` (ensuring that a 'hidden' keyboard results in a 0-position input toolbar)
2014-11-25 18:24:30 -08:00
Sean Conrad
6a04d7585d improve layout for smaller cells
in some cases, `Top` alignment is best - this commit handles those cases
2014-11-25 16:02:44 -08:00
Sean Conrad
28b17896cc scrollToBottomAnimated: fix long message handling
fixes #480
2014-11-25 15:08:37 -08:00
Jesse Squires
8f28bc513e v6.1.0 2014-11-23 16:43:10 -08:00
Jesse Squires
512d2a9394 fix semantics regarding avatarPlaceholderImage and avatarHighlightedImage. close #624 2014-11-23 16:29:19 -08:00
Jesse Squires
81488d5ee6 add an emptyCache property to invalidation context for explicitly clearing the cache via invalidation. close #633 2014-11-23 16:20:49 -08:00
Jesse Squires
08ebf9b608 Update README.md 2014-11-23 12:39:09 -08:00
Jesse Squires
56f0d40214 Merge branch 'develop' of https://github.com/jessesquires/JSQMessagesViewController into develop 2014-11-22 13:47:03 -08:00
Jesse Squires
5d83de37cd expose left and right bar button container views. update docs. close #634 2014-11-22 13:46:31 -08:00
Jesse Squires
2626e8419b make hash required for message data protocols. close #631 2014-11-22 13:21:50 -08:00
dtsbourg
597670e446 JSQCallCollectionViewCell: UI 2014-11-21 23:01:16 +01:00
dtsbourg
881ddddeb4 Demo: make JSQM6.0 work with JSQCall 2014-11-21 22:14:53 +01:00
dtsbourg
32395453ff Finish merging 2014-11-21 13:43:21 +01:00
dtsbourg
5c6dce5a04 Fix merge from master conflicts 2014-11-21 13:37:28 +01:00
dtsbourg
3d248e4af0 JSQCall : layout 2014-11-21 12:02:20 +01:00
dtsbourg
6bbef1949b JSQCall model 2014-11-20 14:57:18 +01:00
dtsbourg
1031a89858 JSQMessage: Add failed state 2014-11-20 10:10:55 +01:00
dtsbourg
21d877492e InputToolbar: blue BarButtonItems 2014-11-19 13:05:35 +01:00
dtsbourg
5670accd2c General: Font update 2014-11-18 18:05:24 +01:00
dtsbourg
d138d4f3a3 InputToolbar: Fine tune enabling on rightBarButtonItem 2014-11-17 20:41:08 +01:00
dtsbourg
cddbbf2c75 InputToolbar: Bar button item always responds to tap events 2014-11-17 20:27:57 +01:00
dtsbourg
104ba0e023 MessageStatus: add kMessageNone status 2014-11-17 20:22:59 +01:00
dtsbourg
a27fa1091e Test: Custom UIButton in factory 2014-11-17 20:17:08 +01:00
Jesse Squires
a7a9cd7c64 Update README.md 2014-11-12 20:50:36 -08:00
dtsbourg
7446b63cbb remove * 2014-11-12 22:31:22 +01:00
dtsbourg
73482f6ace ≈Merge branch 'issue_492_scrollviewperformance' into JSignalQ 2014-11-12 22:26:40 +01:00
dtsbourg
2dad6e3036 add message status 2014-11-12 22:22:59 +01:00
Jesse Squires
b20af9e524 Merge pull request #618 from ranunez/develop
Updated link in README to reflect new stats on iOS 6 adoption
2014-11-11 22:28:42 -08:00
Ricardo Nunez
5ba9d2ee09 Updated link in README to reflect new stats on iOS 6 adoption 2014-11-11 21:45:31 -08:00
Jesse Squires
fe0540167b force toolbar to dismiss to bottom of view on viewDidDisappear. fix #314 2014-11-11 18:32:01 -08:00
Jesse Squires
45ce02baa6 remove todo 2014-11-11 18:05:31 -08:00
Jesse Squires
7722bce9d8 fix pod spec 2014-11-09 15:11:39 -08:00
Jesse Squires
ea74142adb Update README.md 2014-11-09 13:27:27 -08:00
Jesse Squires
ae9869d7ff update screenshots and spec 2014-11-09 13:25:50 -08:00
Jesse Squires
36b066fbdc Update README.md 2014-11-09 13:14:56 -08:00
Jesse Squires
387a7544b3 update pod spec 2014-11-09 13:11:12 -08:00
Jesse Squires
484a4b59bd update screenshots 2014-11-09 13:09:59 -08:00
Jesse Squires
f6e96e9a07 add message bubble red color 2014-11-09 12:21:36 -08:00
Jesse Squires
cdc86cc0a3 Update README.md 2014-11-09 12:08:58 -08:00
Jesse Squires
03b74fe77e Update README.md 2014-11-09 11:37:39 -08:00
Jesse Squires
280ff293f1 Update README.md 2014-11-09 11:21:25 -08:00
Jesse Squires
3b8cf0fa35 Update README.md 2014-11-09 11:20:14 -08:00
Jesse Squires
591e9198bb fix version numbers 2014-11-09 11:10:39 -08:00
Jesse Squires
0d20dadb8e refactor JSQMessage hierarchy. remove Text and Media subclasses, absorb into single JSQMessage class. close #602 2014-11-09 10:58:04 -08:00
Jesse Squires
72a4b5b970 adjust input toolbar layout and button sizes. 2014-11-07 19:27:50 -08:00
Jesse Squires
b291898161 fix toolbar constraints and correctly size send button to fit text. close #603 2014-11-07 19:13:36 -08:00
Jesse Squires
5627228464 expose cache countLimit. set default to 200. ref: #492 2014-11-07 18:18:46 -08:00
Jesse Squires
4281751534 verify outlets. close #604 2014-11-07 18:06:27 -08:00
Jesse Squires
c81aa93726 subclass text view to prevent unwanted text selection. close #591 2014-11-04 22:56:39 -08:00
Jesse Squires
468e102a7c minor optimizations 2014-11-04 22:26:36 -08:00
Jesse Squires
b3a80d9afc minor optimizations 2014-11-04 22:25:03 -08:00
Jesse Squires
dbfe58fc6d revert 4aedb06. close #597. cc: #484. sad face. 2014-11-04 21:35:04 -08:00
Jesse Squires
2070cde22d implement better message bubble size caching in layout for scrolling performance. #492 2014-11-02 20:17:54 -08:00
Jesse Squires
e99b127694 fix scroll position #256 2014-11-02 20:13:47 -08:00
Jesse Squires
08a9ec95fa fix message hashes 2014-11-02 19:56:42 -08:00
Jesse Squires
0d8667d14e fix issue where scroll to bottom fails until contentSize is at least a certain height. close #256 2014-11-02 19:36:40 -08:00
Jesse Squires
5c8dee520c Merge branch 'develop' of https://github.com/jessesquires/JSQMessagesViewController into develop 2014-11-02 19:13:40 -08:00
Jesse Squires
42c29ce4a1 fix auto layout constraint issues and warnings. set bubble width instead of margin. close #584, close #322 2014-11-02 19:13:05 -08:00
Jesse Squires
66a7d47ff3 edit cell 2014-11-02 18:22:27 -08:00
Jesse Squires
049035cdb6 Update README.md 2014-11-02 01:19:41 -07:00
Jesse Squires
4aedb062cc remove FIXME for radar 18326340. issue has been resolved as of Xcode 6.1 ref #484 2014-11-01 15:44:58 -07:00
Jesse Squires
c0edc6f7aa fix tests 2014-10-30 22:32:41 -07:00
Jesse Squires
9b579e91b9 fix composer text view insets. prevent single-line scrolling. fix #559 2014-10-30 21:40:27 -07:00
Jesse Squires
e88501ed61 prevent cell tap delegate methods from being called with a nil indexPath. fix #586 2014-10-30 21:36:43 -07:00
Jesse Squires
49f2c24533 fix #589. update insets when setting topContentAdditional inset 2014-10-30 21:33:17 -07:00
Jesse Squires
74965225f0 Merge pull request #585 from dtsbourg/issue_492_scrollviewperformance
Rasterize collection view cell for scrolling performance, ref #492
2014-10-28 17:20:45 -07:00
dtsbourg
99971a8d38 Merge branch 'JSignalQ' of https://github.com/dtsbourg/JSQMessagesViewController into JSignalQ 2014-10-28 23:39:44 +01:00
dtsbourg
da7203dee2 [UIColor whiteColor] 2014-10-28 23:37:11 +01:00
dtsbourg
88e16ac830 white bg ? 2014-10-28 23:37:11 +01:00
dtsbourg
41dddfea3a cell xib white bg 2014-10-28 23:37:11 +01:00
dtsbourg
ebea4ef0b8 Restore white bg 2014-10-28 23:37:11 +01:00
dtsbourg
f115194efa Signal 2014-10-28 23:37:11 +01:00
dtsbourg
39393e4b8b Rasterize collection view cell for scrolling performance 2014-10-28 23:36:27 +01:00
dtsbourg
06e7c2bec9 [UIColor whiteColor] 2014-10-28 22:51:14 +01:00
dtsbourg
6dded2cce9 white bg ? 2014-10-28 22:28:47 +01:00
dtsbourg
f03bac13a0 cell xib white bg 2014-10-28 22:23:39 +01:00
dtsbourg
33b81ca6b0 Restore white bg 2014-10-28 22:19:28 +01:00
dtsbourg
752edc4f0f Signal 2014-10-28 21:49:55 +01:00
Jesse Squires
71acc85bb4 Update README.md 2014-10-25 15:59:40 -07:00
Jesse Squires
3868620d62 Update README.md 2014-10-25 15:45:16 -07:00
Jesse Squires
58e4ac6624 fix #422 2014-10-25 15:34:22 -07:00
Jesse Squires
b5b6e8c2af update version numbers 2014-10-20 23:23:09 -07:00
Jesse Squires
065d60d997 update umbrella header 2014-10-20 23:22:03 -07:00
Jesse Squires
3f0f52f704 more unit test refinements 2014-10-20 23:17:14 -07:00
Jesse Squires
188ab824b8 refine category unit tests. update image category for asset methods. 2014-10-20 23:07:35 -07:00
Jesse Squires
e2b62c4996 change JSQVideoMediaItem to inherit from JSQMediaItem. mask JSQVideoMediaItem. closes #571. 2014-10-20 22:32:17 -07:00
Jesse Squires
1af170b983 change JSQLocationMediaItem to inherit from JSQMediaItem. mask JSQLocationMediaItem. #571 2014-10-20 22:23:35 -07:00
Jesse Squires
b590303a4f new abstract base class, JSQMediaItem, for pulling out common media item behavior. refactor JSQPhotoMediaItem to inherit from JSQMediaItem. #571 2014-10-20 22:08:23 -07:00
Jesse Squires
a38125e726 mask media views for JSQPhotoMediaItem. ref #571 2014-10-20 21:26:05 -07:00
Jesse Squires
b75870a241 add convenience method to JSQMessagesMediaViewBubbleImageMasker. #571 2014-10-20 21:06:16 -07:00
Jesse Squires
d4a5bc6d09 add new JSQMessagesMediaViewBubbleImageMasker. ref #571 2014-10-20 20:22:44 -07:00
Jesse Squires
da92497d42 update default map region span to 500 meters 2014-10-20 18:44:25 -07:00
Jesse Squires
6f7f318bcd update pods 2014-10-20 18:35:33 -07:00
Jesse Squires
45549628ae more edits for #563 2014-10-19 23:34:07 -07:00
Jesse Squires
02fdee07d0 Allow using a custom nib. ref #563 2014-10-19 23:30:29 -07:00
Jesse Squires
005378f8f3 update demo 2014-10-19 18:44:01 -07:00
Jesse Squires
0638d8caf2 refactor assets directory. use asset bundle. update categories for bundle. update tests 2014-10-19 18:22:22 -07:00
Jesse Squires
a023fee709 complete localization! close #237 whoo! :) 2014-10-19 17:22:42 -07:00
Jesse Squires
f201b085e3 add new empty strings file. ref #237 2014-10-19 16:33:29 -07:00
Jesse Squires
a56ceebdd3 use new collection view layout size helper method. cap label heights and avatar sizes constraints to 1.0 to fix auto-layout warnings. ref #322 2014-10-19 15:49:27 -07:00
Jesse Squires
f914b81044 new flow layout item size helper method 2014-10-19 15:27:29 -07:00
Jesse Squires
639df113ff improve scrolling performance. we don't need to keep calling setNeedsUpdateConstraints. ref #492. 2014-10-19 15:09:02 -07:00
Jesse Squires
3954411213 prepareForReuse 2014-10-19 14:39:18 -07:00
Jesse Squires
3e8a3cbfc1 add spec to project 2014-10-19 14:02:12 -07:00
Jesse Squires
833cf417f5 update JSQLocationMediaItem to specify a custom map region. close #565 2014-10-19 12:18:50 -07:00
Jesse Squires
3684895342 minor fixes 2014-10-19 11:22:08 -07:00
Jesse Squires
dee7714d9e re-add video item 2014-10-19 11:15:31 -07:00
Jesse Squires
dd9458a70f remove video item 2014-10-19 11:14:39 -07:00
Jesse Squires
d85c94e57f fix JSQVideoMediaItem typo. close #570 2014-10-19 11:12:58 -07:00
Jesse Squires
454be29f8d revert @import for #import. fix #541 2014-10-15 21:16:47 -07:00
Jesse Squires
d0fd6e58ec refactor 2014-10-15 21:08:36 -07:00
Jesse Squires
3b159c1df9 Merge pull request #560 from ghazel/menu_disable_scroll_554
don't scroll if the menu is visible. fix #554
2014-10-15 21:00:04 -07:00
Jesse Squires
33e780e535 adjust bubble size calculations. fix #553. 2014-10-15 20:57:13 -07:00
Greg Hazel
ddca80232a don't scroll if the menu is visible 2014-10-14 20:14:20 -07:00
Jesse Squires
1db3c14f6f update readme 2014-10-12 19:30:56 -07:00
Jesse Squires
f348e82249 update spec 2014-10-12 19:29:23 -07:00
Jesse Squires
6a2bda18c8 update demo for video media 2014-10-12 19:21:19 -07:00
Jesse Squires
02e5927a1d add JSQVideoMediaitem. close #504. 2014-10-12 19:09:04 -07:00
Jesse Squires
41d295a6af only respond to cell long press if touch is on bubble. close #548 2014-10-12 17:17:42 -07:00
Jesse Squires
ee51479736 Merge pull request #551 from ranunez/develop
Updated README to reflect beta4
2014-10-12 15:47:54 -07:00
Ricardo Nunez
933f11765c Updated README to reflect beta4 2014-10-12 15:00:35 -07:00
Jesse Squires
ae47a8350f fix bubble assets #524 2014-10-12 13:17:06 -07:00
Jesse Squires
1fb7f9f6cf add play icon for #504 2014-10-12 13:11:16 -07:00
Jesse Squires
31bf2b2fa9 update bubble assets for @3x close #524 2014-10-12 13:07:36 -07:00
Jesse Squires
e839b58a47 update JSQSystemSoundPlayer. beta4 2014-10-11 15:37:16 -07:00
Jesse Squires
da2352c73b pod update 2014-10-11 11:51:55 -07:00
Jesse Squires
bde4ae92aa remove old comment 2014-10-11 11:42:20 -07:00
Jesse Squires
3da13a9fc1 beta 3 2014-10-11 11:41:22 -07:00
Jesse Squires
423588cbc2 better handle layout invalidation. close #549 2014-10-11 11:27:57 -07:00
Jesse Squires
7ef5b4bbc2 handle iOS 7 and 8 specific issues. close #5221. close #544. ref #484. 2014-10-11 11:17:53 -07:00
Jesse Squires
18fc6ec9f6 clean up 2014-10-11 10:29:30 -07:00
Jesse Squires
65a691af96 follow up from PR #535 2014-10-09 21:40:54 -07:00
Jesse Squires
a03344bacc Merge pull request #535 from walsh2000/develop
Refactor JSQMessageBubbleImageFactory to allow custom images
2014-10-09 21:05:42 -07:00
Jesse Squires
a5ad7cfa18 add bubble image asset helper methods to category. #534 2014-10-09 20:04:35 -07:00
Jesse Squires
d1781adc09 re-add stroked bubble assets. close #534 2014-10-09 19:46:35 -07:00
Jesse Squires
fc21561030 fix imports per #537 2014-10-09 19:43:13 -07:00
Jesse Squires
11b74c5113 Merge pull request #537 from AnthonyMDev/develop
Import of 'JSQMessagesInputToolbar' instead of forward class declaration
2014-10-09 19:42:18 -07:00
Anthony Miller
11d25f1393 Import of 'JSQMessagesInputToolbar' instead of forward class declaration for 'JSQMessagesViewController'. 2014-10-07 16:40:01 -07:00
Raymond Walsh
b696ea4117 remove unexpected whitespace 2014-10-07 09:28:59 -07:00
Raymond Walsh
2fdbc9f0f1 Refactor JSQMessagesBubbleImageFactory to enable custom images/insets
The factory is now instantiated, rather than used as a set of static methods.
The default init function creates a factory which behaves exactly like the previous version.
The two-parameter init function takes a custom image and user-defined strechable regions.

Two new unit tests cover the custom image constructor
2014-10-07 09:27:55 -07:00
Jesse Squires
610b1c4e80 update readme 2014-10-07 00:06:17 -07:00
Jesse Squires
08b974cd7d update spec 2014-10-07 00:05:18 -07:00
Jesse Squires
b75bfdf54d use MKMapSnapshotter instead of MKMapView for location items. close #529. 2014-10-07 00:00:07 -07:00
Jesse Squires
cdd92be623 wrap graphics contexts in braces 2014-10-06 23:00:04 -07:00
Jesse Squires
eb35a14807 minor refinements 2014-10-06 20:02:03 -07:00
Jesse Squires
8acf85ea08 animate new cells even when springiness is disabled. close #448. 2014-10-06 19:05:19 -07:00
Jesse Squires
e83b02a74d revert changes from PR #531 2014-10-06 18:42:09 -07:00
Jesse Squires
3ab7fd4918 add isMediaMessage to JSQMessageData. close #530. 2014-10-06 18:38:04 -07:00
Jesse Squires
5c850cf963 Merge pull request #531 from walsh2000/develop
Reset contraints in prepareForReuse
2014-10-06 17:57:58 -07:00
Raymond Walsh
a1b2060796 Reset contraints in prepareForReuse
When a cell with 0 messageBubbleTopLabelHeight is reusing a cell which had a non-0 height, we get "Unable to simultaneously satisfy constraints" exception.
2014-10-06 15:29:52 -07:00
Jesse Squires
f796b576d0 improve iOS 8 scrolling performance. #492 2014-10-05 23:30:09 -07:00
Jesse Squires
ac1660841d fix analyze warning. 2014-10-05 22:18:11 -07:00
Jesse Squires
730927509b flow layout optimizations 2014-10-05 22:13:16 -07:00
Jesse Squires
e9d5f6371b re-add cached media views for JSQLocationMediaItem and JSQPhotoMediaItem. fix cell re-use and dequeuing issues. 2014-10-05 16:03:01 -07:00
Jesse Squires
392ad6936d make media display size bigger on ipad 2014-10-05 14:45:02 -07:00
Jesse Squires
bc2ca5c0c5 remove unused variable 2014-10-05 14:13:59 -07:00
Jesse Squires
b442c87ab8 Update README.md 2014-10-05 14:07:07 -07:00
Jesse Squires
2923ab1d03 update spec 2014-10-05 14:01:19 -07:00
Jesse Squires
d91f26d3e5 remove cached views 2014-10-05 13:58:24 -07:00
Jesse Squires
c3f8361ad3 update podfile 2014-10-05 13:56:03 -07:00
Jesse Squires
51a17030cb update setMedia 2014-10-05 13:50:00 -07:00
Jesse Squires
e436e8d456 add full copy/value semantics for JSQPhotoMediaItem. cache image view. update demo. 2014-10-05 12:57:33 -07:00
Jesse Squires
6ace8da92b cache the map view for JSQLocationMediaItem 2014-10-05 12:48:12 -07:00
Jesse Squires
4d01ea8a14 update demo with options for sending media 2014-10-05 12:47:25 -07:00
Jesse Squires
42b8f45252 add JSQLocationMediaItem. #504. update demo. 2014-10-05 12:03:37 -07:00
Jesse Squires
27efc06859 refine docs 2014-10-05 10:42:36 -07:00
Jesse Squires
2c9977e589 travis workaround 2014-10-05 10:00:54 -07:00
Jesse Squires
643b67b24b update travis script 2014-10-04 18:30:59 -07:00
Jesse Squires
38fcfdb915 update travis script 2014-10-04 18:22:48 -07:00
Jesse Squires
07f3aee64f update travis script 2014-10-04 18:21:51 -07:00
Jesse Squires
7db7eea4fa fix travis script 2014-10-04 18:11:02 -07:00
Jesse Squires
00d8bd162c update travis script 2014-10-04 18:00:31 -07:00
Jesse Squires
b3a1f75221 update travis script 2014-10-04 17:48:38 -07:00
Jesse Squires
67c365a6b7 fix travis script 2014-10-04 17:39:52 -07:00
Jesse Squires
1e5247bce3 convert jpg to png because iOS7 barfs. 2014-10-04 17:19:16 -07:00
Jesse Squires
1052627f59 clean up project. fix travis script for unit tests. 2014-10-04 17:03:51 -07:00
Jesse Squires
0b2280754f fix project file. update spec. 2014-10-04 15:25:50 -07:00
Jesse Squires
df55fd3e71 fix avatar setting. add setting to load empty view. 2014-10-02 21:57:19 -07:00
Jesse Squires
0892296fc3 update demo with avatar settings 2014-10-02 21:44:36 -07:00
Jesse Squires
91753a7169 update demo. new DemoSettingsViewController. 2014-10-02 21:31:20 -07:00
Jesse Squires
23a5402f1f update header docs 2014-10-02 20:40:05 -07:00
Jesse Squires
57fc67be2c update demo for async loading example 2014-10-01 23:38:54 -07:00
Jesse Squires
b0be2b2da2 update JSQMessageMediaData docs 2014-10-01 23:13:33 -07:00
Jesse Squires
a93bd1b8aa finished docs for JSQMessagesMediaPlaceholderView 2014-10-01 23:10:04 -07:00
Jesse Squires
aae37aec37 resolve TODOs 2014-10-01 22:40:09 -07:00
Jesse Squires
4af0466f98 remove duplicate constraints in cell xibs. 2014-10-01 22:23:58 -07:00
Jesse Squires
85caf8535e formatting. update comments 2014-10-01 22:05:25 -07:00
Jesse Squires
1a809a0ba1 fix JSQMessagesTypingIndicatorFooterView layout constraints 2014-09-28 23:23:29 -07:00
Jesse Squires
0c9825cbf9 adjust typing indicator view 2014-09-28 23:15:20 -07:00
Jesse Squires
6df0e07619 implement basic placeholder media view. #504 2014-09-28 22:55:58 -07:00
Jesse Squires
7f624691ee turn off springiness in demo 2014-09-28 20:11:16 -07:00
Jesse Squires
ef8c54597a update assets #502. begin JSQMessagesMediaPlaceholderView. #504 2014-09-28 20:07:27 -07:00
Jesse Squires
fd192ba635 rebuild xcode project - fuck you xcode. update assets (#). clean up file imports. use modern objC. update podfile. 2014-09-28 19:24:44 -07:00
Jesse Squires
ddfd4f5d8c update cocoapods 2014-09-28 14:25:56 -07:00
Jesse Squires
cf5ac56a04 fix #514 2014-09-28 10:33:46 -07:00
Jesse Squires
54b9d85206 Merge branch 'develop' into release-6.0 2014-09-28 10:14:33 -07:00
Jesse Squires
0ab88fb813 Merge pull request #511 from alr/develop
fix crash when user shakes-to-undo after sending text message. close #508
2014-09-26 09:58:00 -07:00
Dmitry Abolmasov
33bbfd7bd7 fix crash when user use Undo after sending text message 2014-09-26 18:53:49 +04:00
Jesse Squires
4e1047d815 add functionality to didPressAccessoryButton: in demo 2014-09-21 17:05:35 -07:00
Jesse Squires
e812298e34 add concrete JSQPhotoMediaItem for photo messages. ref #223. update demo handle media messages 2014-09-21 17:00:43 -07:00
Jesse Squires
efb47ff444 update collection view, controller, and cells for custom media reuse identifier for media message cells. #223 2014-09-21 16:18:19 -07:00
Jesse Squires
818c4af616 add demo photo asset 2014-09-21 15:41:17 -07:00
Jesse Squires
cddf5f4f0d update xibs for xcode6, iOS 8. refine and streamline JSQMessagesCollectionViewCell. 2014-09-21 12:47:01 -07:00
Jesse Squires
202ce0aa0b optimize avatar loading. check layout avatar size first, if zero don't even ask delegate. change default avatar size to 30.0. 2014-09-21 11:32:07 -07:00
Jesse Squires
d82f39ccb9 unit tests 2014-09-21 00:47:30 -07:00
Jesse Squires
1dc1185baa finish updating demo assets. #502 2014-09-21 00:03:07 -07:00
Jesse Squires
4a33d94766 update project assets 2014-09-20 23:56:20 -07:00
Jesse Squires
c144892a45 remove demo class prefixes 2014-09-20 23:27:07 -07:00
Jesse Squires
0a16732842 refactor library for core media message support. refactor JSQMessage to be abstract base class. add JSQTextMessage. add JSQMediaMessage. add JSQMessageMediaData protocol. Update demo. ref #223, #476. 2014-09-20 23:22:34 -07:00
Jesse Squires
0facd44dbd Merge branch 'develop' into release-6.0 2014-09-20 17:13:18 -07:00
Jesse Squires
7198f760aa bump version nums 2014-09-20 17:07:18 -07:00
Jesse Squires
ccb1a284fb fix #498 2014-09-20 17:04:10 -07:00
Jesse Squires
375eda5d4f fix unit tests 2014-09-20 16:51:08 -07:00
Jesse Squires
71c21c06bf fix rendering and size calculations for custom fonts. close #490. close #489. close #407. close #347. credit @ghazel 2014-09-20 16:41:15 -07:00
Jesse Squires
dd5216d5e1 tweak #495 2014-09-20 15:31:24 -07:00
Jesse Squires
961190658b Merge pull request #495 from piercifani/fix-bubblelabel-allignment
Fix inset of messageBubbleTopLabel when there is no avatar
2014-09-20 15:30:46 -07:00
Jesse Squires
39ea9f6eef disable send button when setting custom send button. fix #479 2014-09-20 15:18:48 -07:00
Jesse Squires
3bbb185777 fix merge issue 2014-09-17 23:54:57 -07:00
Jesse Squires
c3376a1eb5 Merge branch 'develop' into release-6.0 2014-09-17 23:54:01 -07:00
Jesse Squires
cf543f6110 fix settings 2014-09-17 23:53:20 -07:00
Jesse Squires
8c0760e766 close #484 2014-09-17 23:51:25 -07:00
Jesse Squires
0c404b528f prevent messageBubbleLeftRightMargin from being negative. fix #486 2014-09-16 20:34:59 -07:00
Pierluigi Cifani
75ee09576a Fix inset of messageBubbleTopLabel when there is no avatar 2014-09-16 15:59:23 +02:00
Jesse Squires
0f619f71a7 apply workaround for #484 2014-09-12 16:14:36 -07:00
Jesse Squires
084be62933 finish avatar highlighted states for #424 and #319 2014-09-12 15:41:52 -07:00
Jesse Squires
a8fe8f961d new header and footer properties on collection view. remove typingIndicatorColor from view controller. refinements for #476 2014-09-07 23:13:43 -06:00
Jesse Squires
d7b4e73d7f add TODO 2014-09-06 19:59:15 -07:00
Jesse Squires
5045f04522 rename JSQMessagesAvatarFactoryTests —> JSQMessagesAvatarImageFactoryTests 2014-09-06 18:59:35 -07:00
Jesse Squires
a68ddf1621 add TODO 2014-09-06 18:57:57 -07:00
Jesse Squires
d3b7f3a1a1 update demo for JSQMessageAvatarImageDataSource changes 2014-09-06 18:55:12 -07:00
Jesse Squires
af9117642c new helper method on JSQMessagesAvatarImage and unit tests. 2014-09-06 18:54:48 -07:00
Jesse Squires
40771ad88a fix JSQMessagesBubbleImageFactory docs typos 2014-09-06 18:53:41 -07:00
Jesse Squires
9591b89800 check for nil data sources. 2014-09-06 18:53:12 -07:00
Jesse Squires
67e6650b3e update library to use JSQMessageAvatarImageDataSource and JSQMessagesAvatarImage. refactor JSQMessagesAvatarImageFactory. add avatarImageView to cell prototype. update unit tests. updates docs. ref #319 2014-09-06 18:51:36 -07:00
Jesse Squires
eec8b614ee new avatar default size constant 2014-09-06 16:44:57 -07:00
Jesse Squires
5020d5ad85 rename JSQMessagesAvatarFactory —> JSQMessagesAvatarImageFactory 2014-09-06 16:40:52 -07:00
Jesse Squires
45ae2095c2 documentation for JSQMessageAvatarImageDataSource and JSQMessagesAvatarImage. slight refactor of both, remove size property. update tests. #319 2014-09-06 16:36:50 -07:00
Jesse Squires
9156843e2e update docs. 2014-09-06 16:01:04 -07:00
Jesse Squires
a434273f03 add documentation for JSQMessageBubbleImageDataSource and JSQMessagesBubbleImage. ref #319 2014-09-06 16:00:52 -07:00
Jesse Squires
95fc8b453f update JSQMessage and JSQMessageData docs 2014-09-06 15:56:27 -07:00
Jesse Squires
39c88d90df fix unit tests 2014-09-06 14:10:35 -07:00
Jesse Squires
7f4a2bab25 update library to utilize JSQMessageBubbleImageDataSource and JSQMessagesBubbleImage. add messageBubbleImageView to JSQMessagesCollectionViewCell prototype. update demo and unit-tests. 2014-09-06 14:03:47 -07:00
Jesse Squires
da83dcbe75 better documentation for JSQMessagesCollectionViewFlowLayout 2014-09-06 12:38:26 -07:00
Jesse Squires
9de1f6f584 add TODOs 2014-09-06 12:03:31 -07:00
Jesse Squires
a97cf9dcbc new JSQMessagesAvatarImage class and unit tests. ref #319 2014-09-06 12:00:25 -07:00
Jesse Squires
00cf2719a7 Merge branch 'develop' into release-6.0 2014-09-02 21:54:51 -07:00
Jesse Squires
027bdc3a30 bump version nums 2014-09-02 21:51:30 -07:00
Jesse Squires
4a140738e4 fix data detectors bug. close #465. 2014-09-02 21:49:58 -07:00
Jesse Squires
430853e7c8 add JSQMessagesBubbleImageTests 2014-09-01 22:10:15 -07:00
Jesse Squires
1787b82224 add new JSQMessagesBubbleImage object. concrete JSQMessageBubbleImageDataSource. #319 2014-09-01 21:59:50 -07:00
Jesse Squires
f0916dc5e3 add new data source protocols. beginning #319 2014-09-01 21:35:35 -07:00
Jesse Squires
efca0b6bac remove unecessary #ifndef 2014-09-01 21:28:36 -07:00
Jesse Squires
751677394c change message data protocol and model to use senderId and senderDisplayName. closes #389. 2014-09-01 19:23:56 -07:00
Jesse Squires
832474648d version nums 2014-09-01 12:17:51 -07:00
Jesse Squires
3bfa3fd49b refine UIMenuController display. #458, #254, #463, #393 2014-09-01 12:11:03 -07:00
Jesse Squires
30bce9b446 expose cell properties messageBubbleContainerView and avatarContainerView 2014-09-01 12:09:41 -07:00
Jesse Squires
92cd02669f remove dead code 2014-09-01 11:43:13 -07:00
Jesse Squires
dcb567e1a5 Merge branch 'newMenuController' into develop 2014-09-01 11:40:11 -07:00
Jesse Squires
00a503f691 move UIMenuController handling from cell to collectionView. fix #458. fix #254. close #463. ref: #393. 2014-09-01 10:37:19 -07:00
Jesse Squires
3c5725059c version nums 2014-08-31 14:22:54 -07:00
Jesse Squires
79abcca365 improve cell selection and menu handling. close #261 2014-08-31 12:25:26 -07:00
Jesse Squires
9ed3f977a0 reset cell text on prepareForReuse. #316 2014-08-30 22:39:40 -07:00
Jesse Squires
b93e090fc9 move JSQMessagesCollectionViewCellDelegate implementation from controller to collectionView 2014-08-30 22:30:11 -07:00
Jesse Squires
c6f108b94b move collection view delegate and data source definitions to their own header files to clean up collection view header. 2014-08-30 22:24:32 -07:00
Jesse Squires
742dcb288a fix toolbar constraint issues and button widths not setting properly. fixes #460 2014-08-30 21:58:26 -07:00
Jesse Squires
47d1c210c5 fix demo 2014-08-30 13:38:11 -07:00
Jesse Squires
0ce760a61a adjust toolbar constraints 2014-08-30 13:33:47 -07:00
Jesse Squires
13b83d0579 fix import 2014-08-24 13:58:34 -07:00
Jesse Squires
fd07b7baca version nums 2014-08-23 21:56:42 -07:00
Jesse Squires
8a616d430c remove duplicate layout constraints in xibs. close #264 2014-08-23 21:40:39 -07:00
Jesse Squires
2da245ed81 remove status bar notifications from keyboard controller, re-add to view controller. fix in-call status bar layout issues. close #434 2014-08-23 20:58:56 -07:00
Jesse Squires
91e35809a1 update collection view docs 2014-08-23 18:49:21 -07:00
Jesse Squires
ba298887b3 refactor keyboard delegate/notification methods to single notify helper method 2014-08-23 18:39:15 -07:00
Jesse Squires
a94cf830db move status bar notification handling to keyboard controller 2014-08-23 18:34:49 -07:00
Jesse Squires
1d0c612ec4 fix podspec. close #449 2014-08-18 20:41:57 -07:00
Jesse Squires
d3bae33297 version 5.2.3 2014-08-17 23:01:00 -07:00
Jesse Squires
01484038b7 fix podspec again. #435 2014-08-17 22:50:21 -07:00
Jesse Squires
5d87411371 fix imports for projects without a pch. bump version numbers. fix podspec assets. close #435 2014-08-17 22:37:37 -07:00
Jesse Squires
14b8045f5d Merge pull request #444 from dim0v/develop
fixes #443
2014-08-17 22:28:11 -07:00
Dmytro Ovcharenko
3502a81989 Closes #443 2014-08-16 00:04:44 +03:00
Jesse Squires
b39f705d05 version nums 2014-08-02 11:34:09 -07:00
Jesse Squires
fe4e4e3f50 use uppercaseStringWithLocale: close #427 2014-08-02 11:33:08 -07:00
Jesse Squires
18ba36af8b Merge pull request #428 from trakov/master
Added new optional delegate methods for JSQMessagesCollectionViewDelegateFlowLayout
2014-08-01 09:47:48 -07:00
Timur Rakov
8511641a24 Added dummy for JSQMessagesCollectionViewDelegateFlowLayout method 2014-08-01 15:20:48 +07:00
Jesse Squires
bbecf44390 update version nums 2014-07-27 23:43:55 -07:00
Jesse Squires
6298f32ecb new APIs for responding to different cell tap events. issue #374. update demo. 2014-07-27 23:41:28 -07:00
Jesse Squires
2f9a222454 update demo to have a “first received” message. ref #413 2014-07-27 23:08:48 -07:00
Jesse Squires
bdef43d27b add support for an additional top content inset. close #325 2014-07-27 22:55:03 -07:00
Jesse Squires
f3cddcff6d Merge pull request #420 from ranunez/master
Updated iOS 6 adoption rate URL in README
2014-07-25 10:17:34 -07:00
Ricardo Nunez
d2a2594f28 Updated iOS 6 adoption rate URL in README 2014-07-25 09:59:52 -07:00
Jesse Squires
752f0e20b7 Update README.md 2014-07-23 10:00:31 -07:00
Jesse Squires
b7f6f6a871 Update README.md 2014-07-22 22:48:24 -07:00
Jesse Squires
50179bb492 version nums 2014-07-22 18:22:07 -07:00
Jesse Squires
99412fe735 Merge pull request #415 from piercifani/master
Fix crash on async message fetch. close #414
2014-07-22 09:34:51 -07:00
Pierluigi Cifani
8bd4fe9ada Fix crash on async message fetch 2014-07-22 16:59:39 +02:00
Jesse Squires
aa658076bd update demo 2014-07-21 23:37:55 -07:00
Jesse Squires
6aade7b583 version nums 2014-07-21 23:11:35 -07:00
Jesse Squires
2c39325220 fix issue where header/footer failed to appear when springiness is enabled. close #409 2014-07-21 23:08:17 -07:00
Jesse Squires
a98115d4cd fix header/footer background colors 2014-07-20 17:41:38 -04:00
Jesse Squires
988dd6fdfa improve swipe back keyboard animation using view snapshot. 2014-07-20 16:23:59 +02:00
Jesse Squires
e18465c03c don’t auto scroll when showing typing indicator. update docs for typing indicator footer and load messages header. 2014-07-20 15:54:53 +02:00
Jesse Squires
57282750cc fix issue where load earlier messages header or typing indicator would not appear. 2014-07-20 13:58:32 +02:00
Jesse Squires
6d96064ebf fix #368 2014-07-19 18:23:56 +02:00
Jesse Squires
f7e6f7f0e9 fix bubble glitchy “bounce” on view appear and swipe back/forward in nav controller. 2014-07-19 18:14:01 +02:00
Jesse Squires
251e5afad8 improve KVO. better handle add/remove observers. close #285 2014-07-19 18:01:43 +02:00
Jesse Squires
51701ca5dd fix travis 2014-07-18 13:29:03 +02:00
Jesse Squires
17c8b10007 Merge branch 'develop' of https://github.com/jessesquires/JSQMessagesViewController into develop 2014-07-17 23:33:35 +02:00
Jesse Squires
5625d9d57b disable springiness by default. update docs. relevant issues: #305, #398, #395 2014-07-17 23:33:23 +02:00
Jesse Squires
60aa9659c1 Update README.md 2014-07-17 23:27:19 +02:00
Jesse Squires
b4b61c2140 make UITextViewDelegate public and check equality for composer text view so that subclasses can properly override. close #361 2014-07-17 16:18:30 +02:00
Jesse Squires
b1d764deb3 Merge pull request #385 from justindeguzman/issue_381_fixLowResolutionAvatar
changed graphics context scale factor. close #381
2014-07-17 15:45:36 +02:00
Jesse Squires
2e9729554c Merge pull request #354 from vitalyster/patch-2
iOS6_support_stable branch uses old pod name
2014-07-17 13:48:01 +02:00
Jesse Squires
8913766cb7 update readme. close #388 2014-07-17 13:42:55 +02:00
Justin de Guzman
49636a9c5c changed graphics context scale factor 2014-06-28 13:09:30 -04:00
Jesse Squires
9772fc8a76 Merge branch 'develop' 2014-06-27 19:21:00 -07:00
Jesse Squires
18849e9279 Update README.md 2014-06-27 19:19:52 -07:00
Jesse Squires
abdd2ddabc Merge pull request #356 from ringring-io/ringring
Add Ringring.io to the apps list
2014-06-16 21:08:11 -07:00
Peter Kosztolanyi
1ee911d36e Add Ringring.io to the apps list 2014-06-17 02:03:36 +01:00
vitalyster
4501c6e657 iOS6_support_stable branch uses old pod name 2014-06-13 21:53:47 +04:00
Jesse Squires
762ae3e581 fix unit tests 2014-06-07 17:20:33 -07:00
Jesse Squires
7751ec3106 update readme 2014-06-07 15:14:10 -07:00
Jesse Squires
29cee9eeb5 update readme 2014-06-07 15:13:08 -07:00
Jesse Squires
ed0301fc14 Merge pull request #337 from yforjindam/patch-1
Update README.md
2014-06-04 22:21:38 -07:00
Yang Forjindam
fbaf58ecad Update README.md
Enhancing messaging functionality, UX in PocketSuite via this excellent library (high quality, well documented, thoughtfully designed).
2014-06-04 19:25:58 -07:00
Jesse Squires
c3e78b5ca8 Update README.md 2014-05-28 19:30:53 -07:00
Jesse Squires
178e1a5ab8 v 5.0.4 2014-05-26 19:10:16 -07:00
Jesse Squires
ff1fc2060a update cocoadocs styles 2014-05-26 19:07:50 -07:00
Jesse Squires
16aa5599d8 update cocoadocs style 2014-05-26 19:01:32 -07:00
Jesse Squires
010b79008a pod update 2014-05-26 15:40:00 -07:00
Jesse Squires
6be7dc59f8 cocoadocs.yml 2014-05-26 13:09:47 -07:00
Jesse Squires
ffbaa0b0b4 v 5.0.3 2014-05-26 12:08:02 -07:00
Jesse Squires
239bba9832 update library common header for new context object 2014-05-26 12:06:14 -07:00
Jesse Squires
22683e94a2 fix docs and formatting and minor refactoring from PR #313. small improvements to lazily applying layout attributes to cells. close #306. close #281. 2014-05-26 11:47:35 -07:00
Jesse Squires
9538b4fdb0 Merge pull request #313 from erysaj/issue_281_improve_cpu_springiness_cpu_usage
Improve springiness cpu usage
2014-05-26 10:51:10 -07:00
Jesse Squires
6fd3209097 schemes 2014-05-24 17:54:53 -07:00
Jesse Squires
86f2ef29bb Update README.md 2014-05-24 14:15:52 -07:00
Eugene Rysaj
9bdf157a39 Revert "avoid expensive view hierarchy modification during cell configuration"
This reverts commit 3f7b5ae86f.
2014-05-25 00:11:41 +03:00
Jesse Squires
31d4e930d8 switch to NSParameterAssert where appropriate 2014-05-23 19:06:04 -07:00
Jesse Squires
3ac741121e add userInfo dictionary to keyboard notification so that it is actually useful. #287. 2014-05-23 18:55:29 -07:00
Eugene Rysaj
3f7b5ae86f avoid expensive view hierarchy modification during cell configuration 2014-05-23 13:52:52 +03:00
Eugene Rysaj
3fcf0340fe do not use concurrent enumeration 2014-05-23 13:52:52 +03:00
Eugene Rysaj
948f2e29c6 lazily update cell’s text view 2014-05-23 13:52:52 +03:00
Eugene Rysaj
211fb75afc lazily update cell’s layout constraints 2014-05-23 13:26:04 +03:00
Eugene Rysaj
d5a6d67638 use custom layout invalidation context to suppress unneeded computations 2014-05-23 12:52:31 +03:00
Jesse Squires
18b773b68c pod update 2014-05-20 18:44:19 -07:00
Jesse Squires
3490051b77 update version nums. update spec. 2014-05-19 00:08:34 -07:00
Jesse Squires
f29354e039 unit tests 2014-05-18 12:53:51 -07:00
Jesse Squires
9d5505624b unit tests 2014-05-18 11:25:25 -07:00
Jesse Squires
8ce72ac3d3 import OCMock 2014-05-18 11:07:56 -07:00
Jesse Squires
9527f91a37 formatting 2014-05-17 22:10:10 -07:00
Jesse Squires
22f1bca66d keyboard controller notification. close #287 2014-05-17 21:21:47 -07:00
Jesse Squires
299f9bf1a9 clean up class forward declarations 2014-05-17 21:06:32 -07:00
Jesse Squires
991f64ab8f fix constraint property name misspellings. close #292 2014-05-17 21:00:07 -07:00
Jesse Squires
4250240152 update readme 2014-05-17 20:39:51 -07:00
Jesse Squires
e77e2ebd03 Merge pull request #293 from kleo/patch-1
Updated README.md
2014-05-16 15:46:27 -07:00
Jessica Burkhardt
597b2aee75 Updated README.md 2014-05-16 23:45:09 +02:00
Jesse Squires
c4fdcd7a0a Update README.md 2014-05-14 09:49:38 -07:00
Jesse Squires
8f50457f5a Update README.md 2014-05-13 22:47:39 -07:00
Jesse Squires
72b9d050d6 Update README.md 2014-05-13 12:59:13 -07:00
Jesse Squires
fcb118c23e version 5.0.1 2014-05-13 08:18:26 -07:00
Jesse Squires
aa953e7e0f doc fixes 2014-05-12 22:43:40 -07:00
Jesse Squires
f321c473a2 make dateFormatter public and readonly. add nil checks for date. close #274 2014-05-12 09:13:01 -07:00
Jesse Squires
74f9a28b80 fix #276 2014-05-12 09:03:00 -07:00
Jesse Squires
0e0e90e3eb schemes 2014-05-12 09:02:30 -07:00
Jesse Squires
2bb26b7f7e update pods 2014-05-11 20:07:24 -07:00
Jesse Squires
ffbc01f626 update spec 2014-05-06 23:24:33 -05:00
Jesse Squires
3eb4c40a12 update podspec 2014-05-06 22:52:49 -05:00
Jesse Squires
30d27ce745 set avatar image view size. close #265 2014-05-06 22:49:46 -05:00
Jesse Squires
42c0dc9973 workaround for dynamic animator crash. close #263 2014-05-06 22:30:34 -05:00
Jesse Squires
b5e6204012 Update README.md
close #267
2014-05-06 22:06:58 -05:00
Jesse Squires
b8ded053e8 Merge branch 'develop' 2014-05-04 14:05:32 -07:00
Jesse Squires
19e74fa0b1 update screenshots 2014-05-04 14:05:18 -07:00
Jesse Squires
9f493caf40 update spec 2014-05-04 02:20:27 -07:00
Jesse Squires
04e33af761 Merge branch 'develop' 2014-05-04 02:14:55 -07:00
Jesse Squires
92188db5b7 Update README.md 2014-05-04 01:31:47 -07:00
Jesse Squires
c119d99625 chat icon 2014-05-03 23:25:35 -07:00
Jesse Squires
553ab0168f Merge branch 'develop' 2014-05-03 22:55:39 -07:00
Jesse Squires
02d89d1ae4 remove sender param from datasource methods. close #262 2014-05-03 22:55:16 -07:00
Jesse Squires
1894236342 reset label height constraints on prepareForReuse ref: #264 2014-05-03 22:34:33 -07:00
Jesse Squires
e3b061457e psd assets 2014-05-03 22:15:27 -07:00
Jesse Squires
ff42633d05 Merge branch 'master' of https://github.com/jessesquires/JSQMessagesViewController 2014-05-03 22:10:57 -07:00
Jesse Squires
cf757b761b Update README.md 2014-05-03 22:09:40 -07:00
Jesse Squires
8cfe21d8e0 new banner 2014-05-03 22:08:13 -07:00
Jesse Squires
ec7e4d90d2 library assets 2014-05-03 21:50:09 -07:00
Jesse Squires
b8c1f8d7a5 launch image 2014-05-03 21:33:11 -07:00
Jesse Squires
1f48465acd app icon 2014-05-03 21:10:22 -07:00
Jesse Squires
3b8bf98776 Update README.md 2014-05-03 17:29:25 -07:00
Jesse Squires
8241aac750 Update README.md 2014-05-03 17:27:09 -07:00
Jesse Squires
f0f9d4caa5 Update README.md 2014-05-03 17:24:36 -07:00
Jesse Squires
1c1754168f Update README.md 2014-05-03 17:23:59 -07:00
Jesse Squires
9a2b37f854 Update README.md 2014-05-03 17:12:35 -07:00
Jesse Squires
f4654c882c update readme 2014-05-03 16:50:47 -07:00
Jesse Squires
607cae9b7c update header comments 2014-05-03 16:41:10 -07:00
Jesse Squires
b30722b34d edit schemes 2014-05-03 16:32:15 -07:00
Jesse Squires
7342a94bf8 add OCMock 2014-05-03 16:23:18 -07:00
Jesse Squires
daa5c23630 merge develop 2014-05-03 16:13:21 -07:00
Jesse Squires
4826acbbdb Update README.md 2014-05-01 22:46:41 -07:00
Jesse Squires
82050acc37 screenshots 2014-05-01 22:45:11 -07:00
Jesse Squires
bea68885eb update readme 2014-05-01 22:43:53 -07:00
Jesse Squires
81d8357f4f update demo 2014-05-01 22:38:39 -07:00
Jesse Squires
f1544d239d update demo 2014-05-01 22:33:03 -07:00
Jesse Squires
821112a001 don’t set text color 2014-05-01 22:32:27 -07:00
Jesse Squires
59a54f100b tweak demo 2014-05-01 22:24:00 -07:00
Jesse Squires
2e83e71fb5 demo comments 2014-05-01 22:20:30 -07:00
Jesse Squires
175e7f8635 beta4 podspec 2014-05-01 21:54:27 -07:00
Jesse Squires
1d4bbd6f86 merge to develop 2014-05-01 21:49:49 -07:00
Jesse Squires
61a7b47d8d Update README.md 2014-05-01 21:36:49 -07:00
Jesse Squires
a60cf53e71 license 2014-05-01 21:27:41 -07:00
Jesse Squires
a039eec10e Update README.md 2014-05-01 21:26:30 -07:00
Jesse Squires
686cefb1ff Update README.md 2014-05-01 21:17:44 -07:00
Jesse Squires
acb32dc8ae fix #257 2014-05-01 20:52:01 -07:00
Jesse Squires
547bc9127f read me 2014-05-01 18:53:57 -07:00
Jesse Squires
b1af4a7925 Update README.md 2014-04-30 23:09:02 -07:00
Jesse Squires
6ec830f0d7 Update README.md 2014-04-30 23:07:49 -07:00
Jesse Squires
a33a34f01c Update README.md 2014-04-30 23:06:01 -07:00
Jesse Squires
f8213531a0 Update README.md 2014-04-30 23:04:03 -07:00
Jesse Squires
1fe27d5bb0 Update README.md 2014-04-30 23:02:28 -07:00
Jesse Squires
7c81a4a149 Update README.md 2014-04-30 22:57:10 -07:00
Jesse Squires
e57d3ac74c Update README.md 2014-04-30 22:55:00 -07:00
Jesse Squires
ef25a30119 Update README.md 2014-04-30 22:53:42 -07:00
Jesse Squires
325478726a Update README.md 2014-04-30 22:48:48 -07:00
Jesse Squires
03791ad38c Update README.md 2014-04-30 22:46:26 -07:00
Jesse Squires
7b3a14372d Update README.md 2014-04-30 22:46:11 -07:00
Jesse Squires
b8f171a745 Update README.md 2014-04-30 22:38:26 -07:00
Jesse Squires
a0e80726db Update README.md 2014-04-30 22:23:00 -07:00
Jesse Squires
eb291a60ee fix initialization flow. close #253 2014-04-29 22:19:13 -07:00
Jesse Squires
ca95d8789b get rid of layout warnings console logs 2014-04-29 20:57:41 -07:00
Jesse Squires
7c8fc8afd8 begin toolbar content view tests 2014-04-29 20:49:49 -07:00
Jesse Squires
7908ad0340 begin header and footer tests 2014-04-29 20:31:01 -07:00
Jesse Squires
d55090df39 toolbar and label tests 2014-04-29 20:26:50 -07:00
Jesse Squires
9d9d46936e stub out remaining unit tests 2014-04-29 20:16:03 -07:00
Jesse Squires
b474f8d448 change API to avoid using JSQMessage objects. per discussion in #172 2014-04-29 20:06:45 -07:00
Jesse Squires
bda909b31a update pod spec 2014-04-28 23:31:02 -07:00
Jesse Squires
6cc981364d fix #252 2014-04-28 23:18:50 -07:00
Jesse Squires
7728cc5a57 fix keyboard issues with rotation and panning. fix #232. close #36. cc: #172 2014-04-28 23:13:25 -07:00
Jesse Squires
fc406d8a14 fix #252 2014-04-28 21:41:51 -07:00
Jesse Squires
d6fb1d2f5a fix #252 2014-04-28 21:30:27 -07:00
Jesse Squires
b22e30dba0 improve rotation handling. fix rotation collection view layout bugs. ref: #36. fix #232. 2014-04-28 21:20:41 -07:00
Jesse Squires
9b27b0d30a fix #230 2014-04-28 18:05:16 -07:00
Jesse Squires
0046e2aa7b begin composer text view tests 2014-04-27 23:28:55 -07:00
Jesse Squires
7a4fba830e begin collection view cell tests 2014-04-27 22:58:11 -07:00
Jesse Squires
bb66b56b5a update demo 2014-04-27 22:27:38 -07:00
Jesse Squires
3a231c0795 fix #241 and other keyboard frame issues when panning with opaque navbar. cc: #217 2014-04-27 22:27:27 -07:00
Jesse Squires
9aa5e525c0 reset keyboard view in endListening. fix #228 2014-04-27 22:01:15 -07:00
Jesse Squires
08ed2d926b update docs re: #241 2014-04-27 21:44:39 -07:00
Jesse Squires
a78c6109b0 fix #241 2014-04-27 21:38:29 -07:00
Jesse Squires
00571d8f11 collection view unit tests 2014-04-27 16:27:45 -07:00
Jesse Squires
1a6210d1fb refactoring. collection view init. unit tests. 2014-04-27 16:25:58 -07:00
Jesse Squires
db3886cc8e fix test 2014-04-27 16:18:11 -07:00
Jesse Squires
337154eead message tests 2014-04-27 15:56:20 -07:00
Jesse Squires
bbd0b93d26 improve message isEqual and hash. unit test. 2014-04-27 15:52:34 -07:00
Jesse Squires
ef5bd9055e improve layout attrs hash and isEqual. unit test 2014-04-27 15:51:27 -07:00
Jesse Squires
9b9ba27f4b update docs 2014-04-27 15:16:38 -07:00
Jesse Squires
cfe5adc2b3 layout attrs test. fix layout attrs isEqual bug. 2014-04-27 15:15:22 -07:00
Jesse Squires
a765fd9897 unit tests. layout tests 2014-04-27 15:02:10 -07:00
Jesse Squires
6f265f68d1 fix flow layout init and mem leak 2014-04-27 14:50:23 -07:00
Jesse Squires
95b927c903 update docs 2014-04-27 14:49:30 -07:00
Jesse Squires
f7cf63cfcf update demo 2014-04-27 14:27:31 -07:00
Jesse Squires
cc00acfb66 tweak spring behavior 2014-04-27 14:27:20 -07:00
Jesse Squires
8db1e1d959 stub out new tests 2014-04-27 12:10:41 -07:00
Jesse Squires
fb33e14894 begin keyboard controller tests 2014-04-27 12:04:56 -07:00
Jesse Squires
a5f0533645 vc tests 2014-04-27 11:46:52 -07:00
Jesse Squires
09d5f6f8cb unit test header docs 2014-04-27 11:43:01 -07:00
Jesse Squires
454afd7947 update docs 2014-04-27 11:40:24 -07:00
Jesse Squires
2075b4b440 time stamp formatter tests 2014-04-27 11:38:18 -07:00
Jesse Squires
3d90d29578 doc fixes 2014-04-27 11:20:14 -07:00
Jesse Squires
16e2a54373 new header comments 2014-04-27 11:05:24 -07:00
Jesse Squires
1ae002d148 update spec 2014-04-26 16:21:15 -07:00
Jesse Squires
56f405c3fe tweak spring behavior 2014-04-26 16:18:20 -07:00
Jesse Squires
010de028f2 tweak spring behavior 2014-04-26 16:16:36 -07:00
Jesse Squires
e7016347b7 update demo 2014-04-26 16:05:20 -07:00
Jesse Squires
97582097ff integrate showing load messages header 2014-04-26 16:02:44 -07:00
Jesse Squires
e087f7d427 update collection view for header 2014-04-26 15:58:56 -07:00
Jesse Squires
eb066d0255 tweak header view 2014-04-26 15:56:48 -07:00
Jesse Squires
8d96dc19bf update header 2014-04-26 15:55:54 -07:00
Jesse Squires
96c4fb004e add load earlier messages header view 2014-04-26 15:06:26 -07:00
Jesse Squires
c5b966cc98 less stringly-typed obj-c 2014-04-26 13:50:23 -07:00
Jesse Squires
dab5ab384b tweak timestamp 2014-04-26 13:28:35 -07:00
Jesse Squires
8a74118cdd copy placeholder 2014-04-26 12:55:13 -07:00
Jesse Squires
a63089407c isEqual and hash for JSQMessage 2014-04-26 12:55:01 -07:00
Jesse Squires
e18002f035 update schemes and proj settings 2014-04-26 12:39:16 -07:00
Jesse Squires
f7f9fa67a3 dealloc 2014-04-26 11:12:52 -07:00
Jesse Squires
8462740e6a change default spring resistance 2014-04-24 23:55:47 -07:00
Jesse Squires
088bbd5b2e pod specs 2014-04-24 23:53:32 -07:00
Jesse Squires
c48a374075 update demo 2014-04-24 23:43:49 -07:00
Jesse Squires
99475aef52 optimizations 2014-04-24 23:23:40 -07:00
Jesse Squires
faf23dd004 docs. 2014-04-24 22:58:35 -07:00
Jesse Squires
96b13f1dc4 formatter tests 2014-04-24 22:54:45 -07:00
Jesse Squires
5255f69a12 remove spaces from test directory names 2014-04-24 22:40:04 -07:00
Jesse Squires
ad3c9d219d begin formatter tests 2014-04-24 22:38:39 -07:00
Jesse Squires
6c711b5199 proj organization 2014-04-24 22:35:21 -07:00
Jesse Squires
4553eb127b fix layout bug 2014-04-24 22:33:13 -07:00
Jesse Squires
5ad9c62590 demo comments 2014-04-24 22:25:13 -07:00
Jesse Squires
35d3dd1944 fix layout and rotation bugs and constraint warnings 2014-04-24 22:11:04 -07:00
Jesse Squires
b3fe29290f simplify travis script and schemes 2014-04-24 21:52:57 -07:00
Jesse Squires
b82c343fce update travis script 2014-04-23 21:44:02 -07:00
Jesse Squires
c506538f99 add finishReceivingMessage. ref #172 2014-04-23 21:36:18 -07:00
Jesse Squires
8207ebd7ab clean up docs 2014-04-22 22:43:14 -07:00
Jesse Squires
315d960d7d clean up. docs. asserts. 2014-04-22 22:35:34 -07:00
Jesse Squires
2da57e27b3 refactor toolbar content view prop names 2014-04-22 22:32:35 -07:00
Jesse Squires
15287977fe clean up. docs. asserts. 2014-04-22 22:29:08 -07:00
Jesse Squires
6210a16612 clean up. docs. asserts. 2014-04-22 22:12:31 -07:00
Jesse Squires
17d8dc9f44 clean up. docs. asserts. 2014-04-22 22:06:37 -07:00
Jesse Squires
2ef4d274d5 clean up. docs fixes. asserts. 2014-04-22 21:51:15 -07:00
Jesse Squires
5c26d81a00 close #221. refactoring. 2014-04-21 23:28:09 -07:00
Jesse Squires
364f4ef462 add VC properties to easily show/hide typing indicator. update demo. ref #172 2014-04-21 22:04:32 -07:00
Jesse Squires
8e141438de add typing indicator. close #58. ref #172. 2014-04-21 21:15:15 -07:00
Jesse Squires
085354c527 toolbar content view docs 2014-04-20 22:47:26 -07:00
Jesse Squires
c77a64e55e toolbar docs 2014-04-20 22:35:13 -07:00
Jesse Squires
01e4c8afae collection view cell docs 2014-04-20 22:20:45 -07:00
Jesse Squires
d41f0a1de4 collection view docs 2014-04-20 12:14:54 -07:00
Jesse Squires
c65b015090 collection view delegate docs 2014-04-20 12:10:36 -07:00
Jesse Squires
008a5c7810 data source docs 2014-04-20 11:57:08 -07:00
Jesse Squires
83ef7e3555 clean up 2014-04-20 11:03:01 -07:00
Jesse Squires
a8fd950e82 improve rotation handling 2014-04-20 10:52:58 -07:00
Jesse Squires
729ae80933 Merge pull request #233 from andrewchae/version_5_offsets
Update collection view insets and scroll to bottom when textView size changes
2014-04-18 17:39:46 -07:00
Andrew Chae
b5621cce63 Only scroll to bottom on textview size changes when automaticallyScrollsToMostRecentMessage is set 2014-04-18 17:09:04 -07:00
Andrew Chae
987a3bc5e6 Update collection view insets and scroll to bottom when textView size changes 2014-04-18 15:57:52 -07:00
Jesse Squires
5436bd3a7d reset statusBarChangeInHeight on rotation. and dismiss keyboard. cc: issue #219 2014-04-17 22:44:49 -07:00
Jesse Squires
5fd9fa22e7 disable keyboard interaction during dismiss 2014-04-17 22:11:31 -07:00
Jesse Squires
fe1e6824a0 only dismiss keyboard if dragging down near input bar 2014-04-17 22:08:55 -07:00
Jesse Squires
648376c273 minor tweaks to keyboard controller 2014-04-17 21:30:00 -07:00
Jesse Squires
2d8b14fef5 Merge pull request #231 from koenbud/issue_213_fix
issue_213_fix - Check if keyboard is visible before animations
2014-04-17 20:25:56 -07:00
Koen Buddelmeijer
39c2d93cd6 Improved keyboardViewIsHidden calculation 2014-04-17 10:03:07 -07:00
Koen Buddelmeijer
0e2b157c7c Compare using <= 0.0f instead of == 0.0f
Slightly more robust, in case the keyboard is ever positioned even
lower than the height of contextView.
2014-04-17 06:08:26 -07:00
Koen Buddelmeijer
994cc03580 Returning early instead of large conditional block
In response to jessesquires pointers in the pull request issue_213_fix.
2014-04-17 03:48:49 -07:00
Jesse Squires
a7d8303869 properly handle in-call status bar. fix #219. remove legacy toolbar frame changing code that used to be necessary with DAKeyboardControl. 2014-04-16 23:53:23 -07:00
Jesse Squires
20a1d1ad9b contributing.md 2014-04-16 21:35:43 -07:00
Koen Buddelmeijer
d10f5cb477 Check if keyboard is visible before animations
Check if keyboard is visible before performing animations on it after
UIGestureRecognizerStateEnded.
When the user swipes down too fast, the keyboard is no longer visible,
but jsq_handlePanGestureRecognizer:(UIPanGestureRecognizer *)pan still
tried to animate it.
2014-04-16 18:06:33 -07:00
Jesse Squires
c72cc1c97c keyboard controller docs. 2014-04-13 20:44:15 -07:00
Jesse Squires
20c913ab8d auto-accept le auto-correct. fix #25. credz to @mabmierau 2014-04-13 13:27:25 -07:00
Jesse Squires
2500e90b92 prevent observers from being added more than once during interactive transitions 2014-04-10 23:28:41 -07:00
Jesse Squires
de8a167929 refactor cell delegate 2014-04-10 22:40:36 -07:00
Jesse Squires
b00fa9dbfc implement tap avatar. ref #172 2014-04-10 21:57:40 -07:00
Jesse Squires
d3594d9067 implement copy & paste messages. ref #172 2014-04-10 21:35:22 -07:00
Jesse Squires
ce8b11a8c2 edit VC docs 2014-04-09 23:21:57 -07:00
Jesse Squires
b02e1aad6e refine image category. 2014-04-09 23:20:14 -07:00
Jesse Squires
98f1a16e9b text view and label docs 2014-04-09 23:11:34 -07:00
Jesse Squires
edabf8086f layout attribute docs ref #172 2014-04-09 23:05:23 -07:00
Jesse Squires
d332e7b502 doc formatting 2014-04-09 21:13:13 -07:00
Jesse Squires
ad1a17f223 fix comments 2014-04-09 20:32:49 -07:00
Jesse Squires
8fc2468a50 JSQMessagesCollectionViewFlowLayout docs. ref #172 2014-04-08 23:38:31 -07:00
Jesse Squires
13bade879a message VC docs. ref #172 2014-04-07 23:40:59 -07:00
Jesse Squires
910b3a1793 timestamp formatter docs. ref #172 2014-04-07 22:48:29 -07:00
Jesse Squires
c173026407 button factory docs 2014-04-07 22:19:02 -07:00
Jesse Squires
f43090d873 avatar factory docs 2014-04-07 22:12:54 -07:00
Jesse Squires
c5931a8f34 remove DAKeyboardControl 2014-04-06 12:27:40 -07:00
Jesse Squires
34fbeea2ac clean up 2014-04-06 12:25:15 -07:00
Jesse Squires
dd75cb3ccd first pass completion on new keyboard control. ref #172 2014-04-06 12:21:20 -07:00
Jesse Squires
294b91104a more progress on keyboard controller 2014-04-06 11:01:59 -07:00
Jesse Squires
cda9f0ffdc implement new keyboard controller. remove DAKeyboardControl. that thing is awful. 2014-04-05 20:07:15 -07:00
Jesse Squires
38f288a7cf add keyboard controller 2014-04-05 17:31:14 -07:00
Jesse Squires
cc47da1120 new view category method 2014-04-05 16:57:41 -07:00
Jesse Squires
36de9c4cc7 enable data detectors by default 2014-04-05 13:03:50 -07:00
Jesse Squires
9b7ab359b7 add textViewFrameInsets to attributes 2014-04-05 12:55:48 -07:00
Jesse Squires
593260171e refactor text container insets property name 2014-04-05 11:57:55 -07:00
Jesse Squires
5016eca08a add text view frame insets constraints 2014-04-05 11:53:12 -07:00
Jesse Squires
4659173e52 trash the tabbar example. that’s terrible UX. this VC should be displayed fullscreen. tabbar should not be showing. no one does that. 2014-04-05 11:37:07 -07:00
Jesse Squires
324e1b3532 Merge pull request #210 from arush/version_5_release
changed viewWillDissappear to viewDidDissappear as the former is not always called
2014-04-05 11:21:43 -07:00
Arush Sehgal
5b0b560616 changed viewWillDissappear to viewDidDissappear as there are some cases where viewWillDissappear is not called and leaves dangling KVObservers 2014-04-05 01:10:32 +01:00
Jesse Squires
e1e7d296bc more robust layout attribute isEqual 2014-04-02 23:19:01 -07:00
Jesse Squires
54a110660b refactor layout to specify avatar size for incoming OR outgoing 2014-04-02 23:16:48 -07:00
Jesse Squires
86dfa19267 refactor cell property names 2014-04-02 22:49:14 -07:00
Jesse Squires
42bbcef8ac clean up 2014-04-02 22:43:35 -07:00
Jesse Squires
4c6876285c clean up 2014-04-02 22:41:17 -07:00
Jesse Squires
4dc36c2ca1 fix scrolling 2014-04-02 22:37:25 -07:00
Jesse Squires
2b1edebb39 vc clean up. fix scroll to bottom issue. 2014-04-02 22:19:48 -07:00
Jesse Squires
a948140f15 refactor / refine layout and cell prototypes. move bubble font to layout / attributes. 2014-04-02 21:40:53 -07:00
Jesse Squires
2f7f75e4d4 re-organize directories 2014-03-30 21:27:13 -07:00
Jesse Squires
1a11ed10e7 edit demo. begin tabbar example. 2014-03-30 20:55:54 -07:00
Jesse Squires
8467c4cba8 tweak storyboard 2014-03-30 17:41:43 -07:00
Jesse Squires
5f91045fef comments 2014-03-30 17:25:46 -07:00
Jesse Squires
61c74ae700 update demo 2014-03-30 16:59:52 -07:00
Jesse Squires
15d39a035c improve bubble size calculations. ref: #172 2014-03-30 16:52:35 -07:00
Jesse Squires
79b0a1bfab tweak layout. update demo content 2014-03-30 16:18:19 -07:00
Jesse Squires
f2d673df6f refactoring 2014-03-30 16:04:54 -07:00
Jesse Squires
3e0e77730a calculate correct bubble widths, ref: #172 2014-03-30 15:55:07 -07:00
Jesse Squires
0513a7cc15 move bubble size calculations to layout and cache them. 2014-03-30 14:49:02 -07:00
Jesse Squires
7872368ff2 update demo 2014-03-30 14:19:25 -07:00
Jesse Squires
34c8122fb0 tweak demo content 2014-03-30 14:15:13 -07:00
Jesse Squires
80e7812e8d fix custom layout and scrolling bug 2014-03-30 13:39:17 -07:00
Jesse Squires
4303d7ac6d update default spring resistance 2014-03-30 13:20:05 -07:00
Jesse Squires
5b3503ca9e refactor initialization flow. update demo. 2014-03-30 13:18:28 -07:00
Jesse Squires
dca211964f properly override collectionViewLayout property 2014-03-30 11:14:12 -07:00
Jesse Squires
e60a8a2f54 clean up constants. add avatar size to layout and attributes. 2014-03-30 11:07:37 -07:00
Jesse Squires
e44eab3b32 tweak unit test 2014-03-29 23:58:01 -07:00
Jesse Squires
ff16f64b89 move textContainerInsets to layout. update item size calculations for new layout delegate methods 2014-03-29 23:48:34 -07:00
Jesse Squires
c0425b7e8c update demo 2014-03-29 23:05:47 -07:00
Jesse Squires
36de500d75 refactor / refine / implement collection view delegate 2014-03-29 22:59:22 -07:00
Jesse Squires
c77ca0d197 fix crash 2014-03-29 21:47:48 -07:00
Jesse Squires
04fcb1cfba layout delegate 2014-03-29 21:45:43 -07:00
Jesse Squires
71710399a6 refine data source 2014-03-29 21:32:40 -07:00
Jesse Squires
e173f3f6b7 refactor flow layout 2014-03-29 18:06:02 -07:00
Jesse Squires
254fbeb1e5 refactor data source methods 2014-03-29 18:05:34 -07:00
Jesse Squires
92858080ae make all data protocol methods required 2014-03-29 18:02:50 -07:00
Jesse Squires
ff93c0f3ac tweak attribute properties 2014-03-29 18:02:22 -07:00
Jesse Squires
79a311685e refactor delegate / data source methods. begin custom layout 2014-03-29 17:14:20 -07:00
Jesse Squires
4eeb6c0caf refactor cell properties and prototypes 2014-03-29 17:06:46 -07:00
Jesse Squires
fde0bc7f28 custom layout attributes class 2014-03-29 13:36:21 -07:00
Jesse Squires
d603c7153b simplify interface for customization. clean up. 2014-03-29 13:13:20 -07:00
Jesse Squires
2ce8e413a4 view category docs 2014-03-28 22:32:34 -07:00
Jesse Squires
7a92308dc2 fix placeholder text distortion on rotation 2014-03-28 21:31:25 -07:00
Jesse Squires
da13cc302c refactor layout properties. fix scroll to bottom issue. 2014-03-25 22:32:12 -07:00
Jesse Squires
06e1502476 refactor delegate for pressing send and accessory buttons. 2014-03-25 22:13:59 -07:00
Jesse Squires
72ab4db082 minor edits 2014-03-25 21:53:12 -07:00
Jesse Squires
7cbc4789dc clean up imports 2014-03-25 19:22:18 -07:00
Jesse Squires
cbd87be5ed remove refs to supplementary view 2014-03-25 19:15:09 -07:00
Jesse Squires
08ae2259ae remove supplementary view 2014-03-25 19:12:03 -07:00
Jesse Squires
6e7e2a78ad clean up flowlayout 2014-03-25 19:10:48 -07:00
Jesse Squires
680af644d7 remove default item size 2014-03-22 23:35:22 -07:00
Jesse Squires
d5eadfba63 edit collection view setup 2014-03-22 20:12:40 -07:00
Jesse Squires
cc324e7655 edit supplementary view constants 2014-03-22 20:12:00 -07:00
Jesse Squires
a58245de64 fix nib bug 2014-03-22 17:35:56 -07:00
Jesse Squires
e9eaddef49 fix default items in section 2014-03-22 12:15:58 -07:00
Jesse Squires
05671a56c8 supplementary view kind strings. register supplementary view nibs. 2014-03-22 12:06:09 -07:00
Jesse Squires
3dc2d7546b rename label class 2014-03-22 11:43:30 -07:00
Jesse Squires
42ae44b751 supplementary view 2014-03-22 11:32:47 -07:00
Jesse Squires
646f7b3c4e toolbar unit tests 2014-03-21 23:35:34 -07:00
Jesse Squires
dcc364e126 bubble image factory tests 2014-03-20 22:30:18 -07:00
Jesse Squires
2b10d0f2f9 avatar factory tests 2014-03-20 22:08:46 -07:00
Jesse Squires
72442413af fix test comment 2014-03-20 00:20:51 -07:00
Jesse Squires
7b79b8232a fix tests 2014-03-20 00:20:16 -07:00
Jesse Squires
ee464c535a modal demo 2014-03-19 23:47:37 -07:00
Jesse Squires
afc975e584 clean up / refactoring. 2014-03-19 23:22:50 -07:00
Jesse Squires
252eb6c19d make springiness optional 2014-03-19 23:20:50 -07:00
Jesse Squires
6c663cd874 clean up 2014-03-19 22:59:26 -07:00
Jesse Squires
f8e9d7684f finish category tests 2014-03-18 09:43:42 -07:00
Jesse Squires
b82f5b44a4 minor changes 2014-03-17 23:32:37 -07:00
Jesse Squires
36238e3c84 unit test color category 2014-03-17 23:10:11 -07:00
Jesse Squires
07e3a32d69 string category unit tests. 2014-03-16 19:02:30 -07:00
Jesse Squires
867cc72901 jsqmessage docs 2014-03-16 18:48:01 -07:00
Jesse Squires
311015bc61 copy jsqmessage date 2014-03-16 18:46:20 -07:00
Jesse Squires
451ac87db7 refine date formatter 2014-03-16 18:44:10 -07:00
Jesse Squires
18e7d316f2 timestamps ref: #172 2014-03-15 12:12:08 -07:00
Jesse Squires
5705f3fcff time stamp attributes and styles 2014-03-15 12:08:16 -07:00
Jesse Squires
fbaa881504 begin timestamp formatter 2014-03-15 12:02:38 -07:00
Jesse Squires
f4ff3eb954 layout improvements 2014-03-12 23:08:48 -07:00
Jesse Squires
c9718c9b0c refactor 2014-03-12 20:58:32 -07:00
Jesse Squires
1318713503 header docs 2014-03-12 00:45:18 -07:00
Jesse Squires
5c74d16e74 bouncy bubbles! ref: #172 2014-03-12 00:40:42 -07:00
Jesse Squires
8697e7fb78 update demo 2014-03-11 01:05:25 -07:00
Jesse Squires
cf0fc8f8e5 fix left bar button item pressed bug 2014-03-11 00:48:52 -07:00
Jesse Squires
99446f2f3b implement VC delegate in demo 2014-03-11 00:41:05 -07:00
Jesse Squires
bd2635fb73 fix storyboard warnings for cell reuse id 2014-03-11 00:40:47 -07:00
Jesse Squires
39bbc898c6 prefix soundplayer category methods 2014-03-11 00:40:06 -07:00
Jesse Squires
df00e3404c update proj settings for xcode 5.1 2014-03-11 00:39:44 -07:00
Jesse Squires
ce1da7bcee refactor has text method 2014-03-11 00:25:06 -07:00
Jesse Squires
54e37e0cac refactor toolbar methods 2014-03-10 11:44:06 -07:00
Jesse Squires
97f8555229 fix remove observer bug when popping VC 2014-03-10 10:57:21 -07:00
Jesse Squires
b00805719b demo improvements and comments 2014-03-10 10:52:50 -07:00
Jesse Squires
94f229a539 add AwesomeChat to readme 2014-03-09 23:57:44 -07:00
Jesse Squires
b3e26a7498 refine cell height calculations. fix bug where really long message would be truncated. add test in demo. 2014-03-09 23:43:05 -07:00
Jesse Squires
d26545b112 refactor layout delegate 2014-03-09 17:36:37 -07:00
Jesse Squires
05c3c30f91 compute cell heights for text. ref: #172 2014-03-09 17:26:59 -07:00
Jesse Squires
7616ce5f28 layout tweaks and demo bug fixes 2014-03-09 16:48:09 -07:00
Jesse Squires
46a5022939 fix demo bugs 2014-03-09 16:10:07 -07:00
Jesse Squires
d718020d25 get basic demo working 2014-03-09 15:45:55 -07:00
Jesse Squires
3e70a95831 set default cell identifiers 2014-03-09 12:29:13 -07:00
Jesse Squires
8082519e71 tweak rotation. fix layout bug if push from landscape. 2014-03-09 11:50:22 -07:00
Jesse Squires
38217f56eb tweak rotation methods 2014-03-09 11:13:34 -07:00
Jesse Squires
6bc7e4b169 jsqmessage helper method. refine VC delegate methods 2014-03-09 11:09:39 -07:00
Jesse Squires
143dbcf434 toolbar button factory. refine input toolbar and its delegate. add target/actions to toolbar buttons via KVO. 2014-03-09 10:30:46 -07:00
Jesse Squires
ff45ec0d06 stub out method TODOs 2014-03-08 23:42:46 -08:00
Jesse Squires
b203ff3854 add string category. add input toolbar helper method. textview delegate methods. refactoring. implement some method TODOs. 2014-03-08 23:14:42 -08:00
Jesse Squires
e7e6aad748 stub out delegate and data source methods and other method TODOs 2014-03-08 20:20:04 -08:00
Jesse Squires
0dc7c5bb21 re-add demo avatars 2014-03-08 18:56:42 -08:00
Jesse Squires
51ed4773ff update demo 2014-03-08 18:51:07 -08:00
Jesse Squires
e80240cc55 upgrade only xctool 2014-03-08 18:40:04 -08:00
Jesse Squires
051d5b63e9 edit before install 2014-03-08 18:34:41 -08:00
Jesse Squires
17282ef553 edit beforeInstall scripts. set 7.0 simulator 2014-03-08 18:31:24 -08:00
Jesse Squires
3587591c41 update travis script 2014-03-08 18:21:19 -08:00
Jesse Squires
f0ca89cf93 remove freshInstall 2014-03-08 18:16:02 -08:00
Jesse Squires
fe8dd1b750 switch from storyboard to nib. allow storyboard or programmatic init. tweak/refactor demo. begin unit tests. 2014-03-08 18:15:33 -08:00
Jesse Squires
85a65f7906 new demo project. refactor and reorganize. remove old code and assets. ref: #172 2014-03-08 17:10:49 -08:00
Jesse Squires
890c4e1408 add custom label subclass for padding text 2014-03-08 13:18:13 -08:00
Jesse Squires
52ea59c680 refactor rotation code 2014-03-08 12:29:42 -08:00
Jesse Squires
dba6bd6a71 add placeholder text to composer text view. 2014-03-08 12:28:11 -08:00
Jesse Squires
933b0bcbac system sound player category 2014-03-08 11:44:05 -08:00
Jesse Squires
f66b041915 minor refinements 2014-03-08 11:36:39 -08:00
Jesse Squires
d76647a773 prefix private methods 2014-03-08 11:10:55 -08:00
Jesse Squires
06c961ad60 adjust composer text view properties. finished text view growing/shrinking when typing. ref: #172 2014-03-08 11:07:16 -08:00
Jesse Squires
6a70a141b9 increase input text view while typing. ref #172 2014-03-07 21:00:59 -08:00
Jesse Squires
d387abc3d9 rename JSMessage —> JSQMessage 2014-03-07 11:59:29 -07:00
Jesse Squires
cb2ea02cbc rename protocol —> JSQMessageData 2014-03-07 11:54:42 -07:00
Jesse Squires
c4f3b2ba11 new avatar factory method for iOS 7 avatars. update demo. ref: #172 2014-03-02 19:43:27 -08:00
Jesse Squires
a1acbd36d9 refactor cell property names 2014-03-02 19:01:54 -08:00
Jesse Squires
2ecfd327bf bubble padding property with default. left padding for outgoing. right padding for incoming. 2014-03-02 18:59:36 -08:00
Jesse Squires
429db56bc0 cell and bubble factory refactoring 2014-03-02 18:44:34 -08:00
Jesse Squires
90a364ef29 refactor cell property names 2014-03-02 18:39:08 -08:00
Jesse Squires
514a4b36b9 adjust default avatar size 2014-03-02 18:36:04 -08:00
Jesse Squires
9aed5929ef fix constraint issues. display bubble view. ref: #172 2014-03-02 12:47:09 -08:00
Jesse Squires
8ce8d9ca69 bug fixes. add bubble views. finished bubble image view factory. update factory docs. 2014-03-02 12:22:12 -08:00
Jesse Squires
75974c13b1 project re-organization 2014-03-02 11:52:03 -08:00
Jesse Squires
7e37f91ad8 rename avatar factory. begin bubble image factory. 2014-03-02 11:49:01 -08:00
Jesse Squires
b7510b3bbe new avatar factory 2014-03-02 11:43:58 -08:00
Jesse Squires
12b8d9a011 refactor category. style toolbar. 2014-03-02 11:13:18 -08:00
Jesse Squires
ae105c22fe image view category 2014-03-02 11:01:28 -08:00
Jesse Squires
8182bc26aa rename color category 2014-03-02 10:50:07 -08:00
Jesse Squires
26108b8ddd refactor color category 2014-03-02 10:43:57 -08:00
Jesse Squires
b76bfec86e refine input textview 2014-03-02 01:23:56 -08:00
Jesse Squires
2a213001ff refine use of constants and default values. finish collection cell setters. ref: #172 2014-03-02 00:58:17 -08:00
Jesse Squires
9c889e582a refine cell properties, constraints, getters, setters. 2014-03-02 00:05:35 -08:00
Jesse Squires
32b0ede235 tweak defaults. edit demo. ref: #172 2014-03-01 22:10:00 -08:00
Jesse Squires
802d4dbd66 refactor collection cell properties and add default values. 2014-03-01 21:56:47 -08:00
Jesse Squires
d755624bc5 hookup cell constraint outlets. 2014-03-01 20:16:27 -08:00
Jesse Squires
451dcbb78a rename storyboard. refine collection view insets. 2014-03-01 19:57:40 -08:00
Jesse Squires
5f0e36023a use DAKeyboardControl for keyboard show/hide. ref: #172. closes #118. 2014-03-01 19:36:53 -08:00
Jesse Squires
830e8a6238 new custom input toolbar with content view. ref: #172 2014-03-01 17:13:22 -08:00
Jesse Squires
ae606e10e0 Merge branch 'master' into version_5_release 2014-02-27 23:48:32 -08:00
Jesse Squires
e15937838b complete layout for incoming cell prototype. tweak layout cell spacing. enable landscape mode. handle layout on rotation. ref: #172 2014-02-24 00:30:47 -08:00
Jesse Squires
ef2f09c3b2 streamline previous delegate design. messageType delegate method not needed because we have message.sender 2014-02-24 00:02:05 -08:00
Jesse Squires
4c67b4999e initial setup and layout for collection view and cells. complete layout for outgoing cell prototype. ref: #172 2014-02-23 23:52:31 -08:00
Jesse Squires
6fdb742e9c stub out collection view cells 2014-02-23 19:38:19 -08:00
Jesse Squires
35e606c1ff stub out collection view classes 2014-02-23 19:31:43 -08:00
Jesse Squires
b8e76ed136 storyboard 2014-02-23 16:32:59 -08:00
Jesse Squires
8efa202e47 begin new collection view storyboard 2014-02-23 13:08:26 -08:00
Jesse Squires
cd23e61945 drop iOS 6 support. remove iOS 6 assets. strip iOS 6 code. ref: #172 2014-02-23 12:52:17 -08:00
Jesse Squires
dbc0d09083 clean up old screenshots 2014-02-23 12:04:54 -08:00
341 changed files with 16443 additions and 4885 deletions

7
.cocoadocs.yml Normal file
View File

@ -0,0 +1,7 @@
highlight-color: "#43D15B"
highlight-dark-color: "#319942"
darker-color: "#8A998C"
darker-dark-color: "#454D46"
background-color: "#D7E5CF"
alt-link-color: "#00A81C"
warning-color: "#0FB82B"

View File

@ -4,7 +4,7 @@ before_install:
- export LANG=en_US.UTF-8
- gem install cocoapods
- brew update
- brew upgrade xctool
- if brew outdated | grep -qx xctool; then brew upgrade xctool; fi
script:
- xctool clean build test -workspace JSMessagesDemo.xcworkspace -scheme JSMessagesDemo -sdk iphonesimulator6.0 ONLY_ACTIVE_ARCH=NO
- xctool clean build test -workspace JSQMessages.xcworkspace -scheme JSQMessages -sdk iphonesimulator7.1 ONLY_ACTIVE_ARCH=NO

BIN
Assets/JSQmessages.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

3
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,3 @@
## How To Contribute
Please follow these sweet [contribution guidelines](https://github.com/jessesquires/HowToContribute).

View File

@ -1,987 +0,0 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
6404600EC231419EB620AF48 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6AFF9601294443728D9481E0 /* libPods.a */; };
885BAC4E185421C8004DCD34 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 885BAC4D185421C8004DCD34 /* QuartzCore.framework */; };
885BAC4F185421D4004DCD34 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 885BAC4D185421C8004DCD34 /* QuartzCore.framework */; };
88A3FB4A182E079E00705327 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 88A3FB49182E079E00705327 /* Foundation.framework */; };
88A3FB4C182E079E00705327 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 88A3FB4B182E079E00705327 /* CoreGraphics.framework */; };
88A3FB4E182E079E00705327 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 88A3FB4D182E079E00705327 /* UIKit.framework */; };
88A3FB54182E079E00705327 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 88A3FB52182E079E00705327 /* InfoPlist.strings */; };
88A3FB56182E079E00705327 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 88A3FB55182E079E00705327 /* main.m */; };
88A3FB5A182E079E00705327 /* JSAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 88A3FB59182E079E00705327 /* JSAppDelegate.m */; };
88A3FB63182E079E00705327 /* JSDemoViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 88A3FB62182E079E00705327 /* JSDemoViewController.m */; };
88A3FB65182E079E00705327 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 88A3FB64182E079E00705327 /* Images.xcassets */; };
88A3FB6C182E079E00705327 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 88A3FB6B182E079E00705327 /* XCTest.framework */; };
88A3FB6D182E079E00705327 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 88A3FB49182E079E00705327 /* Foundation.framework */; };
88A3FB6E182E079E00705327 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 88A3FB4D182E079E00705327 /* UIKit.framework */; };
88A3FB76182E079E00705327 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 88A3FB74182E079E00705327 /* InfoPlist.strings */; };
88A3FB78182E079E00705327 /* JSMessagesDemoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 88A3FB77182E079E00705327 /* JSMessagesDemoTests.m */; };
88A3FC59182E0EA000705327 /* NSString+JSMessagesView.m in Sources */ = {isa = PBXBuildFile; fileRef = 88A3FC1D182E0EA000705327 /* NSString+JSMessagesView.m */; };
88A3FC5A182E0EA000705327 /* NSString+JSMessagesView.m in Sources */ = {isa = PBXBuildFile; fileRef = 88A3FC1D182E0EA000705327 /* NSString+JSMessagesView.m */; };
88A3FC5D182E0EA000705327 /* UIColor+JSMessagesView.m in Sources */ = {isa = PBXBuildFile; fileRef = 88A3FC21182E0EA000705327 /* UIColor+JSMessagesView.m */; };
88A3FC5E182E0EA000705327 /* UIColor+JSMessagesView.m in Sources */ = {isa = PBXBuildFile; fileRef = 88A3FC21182E0EA000705327 /* UIColor+JSMessagesView.m */; };
88A3FC61182E0EA000705327 /* UIImage+JSMessagesView.m in Sources */ = {isa = PBXBuildFile; fileRef = 88A3FC25182E0EA000705327 /* UIImage+JSMessagesView.m */; };
88A3FC62182E0EA000705327 /* UIImage+JSMessagesView.m in Sources */ = {isa = PBXBuildFile; fileRef = 88A3FC25182E0EA000705327 /* UIImage+JSMessagesView.m */; };
88A3FC63182E0EA000705327 /* JSAvatarImageFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = 88A3FC28182E0EA000705327 /* JSAvatarImageFactory.m */; };
88A3FC64182E0EA000705327 /* JSAvatarImageFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = 88A3FC28182E0EA000705327 /* JSAvatarImageFactory.m */; };
88A3FC65182E0EA000705327 /* JSBubbleImageViewFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = 88A3FC2A182E0EA000705327 /* JSBubbleImageViewFactory.m */; };
88A3FC66182E0EA000705327 /* JSBubbleImageViewFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = 88A3FC2A182E0EA000705327 /* JSBubbleImageViewFactory.m */; };
88A3FC67182E0EA000705327 /* JSBubbleMessageCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 88A3FC2C182E0EA000705327 /* JSBubbleMessageCell.m */; };
88A3FC68182E0EA000705327 /* JSBubbleMessageCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 88A3FC2C182E0EA000705327 /* JSBubbleMessageCell.m */; };
88A3FC69182E0EA000705327 /* JSBubbleView.m in Sources */ = {isa = PBXBuildFile; fileRef = 88A3FC2E182E0EA000705327 /* JSBubbleView.m */; };
88A3FC6A182E0EA000705327 /* JSBubbleView.m in Sources */ = {isa = PBXBuildFile; fileRef = 88A3FC2E182E0EA000705327 /* JSBubbleView.m */; };
88A3FC6B182E0EA000705327 /* JSDismissiveTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 88A3FC30182E0EA000705327 /* JSDismissiveTextView.m */; };
88A3FC6C182E0EA000705327 /* JSDismissiveTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 88A3FC30182E0EA000705327 /* JSDismissiveTextView.m */; };
88A3FC6D182E0EA000705327 /* JSMessageInputView.m in Sources */ = {isa = PBXBuildFile; fileRef = 88A3FC32182E0EA000705327 /* JSMessageInputView.m */; };
88A3FC6E182E0EA000705327 /* JSMessageInputView.m in Sources */ = {isa = PBXBuildFile; fileRef = 88A3FC32182E0EA000705327 /* JSMessageInputView.m */; };
88A3FC6F182E0EA000705327 /* JSMessageSoundEffect.m in Sources */ = {isa = PBXBuildFile; fileRef = 88A3FC34182E0EA000705327 /* JSMessageSoundEffect.m */; };
88A3FC70182E0EA000705327 /* JSMessageSoundEffect.m in Sources */ = {isa = PBXBuildFile; fileRef = 88A3FC34182E0EA000705327 /* JSMessageSoundEffect.m */; };
88A3FC71182E0EA000705327 /* JSMessagesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 88A3FC36182E0EA000705327 /* JSMessagesViewController.m */; };
88A3FC72182E0EA000705327 /* JSMessagesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 88A3FC36182E0EA000705327 /* JSMessagesViewController.m */; };
88A3FC73182E0EA000705327 /* JSMessageTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 88A3FC38182E0EA000705327 /* JSMessageTextView.m */; };
88A3FC74182E0EA000705327 /* JSMessageTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 88A3FC38182E0EA000705327 /* JSMessageTextView.m */; };
88A3FC75182E0EA000705327 /* bubble-classic-blue.png in Resources */ = {isa = PBXBuildFile; fileRef = 88A3FC3C182E0EA000705327 /* bubble-classic-blue.png */; };
88A3FC76182E0EA000705327 /* bubble-classic-blue.png in Resources */ = {isa = PBXBuildFile; fileRef = 88A3FC3C182E0EA000705327 /* bubble-classic-blue.png */; };
88A3FC77182E0EA000705327 /* bubble-classic-blue@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 88A3FC3D182E0EA000705327 /* bubble-classic-blue@2x.png */; };
88A3FC78182E0EA000705327 /* bubble-classic-blue@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 88A3FC3D182E0EA000705327 /* bubble-classic-blue@2x.png */; };
88A3FC79182E0EA000705327 /* bubble-classic-gray.png in Resources */ = {isa = PBXBuildFile; fileRef = 88A3FC3E182E0EA000705327 /* bubble-classic-gray.png */; };
88A3FC7A182E0EA000705327 /* bubble-classic-gray.png in Resources */ = {isa = PBXBuildFile; fileRef = 88A3FC3E182E0EA000705327 /* bubble-classic-gray.png */; };
88A3FC7B182E0EA000705327 /* bubble-classic-gray@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 88A3FC3F182E0EA000705327 /* bubble-classic-gray@2x.png */; };
88A3FC7C182E0EA000705327 /* bubble-classic-gray@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 88A3FC3F182E0EA000705327 /* bubble-classic-gray@2x.png */; };
88A3FC7D182E0EA000705327 /* bubble-classic-green.png in Resources */ = {isa = PBXBuildFile; fileRef = 88A3FC40182E0EA000705327 /* bubble-classic-green.png */; };
88A3FC7E182E0EA000705327 /* bubble-classic-green.png in Resources */ = {isa = PBXBuildFile; fileRef = 88A3FC40182E0EA000705327 /* bubble-classic-green.png */; };
88A3FC7F182E0EA000705327 /* bubble-classic-green@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 88A3FC41182E0EA000705327 /* bubble-classic-green@2x.png */; };
88A3FC80182E0EA000705327 /* bubble-classic-green@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 88A3FC41182E0EA000705327 /* bubble-classic-green@2x.png */; };
88A3FC89182E0EA000705327 /* bubble-classic-square-blue.png in Resources */ = {isa = PBXBuildFile; fileRef = 88A3FC47182E0EA000705327 /* bubble-classic-square-blue.png */; };
88A3FC8A182E0EA000705327 /* bubble-classic-square-blue.png in Resources */ = {isa = PBXBuildFile; fileRef = 88A3FC47182E0EA000705327 /* bubble-classic-square-blue.png */; };
88A3FC8B182E0EA000705327 /* bubble-classic-square-blue@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 88A3FC48182E0EA000705327 /* bubble-classic-square-blue@2x.png */; };
88A3FC8C182E0EA000705327 /* bubble-classic-square-blue@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 88A3FC48182E0EA000705327 /* bubble-classic-square-blue@2x.png */; };
88A3FC8D182E0EA000705327 /* bubble-classic-square-gray.png in Resources */ = {isa = PBXBuildFile; fileRef = 88A3FC49182E0EA000705327 /* bubble-classic-square-gray.png */; };
88A3FC8E182E0EA000705327 /* bubble-classic-square-gray.png in Resources */ = {isa = PBXBuildFile; fileRef = 88A3FC49182E0EA000705327 /* bubble-classic-square-gray.png */; };
88A3FC8F182E0EA000705327 /* bubble-classic-square-gray@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 88A3FC4A182E0EA000705327 /* bubble-classic-square-gray@2x.png */; };
88A3FC90182E0EA000705327 /* bubble-classic-square-gray@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 88A3FC4A182E0EA000705327 /* bubble-classic-square-gray@2x.png */; };
88A3FC91182E0EA000705327 /* bubble-classic-square-selected.png in Resources */ = {isa = PBXBuildFile; fileRef = 88A3FC4B182E0EA000705327 /* bubble-classic-square-selected.png */; };
88A3FC92182E0EA000705327 /* bubble-classic-square-selected.png in Resources */ = {isa = PBXBuildFile; fileRef = 88A3FC4B182E0EA000705327 /* bubble-classic-square-selected.png */; };
88A3FC93182E0EA000705327 /* bubble-classic-square-selected@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 88A3FC4C182E0EA000705327 /* bubble-classic-square-selected@2x.png */; };
88A3FC94182E0EA000705327 /* bubble-classic-square-selected@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 88A3FC4C182E0EA000705327 /* bubble-classic-square-selected@2x.png */; };
88A3FCA5182E0EA000705327 /* message-received.aiff in Resources */ = {isa = PBXBuildFile; fileRef = 88A3FC57182E0EA000705327 /* message-received.aiff */; };
88A3FCA6182E0EA000705327 /* message-received.aiff in Resources */ = {isa = PBXBuildFile; fileRef = 88A3FC57182E0EA000705327 /* message-received.aiff */; };
88A3FCA7182E0EA000705327 /* message-sent.aiff in Resources */ = {isa = PBXBuildFile; fileRef = 88A3FC58182E0EA000705327 /* message-sent.aiff */; };
88A3FCA8182E0EA000705327 /* message-sent.aiff in Resources */ = {isa = PBXBuildFile; fileRef = 88A3FC58182E0EA000705327 /* message-sent.aiff */; };
88A6ACFF1839FE87000F56DA /* avatar-placeholder.png in Resources */ = {isa = PBXBuildFile; fileRef = 88A6ACFD1839FE87000F56DA /* avatar-placeholder.png */; };
88A6AD001839FE87000F56DA /* avatar-placeholder@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 88A6ACFE1839FE87000F56DA /* avatar-placeholder@2x.png */; };
88A6AD0E1839FE95000F56DA /* bubble-min-tailless.png in Resources */ = {isa = PBXBuildFile; fileRef = 88A6AD021839FE95000F56DA /* bubble-min-tailless.png */; };
88A6AD0F1839FE95000F56DA /* bubble-min-tailless@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 88A6AD031839FE95000F56DA /* bubble-min-tailless@2x.png */; };
88A6AD101839FE95000F56DA /* bubble-min.png in Resources */ = {isa = PBXBuildFile; fileRef = 88A6AD041839FE95000F56DA /* bubble-min.png */; };
88A6AD111839FE95000F56DA /* bubble-min@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 88A6AD051839FE95000F56DA /* bubble-min@2x.png */; };
88A6AD121839FE95000F56DA /* bubble-stroked-tailless.png in Resources */ = {isa = PBXBuildFile; fileRef = 88A6AD061839FE95000F56DA /* bubble-stroked-tailless.png */; };
88A6AD131839FE95000F56DA /* bubble-stroked-tailless@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 88A6AD071839FE95000F56DA /* bubble-stroked-tailless@2x.png */; };
88A6AD141839FE95000F56DA /* bubble-stroked.png in Resources */ = {isa = PBXBuildFile; fileRef = 88A6AD081839FE95000F56DA /* bubble-stroked.png */; };
88A6AD151839FE95000F56DA /* bubble-stroked@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 88A6AD091839FE95000F56DA /* bubble-stroked@2x.png */; };
88A6AD161839FE95000F56DA /* bubble-tailless.png in Resources */ = {isa = PBXBuildFile; fileRef = 88A6AD0A1839FE95000F56DA /* bubble-tailless.png */; };
88A6AD171839FE95000F56DA /* bubble-tailless@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 88A6AD0B1839FE95000F56DA /* bubble-tailless@2x.png */; };
88A6AD181839FE95000F56DA /* bubble.png in Resources */ = {isa = PBXBuildFile; fileRef = 88A6AD0C1839FE95000F56DA /* bubble.png */; };
88A6AD191839FE95000F56DA /* bubble@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 88A6AD0D1839FE95000F56DA /* bubble@2x.png */; };
88A6AD1F1839FFBB000F56DA /* button-photo.png in Resources */ = {isa = PBXBuildFile; fileRef = 88A6AD1B1839FFBB000F56DA /* button-photo.png */; };
88A6AD201839FFBB000F56DA /* button-photo@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 88A6AD1C1839FFBB000F56DA /* button-photo@2x.png */; };
88A6AD211839FFBB000F56DA /* input-bar-flat.png in Resources */ = {isa = PBXBuildFile; fileRef = 88A6AD1D1839FFBB000F56DA /* input-bar-flat.png */; };
88A6AD221839FFBB000F56DA /* input-bar-flat@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 88A6AD1E1839FFBB000F56DA /* input-bar-flat@2x.png */; };
88BA157A188F8FB900EDD5C0 /* JSMessageTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 88BA1579188F8FB900EDD5C0 /* JSMessageTableView.m */; };
88BA157D188F91BE00EDD5C0 /* JSMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 88BA157C188F91BE00EDD5C0 /* JSMessage.m */; };
88E43C74183011830045E52D /* input-bar-background.png in Resources */ = {isa = PBXBuildFile; fileRef = 88E43C6C183011830045E52D /* input-bar-background.png */; };
88E43C75183011830045E52D /* input-bar-background@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 88E43C6D183011830045E52D /* input-bar-background@2x.png */; };
88E43C76183011830045E52D /* input-field-cover.png in Resources */ = {isa = PBXBuildFile; fileRef = 88E43C6E183011830045E52D /* input-field-cover.png */; };
88E43C77183011830045E52D /* input-field-cover@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 88E43C6F183011830045E52D /* input-field-cover@2x.png */; };
88E43C78183011830045E52D /* send-button-pressed.png in Resources */ = {isa = PBXBuildFile; fileRef = 88E43C70183011830045E52D /* send-button-pressed.png */; };
88E43C79183011830045E52D /* send-button-pressed@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 88E43C71183011830045E52D /* send-button-pressed@2x.png */; };
88E43C7A183011830045E52D /* send-button.png in Resources */ = {isa = PBXBuildFile; fileRef = 88E43C72183011830045E52D /* send-button.png */; };
88E43C7B183011830045E52D /* send-button@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 88E43C73183011830045E52D /* send-button@2x.png */; };
88E43C7E1830138A0045E52D /* bubble-classic-selected.png in Resources */ = {isa = PBXBuildFile; fileRef = 88E43C7C1830138A0045E52D /* bubble-classic-selected.png */; };
88E43C7F1830138A0045E52D /* bubble-classic-selected@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 88E43C7D1830138A0045E52D /* bubble-classic-selected@2x.png */; };
88E43C821830147E0045E52D /* bubble-classic-typing.png in Resources */ = {isa = PBXBuildFile; fileRef = 88E43C801830147E0045E52D /* bubble-classic-typing.png */; };
88E43C831830147E0045E52D /* bubble-classic-typing@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 88E43C811830147E0045E52D /* bubble-classic-typing@2x.png */; };
FF5F1C19A2B14956BCA42EBB /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6AFF9601294443728D9481E0 /* libPods.a */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
88A3FB6F182E079E00705327 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 88A3FB3E182E079D00705327 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 88A3FB45182E079E00705327;
remoteInfo = JSMessagesDemo;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
6AFF9601294443728D9481E0 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; };
885BAC4D185421C8004DCD34 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
88A3FB46182E079E00705327 /* JSMessagesDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = JSMessagesDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
88A3FB49182E079E00705327 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
88A3FB4B182E079E00705327 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
88A3FB4D182E079E00705327 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
88A3FB51182E079E00705327 /* JSMessagesDemo-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "JSMessagesDemo-Info.plist"; sourceTree = "<group>"; };
88A3FB53182E079E00705327 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
88A3FB55182E079E00705327 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
88A3FB57182E079E00705327 /* JSMessagesDemo-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "JSMessagesDemo-Prefix.pch"; sourceTree = "<group>"; };
88A3FB58182E079E00705327 /* JSAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JSAppDelegate.h; sourceTree = "<group>"; };
88A3FB59182E079E00705327 /* JSAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JSAppDelegate.m; sourceTree = "<group>"; };
88A3FB61182E079E00705327 /* JSDemoViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JSDemoViewController.h; sourceTree = "<group>"; };
88A3FB62182E079E00705327 /* JSDemoViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = JSDemoViewController.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
88A3FB64182E079E00705327 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
88A3FB6A182E079E00705327 /* JSMessagesDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = JSMessagesDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
88A3FB6B182E079E00705327 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
88A3FB73182E079E00705327 /* JSMessagesDemoTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "JSMessagesDemoTests-Info.plist"; sourceTree = "<group>"; };
88A3FB75182E079E00705327 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
88A3FB77182E079E00705327 /* JSMessagesDemoTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JSMessagesDemoTests.m; sourceTree = "<group>"; };
88A3FC13182E0B8C00705327 /* JSMessagesViewController.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = JSMessagesViewController.podspec; sourceTree = "<group>"; };
88A3FC14182E0B8C00705327 /* LICENSE.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE.txt; sourceTree = "<group>"; };
88A3FC15182E0B8C00705327 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = text; lineEnding = 0; path = README.md; sourceTree = "<group>"; };
88A3FC1C182E0EA000705327 /* NSString+JSMessagesView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+JSMessagesView.h"; sourceTree = "<group>"; };
88A3FC1D182E0EA000705327 /* NSString+JSMessagesView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+JSMessagesView.m"; sourceTree = "<group>"; };
88A3FC20182E0EA000705327 /* UIColor+JSMessagesView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIColor+JSMessagesView.h"; sourceTree = "<group>"; };
88A3FC21182E0EA000705327 /* UIColor+JSMessagesView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = "UIColor+JSMessagesView.m"; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
88A3FC24182E0EA000705327 /* UIImage+JSMessagesView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+JSMessagesView.h"; sourceTree = "<group>"; };
88A3FC25182E0EA000705327 /* UIImage+JSMessagesView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = "UIImage+JSMessagesView.m"; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
88A3FC27182E0EA000705327 /* JSAvatarImageFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSAvatarImageFactory.h; sourceTree = "<group>"; };
88A3FC28182E0EA000705327 /* JSAvatarImageFactory.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSAvatarImageFactory.m; sourceTree = "<group>"; };
88A3FC29182E0EA000705327 /* JSBubbleImageViewFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSBubbleImageViewFactory.h; sourceTree = "<group>"; };
88A3FC2A182E0EA000705327 /* JSBubbleImageViewFactory.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = JSBubbleImageViewFactory.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
88A3FC2B182E0EA000705327 /* JSBubbleMessageCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSBubbleMessageCell.h; sourceTree = "<group>"; };
88A3FC2C182E0EA000705327 /* JSBubbleMessageCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = JSBubbleMessageCell.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
88A3FC2D182E0EA000705327 /* JSBubbleView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSBubbleView.h; sourceTree = "<group>"; };
88A3FC2E182E0EA000705327 /* JSBubbleView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = JSBubbleView.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
88A3FC2F182E0EA000705327 /* JSDismissiveTextView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSDismissiveTextView.h; sourceTree = "<group>"; };
88A3FC30182E0EA000705327 /* JSDismissiveTextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = JSDismissiveTextView.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
88A3FC31182E0EA000705327 /* JSMessageInputView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSMessageInputView.h; sourceTree = "<group>"; };
88A3FC32182E0EA000705327 /* JSMessageInputView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = JSMessageInputView.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
88A3FC33182E0EA000705327 /* JSMessageSoundEffect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSMessageSoundEffect.h; sourceTree = "<group>"; };
88A3FC34182E0EA000705327 /* JSMessageSoundEffect.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSMessageSoundEffect.m; sourceTree = "<group>"; };
88A3FC35182E0EA000705327 /* JSMessagesViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSMessagesViewController.h; sourceTree = "<group>"; };
88A3FC36182E0EA000705327 /* JSMessagesViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = JSMessagesViewController.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
88A3FC37182E0EA000705327 /* JSMessageTextView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSMessageTextView.h; sourceTree = "<group>"; };
88A3FC38182E0EA000705327 /* JSMessageTextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = JSMessageTextView.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
88A3FC3C182E0EA000705327 /* bubble-classic-blue.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "bubble-classic-blue.png"; sourceTree = "<group>"; };
88A3FC3D182E0EA000705327 /* bubble-classic-blue@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "bubble-classic-blue@2x.png"; sourceTree = "<group>"; };
88A3FC3E182E0EA000705327 /* bubble-classic-gray.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "bubble-classic-gray.png"; sourceTree = "<group>"; };
88A3FC3F182E0EA000705327 /* bubble-classic-gray@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "bubble-classic-gray@2x.png"; sourceTree = "<group>"; };
88A3FC40182E0EA000705327 /* bubble-classic-green.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "bubble-classic-green.png"; sourceTree = "<group>"; };
88A3FC41182E0EA000705327 /* bubble-classic-green@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "bubble-classic-green@2x.png"; sourceTree = "<group>"; };
88A3FC47182E0EA000705327 /* bubble-classic-square-blue.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "bubble-classic-square-blue.png"; sourceTree = "<group>"; };
88A3FC48182E0EA000705327 /* bubble-classic-square-blue@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "bubble-classic-square-blue@2x.png"; sourceTree = "<group>"; };
88A3FC49182E0EA000705327 /* bubble-classic-square-gray.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "bubble-classic-square-gray.png"; sourceTree = "<group>"; };
88A3FC4A182E0EA000705327 /* bubble-classic-square-gray@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "bubble-classic-square-gray@2x.png"; sourceTree = "<group>"; };
88A3FC4B182E0EA000705327 /* bubble-classic-square-selected.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "bubble-classic-square-selected.png"; sourceTree = "<group>"; };
88A3FC4C182E0EA000705327 /* bubble-classic-square-selected@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "bubble-classic-square-selected@2x.png"; sourceTree = "<group>"; };
88A3FC57182E0EA000705327 /* message-received.aiff */ = {isa = PBXFileReference; lastKnownFileType = audio.aiff; path = "message-received.aiff"; sourceTree = "<group>"; };
88A3FC58182E0EA000705327 /* message-sent.aiff */ = {isa = PBXFileReference; lastKnownFileType = audio.aiff; path = "message-sent.aiff"; sourceTree = "<group>"; };
88A6ACFD1839FE87000F56DA /* avatar-placeholder.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "avatar-placeholder.png"; sourceTree = "<group>"; };
88A6ACFE1839FE87000F56DA /* avatar-placeholder@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "avatar-placeholder@2x.png"; sourceTree = "<group>"; };
88A6AD021839FE95000F56DA /* bubble-min-tailless.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "bubble-min-tailless.png"; sourceTree = "<group>"; };
88A6AD031839FE95000F56DA /* bubble-min-tailless@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "bubble-min-tailless@2x.png"; sourceTree = "<group>"; };
88A6AD041839FE95000F56DA /* bubble-min.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "bubble-min.png"; sourceTree = "<group>"; };
88A6AD051839FE95000F56DA /* bubble-min@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "bubble-min@2x.png"; sourceTree = "<group>"; };
88A6AD061839FE95000F56DA /* bubble-stroked-tailless.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "bubble-stroked-tailless.png"; sourceTree = "<group>"; };
88A6AD071839FE95000F56DA /* bubble-stroked-tailless@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "bubble-stroked-tailless@2x.png"; sourceTree = "<group>"; };
88A6AD081839FE95000F56DA /* bubble-stroked.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "bubble-stroked.png"; sourceTree = "<group>"; };
88A6AD091839FE95000F56DA /* bubble-stroked@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "bubble-stroked@2x.png"; sourceTree = "<group>"; };
88A6AD0A1839FE95000F56DA /* bubble-tailless.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "bubble-tailless.png"; sourceTree = "<group>"; };
88A6AD0B1839FE95000F56DA /* bubble-tailless@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "bubble-tailless@2x.png"; sourceTree = "<group>"; };
88A6AD0C1839FE95000F56DA /* bubble.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = bubble.png; sourceTree = "<group>"; };
88A6AD0D1839FE95000F56DA /* bubble@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "bubble@2x.png"; sourceTree = "<group>"; };
88A6AD1B1839FFBB000F56DA /* button-photo.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "button-photo.png"; sourceTree = "<group>"; };
88A6AD1C1839FFBB000F56DA /* button-photo@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "button-photo@2x.png"; sourceTree = "<group>"; };
88A6AD1D1839FFBB000F56DA /* input-bar-flat.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "input-bar-flat.png"; sourceTree = "<group>"; };
88A6AD1E1839FFBB000F56DA /* input-bar-flat@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "input-bar-flat@2x.png"; sourceTree = "<group>"; };
88BA1578188F8FB900EDD5C0 /* JSMessageTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSMessageTableView.h; sourceTree = "<group>"; };
88BA1579188F8FB900EDD5C0 /* JSMessageTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSMessageTableView.m; sourceTree = "<group>"; };
88BA157B188F91BE00EDD5C0 /* JSMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSMessage.h; sourceTree = "<group>"; };
88BA157C188F91BE00EDD5C0 /* JSMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSMessage.m; sourceTree = "<group>"; };
88DB7C74189223F100D7117F /* JSMessageData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSMessageData.h; sourceTree = "<group>"; };
88E43C6C183011830045E52D /* input-bar-background.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "input-bar-background.png"; sourceTree = "<group>"; };
88E43C6D183011830045E52D /* input-bar-background@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "input-bar-background@2x.png"; sourceTree = "<group>"; };
88E43C6E183011830045E52D /* input-field-cover.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "input-field-cover.png"; sourceTree = "<group>"; };
88E43C6F183011830045E52D /* input-field-cover@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "input-field-cover@2x.png"; sourceTree = "<group>"; };
88E43C70183011830045E52D /* send-button-pressed.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "send-button-pressed.png"; sourceTree = "<group>"; };
88E43C71183011830045E52D /* send-button-pressed@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "send-button-pressed@2x.png"; sourceTree = "<group>"; };
88E43C72183011830045E52D /* send-button.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "send-button.png"; sourceTree = "<group>"; };
88E43C73183011830045E52D /* send-button@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "send-button@2x.png"; sourceTree = "<group>"; };
88E43C7C1830138A0045E52D /* bubble-classic-selected.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "bubble-classic-selected.png"; sourceTree = "<group>"; };
88E43C7D1830138A0045E52D /* bubble-classic-selected@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "bubble-classic-selected@2x.png"; sourceTree = "<group>"; };
88E43C801830147E0045E52D /* bubble-classic-typing.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "bubble-classic-typing.png"; sourceTree = "<group>"; };
88E43C811830147E0045E52D /* bubble-classic-typing@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "bubble-classic-typing@2x.png"; sourceTree = "<group>"; };
A8C230F1717F4008B2BFAEA6 /* Pods.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.xcconfig; path = Pods/Pods.xcconfig; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
88A3FB43182E079E00705327 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
885BAC4F185421D4004DCD34 /* QuartzCore.framework in Frameworks */,
88A3FB4C182E079E00705327 /* CoreGraphics.framework in Frameworks */,
88A3FB4E182E079E00705327 /* UIKit.framework in Frameworks */,
88A3FB4A182E079E00705327 /* Foundation.framework in Frameworks */,
6404600EC231419EB620AF48 /* libPods.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
88A3FB67182E079E00705327 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
885BAC4E185421C8004DCD34 /* QuartzCore.framework in Frameworks */,
88A3FB6C182E079E00705327 /* XCTest.framework in Frameworks */,
88A3FB6E182E079E00705327 /* UIKit.framework in Frameworks */,
88A3FB6D182E079E00705327 /* Foundation.framework in Frameworks */,
FF5F1C19A2B14956BCA42EBB /* libPods.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
88A3FB3D182E079D00705327 = {
isa = PBXGroup;
children = (
88A3FC19182E0EA000705327 /* JSMessagesViewController */,
88A3FC13182E0B8C00705327 /* JSMessagesViewController.podspec */,
88A3FC14182E0B8C00705327 /* LICENSE.txt */,
88A3FC15182E0B8C00705327 /* README.md */,
88A3FB4F182E079E00705327 /* JSMessagesDemo */,
88A3FB71182E079E00705327 /* JSMessagesDemoTests */,
88A3FB48182E079E00705327 /* Frameworks */,
88A3FB47182E079E00705327 /* Products */,
A8C230F1717F4008B2BFAEA6 /* Pods.xcconfig */,
);
sourceTree = "<group>";
};
88A3FB47182E079E00705327 /* Products */ = {
isa = PBXGroup;
children = (
88A3FB46182E079E00705327 /* JSMessagesDemo.app */,
88A3FB6A182E079E00705327 /* JSMessagesDemoTests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
88A3FB48182E079E00705327 /* Frameworks */ = {
isa = PBXGroup;
children = (
88A3FB4B182E079E00705327 /* CoreGraphics.framework */,
88A3FB49182E079E00705327 /* Foundation.framework */,
885BAC4D185421C8004DCD34 /* QuartzCore.framework */,
88A3FB4D182E079E00705327 /* UIKit.framework */,
88A3FB6B182E079E00705327 /* XCTest.framework */,
6AFF9601294443728D9481E0 /* libPods.a */,
);
name = Frameworks;
sourceTree = "<group>";
};
88A3FB4F182E079E00705327 /* JSMessagesDemo */ = {
isa = PBXGroup;
children = (
88A3FB58182E079E00705327 /* JSAppDelegate.h */,
88A3FB59182E079E00705327 /* JSAppDelegate.m */,
88A3FB61182E079E00705327 /* JSDemoViewController.h */,
88A3FB62182E079E00705327 /* JSDemoViewController.m */,
88A3FB64182E079E00705327 /* Images.xcassets */,
88A3FB50182E079E00705327 /* Supporting Files */,
);
path = JSMessagesDemo;
sourceTree = "<group>";
};
88A3FB50182E079E00705327 /* Supporting Files */ = {
isa = PBXGroup;
children = (
88A3FB51182E079E00705327 /* JSMessagesDemo-Info.plist */,
88A3FB52182E079E00705327 /* InfoPlist.strings */,
88A3FB55182E079E00705327 /* main.m */,
88A3FB57182E079E00705327 /* JSMessagesDemo-Prefix.pch */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
88A3FB71182E079E00705327 /* JSMessagesDemoTests */ = {
isa = PBXGroup;
children = (
88A3FB77182E079E00705327 /* JSMessagesDemoTests.m */,
88A3FB72182E079E00705327 /* Supporting Files */,
);
path = JSMessagesDemoTests;
sourceTree = "<group>";
};
88A3FB72182E079E00705327 /* Supporting Files */ = {
isa = PBXGroup;
children = (
88A3FB73182E079E00705327 /* JSMessagesDemoTests-Info.plist */,
88A3FB74182E079E00705327 /* InfoPlist.strings */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
88A3FC19182E0EA000705327 /* JSMessagesViewController */ = {
isa = PBXGroup;
children = (
88A3FC1A182E0EA000705327 /* Classes */,
88A3FC39182E0EA000705327 /* Resources */,
);
path = JSMessagesViewController;
sourceTree = "<group>";
};
88A3FC1A182E0EA000705327 /* Classes */ = {
isa = PBXGroup;
children = (
88A3FC1B182E0EA000705327 /* Categories */,
88A3FC26182E0EA000705327 /* Factories */,
88A3FC2B182E0EA000705327 /* JSBubbleMessageCell.h */,
88A3FC2C182E0EA000705327 /* JSBubbleMessageCell.m */,
88A3FC2D182E0EA000705327 /* JSBubbleView.h */,
88A3FC2E182E0EA000705327 /* JSBubbleView.m */,
88A3FC2F182E0EA000705327 /* JSDismissiveTextView.h */,
88A3FC30182E0EA000705327 /* JSDismissiveTextView.m */,
88BA157B188F91BE00EDD5C0 /* JSMessage.h */,
88BA157C188F91BE00EDD5C0 /* JSMessage.m */,
88DB7C74189223F100D7117F /* JSMessageData.h */,
88A3FC31182E0EA000705327 /* JSMessageInputView.h */,
88A3FC32182E0EA000705327 /* JSMessageInputView.m */,
88A3FC33182E0EA000705327 /* JSMessageSoundEffect.h */,
88A3FC34182E0EA000705327 /* JSMessageSoundEffect.m */,
88A3FC35182E0EA000705327 /* JSMessagesViewController.h */,
88A3FC36182E0EA000705327 /* JSMessagesViewController.m */,
88BA1578188F8FB900EDD5C0 /* JSMessageTableView.h */,
88BA1579188F8FB900EDD5C0 /* JSMessageTableView.m */,
88A3FC37182E0EA000705327 /* JSMessageTextView.h */,
88A3FC38182E0EA000705327 /* JSMessageTextView.m */,
);
path = Classes;
sourceTree = "<group>";
};
88A3FC1B182E0EA000705327 /* Categories */ = {
isa = PBXGroup;
children = (
88A3FC1C182E0EA000705327 /* NSString+JSMessagesView.h */,
88A3FC1D182E0EA000705327 /* NSString+JSMessagesView.m */,
88A3FC20182E0EA000705327 /* UIColor+JSMessagesView.h */,
88A3FC21182E0EA000705327 /* UIColor+JSMessagesView.m */,
88A3FC24182E0EA000705327 /* UIImage+JSMessagesView.h */,
88A3FC25182E0EA000705327 /* UIImage+JSMessagesView.m */,
);
path = Categories;
sourceTree = "<group>";
};
88A3FC26182E0EA000705327 /* Factories */ = {
isa = PBXGroup;
children = (
88A3FC27182E0EA000705327 /* JSAvatarImageFactory.h */,
88A3FC28182E0EA000705327 /* JSAvatarImageFactory.m */,
88A3FC29182E0EA000705327 /* JSBubbleImageViewFactory.h */,
88A3FC2A182E0EA000705327 /* JSBubbleImageViewFactory.m */,
);
path = Factories;
sourceTree = "<group>";
};
88A3FC39182E0EA000705327 /* Resources */ = {
isa = PBXGroup;
children = (
88A3FC3A182E0EA000705327 /* Images */,
88A3FC56182E0EA000705327 /* Sounds */,
);
path = Resources;
sourceTree = "<group>";
};
88A3FC3A182E0EA000705327 /* Images */ = {
isa = PBXGroup;
children = (
88A6ACFC1839FE87000F56DA /* avatar */,
88A3FC3B182E0EA000705327 /* bubble-classic */,
88A3FC46182E0EA000705327 /* bubble-classic-square */,
88A6AD011839FE95000F56DA /* bubble-flat */,
88A3FC4D182E0EA000705327 /* input-bar-classic */,
88A6AD1A1839FFBB000F56DA /* input-bar-flat */,
);
path = Images;
sourceTree = "<group>";
};
88A3FC3B182E0EA000705327 /* bubble-classic */ = {
isa = PBXGroup;
children = (
88A3FC3C182E0EA000705327 /* bubble-classic-blue.png */,
88A3FC3D182E0EA000705327 /* bubble-classic-blue@2x.png */,
88A3FC3E182E0EA000705327 /* bubble-classic-gray.png */,
88A3FC3F182E0EA000705327 /* bubble-classic-gray@2x.png */,
88A3FC40182E0EA000705327 /* bubble-classic-green.png */,
88A3FC41182E0EA000705327 /* bubble-classic-green@2x.png */,
88E43C7C1830138A0045E52D /* bubble-classic-selected.png */,
88E43C7D1830138A0045E52D /* bubble-classic-selected@2x.png */,
88E43C801830147E0045E52D /* bubble-classic-typing.png */,
88E43C811830147E0045E52D /* bubble-classic-typing@2x.png */,
);
path = "bubble-classic";
sourceTree = "<group>";
};
88A3FC46182E0EA000705327 /* bubble-classic-square */ = {
isa = PBXGroup;
children = (
88A3FC47182E0EA000705327 /* bubble-classic-square-blue.png */,
88A3FC48182E0EA000705327 /* bubble-classic-square-blue@2x.png */,
88A3FC49182E0EA000705327 /* bubble-classic-square-gray.png */,
88A3FC4A182E0EA000705327 /* bubble-classic-square-gray@2x.png */,
88A3FC4B182E0EA000705327 /* bubble-classic-square-selected.png */,
88A3FC4C182E0EA000705327 /* bubble-classic-square-selected@2x.png */,
);
path = "bubble-classic-square";
sourceTree = "<group>";
};
88A3FC4D182E0EA000705327 /* input-bar-classic */ = {
isa = PBXGroup;
children = (
88E43C6C183011830045E52D /* input-bar-background.png */,
88E43C6D183011830045E52D /* input-bar-background@2x.png */,
88E43C6E183011830045E52D /* input-field-cover.png */,
88E43C6F183011830045E52D /* input-field-cover@2x.png */,
88E43C70183011830045E52D /* send-button-pressed.png */,
88E43C71183011830045E52D /* send-button-pressed@2x.png */,
88E43C72183011830045E52D /* send-button.png */,
88E43C73183011830045E52D /* send-button@2x.png */,
);
path = "input-bar-classic";
sourceTree = "<group>";
};
88A3FC56182E0EA000705327 /* Sounds */ = {
isa = PBXGroup;
children = (
88A3FC57182E0EA000705327 /* message-received.aiff */,
88A3FC58182E0EA000705327 /* message-sent.aiff */,
);
path = Sounds;
sourceTree = "<group>";
};
88A6ACFC1839FE87000F56DA /* avatar */ = {
isa = PBXGroup;
children = (
88A6ACFD1839FE87000F56DA /* avatar-placeholder.png */,
88A6ACFE1839FE87000F56DA /* avatar-placeholder@2x.png */,
);
path = avatar;
sourceTree = "<group>";
};
88A6AD011839FE95000F56DA /* bubble-flat */ = {
isa = PBXGroup;
children = (
88A6AD021839FE95000F56DA /* bubble-min-tailless.png */,
88A6AD031839FE95000F56DA /* bubble-min-tailless@2x.png */,
88A6AD041839FE95000F56DA /* bubble-min.png */,
88A6AD051839FE95000F56DA /* bubble-min@2x.png */,
88A6AD061839FE95000F56DA /* bubble-stroked-tailless.png */,
88A6AD071839FE95000F56DA /* bubble-stroked-tailless@2x.png */,
88A6AD081839FE95000F56DA /* bubble-stroked.png */,
88A6AD091839FE95000F56DA /* bubble-stroked@2x.png */,
88A6AD0A1839FE95000F56DA /* bubble-tailless.png */,
88A6AD0B1839FE95000F56DA /* bubble-tailless@2x.png */,
88A6AD0C1839FE95000F56DA /* bubble.png */,
88A6AD0D1839FE95000F56DA /* bubble@2x.png */,
);
path = "bubble-flat";
sourceTree = "<group>";
};
88A6AD1A1839FFBB000F56DA /* input-bar-flat */ = {
isa = PBXGroup;
children = (
88A6AD1B1839FFBB000F56DA /* button-photo.png */,
88A6AD1C1839FFBB000F56DA /* button-photo@2x.png */,
88A6AD1D1839FFBB000F56DA /* input-bar-flat.png */,
88A6AD1E1839FFBB000F56DA /* input-bar-flat@2x.png */,
);
path = "input-bar-flat";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
88A3FB45182E079E00705327 /* JSMessagesDemo */ = {
isa = PBXNativeTarget;
buildConfigurationList = 88A3FB7B182E079E00705327 /* Build configuration list for PBXNativeTarget "JSMessagesDemo" */;
buildPhases = (
25C7F7328CD4459AB0413ACE /* Check Pods Manifest.lock */,
88A3FB42182E079E00705327 /* Sources */,
88A3FB43182E079E00705327 /* Frameworks */,
88A3FB44182E079E00705327 /* Resources */,
AF4DAD50B0E541D591936B27 /* Copy Pods Resources */,
);
buildRules = (
);
dependencies = (
);
name = JSMessagesDemo;
productName = JSMessagesDemo;
productReference = 88A3FB46182E079E00705327 /* JSMessagesDemo.app */;
productType = "com.apple.product-type.application";
};
88A3FB69182E079E00705327 /* JSMessagesDemoTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 88A3FB7E182E079E00705327 /* Build configuration list for PBXNativeTarget "JSMessagesDemoTests" */;
buildPhases = (
B6BEEE0608204470B44ABB11 /* Check Pods Manifest.lock */,
88A3FB66182E079E00705327 /* Sources */,
88A3FB67182E079E00705327 /* Frameworks */,
88A3FB68182E079E00705327 /* Resources */,
D763FFBCA0C448878FA4C696 /* Copy Pods Resources */,
);
buildRules = (
);
dependencies = (
88A3FB70182E079E00705327 /* PBXTargetDependency */,
);
name = JSMessagesDemoTests;
productName = JSMessagesDemoTests;
productReference = 88A3FB6A182E079E00705327 /* JSMessagesDemoTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
88A3FB3E182E079D00705327 /* Project object */ = {
isa = PBXProject;
attributes = {
CLASSPREFIX = JS;
LastUpgradeCheck = 0510;
ORGANIZATIONNAME = "Hexed Bits";
TargetAttributes = {
88A3FB69182E079E00705327 = {
TestTargetID = 88A3FB45182E079E00705327;
};
};
};
buildConfigurationList = 88A3FB41182E079D00705327 /* Build configuration list for PBXProject "JSMessagesDemo" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 88A3FB3D182E079D00705327;
productRefGroup = 88A3FB47182E079E00705327 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
88A3FB45182E079E00705327 /* JSMessagesDemo */,
88A3FB69182E079E00705327 /* JSMessagesDemoTests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
88A3FB44182E079E00705327 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
88E43C831830147E0045E52D /* bubble-classic-typing@2x.png in Resources */,
88A6AD111839FE95000F56DA /* bubble-min@2x.png in Resources */,
88A6AD141839FE95000F56DA /* bubble-stroked.png in Resources */,
88A6AD201839FFBB000F56DA /* button-photo@2x.png in Resources */,
88A6AD001839FE87000F56DA /* avatar-placeholder@2x.png in Resources */,
88A6AD0F1839FE95000F56DA /* bubble-min-tailless@2x.png in Resources */,
88A3FC75182E0EA000705327 /* bubble-classic-blue.png in Resources */,
88E43C77183011830045E52D /* input-field-cover@2x.png in Resources */,
88A6AD101839FE95000F56DA /* bubble-min.png in Resources */,
88E43C7A183011830045E52D /* send-button.png in Resources */,
88A3FC7F182E0EA000705327 /* bubble-classic-green@2x.png in Resources */,
88A6AD1F1839FFBB000F56DA /* button-photo.png in Resources */,
88A3FCA5182E0EA000705327 /* message-received.aiff in Resources */,
88A6AD211839FFBB000F56DA /* input-bar-flat.png in Resources */,
88A3FC8B182E0EA000705327 /* bubble-classic-square-blue@2x.png in Resources */,
88A6AD0E1839FE95000F56DA /* bubble-min-tailless.png in Resources */,
88A6AD151839FE95000F56DA /* bubble-stroked@2x.png in Resources */,
88A3FC8F182E0EA000705327 /* bubble-classic-square-gray@2x.png in Resources */,
88A6AD161839FE95000F56DA /* bubble-tailless.png in Resources */,
88E43C76183011830045E52D /* input-field-cover.png in Resources */,
88A3FC7D182E0EA000705327 /* bubble-classic-green.png in Resources */,
88E43C79183011830045E52D /* send-button-pressed@2x.png in Resources */,
88E43C78183011830045E52D /* send-button-pressed.png in Resources */,
88E43C7B183011830045E52D /* send-button@2x.png in Resources */,
88A6AD171839FE95000F56DA /* bubble-tailless@2x.png in Resources */,
88A6AD121839FE95000F56DA /* bubble-stroked-tailless.png in Resources */,
88A3FC93182E0EA000705327 /* bubble-classic-square-selected@2x.png in Resources */,
88A3FC91182E0EA000705327 /* bubble-classic-square-selected.png in Resources */,
88A6AD221839FFBB000F56DA /* input-bar-flat@2x.png in Resources */,
88E43C7E1830138A0045E52D /* bubble-classic-selected.png in Resources */,
88A3FC8D182E0EA000705327 /* bubble-classic-square-gray.png in Resources */,
88A3FC89182E0EA000705327 /* bubble-classic-square-blue.png in Resources */,
88A3FB65182E079E00705327 /* Images.xcassets in Resources */,
88A6AD181839FE95000F56DA /* bubble.png in Resources */,
88A6AD191839FE95000F56DA /* bubble@2x.png in Resources */,
88E43C821830147E0045E52D /* bubble-classic-typing.png in Resources */,
88A3FC79182E0EA000705327 /* bubble-classic-gray.png in Resources */,
88A3FB54182E079E00705327 /* InfoPlist.strings in Resources */,
88A3FC7B182E0EA000705327 /* bubble-classic-gray@2x.png in Resources */,
88E43C74183011830045E52D /* input-bar-background.png in Resources */,
88A6ACFF1839FE87000F56DA /* avatar-placeholder.png in Resources */,
88E43C7F1830138A0045E52D /* bubble-classic-selected@2x.png in Resources */,
88E43C75183011830045E52D /* input-bar-background@2x.png in Resources */,
88A3FC77182E0EA000705327 /* bubble-classic-blue@2x.png in Resources */,
88A6AD131839FE95000F56DA /* bubble-stroked-tailless@2x.png in Resources */,
88A3FCA7182E0EA000705327 /* message-sent.aiff in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
88A3FB68182E079E00705327 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
88A3FC8E182E0EA000705327 /* bubble-classic-square-gray.png in Resources */,
88A3FC78182E0EA000705327 /* bubble-classic-blue@2x.png in Resources */,
88A3FCA6182E0EA000705327 /* message-received.aiff in Resources */,
88A3FC8C182E0EA000705327 /* bubble-classic-square-blue@2x.png in Resources */,
88A3FC7A182E0EA000705327 /* bubble-classic-gray.png in Resources */,
88A3FC7E182E0EA000705327 /* bubble-classic-green.png in Resources */,
88A3FC7C182E0EA000705327 /* bubble-classic-gray@2x.png in Resources */,
88A3FCA8182E0EA000705327 /* message-sent.aiff in Resources */,
88A3FC76182E0EA000705327 /* bubble-classic-blue.png in Resources */,
88A3FC94182E0EA000705327 /* bubble-classic-square-selected@2x.png in Resources */,
88A3FC90182E0EA000705327 /* bubble-classic-square-gray@2x.png in Resources */,
88A3FB76182E079E00705327 /* InfoPlist.strings in Resources */,
88A3FC80182E0EA000705327 /* bubble-classic-green@2x.png in Resources */,
88A3FC8A182E0EA000705327 /* bubble-classic-square-blue.png in Resources */,
88A3FC92182E0EA000705327 /* bubble-classic-square-selected.png in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
25C7F7328CD4459AB0413ACE /* Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Check Pods Manifest.lock";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
showEnvVarsInLog = 0;
};
AF4DAD50B0E541D591936B27 /* Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Pods-resources.sh\"\n";
showEnvVarsInLog = 0;
};
B6BEEE0608204470B44ABB11 /* Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Check Pods Manifest.lock";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
showEnvVarsInLog = 0;
};
D763FFBCA0C448878FA4C696 /* Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Pods-resources.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
88A3FB42182E079E00705327 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
88A3FC67182E0EA000705327 /* JSBubbleMessageCell.m in Sources */,
88A3FC6B182E0EA000705327 /* JSDismissiveTextView.m in Sources */,
88BA157D188F91BE00EDD5C0 /* JSMessage.m in Sources */,
88A3FC6F182E0EA000705327 /* JSMessageSoundEffect.m in Sources */,
88A3FC69182E0EA000705327 /* JSBubbleView.m in Sources */,
88BA157A188F8FB900EDD5C0 /* JSMessageTableView.m in Sources */,
88A3FB56182E079E00705327 /* main.m in Sources */,
88A3FC5D182E0EA000705327 /* UIColor+JSMessagesView.m in Sources */,
88A3FB63182E079E00705327 /* JSDemoViewController.m in Sources */,
88A3FC6D182E0EA000705327 /* JSMessageInputView.m in Sources */,
88A3FC63182E0EA000705327 /* JSAvatarImageFactory.m in Sources */,
88A3FC71182E0EA000705327 /* JSMessagesViewController.m in Sources */,
88A3FC73182E0EA000705327 /* JSMessageTextView.m in Sources */,
88A3FB5A182E079E00705327 /* JSAppDelegate.m in Sources */,
88A3FC65182E0EA000705327 /* JSBubbleImageViewFactory.m in Sources */,
88A3FC59182E0EA000705327 /* NSString+JSMessagesView.m in Sources */,
88A3FC61182E0EA000705327 /* UIImage+JSMessagesView.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
88A3FB66182E079E00705327 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
88A3FC68182E0EA000705327 /* JSBubbleMessageCell.m in Sources */,
88A3FB78182E079E00705327 /* JSMessagesDemoTests.m in Sources */,
88A3FC5A182E0EA000705327 /* NSString+JSMessagesView.m in Sources */,
88A3FC64182E0EA000705327 /* JSAvatarImageFactory.m in Sources */,
88A3FC6A182E0EA000705327 /* JSBubbleView.m in Sources */,
88A3FC66182E0EA000705327 /* JSBubbleImageViewFactory.m in Sources */,
88A3FC74182E0EA000705327 /* JSMessageTextView.m in Sources */,
88A3FC70182E0EA000705327 /* JSMessageSoundEffect.m in Sources */,
88A3FC6C182E0EA000705327 /* JSDismissiveTextView.m in Sources */,
88A3FC72182E0EA000705327 /* JSMessagesViewController.m in Sources */,
88A3FC6E182E0EA000705327 /* JSMessageInputView.m in Sources */,
88A3FC62182E0EA000705327 /* UIImage+JSMessagesView.m in Sources */,
88A3FC5E182E0EA000705327 /* UIColor+JSMessagesView.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
88A3FB70182E079E00705327 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 88A3FB45182E079E00705327 /* JSMessagesDemo */;
targetProxy = 88A3FB6F182E079E00705327 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
88A3FB52182E079E00705327 /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
88A3FB53182E079E00705327 /* en */,
);
name = InfoPlist.strings;
sourceTree = "<group>";
};
88A3FB74182E079E00705327 /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
88A3FB75182E079E00705327 /* en */,
);
name = InfoPlist.strings;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
88A3FB79182E079E00705327 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
88A3FB7A182E079E00705327 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = YES;
ENABLE_NS_ASSERTIONS = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
88A3FB7C182E079E00705327 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = A8C230F1717F4008B2BFAEA6 /* Pods.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "JSMessagesDemo/JSMessagesDemo-Prefix.pch";
INFOPLIST_FILE = "JSMessagesDemo/JSMessagesDemo-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
name = Debug;
};
88A3FB7D182E079E00705327 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = A8C230F1717F4008B2BFAEA6 /* Pods.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "JSMessagesDemo/JSMessagesDemo-Prefix.pch";
INFOPLIST_FILE = "JSMessagesDemo/JSMessagesDemo-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
name = Release;
};
88A3FB7F182E079E00705327 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = A8C230F1717F4008B2BFAEA6 /* Pods.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/JSMessagesDemo.app/JSMessagesDemo";
FRAMEWORK_SEARCH_PATHS = (
"$(SDKROOT)/Developer/Library/Frameworks",
"$(inherited)",
"$(DEVELOPER_FRAMEWORKS_DIR)",
);
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "JSMessagesDemo/JSMessagesDemo-Prefix.pch";
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"\"${PODS_ROOT}/Headers\"",
"\"${PODS_ROOT}/Headers/JSQSystemSoundPlayer\"",
);
INFOPLIST_FILE = "JSMessagesDemoTests/JSMessagesDemoTests-Info.plist";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUNDLE_LOADER)";
WRAPPER_EXTENSION = xctest;
};
name = Debug;
};
88A3FB80182E079E00705327 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = A8C230F1717F4008B2BFAEA6 /* Pods.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/JSMessagesDemo.app/JSMessagesDemo";
FRAMEWORK_SEARCH_PATHS = (
"$(SDKROOT)/Developer/Library/Frameworks",
"$(inherited)",
"$(DEVELOPER_FRAMEWORKS_DIR)",
);
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "JSMessagesDemo/JSMessagesDemo-Prefix.pch";
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"\"${PODS_ROOT}/Headers\"",
"\"${PODS_ROOT}/Headers/JSQSystemSoundPlayer\"",
);
INFOPLIST_FILE = "JSMessagesDemoTests/JSMessagesDemoTests-Info.plist";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUNDLE_LOADER)";
WRAPPER_EXTENSION = xctest;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
88A3FB41182E079D00705327 /* Build configuration list for PBXProject "JSMessagesDemo" */ = {
isa = XCConfigurationList;
buildConfigurations = (
88A3FB79182E079E00705327 /* Debug */,
88A3FB7A182E079E00705327 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
88A3FB7B182E079E00705327 /* Build configuration list for PBXNativeTarget "JSMessagesDemo" */ = {
isa = XCConfigurationList;
buildConfigurations = (
88A3FB7C182E079E00705327 /* Debug */,
88A3FB7D182E079E00705327 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
88A3FB7E182E079E00705327 /* Build configuration list for PBXNativeTarget "JSMessagesDemoTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
88A3FB7F182E079E00705327 /* Debug */,
88A3FB80182E079E00705327 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 88A3FB3E182E079D00705327 /* Project object */;
}

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded</key>
<false/>
</dict>
</plist>

View File

@ -1 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?><Workspace version='1.0'><FileRef location='group:JSMessagesDemo.xcodeproj'/><FileRef location='group:Pods/Pods.xcodeproj'/></Workspace>

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded</key>
<false/>
</dict>
</plist>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

View File

@ -1,21 +0,0 @@
//
// Created by Jesse Squires
// http://www.hexedbits.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSMessagesViewController
//
//
// The MIT License
// Copyright (c) 2013 Jesse Squires
// http://opensource.org/licenses/MIT
//
#import <UIKit/UIKit.h>
@interface JSAppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@end

View File

@ -1,32 +0,0 @@
//
// Created by Jesse Squires
// http://www.hexedbits.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSMessagesViewController
//
//
// The MIT License
// Copyright (c) 2013 Jesse Squires
// http://opensource.org/licenses/MIT
//
#import "JSAppDelegate.h"
#import "JSDemoViewController.h"
@implementation JSAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
JSDemoViewController *vc = [[JSDemoViewController alloc] init];
UINavigationController *nc = [[UINavigationController alloc] initWithRootViewController:vc];
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.rootViewController = nc;
[self.window makeKeyAndVisible];
return YES;
}
@end

View File

@ -1,22 +0,0 @@
//
// Created by Jesse Squires
// http://www.hexedbits.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSMessagesViewController
//
//
// The MIT License
// Copyright (c) 2013 Jesse Squires
// http://opensource.org/licenses/MIT
//
#import "JSMessagesViewController.h"
@interface JSDemoViewController : JSMessagesViewController <JSMessagesViewDataSource, JSMessagesViewDelegate>
@property (strong, nonatomic) NSMutableArray *messages;
@property (strong, nonatomic) NSDictionary *avatars;
@end

View File

@ -1,204 +0,0 @@
//
// Created by Jesse Squires
// http://www.hexedbits.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSMessagesViewController
//
//
// The MIT License
// Copyright (c) 2013 Jesse Squires
// http://opensource.org/licenses/MIT
//
#import "JSDemoViewController.h"
#import "JSMessage.h"
#define kSubtitleJobs @"Jobs"
#define kSubtitleWoz @"Steve Wozniak"
#define kSubtitleCook @"Mr. Cook"
@implementation JSDemoViewController
#pragma mark - View lifecycle
- (void)viewDidLoad
{
self.delegate = self;
self.dataSource = self;
[super viewDidLoad];
[[JSBubbleView appearance] setFont:[UIFont systemFontOfSize:16.0f]];
self.title = @"Messages";
self.messageInputView.textView.placeHolder = @"New Message";
self.sender = @"Jobs";
[self setBackgroundColor:[UIColor whiteColor]];
self.messages = [[NSMutableArray alloc] initWithObjects:
[[JSMessage alloc] initWithText:@"JSMessagesViewController is simple and easy to use." sender:kSubtitleJobs date:[NSDate distantPast]],
[[JSMessage alloc] initWithText:@"It's highly customizable." sender:kSubtitleWoz date:[NSDate distantPast]],
[[JSMessage alloc] initWithText:@"It even has data detectors. You can call me tonight. My cell number is 452-123-4567. \nMy website is www.hexedbits.com." sender:kSubtitleJobs date:[NSDate distantPast]],
[[JSMessage alloc] initWithText:@"Group chat. Sound effects and images included. Animations are smooth. Messages can be of arbitrary size!" sender:kSubtitleCook date:[NSDate distantPast]],
[[JSMessage alloc] initWithText:@"Group chat. Sound effects and images included. Animations are smooth. Messages can be of arbitrary size!" sender:kSubtitleJobs date:[NSDate date]],
[[JSMessage alloc] initWithText:@"Group chat. Sound effects and images included. Animations are smooth. Messages can be of arbitrary size!" sender:kSubtitleWoz date:[NSDate date]],
nil];
for (NSUInteger i = 0; i < 3; i++) {
[self.messages addObjectsFromArray:self.messages];
}
self.avatars = [[NSDictionary alloc] initWithObjectsAndKeys:
[JSAvatarImageFactory avatarImageNamed:@"demo-avatar-jobs" croppedToCircle:YES], kSubtitleJobs,
[JSAvatarImageFactory avatarImageNamed:@"demo-avatar-woz" croppedToCircle:YES], kSubtitleWoz,
[JSAvatarImageFactory avatarImageNamed:@"demo-avatar-cook" croppedToCircle:YES], kSubtitleCook,
nil];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFastForward
target:self
action:@selector(buttonPressed:)];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self scrollToBottomAnimated:NO];
}
#pragma mark - Actions
- (void)buttonPressed:(UIBarButtonItem *)sender
{
// Testing pushing/popping messages view
JSDemoViewController *vc = [[JSDemoViewController alloc] initWithNibName:nil bundle:nil];
[self.navigationController pushViewController:vc animated:YES];
}
#pragma mark - Table view data source
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.messages.count;
}
#pragma mark - Messages view delegate: REQUIRED
- (void)didSendText:(NSString *)text fromSender:(NSString *)sender onDate:(NSDate *)date
{
if ((self.messages.count - 1) % 2) {
[JSMessageSoundEffect playMessageSentSound];
}
else {
// for demo purposes only, mimicing received messages
[JSMessageSoundEffect playMessageReceivedSound];
sender = arc4random_uniform(10) % 2 ? kSubtitleCook : kSubtitleWoz;
}
[self.messages addObject:[[JSMessage alloc] initWithText:text sender:sender date:date]];
[self finishSend];
[self scrollToBottomAnimated:YES];
}
- (JSBubbleMessageType)messageTypeForRowAtIndexPath:(NSIndexPath *)indexPath
{
return (indexPath.row % 2) ? JSBubbleMessageTypeIncoming : JSBubbleMessageTypeOutgoing;
}
- (UIImageView *)bubbleImageViewWithType:(JSBubbleMessageType)type
forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row % 2) {
return [JSBubbleImageViewFactory bubbleImageViewForType:type
color:[UIColor js_bubbleLightGrayColor]];
}
return [JSBubbleImageViewFactory bubbleImageViewForType:type
color:[UIColor js_bubbleBlueColor]];
}
- (JSMessageInputViewStyle)inputViewStyle
{
return JSMessageInputViewStyleFlat;
}
#pragma mark - Messages view delegate: OPTIONAL
- (BOOL)shouldDisplayTimestampForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row % 3 == 0) {
return YES;
}
return NO;
}
//
// *** Implement to customize cell further
//
- (void)configureCell:(JSBubbleMessageCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
if ([cell messageType] == JSBubbleMessageTypeOutgoing) {
cell.bubbleView.textView.textColor = [UIColor whiteColor];
if ([cell.bubbleView.textView respondsToSelector:@selector(linkTextAttributes)]) {
NSMutableDictionary *attrs = [cell.bubbleView.textView.linkTextAttributes mutableCopy];
[attrs setValue:[UIColor blueColor] forKey:UITextAttributeTextColor];
cell.bubbleView.textView.linkTextAttributes = attrs;
}
}
if (cell.timestampLabel) {
cell.timestampLabel.textColor = [UIColor lightGrayColor];
cell.timestampLabel.shadowOffset = CGSizeZero;
}
if (cell.subtitleLabel) {
cell.subtitleLabel.textColor = [UIColor lightGrayColor];
}
#if TARGET_IPHONE_SIMULATOR
cell.bubbleView.textView.dataDetectorTypes = UIDataDetectorTypeNone;
#else
cell.bubbleView.textView.dataDetectorTypes = UIDataDetectorTypeAll;
#endif
}
// *** Implement to use a custom send button
//
// The button's frame is set automatically for you
//
// - (UIButton *)sendButtonForInputView
//
// *** Implement to prevent auto-scrolling when message is added
//
- (BOOL)shouldPreventScrollToBottomWhileUserScrolling
{
return YES;
}
// *** Implemnt to enable/disable pan/tap todismiss keyboard
//
- (BOOL)allowsPanToDismissKeyboard
{
return YES;
}
#pragma mark - Messages view data source: REQUIRED
- (JSMessage *)messageForRowAtIndexPath:(NSIndexPath *)indexPath
{
return [self.messages objectAtIndex:indexPath.row];
}
- (UIImageView *)avatarImageViewForRowAtIndexPath:(NSIndexPath *)indexPath sender:(NSString *)sender
{
UIImage *image = [self.avatars objectForKey:sender];
return [[UIImageView alloc] initWithImage:image];
}
@end

View File

@ -1,19 +0,0 @@
//
// Prefix header
//
// The contents of this file are implicitly included at the beginning of every source file.
//
#import <Availability.h>
#ifndef __IPHONE_5_0
#warning "This project uses features only available in iOS SDK 5.0 and later."
#endif
// Make NSLog more useful and pretty
#define NSLog(__FORMAT__, ...) NSLog((@"%s [Line %d] " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#endif

View File

@ -1,2 +0,0 @@
/* Localized versions of Info.plist keys */

View File

@ -1,18 +0,0 @@
//
// main.m
// JSMessagesDemo
//
// Created by Jesse Squires on 11/8/13.
// Copyright (c) 2013 Hexed Bits. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "JSAppDelegate.h"
int main(int argc, char * argv[])
{
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([JSAppDelegate class]));
}
}

View File

@ -1,43 +0,0 @@
//
// Created by Jesse Squires
// http://www.hexedbits.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSMessagesViewController
//
//
// The MIT License
// Copyright (c) 2013 Jesse Squires
// http://opensource.org/licenses/MIT
//
#import <XCTest/XCTest.h>
#import "JSMessagesViewController.h"
@interface JSMessagesDemoTests : XCTestCase
@end
@implementation JSMessagesDemoTests
- (void)setUp
{
[super setUp];
}
- (void)tearDown
{
[super tearDown];
}
- (void)testInit
{
JSMessagesViewController *vc = [[JSMessagesViewController alloc] init];
XCTAssertNotNil(vc, @"View controller should not be nil");
}
@end

View File

@ -1,2 +0,0 @@
/* Localized versions of Info.plist keys */

View File

@ -1,17 +0,0 @@
Pod::Spec.new do |s|
s.name = 'JSMessagesViewController'
s.version = '4.0.4'
s.summary = 'A messages UI for iPhone and iPad.'
s.homepage = 'https://github.com/jessesquires/MessagesTableViewController'
s.social_media_url = 'https://twitter.com/jesse_squires'
s.license = 'MIT'
s.authors = { 'Jesse Squires' => 'jesse.squires.developer@gmail.com' }
s.source = { :git => 'https://github.com/jessesquires/MessagesTableViewController.git', :tag => s.version.to_s }
s.platform = :ios, '6.0'
s.source_files = 'JSMessagesViewController/Classes/**/*'
s.resources = 'JSMessagesViewController/Resources/**/**/*'
s.frameworks = 'QuartzCore'
s.requires_arc = true
s.dependency 'JSQSystemSoundPlayer'
end

View File

@ -1,33 +0,0 @@
//
// Created by Jesse Squires
// http://www.hexedbits.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSMessagesViewController
//
//
// The MIT License
// Copyright (c) 2013 Jesse Squires
// http://opensource.org/licenses/MIT
//
#import <Foundation/Foundation.h>
@interface NSString (JSMessagesView)
/**
* Returns a copy of the receiver with all whitespaced removed from the front and back.
*
* @return A copied string with all leading and trailing whitespace removed.
*/
- (NSString *)js_stringByTrimingWhitespace;
/**
* Returns the number of lines in the receiver by counting the number of occurences of the newline character, `\n`.
*
* @return An unsigned integer describing the number of lines in the string.
*/
- (NSUInteger)js_numberOfLines;
@end

View File

@ -1,29 +0,0 @@
//
// Created by Jesse Squires
// http://www.hexedbits.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSMessagesViewController
//
//
// The MIT License
// Copyright (c) 2013 Jesse Squires
// http://opensource.org/licenses/MIT
//
#import "NSString+JSMessagesView.h"
@implementation NSString (JSMessagesView)
- (NSString *)js_stringByTrimingWhitespace
{
return [self stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
- (NSUInteger)js_numberOfLines
{
return [[self componentsSeparatedByString:@"\n"] count] + 1;
}
@end

View File

@ -1,59 +0,0 @@
//
// Created by Jesse Squires
// http://www.hexedbits.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSMessagesViewController
//
//
// The MIT License
// Copyright (c) 2013 Jesse Squires
// http://opensource.org/licenses/MIT
//
#import <UIKit/UIKit.h>
@interface UIColor (JSMessagesView)
#pragma mark - Colors
/**
* @return A color object containing the RGB values for the iOS 6 messages app background color.
*/
+ (UIColor *)js_backgroundColorClassic;
/**
* @return A color object containing the RGB values iOS 6 messages app timestamp text color.
*/
+ (UIColor *)js_messagesTimestampColorClassic;
#pragma mark - Bubble colors
/**
* @return A color object containing the HSB values iOS 7 messages app green bubble color.
*/
+ (UIColor *)js_bubbleGreenColor;
/**
* @return A color object containing the HSB values iOS 7 messages app blue bubble color.
*/
+ (UIColor *)js_bubbleBlueColor;
/**
* @return A color object containing the HSB values iOS 7 messages app light gray bubble color.
*/
+ (UIColor *)js_bubbleLightGrayColor;
#pragma mark - Utilities
/**
* Creates and returns a new color object whose brightness component is decreased by the given value, using the initial color values of the receiver.
*
* @param value A floating point value describing the amount by which to decrease the brightness of the receiver.
*
* @return A new color object whose brightness is decreased by the given values. The other color values remain the same as the receiver.
*/
- (UIColor *)js_darkenColorWithValue:(CGFloat)value;
@end

View File

@ -1,63 +0,0 @@
//
// Created by Jesse Squires
// http://www.hexedbits.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSMessagesViewController
//
//
// The MIT License
// Copyright (c) 2013 Jesse Squires
// http://opensource.org/licenses/MIT
//
#import <UIKit/UIKit.h>
@interface UIImage (JSMessagesView)
/**
* Creates and returns an image object that is a copy of the receiver and flipped horizontally.
*
* @return A new image object.
*/
- (UIImage *)js_imageFlippedHorizontal;
/**
* Creates and returns a new stretchable image object with the specified cap insets.
*
* @param capInsets The values to use for the cap insets.
*
* @return A new image object with the specified cap insets and mode `UIImageResizingModeStretch`.
*/
- (UIImage *)js_stretchableImageWithCapInsets:(UIEdgeInsets)capInsets;
/**
* Creates and returns a new image object with the specified attributes.
*
* @param clipToCircle A boolean value indicating whether or not the returned image should be cropped to a circle. Pass `YES` to crop the returned image to a circle, and `NO` to crop as a square.
* @param diameter A floating point value indicating the diamater of the returned image.
* @param borderColor The color with which to stroke the returned image.
* @param borderWidth The width of the border of the returned image.
* @param shadowOffset The values for the shadow offset of the returned image.
*
* @return A new image object with the specified attributes.
*
* @warning This method crops a copy of the receiver into a perfect square centered on the center point of the image before applying the other attributes.
*/
- (UIImage *)js_imageAsCircle:(BOOL)clipToCircle
withDiamter:(CGFloat)diameter
borderColor:(UIColor *)borderColor
borderWidth:(CGFloat)borderWidth
shadowOffSet:(CGSize)shadowOffset;
/**
* Creates and returns a new image object that is masked with the specified mask color.
*
* @param maskColor The color value for the mask.
*
* @return A new image object masked with the specified color.
*/
- (UIImage *)js_imageMaskWithColor:(UIColor *)maskColor;
@end

View File

@ -1,111 +0,0 @@
//
// Created by Jesse Squires
// http://www.hexedbits.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSMessagesViewController
//
//
// The MIT License
// Copyright (c) 2013 Jesse Squires
// http://opensource.org/licenses/MIT
//
#import "UIImage+JSMessagesView.h"
@implementation UIImage (JSMessagesView)
- (UIImage *)js_imageFlippedHorizontal
{
return [UIImage imageWithCGImage:self.CGImage
scale:self.scale
orientation:UIImageOrientationUpMirrored];
}
- (UIImage *)js_stretchableImageWithCapInsets:(UIEdgeInsets)capInsets
{
return [self resizableImageWithCapInsets:capInsets
resizingMode:UIImageResizingModeStretch];
}
- (UIImage *)js_imageAsCircle:(BOOL)clipToCircle
withDiamter:(CGFloat)diameter
borderColor:(UIColor *)borderColor
borderWidth:(CGFloat)borderWidth
shadowOffSet:(CGSize)shadowOffset
{
// increase given size for border and shadow
CGFloat increase = diameter * 0.1f;
CGFloat newSize = diameter + increase;
CGRect newRect = CGRectMake(0.0f,
0.0f,
newSize,
newSize);
// fit image inside border and shadow
CGRect imgRect = CGRectMake(increase,
increase,
newRect.size.width - (increase * 2.0f),
newRect.size.height - (increase * 2.0f));
UIGraphicsBeginImageContextWithOptions(newRect.size, NO, [UIScreen mainScreen].scale);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
// draw shadow
if (!CGSizeEqualToSize(shadowOffset, CGSizeZero)) {
CGContextSetShadowWithColor(context,
CGSizeMake(shadowOffset.width, shadowOffset.height),
2.0f,
[UIColor colorWithWhite:0.0f alpha:0.45f].CGColor);
}
// draw border
if (borderColor && borderWidth) {
CGPathRef borderPath = (clipToCircle) ? CGPathCreateWithEllipseInRect(imgRect, NULL) : CGPathCreateWithRect(imgRect, NULL);
CGContextAddPath(context, borderPath);
CGContextSetStrokeColorWithColor(context, borderColor.CGColor);
CGContextSetLineWidth(context, borderWidth);
CGContextDrawPath(context, kCGPathFillStroke);
CGPathRelease(borderPath);
}
CGContextRestoreGState(context);
if (clipToCircle) {
UIBezierPath *imgPath = [UIBezierPath bezierPathWithOvalInRect:imgRect];
[imgPath addClip];
}
[self drawInRect:imgRect];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
- (UIImage *)js_imageMaskWithColor:(UIColor *)maskColor
{
CGRect imageRect = CGRectMake(0.0f, 0.0f, self.size.width, self.size.height);
UIGraphicsBeginImageContextWithOptions(imageRect.size, NO, self.scale);
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextScaleCTM(ctx, 1.0f, -1.0f);
CGContextTranslateCTM(ctx, 0.0f, -(imageRect.size.height));
CGContextClipToMask(ctx, imageRect, self.CGImage);
CGContextSetFillColorWithColor(ctx, maskColor.CGColor);
CGContextFillRect(ctx, imageRect);
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
@end

View File

@ -1,60 +0,0 @@
//
// Created by Jesse Squires
// http://www.hexedbits.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSMessagesViewController
//
//
// The MIT License
// Copyright (c) 2013 Jesse Squires
// http://opensource.org/licenses/MIT
//
#import <Foundation/Foundation.h>
/**
* A constant defining the size of an avatar image.
*/
extern CGFloat const kJSAvatarImageSize;
/**
* `JSAvatarImageFactory` is a factory that provides a means for styling avatar images to be displayed in a `JSBubbleMessageCell` of a `JSMessagesViewController`.
*/
@interface JSAvatarImageFactory : NSObject
/**
* Returns the image object associated with the specified filename. The image is cropped to a circle if the value of croppedToCircle is `YES`, otherwise the image is cropped to a square. The image has a flat, iOS 7 appearance.
*
* @param filename The name of the file. If this is the first time the image is being loaded, the method looks for an image with the specified name in the applications main bundle.
* @param croppedToCircle A boolean value indicating whether or not the image should be cropped as a circle or square. Pass `YES` to crop to a circle, and `NO` to crop to a square.
*
* @return The image object for the specified file (cropped as specified), or `nil` if the method could not find the specified image.
*/
+ (UIImage *)avatarImageNamed:(NSString *)filename
croppedToCircle:(BOOL)croppedToCircle;
/**
* Returns a copy of the image object associated with the specified originalImage. The image is cropped to a circle if the value of croppedToCircle is `YES`, otherwise the image is cropped to a square. The image has a flat, iOS 7 appearance.
*
* @param originalImage The origin image object to be styled for an avatar.
* @param croppedToCircle A boolean value indicating whether or not the image should be cropped as a circle or square. Pass `YES` to crop to a circle, and `NO` to crop to a square.
*
* @return A new image object for the specified originalImage (cropped as specified), or `nil` if originalImage is not a valid, initialized image object.
*/
+ (UIImage *)avatarImage:(UIImage *)originalImage
croppedToCircle:(BOOL)croppedToCircle;
/**
* Returns the image object associated with the specified filename. The image is cropped to a circle if the value of croppedToCircle is `YES`, otherwise the image is cropped to a square. The image has a glossy, iOS 6 appearance.
*
* @param filename The name of the file. If this is the first time the image is being loaded, the method looks for an image with the specified name in the applications main bundle.
* @param croppedToCircle A boolean value indicating whether or not the image should be cropped as a circle or square. Pass `YES` to crop to a circle, and `NO` to crop to a square.
*
* @return The image object for the specified file (cropped as specified), or `nil` if the method could not find the specified image.
*/
+ (UIImage *)classicAvatarImageNamed:(NSString *)filename
croppedToCircle:(BOOL)croppedToCircle;
@end

View File

@ -1,50 +0,0 @@
//
// Created by Jesse Squires
// http://www.hexedbits.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSMessagesViewController
//
//
// The MIT License
// Copyright (c) 2013 Jesse Squires
// http://opensource.org/licenses/MIT
//
#import "JSAvatarImageFactory.h"
#import "UIImage+JSMessagesView.h"
CGFloat const kJSAvatarImageSize = 50.0f;
@implementation JSAvatarImageFactory
+ (UIImage *)avatarImageNamed:(NSString *)filename
croppedToCircle:(BOOL)croppedToCircle
{
UIImage *image = [UIImage imageNamed:filename];
return [self avatarImage:image croppedToCircle:croppedToCircle];
}
+ (UIImage *)avatarImage:(UIImage *)originalImage
croppedToCircle:(BOOL)croppedToCircle
{
return [originalImage js_imageAsCircle:croppedToCircle
withDiamter:kJSAvatarImageSize
borderColor:nil
borderWidth:0.0f
shadowOffSet:CGSizeZero];
}
+ (UIImage *)classicAvatarImageNamed:(NSString *)filename
croppedToCircle:(BOOL)croppedToCircle
{
UIImage *image = [UIImage imageNamed:filename];
return [image js_imageAsCircle:croppedToCircle
withDiamter:kJSAvatarImageSize
borderColor:[UIColor colorWithHue:0.0f saturation:0.0f brightness:0.8f alpha:1.0f]
borderWidth:1.0f
shadowOffSet:CGSizeMake(0.0f, 1.0f)];
}
@end

View File

@ -1,86 +0,0 @@
//
// Created by Jesse Squires
// http://www.hexedbits.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSMessagesViewController
//
//
// The MIT License
// Copyright (c) 2013 Jesse Squires
// http://opensource.org/licenses/MIT
//
#import <Foundation/Foundation.h>
/**
* The type of bubble for a `JSBubbleMessageCell` object.
*/
typedef NS_ENUM(NSUInteger, JSBubbleMessageType) {
/**
* Specifies an incoming, or received message.
*/
JSBubbleMessageTypeIncoming,
/**
* Specifies an outgoing, or sent message.
*/
JSBubbleMessageTypeOutgoing
};
/**
* The style of a classic bubble image with an iOS 6 appearance.
*/
typedef NS_ENUM(NSUInteger, JSBubbleImageViewStyle) {
/**
* Specifies a glossy gray messsage bubble.
*/
JSBubbleImageViewStyleClassicGray,
/**
* Specifies a glossy blue messsage bubble.
*/
JSBubbleImageViewStyleClassicBlue,
/**
* Specifies a glossy green messsage bubble.
*/
JSBubbleImageViewStyleClassicGreen,
/**
* Specifies a glossy gray square messsage bubble.
*/
JSBubbleImageViewStyleClassicSquareGray,
/**
* Specifies a glossy blue square messsage bubble.
*/
JSBubbleImageViewStyleClassicSquareBlue
};
/**
* `JSBubbleImageViewFactory` is a factory that provides a means for styling bubble image views to be displayed in a `JSBubbleMessageCell` of a `JSMessagesViewController`.
*/
@interface JSBubbleImageViewFactory : NSObject
/**
* Creates and returns an image view object with the specified type and color. The `image` property of the image view is configured with a flat, iOS7-style bubble image, masked with the given color. The `highlightedImage` property is configured similarly, but with a slightly darkened version of the given color.
*
* @param type The type of the bubble image view.
* @param color The color of the bubble image in the image view.
*
* @return An initialized image view object if created successfully, `nil` otherwise.
*/
+ (UIImageView *)bubbleImageViewForType:(JSBubbleMessageType)type
color:(UIColor *)color;
/**
* Creates and returns an image view object with the specified type and style. The `image` property of the image view is configured with a glossy, iOS6-style bubble image, corresponding to the given style. The `highlightedImage` property is configured similarly, but with a selected version of the bubble image.
*
* @param type The type of the bubble image view.
* @param style The style of the bubble image in the image view.
*
* @see `JSBubbleImageViewStyle`.
*
* @return An initialized image view object if created successfully, `nil` otherwise.
*/
+ (UIImageView *)classicBubbleImageViewForType:(JSBubbleMessageType)type
style:(JSBubbleImageViewStyle)style;
@end

View File

@ -1,138 +0,0 @@
//
// Created by Jesse Squires
// http://www.hexedbits.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSMessagesViewController
//
//
// The MIT License
// Copyright (c) 2013 Jesse Squires
// http://opensource.org/licenses/MIT
//
#import "JSBubbleImageViewFactory.h"
#import "UIImage+JSMessagesView.h"
#import "UIColor+JSMessagesView.h"
static NSDictionary *bubbleImageDictionary;
@interface JSBubbleImageViewFactory()
+ (UIImageView *)classicBubbleImageViewForStyle:(JSBubbleImageViewStyle)style
isOutgoing:(BOOL)isOutgoing;
+ (UIImage *)classicHighlightedBubbleImageForStyle:(JSBubbleImageViewStyle)style;
+ (UIEdgeInsets)classicBubbleImageCapInsetsForStyle:(JSBubbleImageViewStyle)style
isOutgoing:(BOOL)isOutgoing;
@end
@implementation JSBubbleImageViewFactory
#pragma mark - Initialization
+ (void)initialize
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
bubbleImageDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
@"bubble-classic-gray", @(JSBubbleImageViewStyleClassicGray),
@"bubble-classic-blue", @(JSBubbleImageViewStyleClassicBlue),
@"bubble-classic-green", @(JSBubbleImageViewStyleClassicGreen),
@"bubble-classic-square-gray", @(JSBubbleImageViewStyleClassicSquareGray),
@"bubble-classic-square-blue", @(JSBubbleImageViewStyleClassicSquareBlue),
nil];
});
}
#pragma mark - Public
+ (UIImageView *)bubbleImageViewForType:(JSBubbleMessageType)type
color:(UIColor *)color
{
UIImage *bubble = [UIImage imageNamed:@"bubble-min"];
UIImage *normalBubble = [bubble js_imageMaskWithColor:color];
UIImage *highlightedBubble = [bubble js_imageMaskWithColor:[color js_darkenColorWithValue:0.12f]];
if (type == JSBubbleMessageTypeIncoming) {
normalBubble = [normalBubble js_imageFlippedHorizontal];
highlightedBubble = [highlightedBubble js_imageFlippedHorizontal];
}
// make image stretchable from center point
CGPoint center = CGPointMake(bubble.size.width / 2.0f, bubble.size.height / 2.0f);
UIEdgeInsets capInsets = UIEdgeInsetsMake(center.y, center.x, center.y, center.x);
return [[UIImageView alloc] initWithImage:[normalBubble js_stretchableImageWithCapInsets:capInsets]
highlightedImage:[highlightedBubble js_stretchableImageWithCapInsets:capInsets]];
}
+ (UIImageView *)classicBubbleImageViewForType:(JSBubbleMessageType)type
style:(JSBubbleImageViewStyle)style
{
return [JSBubbleImageViewFactory classicBubbleImageViewForStyle:style
isOutgoing:type == JSBubbleMessageTypeOutgoing];
}
#pragma mark - Private
+ (UIImageView *)classicBubbleImageViewForStyle:(JSBubbleImageViewStyle)style
isOutgoing:(BOOL)isOutgoing
{
UIImage *image = [UIImage imageNamed:[bubbleImageDictionary objectForKey:@(style)]];
UIImage *highlightedImage = [JSBubbleImageViewFactory classicHighlightedBubbleImageForStyle:style];
if (!isOutgoing) {
image = [image js_imageFlippedHorizontal];
highlightedImage = [highlightedImage js_imageFlippedHorizontal];
}
UIEdgeInsets capInsets = [JSBubbleImageViewFactory classicBubbleImageCapInsetsForStyle:style
isOutgoing:isOutgoing];
return [[UIImageView alloc] initWithImage:[image js_stretchableImageWithCapInsets:capInsets]
highlightedImage:[highlightedImage js_stretchableImageWithCapInsets:capInsets]];
}
+ (UIImage *)classicHighlightedBubbleImageForStyle:(JSBubbleImageViewStyle)style
{
switch (style) {
case JSBubbleImageViewStyleClassicGray:
case JSBubbleImageViewStyleClassicBlue:
case JSBubbleImageViewStyleClassicGreen:
return [UIImage imageNamed:@"bubble-classic-selected"];
case JSBubbleImageViewStyleClassicSquareGray:
case JSBubbleImageViewStyleClassicSquareBlue:
return [UIImage imageNamed:@"bubble-classic-square-selected"];
default:
return nil;
}
}
+ (UIEdgeInsets)classicBubbleImageCapInsetsForStyle:(JSBubbleImageViewStyle)style
isOutgoing:(BOOL)isOutgoing
{
switch (style) {
case JSBubbleImageViewStyleClassicGray:
case JSBubbleImageViewStyleClassicBlue:
case JSBubbleImageViewStyleClassicGreen:
return UIEdgeInsetsMake(15.0f, 20.0f, 15.0f, 20.0f);
case JSBubbleImageViewStyleClassicSquareGray:
case JSBubbleImageViewStyleClassicSquareBlue:
return isOutgoing ? UIEdgeInsetsMake(15.0f, 18.0f, 16.0f, 23.0f) : UIEdgeInsetsMake(15.0f, 25.0f, 16.0f, 23.0f);
default:
return UIEdgeInsetsZero;
}
}
@end

View File

@ -1,111 +0,0 @@
//
// Created by Jesse Squires
// http://www.hexedbits.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSMessagesViewController
//
//
// The MIT License
// Copyright (c) 2013 Jesse Squires
// http://opensource.org/licenses/MIT
//
#import <UIKit/UIKit.h>
#import "JSBubbleView.h"
#import "JSMessageData.h"
/**
* The `JSBubbleMessageCell` class defines the attributes and behavior of the cells that appear in `JSMessagesViewController`. This class includes properties and methods for setting and managing cell content.
*/
@interface JSBubbleMessageCell : UITableViewCell
/**
* Returns the bubble view used in the cell. JSBubbleMessageCell adds the appropriate bubble view when you create the cell with a given cell type and bubbleImageView. This property is never `nil`.
* @see JSBubbleView.
* @see JSBubbleMessageType.
*/
@property (weak, nonatomic, readonly) JSBubbleView *bubbleView;
/**
* Returns the label used to display the timestamp for the cell. This property may be `nil` if no timestamp is provided.
* @see JSMessagesViewDataSource.
*/
@property (weak, nonatomic, readonly) UILabel *timestampLabel;
/**
* Returns the image view used to display the avatar for the cell. This property may be `nil` if no avatar is provided.
* @see JSMessagesViewDataSource.
*/
@property (weak, nonatomic, readonly) UIImageView *avatarImageView;
/**
* Returns the label used to display the subtitle for the cell. This property may be `nil` if no subtitle is provided.
* @see JSMessagesViewDataSource.
*/
@property (weak, nonatomic, readonly) UILabel *subtitleLabel;
#pragma mark - Initialization
/**
* Initializes a message cell and returns it to the caller.
*
* @param type A constant indicating a message type. @see JSBubbleMessageType for details.
* @param bubbleImageView An image view initialized with bubble images. The `UIImageView` properties `image` and `highlightedImage` must not be `nil`. @see JSBubbleImageViewFactory.
* @param message An object that conforms to the `JSMessageData` protocol containing the message data for the cell.
* @param displaysTimestamp A boolean value indicating whether or not the cell should display the date contained in message. Pass `YES` to display a timestamp, `NO` otherwise.
* @param hasAvatar A boolean value indicating whether or not the cell should be initialized with an avatarImageView. Pass `YES` to initialize with an avatar, `NO` otherwise.
* @param reuseIdentifier A string used to identify the cell object to be reused for drawing multiple rows of a JSMessagesViewController. This property must not be `nil`.
*
* @return An initialized `JSBubbleMessageCell` object or `nil` if the object could not be created.
*/
- (instancetype)initWithBubbleType:(JSBubbleMessageType)type
bubbleImageView:(UIImageView *)bubbleImageView
message:(id<JSMessageData>)message
displaysTimestamp:(BOOL)displaysTimestamp
hasAvatar:(BOOL)hasAvatar
reuseIdentifier:(NSString *)reuseIdentifier;
#pragma mark - Setters
/**
* Sets the message object for the cell.
*
* @param message An object that conforms to the `JSMessageData` protocol containing the message data for the cell.
*/
- (void)setMessage:(id<JSMessageData>)message;
/**
* Sets the imageView for the avatarImageView of the cell. The frame is set for you by `JSBubbleMessageCell`.
*
* @param imageView An imageView containing an avatar image. The `image` property of the `UIImageView` must not be `nil`.
*/
- (void)setAvatarImageView:(UIImageView *)imageView;
#pragma mark - Getters
/**
* Returns a contant indicating the message type for the cell.
* @see JSBubbleMessageType.
*
* @return A contant indicating the message type.
*/
- (JSBubbleMessageType)messageType;
#pragma mark - Class methods
/**
* Computes and returns the minimum necessary height of a `JSBubbleMessageCell` needed to display its contents.
*
* @param message An object that conforms to the `JSMessageData` protocol to display in the cell.
* @param displaysAvatar A boolean value indicating whether or not the cell displays an avatar.
* @param displaysTimestamp A boolean value indicating whether or not the cell displays a timestamp.
*
* @return The height required for the frame of the cell in order for the cell to display the entire contents of its subviews.
*/
+ (CGFloat)neededHeightForBubbleMessageCellWithMessage:(id<JSMessageData>)message
displaysAvatar:(BOOL)displaysAvatar
displaysTimestamp:(BOOL)displaysTimestamp;
@end

View File

@ -1,372 +0,0 @@
//
// Created by Jesse Squires
// http://www.hexedbits.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSMessagesViewController
//
//
// The MIT License
// Copyright (c) 2013 Jesse Squires
// http://opensource.org/licenses/MIT
//
#import "JSBubbleMessageCell.h"
#import "JSAvatarImageFactory.h"
#import "UIColor+JSMessagesView.h"
static const CGFloat kJSLabelPadding = 5.0f;
static const CGFloat kJSTimeStampLabelHeight = 15.0f;
static const CGFloat kJSSubtitleLabelHeight = 15.0f;
@interface JSBubbleMessageCell()
- (void)setup;
- (void)configureTimestampLabel;
- (void)configureAvatarImageView:(UIImageView *)imageView forMessageType:(JSBubbleMessageType)type;
- (void)configureSubtitleLabelForMessageType:(JSBubbleMessageType)type;
- (void)configureWithType:(JSBubbleMessageType)type
bubbleImageView:(UIImageView *)bubbleImageView
message:(id<JSMessageData>)message
displaysTimestamp:(BOOL)displaysTimestamp
avatar:(BOOL)hasAvatar;
- (void)setText:(NSString *)text;
- (void)setTimestamp:(NSDate *)date;
- (void)setSubtitle:(NSString *)subtitle;
- (void)handleLongPressGesture:(UILongPressGestureRecognizer *)longPress;
- (void)handleMenuWillHideNotification:(NSNotification *)notification;
- (void)handleMenuWillShowNotification:(NSNotification *)notification;
@end
@implementation JSBubbleMessageCell
#pragma mark - Setup
- (void)setup
{
self.backgroundColor = [UIColor clearColor];
self.selectionStyle = UITableViewCellSelectionStyleNone;
self.accessoryType = UITableViewCellAccessoryNone;
self.accessoryView = nil;
self.imageView.image = nil;
self.imageView.hidden = YES;
self.textLabel.text = nil;
self.textLabel.hidden = YES;
self.detailTextLabel.text = nil;
self.detailTextLabel.hidden = YES;
UILongPressGestureRecognizer *recognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self
action:@selector(handleLongPressGesture:)];
[recognizer setMinimumPressDuration:0.4f];
[self addGestureRecognizer:recognizer];
}
- (void)configureTimestampLabel
{
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(kJSLabelPadding,
kJSLabelPadding,
self.contentView.frame.size.width - (kJSLabelPadding * 2.0f),
kJSTimeStampLabelHeight)];
label.autoresizingMask = UIViewAutoresizingFlexibleWidth;
label.backgroundColor = [UIColor clearColor];
label.textAlignment = NSTextAlignmentCenter;
label.textColor = [UIColor js_messagesTimestampColorClassic];
label.shadowColor = [UIColor whiteColor];
label.shadowOffset = CGSizeMake(0.0f, 1.0f);
label.font = [UIFont boldSystemFontOfSize:12.0f];
[self.contentView addSubview:label];
[self.contentView bringSubviewToFront:label];
_timestampLabel = label;
}
- (void)configureAvatarImageView:(UIImageView *)imageView forMessageType:(JSBubbleMessageType)type
{
CGFloat avatarX = 0.5f;
if (type == JSBubbleMessageTypeOutgoing) {
avatarX = (self.contentView.frame.size.width - kJSAvatarImageSize);
}
CGFloat avatarY = self.contentView.frame.size.height - kJSAvatarImageSize;
if (_subtitleLabel) {
avatarY -= kJSSubtitleLabelHeight;
}
imageView.frame = CGRectMake(avatarX, avatarY, kJSAvatarImageSize, kJSAvatarImageSize);
imageView.autoresizingMask = (UIViewAutoresizingFlexibleTopMargin
| UIViewAutoresizingFlexibleLeftMargin
| UIViewAutoresizingFlexibleRightMargin);
[self.contentView addSubview:imageView];
_avatarImageView = imageView;
}
- (void)configureSubtitleLabelForMessageType:(JSBubbleMessageType)type
{
UILabel *label = [[UILabel alloc] initWithFrame:CGRectZero];
label.autoresizingMask = UIViewAutoresizingFlexibleWidth;
label.backgroundColor = [UIColor clearColor];
label.textAlignment = (type == JSBubbleMessageTypeOutgoing) ? NSTextAlignmentRight : NSTextAlignmentLeft;
label.textColor = [UIColor js_messagesTimestampColorClassic];
label.font = [UIFont systemFontOfSize:12.5f];
[self.contentView addSubview:label];
_subtitleLabel = label;
}
- (void)configureWithType:(JSBubbleMessageType)type
bubbleImageView:(UIImageView *)bubbleImageView
message:(id<JSMessageData>)message
displaysTimestamp:(BOOL)displaysTimestamp
avatar:(BOOL)hasAvatar
{
CGFloat bubbleY = 0.0f;
CGFloat bubbleX = 0.0f;
CGFloat offsetX = 0.0f;
if (displaysTimestamp) {
[self configureTimestampLabel];
bubbleY = 14.0f;
}
if ([message sender]) {
[self configureSubtitleLabelForMessageType:type];
}
if (hasAvatar) {
offsetX = 4.0f;
bubbleX = kJSAvatarImageSize;
if (type == JSBubbleMessageTypeOutgoing) {
offsetX = kJSAvatarImageSize - 4.0f;
}
[self configureAvatarImageView:[[UIImageView alloc] init] forMessageType:type];
}
CGRect frame = CGRectMake(bubbleX - offsetX,
bubbleY,
self.contentView.frame.size.width - bubbleX,
self.contentView.frame.size.height - _timestampLabel.frame.size.height - _subtitleLabel.frame.size.height);
JSBubbleView *bubbleView = [[JSBubbleView alloc] initWithFrame:frame
bubbleType:type
bubbleImageView:bubbleImageView];
bubbleView.autoresizingMask = (UIViewAutoresizingFlexibleWidth
| UIViewAutoresizingFlexibleHeight
| UIViewAutoresizingFlexibleBottomMargin);
[self.contentView addSubview:bubbleView];
[self.contentView sendSubviewToBack:bubbleView];
_bubbleView = bubbleView;
}
#pragma mark - Initialization
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self setup];
}
return self;
}
- (instancetype)initWithBubbleType:(JSBubbleMessageType)type
bubbleImageView:(UIImageView *)bubbleImageView
message:(id<JSMessageData>)message
displaysTimestamp:(BOOL)displaysTimestamp
hasAvatar:(BOOL)hasAvatar
reuseIdentifier:(NSString *)reuseIdentifier
{
self = [self initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier];
if (self) {
[self configureWithType:type
bubbleImageView:bubbleImageView
message:message
displaysTimestamp:displaysTimestamp
avatar:hasAvatar];
}
return self;
}
- (void)dealloc
{
_bubbleView = nil;
_timestampLabel = nil;
_avatarImageView = nil;
_subtitleLabel = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
#pragma mark - TableViewCell
- (void)prepareForReuse
{
[super prepareForReuse];
self.bubbleView.textView.text = nil;
self.timestampLabel.text = nil;
self.avatarImageView = nil;
self.subtitleLabel.text = nil;
}
- (void)setBackgroundColor:(UIColor *)color
{
[super setBackgroundColor:color];
[self.contentView setBackgroundColor:color];
[self.bubbleView setBackgroundColor:color];
}
#pragma mark - Setters
- (void)setText:(NSString *)text
{
self.bubbleView.textView.text = text;
}
- (void)setTimestamp:(NSDate *)date
{
self.timestampLabel.text = [NSDateFormatter localizedStringFromDate:date
dateStyle:NSDateFormatterMediumStyle
timeStyle:NSDateFormatterShortStyle];
}
- (void)setSubtitle:(NSString *)subtitle
{
self.subtitleLabel.text = subtitle;
}
- (void)setMessage:(id<JSMessageData>)message
{
[self setText:[message text]];
[self setTimestamp:[message date]];
[self setSubtitle:[message sender]];
}
- (void)setAvatarImageView:(UIImageView *)imageView
{
[_avatarImageView removeFromSuperview];
_avatarImageView = nil;
[self configureAvatarImageView:imageView forMessageType:[self messageType]];
}
#pragma mark - Getters
- (JSBubbleMessageType)messageType
{
return _bubbleView.type;
}
#pragma mark - Class methods
+ (CGFloat)neededHeightForBubbleMessageCellWithMessage:(id<JSMessageData>)message
displaysAvatar:(BOOL)displaysAvatar
displaysTimestamp:(BOOL)displaysTimestamp
{
CGFloat timestampHeight = displaysTimestamp ? kJSTimeStampLabelHeight : 0.0f;
CGFloat avatarHeight = displaysAvatar ? kJSAvatarImageSize : 0.0f;
CGFloat subtitleHeight = [message sender] ? kJSSubtitleLabelHeight : 0.0f;
CGFloat subviewHeights = timestampHeight + subtitleHeight + kJSLabelPadding;
CGFloat bubbleHeight = [JSBubbleView neededHeightForText:[message text]];
return subviewHeights + MAX(avatarHeight, bubbleHeight);
}
#pragma mark - Layout
- (void)layoutSubviews
{
[super layoutSubviews];
if (self.subtitleLabel) {
self.subtitleLabel.frame = CGRectMake(kJSLabelPadding,
self.contentView.frame.size.height - kJSSubtitleLabelHeight,
self.contentView.frame.size.width - (kJSLabelPadding * 2.0f),
kJSSubtitleLabelHeight);
}
}
#pragma mark - Copying
- (BOOL)canBecomeFirstResponder
{
return YES;
}
- (BOOL)becomeFirstResponder
{
return [super becomeFirstResponder];
}
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
return (action == @selector(copy:));
}
- (void)copy:(id)sender
{
[[UIPasteboard generalPasteboard] setString:self.bubbleView.textView.text];
[self resignFirstResponder];
}
#pragma mark - Gestures
- (void)handleLongPressGesture:(UILongPressGestureRecognizer *)longPress
{
if (longPress.state != UIGestureRecognizerStateBegan || ![self becomeFirstResponder])
return;
UIMenuController *menu = [UIMenuController sharedMenuController];
CGRect targetRect = [self convertRect:[self.bubbleView bubbleFrame]
fromView:self.bubbleView];
[menu setTargetRect:CGRectInset(targetRect, 0.0f, 4.0f) inView:self];
self.bubbleView.bubbleImageView.highlighted = YES;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleMenuWillShowNotification:)
name:UIMenuControllerWillShowMenuNotification
object:nil];
[menu setMenuVisible:YES animated:YES];
}
#pragma mark - Notifications
- (void)handleMenuWillHideNotification:(NSNotification *)notification
{
self.bubbleView.bubbleImageView.highlighted = NO;
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIMenuControllerWillHideMenuNotification
object:nil];
}
- (void)handleMenuWillShowNotification:(NSNotification *)notification
{
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIMenuControllerWillShowMenuNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleMenuWillHideNotification:)
name:UIMenuControllerWillHideMenuNotification
object:nil];
}
@end

View File

@ -1,85 +0,0 @@
//
// Created by Jesse Squires
// http://www.hexedbits.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSMessagesViewController
//
//
// The MIT License
// Copyright (c) 2013 Jesse Squires
// http://opensource.org/licenses/MIT
//
#import <UIKit/UIKit.h>
#import "JSBubbleImageViewFactory.h"
/**
* An instance of JSBubbleView is a means for displaying text in a speech bubble image to be placed in a JSBubbleMessageCell.
* @see JSBubbleMessageCell.
*/
@interface JSBubbleView : UIView
/**
* Returns the message type for this bubble view.
* @see JSBubbleMessageType for descriptions of the constants used to specify bubble message type.
*/
@property (assign, nonatomic, readonly) JSBubbleMessageType type;
/**
* Returns the image view containing the bubble image for this bubble view.
*/
@property (weak, nonatomic, readonly) UIImageView *bubbleImageView;
/**
* Returns the text view containing the message text for this bubble view.
*
* @warning You may customize the propeties of textView, however you *must not* change its `font` property directly. Please use the `JSBubbleView` font property instead.
*/
@property (weak, nonatomic, readonly) UITextView *textView;
/**
* The font for the text contained in the bubble view. The default value is `[UIFont systemFontOfSize:16.0f]`.
*
* @warning You must set this propety via `UIAppearance` only. *DO NOT set this property directly*.
* @bug Setting this property directly, rather than via `UIAppearance` will cause the message bubbles and text to be laid out incorrectly.
*/
@property (strong, nonatomic) UIFont *font UI_APPEARANCE_SELECTOR;
#pragma mark - Initialization
/**
* Initializes and returns a bubble view object having the given frame, bubble type, and bubble image view.
*
* @param frame A rectangle specifying the initial location and size of the bubble view in its superview's coordinates.
* @param bubleType A constant that specifies the type of the bubble view. @see JSBubbleMessageType.
* @param bubbleImageView An image view initialized with an image and highlighted image for this bubble view. @see JSBubbleImageViewFactory.
*
* @return An initialized `JSBubbleView` object or `nil` if the object could not be successfully initialized.
*/
- (instancetype)initWithFrame:(CGRect)frame
bubbleType:(JSBubbleMessageType)bubleType
bubbleImageView:(UIImageView *)bubbleImageView;
#pragma mark - Getters
/**
* The bubble view's frame rectangle is computed and set based on the size of the text that it needs to display.
*
* @return The frame of the bubble view.
*/
- (CGRect)bubbleFrame;
#pragma mark - Class methods
/**
* Computes and returns the minimum necessary height of a `JSBubbleView` needed to display the given text.
*
* @param text The text to display in the bubble view.
*
* @return The height required for the frame of the bubble view in order to display the given text.
*/
+ (CGFloat)neededHeightForText:(NSString *)text;
@end

View File

@ -1,250 +0,0 @@
//
// Created by Jesse Squires
// http://www.hexedbits.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSMessagesViewController
//
//
// The MIT License
// Copyright (c) 2013 Jesse Squires
// http://opensource.org/licenses/MIT
//
#import "JSBubbleView.h"
#import "JSMessageInputView.h"
#import "JSAvatarImageFactory.h"
#import "NSString+JSMessagesView.h"
#define kMarginTop 8.0f
#define kMarginBottom 4.0f
#define kPaddingTop 4.0f
#define kPaddingBottom 8.0f
#define kBubblePaddingRight 35.0f
@interface JSBubbleView()
- (void)setup;
- (void)addTextViewObservers;
- (void)removeTextViewObservers;
+ (CGSize)textSizeForText:(NSString *)txt;
+ (CGSize)neededSizeForText:(NSString *)text;
+ (CGFloat)neededHeightForText:(NSString *)text;
@end
@implementation JSBubbleView
@synthesize font = _font;
#pragma mark - Setup
- (void)setup
{
self.backgroundColor = [UIColor clearColor];
self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
}
#pragma mark - Initialization
- (instancetype)initWithFrame:(CGRect)frame
bubbleType:(JSBubbleMessageType)bubleType
bubbleImageView:(UIImageView *)bubbleImageView
{
self = [super initWithFrame:frame];
if (self) {
[self setup];
_type = bubleType;
bubbleImageView.userInteractionEnabled = YES;
[self addSubview:bubbleImageView];
_bubbleImageView = bubbleImageView;
UITextView *textView = [[UITextView alloc] init];
textView.font = [UIFont systemFontOfSize:16.0f];
textView.textColor = [UIColor blackColor];
textView.editable = NO;
textView.userInteractionEnabled = YES;
textView.showsHorizontalScrollIndicator = NO;
textView.showsVerticalScrollIndicator = NO;
textView.scrollEnabled = NO;
textView.backgroundColor = [UIColor clearColor];
textView.contentInset = UIEdgeInsetsZero;
textView.scrollIndicatorInsets = UIEdgeInsetsZero;
textView.contentOffset = CGPointZero;
[self addSubview:textView];
[self bringSubviewToFront:textView];
_textView = textView;
if ([_textView respondsToSelector:@selector(textContainerInset)]) {
_textView.textContainerInset = UIEdgeInsetsMake(8.0f, 4.0f, 2.0f, 4.0f);
}
[self addTextViewObservers];
// NOTE: TODO: textView frame & text inset
// --------------------
// future implementation for textView frame
// in layoutSubviews : "self.textView.frame = textFrame;" is not needed
// when setting the property : "_textView.textContainerInset = UIEdgeInsetsZero;"
// unfortunately, this API is available in iOS 7.0+
// update after dropping support for iOS 6.0
// --------------------
}
return self;
}
- (void)dealloc
{
[self removeTextViewObservers];
_bubbleImageView = nil;
_textView = nil;
}
#pragma mark - KVO
- (void)addTextViewObservers
{
[_textView addObserver:self
forKeyPath:@"text"
options:NSKeyValueObservingOptionNew
context:nil];
[_textView addObserver:self
forKeyPath:@"font"
options:NSKeyValueObservingOptionNew
context:nil];
[_textView addObserver:self
forKeyPath:@"textColor"
options:NSKeyValueObservingOptionNew
context:nil];
}
- (void)removeTextViewObservers
{
[_textView removeObserver:self forKeyPath:@"text"];
[_textView removeObserver:self forKeyPath:@"font"];
[_textView removeObserver:self forKeyPath:@"textColor"];
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
if (object == self.textView) {
if ([keyPath isEqualToString:@"text"]
|| [keyPath isEqualToString:@"font"]
|| [keyPath isEqualToString:@"textColor"]) {
[self setNeedsLayout];
}
}
}
#pragma mark - Setters
- (void)setFont:(UIFont *)font
{
_font = font;
_textView.font = font;
}
#pragma mark - UIAppearance Getters
- (UIFont *)font
{
if (_font == nil) {
_font = [[[self class] appearance] font];
}
if (_font != nil) {
return _font;
}
return [UIFont systemFontOfSize:16.0f];
}
#pragma mark - Getters
- (CGRect)bubbleFrame
{
CGSize bubbleSize = [JSBubbleView neededSizeForText:self.textView.text];
return CGRectIntegral(CGRectMake((self.type == JSBubbleMessageTypeOutgoing ? self.frame.size.width - bubbleSize.width : 0.0f),
kMarginTop,
bubbleSize.width,
bubbleSize.height + kMarginBottom));
}
#pragma mark - Layout
- (void)layoutSubviews
{
[super layoutSubviews];
self.bubbleImageView.frame = [self bubbleFrame];
CGFloat textX = self.bubbleImageView.frame.origin.x;
if (self.type == JSBubbleMessageTypeIncoming) {
textX += (self.bubbleImageView.image.capInsets.left / 2.0f);
}
CGRect textFrame = CGRectMake(textX,
self.bubbleImageView.frame.origin.y,
self.bubbleImageView.frame.size.width - (self.bubbleImageView.image.capInsets.right / 2.0f),
self.bubbleImageView.frame.size.height - kMarginTop);
self.textView.frame = CGRectIntegral(textFrame);
}
#pragma mark - Bubble view
+ (CGSize)textSizeForText:(NSString *)txt
{
CGFloat maxWidth = [UIScreen mainScreen].applicationFrame.size.width * 0.70f;
CGFloat maxHeight = MAX([JSMessageTextView numberOfLinesForMessage:txt],
[txt js_numberOfLines]) * [JSMessageInputView textViewLineHeight];
maxHeight += kJSAvatarImageSize;
CGSize stringSize;
if (NSFoundationVersionNumber > NSFoundationVersionNumber_iOS_6_0) {
CGRect stringRect = [txt boundingRectWithSize:CGSizeMake(maxWidth, maxHeight)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:@{ NSFontAttributeName : [[JSBubbleView appearance] font] }
context:nil];
stringSize = CGRectIntegral(stringRect).size;
}
else {
stringSize = [txt sizeWithFont:[[JSBubbleView appearance] font]
constrainedToSize:CGSizeMake(maxWidth, maxHeight)];
}
return CGSizeMake(roundf(stringSize.width), roundf(stringSize.height));
}
+ (CGSize)neededSizeForText:(NSString *)text
{
CGSize textSize = [JSBubbleView textSizeForText:text];
return CGSizeMake(textSize.width + kBubblePaddingRight,
textSize.height + kPaddingTop + kPaddingBottom);
}
+ (CGFloat)neededHeightForText:(NSString *)text
{
CGSize size = [JSBubbleView neededSizeForText:text];
return size.height + kMarginTop + kMarginBottom;
}
@end

View File

@ -1,68 +0,0 @@
//
// Taken from MADismissiveTextView
// https://github.com/mikeahmarani/MADismissiveTextView
//
// Created by Mike Ahmarani on 12-02-18.
// Copyright (c) 2012 Mike Ahmarani. All rights reserved.
//
//
// Documentation
// http://cocoadocs.org/docsets/JSMessagesViewController
//
//
// The MIT License
// Copyright (c) 2013 Jesse Squires
// http://opensource.org/licenses/MIT
//
#import <UIKit/UIKit.h>
/**
* The delegate of a `JSDismissiveTextView` object must adopt the `JSDismissiveTextViewDelegate` protocol.
*/
@protocol JSDismissiveTextViewDelegate <NSObject>
@optional
/**
* Tells the delegate that the keyboard has full appeared on screen.
*/
- (void)keyboardDidShow;
/**
* Tells the delegate that the keyboard origin has moved to the specified point.
*
* @param point The origin of the keyboard's frame in its superview's coordinate system.
*/
- (void)keyboardDidScrollToPoint:(CGPoint)point;
/**
* Tells the delegate that the keyboard is about to be dismissed. The keyboard will be removed from from its superview and resign first responder.
*/
- (void)keyboardWillBeDismissed;
/**
* Tells the delegate that the keyboard origin is about to move back to the specified point.
*
* @param point The new origin of the keyboard's frame after it has completed animation.
*/
- (void)keyboardWillSnapBackToPoint:(CGPoint)point;
@end
/**
* An instance of `JSDismissiveTextView` is a means for displaying a text view that is contained as a subview of the keyboard's `inputAccessoryView` and responds to a pan gesture to dismiss the keyboard and end editing.
*/
@interface JSDismissiveTextView : UITextView
/**
* The object that acts as the delegate of the receiving text view.
*/
@property (weak, nonatomic) id<JSDismissiveTextViewDelegate> keyboardDelegate;
/**
* The pan gesture recognizer for the text view.
*/
@property (strong, nonatomic) UIPanGestureRecognizer *dismissivePanGestureRecognizer;
@end

View File

@ -1,193 +0,0 @@
//
// Taken from MADismissiveTextView
// https://github.com/mikeahmarani/MADismissiveTextView
//
// Created by Mike Ahmarani on 12-02-18.
// Copyright (c) 2012 Mike Ahmarani. All rights reserved.
//
//
// Documentation
// http://cocoadocs.org/docsets/JSMessagesViewController
//
//
// The MIT License
// Copyright (c) 2013 Jesse Squires
// http://opensource.org/licenses/MIT
//
#import "JSDismissiveTextView.h"
@interface JSDismissiveTextView ()
@property (strong, nonatomic) UIView *keyboardView;
@property (assign, nonatomic) CGFloat previousKeyboardY;
- (void)handleKeyboardWillShowHideNotification:(NSNotification *)notification;
- (void)handlePanGesture:(UIPanGestureRecognizer *)pan;
@end
@implementation JSDismissiveTextView
#pragma mark - Initialization
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.editable = YES;
// FIXME: this is a hack
// ---------------------
// init an empty inputAccessoryView to get a reference to the keyboard when it appears
// i.e., self.inputAccessoryView.superview <-- the keyboard (see notification handler below)
// otherwise self.inputAccessoryView == nil, thus self.inputAccessoryView.superivew == nil
// ---------------------
// Can you fix this? Submit a PR! :)
self.inputAccessoryView = [[UIView alloc] init];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleKeyboardWillShowHideNotification:)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleKeyboardWillShowHideNotification:)
name:UIKeyboardDidShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleKeyboardWillShowHideNotification:)
name:UIKeyboardDidHideNotification
object:nil];
}
return self;
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[_dismissivePanGestureRecognizer removeTarget:self action:@selector(handlePanGesture:)];
_dismissivePanGestureRecognizer = nil;
_keyboardDelegate = nil;
_keyboardView = nil;
}
#pragma mark - Setters
- (void)setDismissivePanGestureRecognizer:(UIPanGestureRecognizer *)pan
{
_dismissivePanGestureRecognizer = pan;
[_dismissivePanGestureRecognizer addTarget:self action:@selector(handlePanGesture:)];
}
#pragma mark - Notifications
- (void)handleKeyboardWillShowHideNotification:(NSNotification *)notification
{
if ([notification.name isEqualToString:UIKeyboardWillShowNotification]) {
self.keyboardView.hidden = NO;
}
else if ([notification.name isEqualToString:UIKeyboardDidShowNotification]) {
self.keyboardView = self.inputAccessoryView.superview;
self.keyboardView.hidden = NO;
if (self.keyboardDelegate && [self.keyboardDelegate respondsToSelector:@selector(keyboardDidShow)])
[self.keyboardDelegate keyboardDidShow];
}
else if ([notification.name isEqualToString:UIKeyboardDidHideNotification]) {
self.keyboardView.hidden = NO;
BOOL isPhone = [UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone;
BOOL keyBoardWasSplitOnPad = [[notification userInfo][@"UIKeyboardFrameChangedByUserInteraction"] boolValue];
if (isPhone || keyBoardWasSplitOnPad) {
[self resignFirstResponder];
}
}
}
#pragma mark - Gestures
- (void)handlePanGesture:(UIPanGestureRecognizer *)pan
{
if (!self.keyboardView || self.keyboardView.hidden)
return;
CGRect screenRect = [[UIScreen mainScreen] bounds];
CGFloat screenHeight = screenRect.size.height;
UIWindow *panWindow = [[UIApplication sharedApplication] keyWindow];
CGPoint location = [pan locationInView:panWindow];
CGPoint velocity = [pan velocityInView:panWindow];
switch (pan.state) {
case UIGestureRecognizerStateBegan:
self.previousKeyboardY = self.keyboardView.frame.origin.y;
break;
case UIGestureRecognizerStateEnded:
if (velocity.y > 0 && self.keyboardView.frame.origin.y > self.previousKeyboardY) {
[UIView animateWithDuration:0.3
delay:0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
self.keyboardView.frame = CGRectMake(0.0f,
screenHeight,
self.keyboardView.frame.size.width,
self.keyboardView.frame.size.height);
if (self.keyboardDelegate && [self.keyboardDelegate respondsToSelector:@selector(keyboardWillBeDismissed)])
[self.keyboardDelegate keyboardWillBeDismissed];
}
completion:^(BOOL finished) {
self.keyboardView.hidden = YES;
self.keyboardView.frame = CGRectMake(0.0f,
self.previousKeyboardY,
self.keyboardView.frame.size.width,
self.keyboardView.frame.size.height);
[self resignFirstResponder];
}];
}
else {
// gesture ended with no flick or a flick upwards, snap keyboard back to original position
[UIView animateWithDuration:0.2
delay:0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
if (self.keyboardDelegate && [self.keyboardDelegate respondsToSelector:@selector(keyboardWillSnapBackToPoint:)]) {
[self.keyboardDelegate keyboardWillSnapBackToPoint:CGPointMake(0.0f, self.previousKeyboardY)];
}
self.keyboardView.frame = CGRectMake(0.0f,
self.previousKeyboardY,
self.keyboardView.frame.size.width,
self.keyboardView.frame.size.height);
}
completion:nil];
}
break;
default:
// gesture is currently panning, match keyboard y to touch y
if (location.y > self.keyboardView.frame.origin.y || self.keyboardView.frame.origin.y != self.previousKeyboardY) {
CGFloat newKeyboardY = self.previousKeyboardY + (location.y - self.previousKeyboardY);
newKeyboardY = newKeyboardY < self.previousKeyboardY ? self.previousKeyboardY : newKeyboardY;
newKeyboardY = newKeyboardY > screenHeight ? screenHeight : newKeyboardY;
self.keyboardView.frame = CGRectMake(0.0f,
newKeyboardY,
self.keyboardView.frame.size.width,
self.keyboardView.frame.size.height);
if (self.keyboardDelegate && [self.keyboardDelegate respondsToSelector:@selector(keyboardDidScrollToPoint:)])
[self.keyboardDelegate keyboardDidScrollToPoint:CGPointMake(0.0f, newKeyboardY)];
}
break;
}
}
@end

View File

@ -1,53 +0,0 @@
//
// Created by Jesse Squires
// http://www.hexedbits.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSMessagesViewController
//
//
// The MIT License
// Copyright (c) 2013 Jesse Squires
// http://opensource.org/licenses/MIT
//
#import <Foundation/Foundation.h>
#import "JSMessageData.h"
/**
* A `JSMessage` object represents a single user message. This is a concrete class that implements the `JSMessageData` protocol. It contains the message text, its sender, and the date that the message was sent.
*/
@interface JSMessage : NSObject <JSMessageData, NSCoding, NSCopying>
/**
* The body text of the message. The default value is the empty string `@" "`. This value must not be `nil`.
*/
@property (copy, nonatomic) NSString *text;
/**
* The name of user who sent the message. The default value is `nil`.
*/
@property (copy, nonatomic) NSString *sender;
/**
* The date that the message was sent. The default value is `nil`.
*/
@property (strong, nonatomic) NSDate *date;
#pragma mark - Initialization
/**
* Initializes and returns a message object having the given text, sender, and date.
*
* @param text The body text of the message.
* @param sender The name of the user who sent the message.
* @param date The date that the message was sent.
*
* @return An initialized `JSMessage` object or `nil` if the object could not be successfully initialized.
*/
- (instancetype)initWithText:(NSString *)text
sender:(NSString *)sender
date:(NSDate *)date;
@end

View File

@ -1,70 +0,0 @@
//
// Created by Jesse Squires
// http://www.hexedbits.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSMessagesViewController
//
//
// The MIT License
// Copyright (c) 2013 Jesse Squires
// http://opensource.org/licenses/MIT
//
#import "JSMessage.h"
@implementation JSMessage
#pragma mark - Initialization
- (instancetype)initWithText:(NSString *)text
sender:(NSString *)sender
date:(NSDate *)date
{
self = [super init];
if (self) {
_text = text ? text : @" ";
_sender = sender;
_date = date;
}
return self;
}
- (void)dealloc
{
_text = nil;
_sender = nil;
_date = nil;
}
#pragma mark - NSCoding
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self) {
_text = [aDecoder decodeObjectForKey:@"text"];
_sender = [aDecoder decodeObjectForKey:@"sender"];
_date = [aDecoder decodeObjectForKey:@"date"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:self.text forKey:@"text"];
[aCoder encodeObject:self.sender forKey:@"sender"];
[aCoder encodeObject:self.date forKey:@"date"];
}
#pragma mark - NSCopying
- (id)copyWithZone:(NSZone *)zone
{
return [[[self class] allocWithZone:zone] initWithText:[self.text copy]
sender:[self.sender copy]
date:[self.date copy]];
}
@end

View File

@ -1,43 +0,0 @@
//
// Created by Jesse Squires
// http://www.hexedbits.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSMessagesViewController
//
//
// The MIT License
// Copyright (c) 2013 Jesse Squires
// http://opensource.org/licenses/MIT
//
#import <Foundation/Foundation.h>
/**
* The `JSMessageData` protocol defines the common interface through which `JSMessagesViewController` interacts with message model objects.
* It declares the methods that a class must implement so that instances of that class can be displayed properly by a `JSMessagesViewController`.
*/
@protocol JSMessageData <NSObject>
@required
/**
* @return The body text of the message.
* @warning This value must not be `nil`.
*/
- (NSString *)text;
@optional
/**
* @return The name of the user who sent the message.
*/
- (NSString *)sender;
/**
* @return The date that the message was sent.
*/
- (NSDate *)date;
@end

View File

@ -1,97 +0,0 @@
//
// Created by Jesse Squires
// http://www.hexedbits.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSMessagesViewController
//
//
// The MIT License
// Copyright (c) 2013 Jesse Squires
// http://opensource.org/licenses/MIT
//
#import <UIKit/UIKit.h>
#import "JSMessageTextView.h"
/**
* The appearance style of the input bar view for composing a new message.
*/
typedef NS_ENUM(NSUInteger, JSMessageInputViewStyle) {
/**
* An input view style that has the appearance as seen in iOS 6 and before.
*/
JSMessageInputViewStyleClassic,
/**
* An input view style that has the appearance as seen in iOS 7 and later.
*/
JSMessageInputViewStyleFlat
};
/**
* An instance of `JSMessageInputView` defines the input toolbar for composing a new message that is to be displayed above the keyboard.
*/
@interface JSMessageInputView : UIImageView
/**
* Returns the style appearance for the input view.
* @see JSMessageInputViewStyle.
*/
@property (assign, nonatomic, readonly) JSMessageInputViewStyle style;
/**
* Returns the textView into which a new message is composed. This property is never `nil`.
*/
@property (weak, nonatomic, readonly) JSMessageTextView *textView;
/**
* The send button for the input view. The default value is an initialized `UIButton` whose appearance is styled according to the value of style during initialization.
* @see JSMessageInputViewStyle.
*/
@property (weak, nonatomic) UIButton *sendButton;
#pragma mark - Initialization
/**
* Initializes and returns an input view having the given frame, style, delegate, and panGestureRecognizer.
*
* @param frame A rectangle specifying the initial location and size of the bubble view in its superview's coordinates.
* @param style The style of the input view. @see JSMessageInputViewStyle.
* @param delegate An object that conforms to the `UITextViewDelegate` protocol and `JSDismissiveTextViewDelegate` protocol.
* @see JSDismissiveTextViewDelegate.
* @param panGestureRecognizer A `UIPanGestureRecognizer` used to dismiss the input view by dragging down.
*
* @return An initialized `JSMessageInputView` object or `nil` if the object could not be successfully initialized.
*/
- (instancetype)initWithFrame:(CGRect)frame
style:(JSMessageInputViewStyle)style
delegate:(id<UITextViewDelegate, JSDismissiveTextViewDelegate>)delegate
panGestureRecognizer:(UIPanGestureRecognizer *)panGestureRecognizer;
#pragma mark - Message input view
/**
* Adjusts the input view's frame height by the given value.
*
* @param changeInHeight The delta value by which to increase or decrease the existing height for the input view.
*/
- (void)adjustTextViewHeightBy:(CGFloat)changeInHeight;
/**
* @return A constant indicating the height of one line of text in the input view.
*/
+ (CGFloat)textViewLineHeight;
/**
* @return A contant indicating the maximum number of lines of text that can be displayed in the textView.
*/
+ (CGFloat)maxLines;
/**
* @return The maximum height of the input view as determined by `maxLines` and `textViewLineHeight`. This value is used for controlling the animation of the growing and shrinking of the input view as the text changes in the textView.
*/
+ (CGFloat)maxHeight;
@end

View File

@ -1,238 +0,0 @@
//
// Created by Jesse Squires
// http://www.hexedbits.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSMessagesViewController
//
//
// The MIT License
// Copyright (c) 2013 Jesse Squires
// http://opensource.org/licenses/MIT
//
#import "JSMessageInputView.h"
#import <QuartzCore/QuartzCore.h>
#import "JSBubbleView.h"
#import "NSString+JSMessagesView.h"
#import "UIColor+JSMessagesView.h"
@interface JSMessageInputView ()
- (void)setup;
- (void)configureInputBarWithStyle:(JSMessageInputViewStyle)style;
- (void)configureSendButtonWithStyle:(JSMessageInputViewStyle)style;
@end
@implementation JSMessageInputView
#pragma mark - Initialization
- (void)setup
{
self.backgroundColor = [UIColor whiteColor];
self.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin);
self.opaque = YES;
self.userInteractionEnabled = YES;
}
- (void)configureInputBarWithStyle:(JSMessageInputViewStyle)style
{
CGFloat sendButtonWidth = (style == JSMessageInputViewStyleClassic) ? 78.0f : 64.0f;
CGFloat width = self.frame.size.width - sendButtonWidth;
CGFloat height = [JSMessageInputView textViewLineHeight];
JSMessageTextView *textView = [[JSMessageTextView alloc] initWithFrame:CGRectZero];
[self addSubview:textView];
_textView = textView;
if (style == JSMessageInputViewStyleClassic) {
_textView.frame = CGRectMake(6.0f, 3.0f, width, height);
_textView.backgroundColor = [UIColor whiteColor];
self.image = [[UIImage imageNamed:@"input-bar-background"] resizableImageWithCapInsets:UIEdgeInsetsMake(19.0f, 3.0f, 19.0f, 3.0f)
resizingMode:UIImageResizingModeStretch];
UIImageView *inputFieldBack = [[UIImageView alloc] initWithFrame:CGRectMake(_textView.frame.origin.x - 1.0f,
0.0f,
_textView.frame.size.width + 2.0f,
self.frame.size.height)];
inputFieldBack.image = [[UIImage imageNamed:@"input-field-cover"] resizableImageWithCapInsets:UIEdgeInsetsMake(20.0f, 12.0f, 18.0f, 18.0f)];
inputFieldBack.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
inputFieldBack.backgroundColor = [UIColor clearColor];
[self addSubview:inputFieldBack];
}
else {
_textView.frame = CGRectMake(4.0f, 4.5f, width, height);
_textView.backgroundColor = [UIColor clearColor];
_textView.layer.borderColor = [UIColor colorWithWhite:0.8f alpha:1.0f].CGColor;
_textView.layer.borderWidth = 0.65f;
_textView.layer.cornerRadius = 6.0f;
self.image = [[UIImage imageNamed:@"input-bar-flat"] resizableImageWithCapInsets:UIEdgeInsetsMake(2.0f, 0.0f, 0.0f, 0.0f)
resizingMode:UIImageResizingModeStretch];
}
}
- (void)configureSendButtonWithStyle:(JSMessageInputViewStyle)style
{
UIButton *sendButton;
if (style == JSMessageInputViewStyleClassic) {
sendButton = [UIButton buttonWithType:UIButtonTypeCustom];
UIEdgeInsets insets = UIEdgeInsetsMake(0.0f, 13.0f, 0.0f, 13.0f);
UIImage *sendBack = [[UIImage imageNamed:@"send-button"] resizableImageWithCapInsets:insets];
UIImage *sendBackHighLighted = [[UIImage imageNamed:@"send-button-pressed"] resizableImageWithCapInsets:insets];
[sendButton setBackgroundImage:sendBack forState:UIControlStateNormal];
[sendButton setBackgroundImage:sendBack forState:UIControlStateDisabled];
[sendButton setBackgroundImage:sendBackHighLighted forState:UIControlStateHighlighted];
UIColor *titleShadow = [UIColor colorWithRed:0.325f green:0.463f blue:0.675f alpha:1.0f];
[sendButton setTitleShadowColor:titleShadow forState:UIControlStateNormal];
[sendButton setTitleShadowColor:titleShadow forState:UIControlStateHighlighted];
sendButton.titleLabel.shadowOffset = CGSizeMake(0.0f, -1.0f);
[sendButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[sendButton setTitleColor:[UIColor whiteColor] forState:UIControlStateHighlighted];
[sendButton setTitleColor:[UIColor colorWithWhite:1.0f alpha:0.5f] forState:UIControlStateDisabled];
sendButton.titleLabel.font = [UIFont boldSystemFontOfSize:16.0f];
}
else {
sendButton = [UIButton buttonWithType:UIButtonTypeCustom];
sendButton.backgroundColor = [UIColor clearColor];
[sendButton setTitleColor:[UIColor js_bubbleBlueColor] forState:UIControlStateNormal];
[sendButton setTitleColor:[UIColor js_bubbleBlueColor] forState:UIControlStateHighlighted];
[sendButton setTitleColor:[UIColor js_bubbleLightGrayColor] forState:UIControlStateDisabled];
sendButton.titleLabel.font = [UIFont boldSystemFontOfSize:18.0f];
}
NSString *title = NSLocalizedString(@"Send", nil);
[sendButton setTitle:title forState:UIControlStateNormal];
[sendButton setTitle:title forState:UIControlStateHighlighted];
[sendButton setTitle:title forState:UIControlStateDisabled];
sendButton.autoresizingMask = (UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin);
[self setSendButton:sendButton];
}
- (instancetype)initWithFrame:(CGRect)frame
style:(JSMessageInputViewStyle)style
delegate:(id<UITextViewDelegate, JSDismissiveTextViewDelegate>)delegate
panGestureRecognizer:(UIPanGestureRecognizer *)panGestureRecognizer
{
self = [super initWithFrame:frame];
if (self) {
_style = style;
[self setup];
[self configureInputBarWithStyle:style];
[self configureSendButtonWithStyle:style];
_textView.delegate = delegate;
_textView.keyboardDelegate = delegate;
_textView.dismissivePanGestureRecognizer = panGestureRecognizer;
}
return self;
}
- (void)dealloc
{
_textView = nil;
_sendButton = nil;
}
#pragma mark - UIView
- (BOOL)resignFirstResponder
{
[self.textView resignFirstResponder];
return [super resignFirstResponder];
}
#pragma mark - Setters
- (void)setSendButton:(UIButton *)btn
{
if (_sendButton)
[_sendButton removeFromSuperview];
if (self.style == JSMessageInputViewStyleClassic) {
btn.frame = CGRectMake(self.frame.size.width - 65.0f, 8.0f, 59.0f, 26.0f);
}
else {
CGFloat padding = 8.0f;
btn.frame = CGRectMake(self.textView.frame.origin.x + self.textView.frame.size.width,
padding,
60.0f,
self.textView.frame.size.height - padding);
}
[self addSubview:btn];
_sendButton = btn;
}
#pragma mark - Message input view
- (void)adjustTextViewHeightBy:(CGFloat)changeInHeight
{
CGRect prevFrame = self.textView.frame;
NSUInteger numLines = MAX([self.textView numberOfLinesOfText],
[self.textView.text js_numberOfLines]);
// below iOS 7, if you set the text view frame programmatically, the KVO will continue notifying
// to avoid that, we are removing the observer before setting the frame and add the observer after setting frame here.
[self.textView removeObserver:_textView.keyboardDelegate
forKeyPath:@"contentSize"];
self.textView.frame = CGRectMake(prevFrame.origin.x,
prevFrame.origin.y,
prevFrame.size.width,
prevFrame.size.height + changeInHeight);
[self.textView addObserver:_textView.keyboardDelegate
forKeyPath:@"contentSize"
options:NSKeyValueObservingOptionNew
context:nil];
self.textView.contentInset = UIEdgeInsetsMake((numLines >= 6 ? 4.0f : 0.0f),
0.0f,
(numLines >= 6 ? 4.0f : 0.0f),
0.0f);
// from iOS 7, the content size will be accurate only if the scrolling is enabled.
self.textView.scrollEnabled = YES;
if (numLines >= 6) {
CGPoint bottomOffset = CGPointMake(0.0f, self.textView.contentSize.height - self.textView.bounds.size.height);
[self.textView setContentOffset:bottomOffset animated:YES];
[self.textView scrollRangeToVisible:NSMakeRange(self.textView.text.length - 2, 1)];
}
}
+ (CGFloat)textViewLineHeight
{
return 36.0f; // for fontSize 16.0f
}
+ (CGFloat)maxLines
{
return ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone) ? 4.0f : 8.0f;
}
+ (CGFloat)maxHeight
{
return ([JSMessageInputView maxLines] + 1.0f) * [JSMessageInputView textViewLineHeight];
}
@end

View File

@ -1,42 +0,0 @@
//
// Created by Jesse Squires
// http://www.hexedbits.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSMessagesViewController
//
//
// The MIT License
// Copyright (c) 2013 Jesse Squires
// http://opensource.org/licenses/MIT
//
#import <Foundation/Foundation.h>
/**
* `JSMessageSoundEffect` is a class that encapsulates the playing of the default sound effects for sending and receiving messages in `JSMessagesViewController`. It is backed by an instance of `JSQSystemSoundPlayer`.
*/
@interface JSMessageSoundEffect : NSObject
/**
* Plays the default sound for received messages.
*/
+ (void)playMessageReceivedSound;
/**
* Plays the default sound for received messages *as an alert*, invoking device vibration if available.
*/
+ (void)playMessageReceivedAlert;
/**
* Plays the default sound for sent messages.
*/
+ (void)playMessageSentSound;
/**
* Plays the default sound for sent messages *as an alert*, invoking device vibration if available.
*/
+ (void)playMessageSentAlert;
@end

View File

@ -1,47 +0,0 @@
//
// Created by Jesse Squires
// http://www.hexedbits.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSMessagesViewController
//
//
// The MIT License
// Copyright (c) 2013 Jesse Squires
// http://opensource.org/licenses/MIT
//
#import "JSMessageSoundEffect.h"
#import <JSQSystemSoundPlayer/JSQSystemSoundPlayer.h>
static NSString * const kJSMessageReceived = @"message-received";
static NSString * const kJSMessageSent = @"message-sent";
@implementation JSMessageSoundEffect
+ (void)playMessageReceivedSound
{
[[JSQSystemSoundPlayer sharedPlayer] playSoundWithName:kJSMessageReceived
extension:kJSQSystemSoundTypeAIFF];
}
+ (void)playMessageReceivedAlert
{
[[JSQSystemSoundPlayer sharedPlayer] playAlertSoundWithName:kJSMessageReceived
extension:kJSQSystemSoundTypeAIFF];
}
+ (void)playMessageSentSound
{
[[JSQSystemSoundPlayer sharedPlayer] playSoundWithName:kJSMessageSent
extension:kJSQSystemSoundTypeAIFF];
}
+ (void)playMessageSentAlert
{
[[JSQSystemSoundPlayer sharedPlayer] playAlertSoundWithName:kJSMessageSent
extension:kJSQSystemSoundTypeAIFF];
}
@end

View File

@ -1,22 +0,0 @@
//
// Created by Jesse Squires
// http://www.hexedbits.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSMessagesViewController
//
//
// The MIT License
// Copyright (c) 2013 Jesse Squires
// http://opensource.org/licenses/MIT
//
#import <UIKit/UIKit.h>
/**
* An instance of `JSMessageTableView` is a subclass of `UITableView` and is means for displaying a list of messages between a group of users.
*/
@interface JSMessageTableView : UITableView
@end

View File

@ -1,19 +0,0 @@
//
// Created by Jesse Squires
// http://www.hexedbits.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSMessagesViewController
//
//
// The MIT License
// Copyright (c) 2013 Jesse Squires
// http://opensource.org/licenses/MIT
//
#import "JSMessageTableView.h"
@implementation JSMessageTableView
@end

View File

@ -1,55 +0,0 @@
//
// Created by Jesse Squires
// http://www.hexedbits.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSMessagesViewController
//
//
// The MIT License
// Copyright (c) 2013 Jesse Squires
// http://opensource.org/licenses/MIT
//
#import "JSDismissiveTextView.h"
/**
* An instance of `JSMessageTextView` is a means for displaying an input text view above a keyboard as a subview of a keyboard's `inputAccessoryView`. It is used for composing messages and adds support for a placeholder like UITextField.
*/
@interface JSMessageTextView : JSDismissiveTextView
/**
* The text to be displayed when the text view is empty. The default value is `nil`.
*/
@property (copy, nonatomic) NSString *placeHolder;
/**
* The color of the place holder text. The default value is `[UIColor lightGrayColor]`.
*/
@property (strong, nonatomic) UIColor *placeHolderTextColor;
/**
* Returns an unsigned integer describing the number of lines of text contained in the text view.
*
* @return The number of lines of text in the text view.
*/
- (NSUInteger)numberOfLinesOfText;
/**
* Returns a constant describing the maximum number of characters that can fit on a single line of the text view.
*
* @return The maximum number of characters per line in the text view.
*/
+ (NSUInteger)maxCharactersPerLine;
/**
* Returns an unsigned integer describing the number of lines necessary to display the given text in the text view.
*
* @param text The text to be displayed in the text view.
*
* @return The number of lines needed to display the given text.
*/
+ (NSUInteger)numberOfLinesForMessage:(NSString *)text;
@end

View File

@ -1,222 +0,0 @@
//
// Created by Jesse Squires
// http://www.hexedbits.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSMessagesViewController
//
//
// The MIT License
// Copyright (c) 2013 Jesse Squires
// http://opensource.org/licenses/MIT
//
#import <UIKit/UIKit.h>
#import "JSMessageTableView.h"
#import "JSMessageData.h"
#import "JSBubbleMessageCell.h"
#import "JSMessageInputView.h"
#import "JSAvatarImageFactory.h"
#import "JSBubbleImageViewFactory.h"
#import "JSMessageSoundEffect.h"
#import "UIColor+JSMessagesView.h"
/**
* The delegate of a `JSMessagesViewController` must adopt the `JSMessagesViewDelegate` protocol.
*/
@protocol JSMessagesViewDelegate <NSObject>
@required
/**
* Tells the delegate that the user has sent a message with the specified text, sender, and date.
*
* @param text The text that was present in the textView of the messageInputView when the send button was pressed.
* @param sender The user who sent the message.
* @param date The date and time at which the message was sent.
*/
- (void)didSendText:(NSString *)text fromSender:(NSString *)sender onDate:(NSDate *)date;
/**
* Asks the delegate for the message type for the row at the specified index path.
*
* @param indexPath The index path of the row to be displayed.
*
* @return A constant describing the message type.
* @see JSBubbleMessageType.
*/
- (JSBubbleMessageType)messageTypeForRowAtIndexPath:(NSIndexPath *)indexPath;
/**
* Asks the delegate for the bubble image view for the row at the specified index path with the specified type.
*
* @param type The type of message for the row located at indexPath.
* @param indexPath The index path of the row to be displayed.
*
* @return A `UIImageView` with both `image` and `highlightedImage` properties set.
* @see JSBubbleImageViewFactory.
*/
- (UIImageView *)bubbleImageViewWithType:(JSBubbleMessageType)type
forRowAtIndexPath:(NSIndexPath *)indexPath;
/**
* Asks the delegate for the input view style.
*
* @return A constant describing the input view style.
* @see JSMessageInputViewStyle.
*/
- (JSMessageInputViewStyle)inputViewStyle;
@optional
/**
* Asks the delegate if a timestamp should be displayed *above* the row at the specified index path.
*
* @param indexPath The index path of the row to be displayed.
*
* @return A boolean value specifying whether or not a timestamp should be displayed for the row at indexPath. The default value is `YES`.
*/
- (BOOL)shouldDisplayTimestampForRowAtIndexPath:(NSIndexPath *)indexPath;
/**
* Asks the delegate to configure or further customize the given cell at the specified index path.
*
* @param cell The message cell to configure.
* @param indexPath The index path for cell.
*/
- (void)configureCell:(JSBubbleMessageCell *)cell atIndexPath:(NSIndexPath *)indexPath;
/**
* Asks the delegate if should always scroll to bottom automatically when new messages are sent or received.
*
* @return `YES` if you would like to prevent the table view from being scrolled to the bottom while the user is scrolling the table view manually, `NO` otherwise.
*/
- (BOOL)shouldPreventScrollToBottomWhileUserScrolling;
/**
* Ask the delegate if the keyboard should be dismissed by panning/swiping downward. The default value is `YES`. Return `NO` to dismiss the keyboard by tapping.
*
* @return A boolean value specifying whether the keyboard should be dismissed by panning/swiping.
*/
- (BOOL)allowsPanToDismissKeyboard;
/**
* Asks the delegate for the send button to be used in messageInputView. Implement this method if you wish to use a custom send button. The button must be a `UIButton` or a subclass of `UIButton`. The button's frame is set for you.
*
* @return A custom `UIButton` to use in messageInputView.
*/
- (UIButton *)sendButtonForInputView;
/**
* Asks the delegate for a custom cell reuse identifier for the row to be displayed at the specified index path.
*
* @param indexPath The index path of the row to be displayed.
*
* @return A string specifying the cell reuse identifier for the row at indexPath.
*/
- (NSString *)customCellIdentifierForRowAtIndexPath:(NSIndexPath *)indexPath;
@end
@protocol JSMessagesViewDataSource <NSObject>
@required
/**
* Asks the data soruce for the message object to display for the row at the specified index path. The message text is displayed in the bubble at index path. The message date is displayed *above* the row at the specified index path. The message sender is displayed *below* the row at the specified index path.
*
* @param indexPath An index path locating a row in the table view.
*
* @return An object that conforms to the `JSMessageData` protocol containing the message data. This value must not be `nil`.
*/
- (id<JSMessageData>)messageForRowAtIndexPath:(NSIndexPath *)indexPath;
/**
* Asks the data source for the imageView to display for the row at the specified index path with the given sender. The imageView must have its `image` property set.
*
* @param indexPath An index path locating a row in the table view.
* @param sender The name of the user who sent the message at indexPath.
*
* @return An image view specifying the avatar for the message at indexPath. This value may be `nil`.
*/
- (UIImageView *)avatarImageViewForRowAtIndexPath:(NSIndexPath *)indexPath sender:(NSString *)sender;
@end
/**
* An instance of `JSMessagesViewController` is a subclass of `UIViewController` specialized to display a messaging interface.
*/
@interface JSMessagesViewController : UIViewController <UITableViewDataSource, UITableViewDelegate, UITextViewDelegate>
/**
* The object that acts as the delegate of the receiving messages view.
*/
@property (weak, nonatomic) id<JSMessagesViewDelegate> delegate;
/**
* The object that acts as the data source of receiving messages view.
*/
@property (weak, nonatomic) id<JSMessagesViewDataSource> dataSource;
/**
* Returns the table view that displays the messages in `JSMessagesViewController`.
*/
@property (weak, nonatomic, readonly) JSMessageTableView *tableView;
/**
* Returns the message input view with which new messages are composed.
*/
@property (weak, nonatomic, readonly) JSMessageInputView *messageInputView;
/**
* The name of the user sending messages. The default value is `nil`.
*/
@property (copy, nonatomic) NSString *sender;
#pragma mark - Messages view controller
/**
* Animates and resets the text view in messageInputView. Call this method at the end of the delegate method `didSendText:`.
* @see JSMessagesViewDelegate.
*/
- (void)finishSend;
/**
* Sets the background color of the table view, the table view cells, and the table view separator.
*
* @param color The color to be used as the new background color.
*/
- (void)setBackgroundColor:(UIColor *)color;
/**
* Scrolls the table view such that the bottom most cell is completely visible, above the messageInputView.
*
* This method respects the delegate method `shouldPreventScrollToBottomWhileUserScrolling`.
*
* @see JSMessagesViewDelegate.
*
* @param animated `YES` if you want to animate scrolling, `NO` if it should be immediate.
*/
- (void)scrollToBottomAnimated:(BOOL)animated;
/**
* Scrolls the receiver until a row identified by index path is at a particular location on the screen.
*
* This method respects the delegate method `shouldPreventScrollToBottomWhileUserScrolling`.
*
* @see JSMessagesViewDelegate.
*
* @param indexPath An index path that identifies a row in the table view by its row index and its section index.
* @param position A constant defined in `UITableViewScrollPosition` that identifies a relative position in the receiving table view.
* @param animated `YES` if you want to animate the change in position, `NO` if it should be immediate.
*/
- (void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath
atScrollPosition:(UITableViewScrollPosition)position
animated:(BOOL)animated;
@end

View File

@ -1,531 +0,0 @@
//
// Created by Jesse Squires
// http://www.hexedbits.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSMessagesViewController
//
//
// The MIT License
// Copyright (c) 2013 Jesse Squires
// http://opensource.org/licenses/MIT
//
#import "JSMessagesViewController.h"
#import "JSMessageTextView.h"
#import "NSString+JSMessagesView.h"
@interface JSMessagesViewController () <JSDismissiveTextViewDelegate>
@property (assign, nonatomic) CGFloat previousTextViewContentHeight;
@property (assign, nonatomic) BOOL isUserScrolling;
- (void)setup;
- (void)sendPressed:(UIButton *)sender;
- (void)handleTapGestureRecognizer:(UITapGestureRecognizer *)tap;
- (BOOL)shouldAllowScroll;
- (void)layoutAndAnimateMessageInputTextView:(UITextView *)textView;
- (void)setTableViewInsetsWithBottomValue:(CGFloat)bottom;
- (UIEdgeInsets)tableViewInsetsWithBottomValue:(CGFloat)bottom;
- (void)handleWillShowKeyboardNotification:(NSNotification *)notification;
- (void)handleWillHideKeyboardNotification:(NSNotification *)notification;
- (void)keyboardWillShowHide:(NSNotification *)notification;
- (void)animationForMessageInputViewAtPoint:(CGPoint)point;
@end
@implementation JSMessagesViewController
#pragma mark - Initialization
- (void)setup
{
if ([self.view isKindOfClass:[UIScrollView class]]) {
// FIXME: hack-ish fix for ipad modal form presentations
((UIScrollView *)self.view).scrollEnabled = NO;
}
_isUserScrolling = NO;
JSMessageInputViewStyle inputViewStyle = [self.delegate inputViewStyle];
CGFloat inputViewHeight = (inputViewStyle == JSMessageInputViewStyleFlat) ? 45.0f : 40.0f;
JSMessageTableView *tableView = [[JSMessageTableView alloc] initWithFrame:self.view.frame style:UITableViewStylePlain];
tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
tableView.dataSource = self;
tableView.delegate = self;
[self.view addSubview:tableView];
_tableView = tableView;
[self setTableViewInsetsWithBottomValue:inputViewHeight];
[self setBackgroundColor:[UIColor js_backgroundColorClassic]];
CGRect inputFrame = CGRectMake(0.0f,
self.view.frame.size.height - inputViewHeight,
self.view.frame.size.width,
inputViewHeight);
BOOL allowsPan = YES;
if ([self.delegate respondsToSelector:@selector(allowsPanToDismissKeyboard)]) {
allowsPan = [self.delegate allowsPanToDismissKeyboard];
}
UIPanGestureRecognizer *pan = allowsPan ? _tableView.panGestureRecognizer : nil;
JSMessageInputView *inputView = [[JSMessageInputView alloc] initWithFrame:inputFrame
style:inputViewStyle
delegate:self
panGestureRecognizer:pan];
if (!allowsPan) {
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGestureRecognizer:)];
[_tableView addGestureRecognizer:tap];
}
if ([self.delegate respondsToSelector:@selector(sendButtonForInputView)]) {
UIButton *sendButton = [self.delegate sendButtonForInputView];
[inputView setSendButton:sendButton];
}
inputView.sendButton.enabled = NO;
[inputView.sendButton addTarget:self
action:@selector(sendPressed:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:inputView];
_messageInputView = inputView;
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
[self setup];
[[JSBubbleView appearance] setFont:[UIFont systemFontOfSize:16.0f]];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleWillShowKeyboardNotification:)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleWillHideKeyboardNotification:)
name:UIKeyboardWillHideNotification
object:nil];
[self.messageInputView.textView addObserver:self
forKeyPath:@"contentSize"
options:NSKeyValueObservingOptionNew
context:nil];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[self.messageInputView resignFirstResponder];
[self setEditing:NO animated:YES];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
[self.messageInputView.textView removeObserver:self forKeyPath:@"contentSize"];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
NSLog(@"*** %@: didReceiveMemoryWarning ***", [self class]);
}
- (void)dealloc
{
_delegate = nil;
_dataSource = nil;
_tableView.delegate = nil;
_tableView.dataSource = nil;
_tableView = nil;
_messageInputView = nil;
}
#pragma mark - View rotation
- (BOOL)shouldAutorotate
{
return NO;
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
[self.tableView reloadData];
[self.tableView setNeedsLayout];
}
#pragma mark - Actions
- (void)sendPressed:(UIButton *)sender
{
// add a space to accept any auto-correct suggestions
NSString *text = self.messageInputView.textView.text;
self.messageInputView.textView.text = [text stringByAppendingString:@" "];
[self.delegate didSendText:[self.messageInputView.textView.text js_stringByTrimingWhitespace]
fromSender:self.sender
onDate:[NSDate date]];
}
- (void)handleTapGestureRecognizer:(UITapGestureRecognizer *)tap
{
[self.messageInputView.textView resignFirstResponder];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 0;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
JSBubbleMessageType type = [self.delegate messageTypeForRowAtIndexPath:indexPath];
UIImageView *bubbleImageView = [self.delegate bubbleImageViewWithType:type
forRowAtIndexPath:indexPath];
id<JSMessageData> message = [self.dataSource messageForRowAtIndexPath:indexPath];
UIImageView *avatar = [self.dataSource avatarImageViewForRowAtIndexPath:indexPath sender:[message sender]];
BOOL displayTimestamp = YES;
if ([self.delegate respondsToSelector:@selector(shouldDisplayTimestampForRowAtIndexPath:)]) {
displayTimestamp = [self.delegate shouldDisplayTimestampForRowAtIndexPath:indexPath];
}
NSString *CellIdentifier = nil;
if ([self.delegate respondsToSelector:@selector(customCellIdentifierForRowAtIndexPath:)]) {
CellIdentifier = [self.delegate customCellIdentifierForRowAtIndexPath:indexPath];
}
if (!CellIdentifier) {
CellIdentifier = [NSString stringWithFormat:@"JSMessageCell_%d_%d_%d_%d", (int)type, displayTimestamp, avatar != nil, [message sender] != nil];
}
JSBubbleMessageCell *cell = (JSBubbleMessageCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell) {
cell = [[JSBubbleMessageCell alloc] initWithBubbleType:type
bubbleImageView:bubbleImageView
message:message
displaysTimestamp:displayTimestamp
hasAvatar:avatar != nil
reuseIdentifier:CellIdentifier];
}
[cell setMessage:message];
[cell setAvatarImageView:avatar];
[cell setBackgroundColor:tableView.backgroundColor];
if ([self.delegate respondsToSelector:@selector(configureCell:atIndexPath:)]) {
[self.delegate configureCell:cell atIndexPath:indexPath];
}
return cell;
}
#pragma mark - Table view delegate
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
id<JSMessageData> message = [self.dataSource messageForRowAtIndexPath:indexPath];
UIImageView *avatar = [self.dataSource avatarImageViewForRowAtIndexPath:indexPath sender:[message sender]];
BOOL displayTimestamp = YES;
if ([self.delegate respondsToSelector:@selector(shouldDisplayTimestampForRowAtIndexPath:)]) {
displayTimestamp = [self.delegate shouldDisplayTimestampForRowAtIndexPath:indexPath];
}
return [JSBubbleMessageCell neededHeightForBubbleMessageCellWithMessage:message
displaysAvatar:avatar != nil
displaysTimestamp:displayTimestamp];
}
#pragma mark - Messages view controller
- (void)finishSend
{
[self.messageInputView.textView setText:nil];
[self textViewDidChange:self.messageInputView.textView];
[self.tableView reloadData];
}
- (void)setBackgroundColor:(UIColor *)color
{
self.view.backgroundColor = color;
_tableView.backgroundColor = color;
_tableView.separatorColor = color;
}
- (void)scrollToBottomAnimated:(BOOL)animated
{
if (![self shouldAllowScroll])
return;
NSInteger rows = [self.tableView numberOfRowsInSection:0];
if (rows > 0) {
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:rows - 1 inSection:0]
atScrollPosition:UITableViewScrollPositionBottom
animated:animated];
}
}
- (void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath
atScrollPosition:(UITableViewScrollPosition)position
animated:(BOOL)animated
{
if (![self shouldAllowScroll])
return;
[self.tableView scrollToRowAtIndexPath:indexPath
atScrollPosition:position
animated:animated];
}
- (BOOL)shouldAllowScroll
{
if (self.isUserScrolling) {
if ([self.delegate respondsToSelector:@selector(shouldPreventScrollToBottomWhileUserScrolling)]
&& [self.delegate shouldPreventScrollToBottomWhileUserScrolling]) {
return NO;
}
}
return YES;
}
#pragma mark - Scroll view delegate
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
self.isUserScrolling = YES;
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
self.isUserScrolling = NO;
}
#pragma mark - Text view delegate
- (void)textViewDidBeginEditing:(UITextView *)textView
{
[textView becomeFirstResponder];
if (!self.previousTextViewContentHeight)
self.previousTextViewContentHeight = textView.contentSize.height;
[self scrollToBottomAnimated:YES];
}
- (void)textViewDidChange:(UITextView *)textView
{
self.messageInputView.sendButton.enabled = ([[textView.text js_stringByTrimingWhitespace] length] > 0);
}
- (void)textViewDidEndEditing:(UITextView *)textView
{
[textView resignFirstResponder];
}
#pragma mark - Layout message input view
- (void)layoutAndAnimateMessageInputTextView:(UITextView *)textView
{
CGFloat maxHeight = [JSMessageInputView maxHeight];
BOOL isShrinking = textView.contentSize.height < self.previousTextViewContentHeight;
CGFloat changeInHeight = textView.contentSize.height - self.previousTextViewContentHeight;
if (!isShrinking && (self.previousTextViewContentHeight == maxHeight || textView.text.length == 0)) {
changeInHeight = 0;
}
else {
changeInHeight = MIN(changeInHeight, maxHeight - self.previousTextViewContentHeight);
}
if (changeInHeight != 0.0f) {
[UIView animateWithDuration:0.25f
animations:^{
[self setTableViewInsetsWithBottomValue:self.tableView.contentInset.bottom + changeInHeight];
[self scrollToBottomAnimated:NO];
if (isShrinking) {
// if shrinking the view, animate text view frame BEFORE input view frame
[self.messageInputView adjustTextViewHeightBy:changeInHeight];
}
CGRect inputViewFrame = self.messageInputView.frame;
self.messageInputView.frame = CGRectMake(0.0f,
inputViewFrame.origin.y - changeInHeight,
inputViewFrame.size.width,
inputViewFrame.size.height + changeInHeight);
if (!isShrinking) {
// growing the view, animate the text view frame AFTER input view frame
[self.messageInputView adjustTextViewHeightBy:changeInHeight];
}
}
completion:^(BOOL finished) {
}];
self.previousTextViewContentHeight = MIN(textView.contentSize.height, maxHeight);
}
// Once we reached the max height, we have to consider the bottom offset for the text view.
// To make visible the last line, again we have to set the content offset.
if (self.previousTextViewContentHeight == maxHeight) {
double delayInSeconds = 0.01;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime,
dispatch_get_main_queue(),
^(void) {
CGPoint bottomOffset = CGPointMake(0.0f, textView.contentSize.height - textView.bounds.size.height);
[textView setContentOffset:bottomOffset animated:YES];
});
}
}
- (void)setTableViewInsetsWithBottomValue:(CGFloat)bottom
{
UIEdgeInsets insets = [self tableViewInsetsWithBottomValue:bottom];
self.tableView.contentInset = insets;
self.tableView.scrollIndicatorInsets = insets;
}
- (UIEdgeInsets)tableViewInsetsWithBottomValue:(CGFloat)bottom
{
UIEdgeInsets insets = UIEdgeInsetsZero;
if ([self respondsToSelector:@selector(topLayoutGuide)]) {
insets.top = self.topLayoutGuide.length;
}
insets.bottom = bottom;
return insets;
}
#pragma mark - Key-value observing
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
if (object == self.messageInputView.textView && [keyPath isEqualToString:@"contentSize"]) {
[self layoutAndAnimateMessageInputTextView:object];
}
}
#pragma mark - Keyboard notifications
- (void)handleWillShowKeyboardNotification:(NSNotification *)notification
{
[self keyboardWillShowHide:notification];
}
- (void)handleWillHideKeyboardNotification:(NSNotification *)notification
{
[self keyboardWillShowHide:notification];
}
- (void)keyboardWillShowHide:(NSNotification *)notification
{
CGRect keyboardRect = [[notification.userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
UIViewAnimationCurve curve = [[notification.userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] integerValue];
double duration = [[notification.userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
NSInteger animationCurveOption = (curve << 16);
[UIView animateWithDuration:duration
delay:0.0
options:animationCurveOption
animations:^{
CGFloat keyboardY = [self.view convertRect:keyboardRect fromView:nil].origin.y;
CGRect inputViewFrame = self.messageInputView.frame;
CGFloat inputViewFrameY = keyboardY - inputViewFrame.size.height;
// for ipad modal form presentations
CGFloat messageViewFrameBottom = self.view.frame.size.height - inputViewFrame.size.height;
if (inputViewFrameY > messageViewFrameBottom)
inputViewFrameY = messageViewFrameBottom;
self.messageInputView.frame = CGRectMake(inputViewFrame.origin.x,
inputViewFrameY,
inputViewFrame.size.width,
inputViewFrame.size.height);
[self setTableViewInsetsWithBottomValue:self.view.frame.size.height
- self.messageInputView.frame.origin.y];
}
completion:nil];
}
#pragma mark - Dismissive text view delegate
- (void)keyboardDidScrollToPoint:(CGPoint)point
{
[self animationForMessageInputViewAtPoint:point];
}
- (void)keyboardWillSnapBackToPoint:(CGPoint)point
{
[self animationForMessageInputViewAtPoint:point];
}
- (void)keyboardWillBeDismissed
{
CGRect inputViewFrame = self.messageInputView.frame;
inputViewFrame.origin.y = self.view.bounds.size.height - inputViewFrame.size.height;
self.messageInputView.frame = inputViewFrame;
}
- (void)animationForMessageInputViewAtPoint:(CGPoint)point
{
CGRect inputViewFrame = self.messageInputView.frame;
CGPoint keyboardOrigin = [self.view convertPoint:point fromView:nil];
inputViewFrame.origin.y = keyboardOrigin.y - inputViewFrame.size.height;
self.messageInputView.frame = inputViewFrame;
}
@end

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 421 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1015 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,6 @@
<Workspace
version = "1.0">
<FileRef
location = "self:JSMessagesDemo.xcodeproj">
location = "self:JSQMessages.xcodeproj">
</FileRef>
</Workspace>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0510"
LastUpgradeVersion = "0600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@ -14,7 +14,7 @@
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "5B185E1CBBDD4078BDD6E5B6"
BlueprintIdentifier = "4F6B28C25B7B1259BD8C5B8B"
BuildableName = "libPods.a"
BlueprintName = "Pods"
ReferencedContainer = "container:Pods/Pods.xcodeproj">
@ -28,24 +28,24 @@
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "88A3FB45182E079E00705327"
BuildableName = "JSMessagesDemo.app"
BlueprintName = "JSMessagesDemo"
ReferencedContainer = "container:JSMessagesDemo.xcodeproj">
BlueprintIdentifier = "88A25F0119D8DEC400924534"
BuildableName = "JSQMessages.app"
BlueprintName = "JSQMessages"
ReferencedContainer = "container:JSQMessages.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "88A3FB69182E079E00705327"
BuildableName = "JSMessagesDemoTests.xctest"
BlueprintName = "JSMessagesDemoTests"
ReferencedContainer = "container:JSMessagesDemo.xcodeproj">
BlueprintIdentifier = "88A25F1A19D8DEC400924534"
BuildableName = "JSQMessagesTests.xctest"
BlueprintName = "JSQMessagesTests"
ReferencedContainer = "container:JSQMessages.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
@ -60,20 +60,20 @@
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "88A3FB69182E079E00705327"
BuildableName = "JSMessagesDemoTests.xctest"
BlueprintName = "JSMessagesDemoTests"
ReferencedContainer = "container:JSMessagesDemo.xcodeproj">
BlueprintIdentifier = "88A25F1A19D8DEC400924534"
BuildableName = "JSQMessagesTests.xctest"
BlueprintName = "JSQMessagesTests"
ReferencedContainer = "container:JSQMessages.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "88A3FB45182E079E00705327"
BuildableName = "JSMessagesDemo.app"
BlueprintName = "JSMessagesDemo"
ReferencedContainer = "container:JSMessagesDemo.xcodeproj">
BlueprintIdentifier = "88A25F0119D8DEC400924534"
BuildableName = "JSQMessages.app"
BlueprintName = "JSQMessages"
ReferencedContainer = "container:JSQMessages.xcodeproj">
</BuildableReference>
</MacroExpansion>
</TestAction>
@ -89,10 +89,10 @@
<BuildableProductRunnable>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "88A3FB45182E079E00705327"
BuildableName = "JSMessagesDemo.app"
BlueprintName = "JSMessagesDemo"
ReferencedContainer = "container:JSMessagesDemo.xcodeproj">
BlueprintIdentifier = "88A25F0119D8DEC400924534"
BuildableName = "JSQMessages.app"
BlueprintName = "JSQMessages"
ReferencedContainer = "container:JSQMessages.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
@ -107,10 +107,10 @@
<BuildableProductRunnable>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "88A3FB45182E079E00705327"
BuildableName = "JSMessagesDemo.app"
BlueprintName = "JSMessagesDemo"
ReferencedContainer = "container:JSMessagesDemo.xcodeproj">
BlueprintIdentifier = "88A25F0119D8DEC400924534"
BuildableName = "JSQMessages.app"
BlueprintName = "JSQMessages"
ReferencedContainer = "container:JSQMessages.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:JSQMessages.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>

View File

@ -0,0 +1,26 @@
//
// Created by Jesse Squires
// http://www.jessesquires.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSQMessagesViewController
//
//
// GitHub
// https://github.com/jessesquires/JSQMessagesViewController
//
//
// License
// Copyright (c) 2014 Jesse Squires
// Released under an MIT license: http://opensource.org/licenses/MIT
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@end

View File

@ -0,0 +1,35 @@
//
// Created by Jesse Squires
// http://www.jessesquires.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSQMessagesViewController
//
//
// GitHub
// https://github.com/jessesquires/JSQMessagesViewController
//
//
// License
// Copyright (c) 2014 Jesse Squires
// Released under an MIT license: http://opensource.org/licenses/MIT
//
#import "AppDelegate.h"
#import "NSUserDefaults+DemoSettings.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Load our default settings
[NSUserDefaults saveIncomingAvatarSetting:YES];
[NSUserDefaults saveOutgoingAvatarSetting:YES];
return YES;
}
@end

View File

@ -0,0 +1,360 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6245" systemVersion="13F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" initialViewController="JRd-Be-psV">
<dependencies>
<deployment defaultVersion="1792" identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6238"/>
</dependencies>
<scenes>
<!--Root View Controller-->
<scene sceneID="IjL-71-Ll6">
<objects>
<tableViewController id="rXs-qR-ns2" customClass="TableViewController" sceneMemberID="viewController">
<tableView key="view" opaque="NO" clipsSubviews="YES" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="44" sectionHeaderHeight="10" sectionFooterHeight="10" id="d0b-Sx-5kJ">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
<prototypes>
<tableViewCell contentMode="scaleToFill" selectionStyle="blue" accessoryType="disclosureIndicator" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="CellIdentifier" textLabel="2qz-Z2-GmT" style="IBUITableViewCellStyleDefault" id="k8B-cw-dMU">
<rect key="frame" x="0.0" y="119" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="k8B-cw-dMU" id="kX4-QF-oKx">
<rect key="frame" x="0.0" y="0.0" width="287" height="43"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="left" text="Title" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="2qz-Z2-GmT">
<rect key="frame" x="15" y="0.0" width="270" height="43"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" type="system" pointSize="18"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
</tableViewCell>
</prototypes>
</tableView>
<navigationItem key="navigationItem" title="Root View Controller" id="irr-Pn-9x5"/>
<connections>
<segue destination="JdJ-0X-ZL8" kind="push" identifier="seguePushDemoVC" id="Hvf-k8-GSP"/>
<segue destination="s4z-xn-r6C" kind="modal" identifier="segueModalDemoVC" id="4UP-0e-ylo"/>
<segue destination="y7K-ZU-Xs9" kind="push" identifier="SegueToSettings" id="u7b-st-FND"/>
</connections>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="IcC-ug-dCB" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-2982" y="274"/>
</scene>
<!--Settings-->
<scene sceneID="6rZ-ao-qyI">
<objects>
<tableViewController id="y7K-ZU-Xs9" customClass="DemoSettingsViewController" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="grouped" separatorStyle="default" allowsSelection="NO" rowHeight="44" sectionHeaderHeight="10" sectionFooterHeight="10" id="GY5-ob-knb">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
<sections>
<tableViewSection headerTitle="Messages" id="ygb-Dp-o4r">
<cells>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="Eii-ro-1yZ">
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Eii-ro-1yZ" id="qU9-o3-MWC">
<autoresizingMask key="autoresizingMask"/>
<subviews>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="K7N-os-fuc">
<rect key="frame" x="263" y="6" width="51" height="31"/>
<constraints>
<constraint firstAttribute="width" constant="49" id="A6s-Sr-u8U"/>
<constraint firstAttribute="height" constant="31" id="z9i-uJ-MBq"/>
</constraints>
<connections>
<action selector="didTapSwitch:" destination="y7K-ZU-Xs9" eventType="valueChanged" id="Ebi-bs-7Bc"/>
</connections>
</switch>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Load extra messages" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bSS-CD-nfD">
<rect key="frame" x="8" y="10" width="247" height="24"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="bSS-CD-nfD" firstAttribute="top" secondItem="qU9-o3-MWC" secondAttribute="top" constant="10" id="4Iw-W0-qZs"/>
<constraint firstAttribute="trailing" secondItem="K7N-os-fuc" secondAttribute="trailing" constant="8" id="Fex-nr-C1R"/>
<constraint firstAttribute="bottom" secondItem="K7N-os-fuc" secondAttribute="bottom" constant="6" id="IBG-gn-Fpk"/>
<constraint firstItem="bSS-CD-nfD" firstAttribute="leading" secondItem="qU9-o3-MWC" secondAttribute="leading" constant="8" id="N5B-E5-Pzk"/>
<constraint firstItem="K7N-os-fuc" firstAttribute="leading" secondItem="bSS-CD-nfD" secondAttribute="trailing" constant="8" id="O2Y-G9-d6n"/>
<constraint firstItem="K7N-os-fuc" firstAttribute="top" secondItem="qU9-o3-MWC" secondAttribute="top" constant="6" id="UbM-aE-caS"/>
<constraint firstAttribute="bottom" secondItem="bSS-CD-nfD" secondAttribute="bottom" constant="9" id="poL-wE-Eir"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="bZI-G1-eYA">
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="bZI-G1-eYA" id="1Ho-Zz-KN0">
<autoresizingMask key="autoresizingMask"/>
<subviews>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="hMq-Ee-EJK">
<rect key="frame" x="263" y="6" width="51" height="31"/>
<constraints>
<constraint firstAttribute="height" constant="31" id="gM4-dK-cl7"/>
<constraint firstAttribute="width" constant="49" id="hvu-Yy-NTG"/>
</constraints>
<connections>
<action selector="didTapSwitch:" destination="y7K-ZU-Xs9" eventType="valueChanged" id="JCb-EJ-M9U"/>
</connections>
</switch>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Load really long message" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="YV3-GH-Yul">
<rect key="frame" x="8" y="10" width="247" height="24"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="hMq-Ee-EJK" firstAttribute="leading" secondItem="YV3-GH-Yul" secondAttribute="trailing" constant="8" id="KFO-nO-4r4"/>
<constraint firstItem="YV3-GH-Yul" firstAttribute="top" secondItem="1Ho-Zz-KN0" secondAttribute="top" constant="10" id="Lgl-fY-a8c"/>
<constraint firstAttribute="bottom" secondItem="YV3-GH-Yul" secondAttribute="bottom" constant="9" id="NvL-b8-QDl"/>
<constraint firstAttribute="bottom" secondItem="hMq-Ee-EJK" secondAttribute="bottom" constant="6" id="aef-Rh-49d"/>
<constraint firstItem="hMq-Ee-EJK" firstAttribute="top" secondItem="1Ho-Zz-KN0" secondAttribute="top" constant="6" id="csX-us-yCd"/>
<constraint firstAttribute="trailing" secondItem="hMq-Ee-EJK" secondAttribute="trailing" constant="8" id="xDE-tf-5mB"/>
<constraint firstItem="YV3-GH-Yul" firstAttribute="leading" secondItem="1Ho-Zz-KN0" secondAttribute="leading" constant="8" id="xyR-oc-iUp"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="2Th-gL-oAN">
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="2Th-gL-oAN" id="OFq-Mz-mbl">
<autoresizingMask key="autoresizingMask"/>
<subviews>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="baL-Tb-bTJ">
<rect key="frame" x="263" y="6" width="51" height="31"/>
<constraints>
<constraint firstAttribute="width" constant="49" id="SaV-RG-DVL"/>
<constraint firstAttribute="height" constant="31" id="dXb-pb-etA"/>
</constraints>
<connections>
<action selector="didTapSwitch:" destination="y7K-ZU-Xs9" eventType="valueChanged" id="w1H-5W-oip"/>
</connections>
</switch>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Empty view, no messages" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="DoU-SU-Nek">
<rect key="frame" x="8" y="10" width="247" height="24"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="DoU-SU-Nek" firstAttribute="leading" secondItem="OFq-Mz-mbl" secondAttribute="leading" constant="8" id="A9k-hI-0BH"/>
<constraint firstAttribute="bottom" secondItem="baL-Tb-bTJ" secondAttribute="bottom" constant="6" id="Ff7-1T-EbZ"/>
<constraint firstItem="DoU-SU-Nek" firstAttribute="top" secondItem="OFq-Mz-mbl" secondAttribute="top" constant="10" id="Uji-LE-8IA"/>
<constraint firstAttribute="trailing" secondItem="baL-Tb-bTJ" secondAttribute="trailing" constant="8" id="dUp-7b-g3p"/>
<constraint firstItem="baL-Tb-bTJ" firstAttribute="leading" secondItem="DoU-SU-Nek" secondAttribute="trailing" constant="8" id="fxZ-zy-ksL"/>
<constraint firstAttribute="bottom" secondItem="DoU-SU-Nek" secondAttribute="bottom" constant="9" id="h0B-EX-3MW"/>
<constraint firstItem="baL-Tb-bTJ" firstAttribute="top" secondItem="OFq-Mz-mbl" secondAttribute="top" constant="6" id="v5e-xk-srk"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
</cells>
</tableViewSection>
<tableViewSection headerTitle="Avatars" id="ns0-OO-PGu">
<cells>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="hYS-4f-iL6">
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="hYS-4f-iL6" id="dk1-tc-gux">
<autoresizingMask key="autoresizingMask"/>
<subviews>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="fLZ-NC-aPO">
<rect key="frame" x="263" y="6" width="51" height="31"/>
<constraints>
<constraint firstAttribute="width" constant="49" id="1lf-6y-hgz"/>
<constraint firstAttribute="height" constant="31" id="rdQ-ol-d20"/>
</constraints>
<connections>
<action selector="didTapSwitch:" destination="y7K-ZU-Xs9" eventType="valueChanged" id="WnT-b7-og6"/>
</connections>
</switch>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Incoming avatars" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="RUq-Pa-3nx">
<rect key="frame" x="8" y="10" width="247" height="24"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstAttribute="bottom" secondItem="RUq-Pa-3nx" secondAttribute="bottom" constant="9" id="7Yt-uN-sOS"/>
<constraint firstItem="RUq-Pa-3nx" firstAttribute="top" secondItem="dk1-tc-gux" secondAttribute="top" constant="10" id="Aih-Lc-tq7"/>
<constraint firstItem="fLZ-NC-aPO" firstAttribute="leading" secondItem="RUq-Pa-3nx" secondAttribute="trailing" constant="8" id="Cfo-7m-Vyd"/>
<constraint firstAttribute="trailing" secondItem="fLZ-NC-aPO" secondAttribute="trailing" constant="8" id="Mgz-VG-tDn"/>
<constraint firstItem="fLZ-NC-aPO" firstAttribute="top" secondItem="dk1-tc-gux" secondAttribute="top" constant="6" id="S6i-he-vB9"/>
<constraint firstItem="RUq-Pa-3nx" firstAttribute="leading" secondItem="dk1-tc-gux" secondAttribute="leading" constant="8" id="mhd-oX-p53"/>
<constraint firstAttribute="bottom" secondItem="fLZ-NC-aPO" secondAttribute="bottom" constant="6" id="yyq-Sg-nJi"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="VF7-uo-6Pc">
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="VF7-uo-6Pc" id="zad-JQ-TRI">
<autoresizingMask key="autoresizingMask"/>
<subviews>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="LuM-mk-Zj6">
<rect key="frame" x="263" y="6" width="51" height="31"/>
<constraints>
<constraint firstAttribute="height" constant="31" id="QXv-ty-g0B"/>
<constraint firstAttribute="width" constant="49" id="rZ3-cO-f8t"/>
</constraints>
<connections>
<action selector="didTapSwitch:" destination="y7K-ZU-Xs9" eventType="valueChanged" id="rNB-Bx-Uh3"/>
</connections>
</switch>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Outgoing avatars" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="9Rr-S8-Uae">
<rect key="frame" x="8" y="10" width="247" height="24"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstAttribute="bottom" secondItem="LuM-mk-Zj6" secondAttribute="bottom" constant="6" id="1G7-8S-rYC"/>
<constraint firstAttribute="bottom" secondItem="9Rr-S8-Uae" secondAttribute="bottom" constant="9" id="5xp-ez-tac"/>
<constraint firstItem="LuM-mk-Zj6" firstAttribute="leading" secondItem="9Rr-S8-Uae" secondAttribute="trailing" constant="8" id="C5c-2A-mli"/>
<constraint firstAttribute="trailing" secondItem="LuM-mk-Zj6" secondAttribute="trailing" constant="8" id="Lep-LY-D1h"/>
<constraint firstItem="LuM-mk-Zj6" firstAttribute="top" secondItem="zad-JQ-TRI" secondAttribute="top" constant="6" id="Y6a-JS-aLj"/>
<constraint firstItem="9Rr-S8-Uae" firstAttribute="leading" secondItem="zad-JQ-TRI" secondAttribute="leading" constant="8" id="Yao-VR-Is6"/>
<constraint firstItem="9Rr-S8-Uae" firstAttribute="top" secondItem="zad-JQ-TRI" secondAttribute="top" constant="10" id="Yiv-t6-Xj5"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
</cells>
</tableViewSection>
<tableViewSection headerTitle="Dynamic Behaviors" footerTitle="NOTE: This feature is experimental" id="o5m-OT-1Iw">
<cells>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="q07-lZ-YdI">
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="q07-lZ-YdI" id="btE-Mk-fSE">
<autoresizingMask key="autoresizingMask"/>
<subviews>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="uXC-2j-cgi">
<rect key="frame" x="263" y="6" width="51" height="31"/>
<constraints>
<constraint firstAttribute="height" constant="31" id="5bI-xQ-njj"/>
<constraint firstAttribute="width" constant="49" id="PG2-X2-FdD"/>
</constraints>
<connections>
<action selector="didTapSwitch:" destination="y7K-ZU-Xs9" eventType="valueChanged" id="oHB-iq-JSz"/>
</connections>
</switch>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Springy bubbles" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="3d2-fZ-dx9">
<rect key="frame" x="8" y="10" width="247" height="24"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="3d2-fZ-dx9" firstAttribute="leading" secondItem="btE-Mk-fSE" secondAttribute="leading" constant="8" id="6Cs-6l-Xzp"/>
<constraint firstItem="uXC-2j-cgi" firstAttribute="leading" secondItem="3d2-fZ-dx9" secondAttribute="trailing" constant="8" id="7Mq-HR-4nY"/>
<constraint firstItem="uXC-2j-cgi" firstAttribute="top" secondItem="btE-Mk-fSE" secondAttribute="top" constant="6" id="Ajq-5g-XKo"/>
<constraint firstAttribute="bottom" secondItem="3d2-fZ-dx9" secondAttribute="bottom" constant="9" id="EzH-aB-ePQ"/>
<constraint firstAttribute="bottom" secondItem="uXC-2j-cgi" secondAttribute="bottom" constant="6" id="RCb-Mi-FHX"/>
<constraint firstItem="3d2-fZ-dx9" firstAttribute="top" secondItem="btE-Mk-fSE" secondAttribute="top" constant="10" id="btF-q4-HJg"/>
<constraint firstAttribute="trailing" secondItem="uXC-2j-cgi" secondAttribute="trailing" constant="8" id="zzu-aP-ZiL"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
</cells>
</tableViewSection>
</sections>
<connections>
<outlet property="dataSource" destination="y7K-ZU-Xs9" id="4gP-Ip-VMs"/>
<outlet property="delegate" destination="y7K-ZU-Xs9" id="RdT-hW-mFp"/>
</connections>
</tableView>
<navigationItem key="navigationItem" title="Settings" id="hrw-Dp-Tor"/>
<connections>
<outlet property="emptySwitch" destination="baL-Tb-bTJ" id="qk7-dH-53U"/>
<outlet property="extraMessagesSwitch" destination="K7N-os-fuc" id="qlF-0W-3JO"/>
<outlet property="incomingAvatarsSwitch" destination="fLZ-NC-aPO" id="0ck-t9-Hwt"/>
<outlet property="longMessageSwitch" destination="hMq-Ee-EJK" id="Vs2-fX-Hys"/>
<outlet property="outgoingAvatarsSwitch" destination="LuM-mk-Zj6" id="EAh-35-xjI"/>
<outlet property="springySwitch" destination="uXC-2j-cgi" id="Jzx-1y-VZP"/>
</connections>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="5u5-k6-W2j" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-2982" y="1039"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="eGf-GM-ORQ">
<objects>
<navigationController definesPresentationContext="YES" id="s4z-xn-r6C" sceneMemberID="viewController">
<navigationBar key="navigationBar" contentMode="scaleToFill" id="G3q-Gy-0Lf">
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<connections>
<segue destination="2Qx-iu-03V" kind="relationship" relationship="rootViewController" id="cuu-7c-yOr"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="8DO-Rt-kgb" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-2409" y="619"/>
</scene>
<!--Demo Messages View Controller-->
<scene sceneID="gOX-cv-CuN">
<objects>
<viewController storyboardIdentifier="DemoVC" id="JdJ-0X-ZL8" customClass="DemoMessagesViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="TE8-r2-pE8"/>
<viewControllerLayoutGuide type="bottom" id="H2n-HB-Ssl"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Vop-TB-ImV">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
<navigationItem key="navigationItem" id="URv-4c-d6h"/>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="B2s-hQ-gD4" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-2409" y="-71"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="A8N-cL-8VP">
<objects>
<navigationController definesPresentationContext="YES" id="JRd-Be-psV" sceneMemberID="viewController">
<navigationBar key="navigationBar" contentMode="scaleToFill" id="YZb-OI-WKd">
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<connections>
<segue destination="rXs-qR-ns2" kind="relationship" relationship="rootViewController" id="8xe-QC-QaY"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="OC7-Ng-9xQ" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-3413" y="274"/>
</scene>
<!--Demo Messages View Controller-->
<scene sceneID="Rr5-W8-Xyh">
<objects>
<viewController id="2Qx-iu-03V" customClass="DemoMessagesViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="hTc-sT-can"/>
<viewControllerLayoutGuide type="bottom" id="BPF-eU-3uQ"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="SsE-pA-zOd">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
<navigationItem key="navigationItem" id="gBw-Dh-o4F"/>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="jTG-EA-PIP" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-1989" y="619"/>
</scene>
</scenes>
<simulatedMetricsContainer key="defaultSimulatedMetrics">
<simulatedStatusBarMetrics key="statusBar"/>
<simulatedOrientationMetrics key="orientation"/>
<simulatedScreenMetrics key="destination" type="retina4"/>
</simulatedMetricsContainer>
</document>

View File

@ -0,0 +1,48 @@
//
// Created by Jesse Squires
// http://www.jessesquires.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSQMessagesViewController
//
//
// GitHub
// https://github.com/jessesquires/JSQMessagesViewController
//
//
// License
// Copyright (c) 2014 Jesse Squires
// Released under an MIT license: http://opensource.org/licenses/MIT
//
// Import all the things
#import "JSQMessages.h"
#import "DemoModelData.h"
#import "NSUserDefaults+DemoSettings.h"
@class DemoMessagesViewController;
@protocol JSQDemoViewControllerDelegate <NSObject>
- (void)didDismissJSQDemoViewController:(DemoMessagesViewController *)vc;
@end
@interface DemoMessagesViewController : JSQMessagesViewController <UIActionSheetDelegate>
@property (weak, nonatomic) id<JSQDemoViewControllerDelegate> delegateModal;
@property (strong, nonatomic) DemoModelData *demoData;
- (void)receiveMessagePressed:(UIBarButtonItem *)sender;
- (void)closePressed:(UIBarButtonItem *)sender;
@end

View File

@ -0,0 +1,584 @@
//
// Created by Jesse Squires
// http://www.jessesquires.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSQMessagesViewController
//
//
// GitHub
// https://github.com/jessesquires/JSQMessagesViewController
//
//
// License
// Copyright (c) 2014 Jesse Squires
// Released under an MIT license: http://opensource.org/licenses/MIT
//
#import "DemoMessagesViewController.h"
#import "JSQCallCollectionViewCell.h"
#import "JSQCall.h"
#import "JSQDisplayedMessageCollectionViewCell.h"
#import "JSQErrorMessage.h"
#import "JSQInfoMessage.h"
@implementation DemoMessagesViewController
#pragma mark - View lifecycle
/**
* Override point for customization.
*
* Customize your view.
* Look at the properties on `JSQMessagesViewController` and `JSQMessagesCollectionView` to see what is possible.
*
* Customize your layout.
* Look at the properties on `JSQMessagesCollectionViewFlowLayout` to see what is possible.
*/
- (void)viewDidLoad
{
[super viewDidLoad];
self.title = @"JSQMessages";
/**
* You MUST set your senderId and display name
*/
self.senderId = kJSQDemoAvatarIdSquires;
self.senderDisplayName = kJSQDemoAvatarDisplayNameSquires;
/**
* Load up our fake data for the demo
*/
self.demoData = [[DemoModelData alloc] init];
/**
* You can set custom avatar sizes
*/
if (![NSUserDefaults incomingAvatarSetting]) {
self.collectionView.collectionViewLayout.incomingAvatarViewSize = CGSizeZero;
}
if (![NSUserDefaults outgoingAvatarSetting]) {
self.collectionView.collectionViewLayout.outgoingAvatarViewSize = CGSizeZero;
}
self.showLoadEarlierMessagesHeader = YES;
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage jsq_defaultTypingIndicatorImage]
style:UIBarButtonItemStyleBordered
target:self
action:@selector(receiveMessagePressed:)];
/**
* Customize your toolbar buttons
*
* self.inputToolbar.contentView.leftBarButtonItem = custom button or nil to remove
* self.inputToolbar.contentView.rightBarButtonItem = custom button or nil to remove
*/
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (self.delegateModal) {
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemStop
target:self
action:@selector(closePressed:)];
}
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
/**
* Enable/disable springy bubbles, default is NO.
* You must set this from `viewDidAppear:`
* Note: this feature is mostly stable, but still experimental
*/
self.collectionView.collectionViewLayout.springinessEnabled = [NSUserDefaults springinessSetting];
}
#pragma mark - Testing
- (void)pushMainViewController
{
UIStoryboard *sb = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
UINavigationController *nc = [sb instantiateInitialViewController];
[self.navigationController pushViewController:nc.topViewController animated:YES];
}
#pragma mark - Actions
- (void)receiveMessagePressed:(UIBarButtonItem *)sender
{
/**
* DEMO ONLY
*
* The following is simply to simulate received messages for the demo.
* Do not actually do this.
*/
/**
* Show the typing indicator to be shown
*/
self.showTypingIndicator = !self.showTypingIndicator;
/**
* Scroll to actually view the indicator
*/
[self scrollToBottomAnimated:YES];
/**
* Copy last sent message, this will be the new "received" message
*/
JSQMessage *copyMessage = [[self.demoData.messages lastObject] copy];
if (!copyMessage) {
copyMessage = [JSQMessage messageWithSenderId:kJSQDemoAvatarIdJobs
displayName:kJSQDemoAvatarDisplayNameJobs
text:@"First received!"];
}
/**
* Allow typing indicator to show
*/
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSMutableArray *userIds = [[self.demoData.users allKeys] mutableCopy];
[userIds removeObject:self.senderId];
NSString *randomUserId = userIds[arc4random_uniform((int)[userIds count])];
JSQMessage *newMessage = nil;
id<JSQMessageMediaData> newMediaData = nil;
id newMediaAttachmentCopy = nil;
if ([copyMessage isKindOfClass:[JSQMessage class]]) {
/**
* Last message was a media message
*/
id<JSQMessageMediaData> copyMediaData = copyMessage.media;
if ([copyMediaData isKindOfClass:[JSQPhotoMediaItem class]]) {
JSQPhotoMediaItem *photoItemCopy = [((JSQPhotoMediaItem *)copyMediaData) copy];
photoItemCopy.appliesMediaViewMaskAsOutgoing = NO;
newMediaAttachmentCopy = [UIImage imageWithCGImage:photoItemCopy.image.CGImage];
/**
* Set image to nil to simulate "downloading" the image
* and show the placeholder view
*/
photoItemCopy.image = nil;
newMediaData = photoItemCopy;
}
else if ([copyMediaData isKindOfClass:[JSQLocationMediaItem class]]) {
JSQLocationMediaItem *locationItemCopy = [((JSQLocationMediaItem *)copyMediaData) copy];
locationItemCopy.appliesMediaViewMaskAsOutgoing = NO;
newMediaAttachmentCopy = [locationItemCopy.location copy];
/**
* Set location to nil to simulate "downloading" the location data
*/
locationItemCopy.location = nil;
newMediaData = locationItemCopy;
}
else if ([copyMediaData isKindOfClass:[JSQVideoMediaItem class]]) {
JSQVideoMediaItem *videoItemCopy = [((JSQVideoMediaItem *)copyMediaData) copy];
videoItemCopy.appliesMediaViewMaskAsOutgoing = NO;
newMediaAttachmentCopy = [videoItemCopy.fileURL copy];
/**
* Reset video item to simulate "downloading" the video
*/
videoItemCopy.fileURL = nil;
videoItemCopy.isReadyToPlay = NO;
newMediaData = videoItemCopy;
}
else {
NSLog(@"%s error: unrecognized media item", __PRETTY_FUNCTION__);
}
newMessage = [JSQMessage messageWithSenderId:randomUserId
displayName:self.demoData.users[randomUserId]
media:newMediaData];
}
else {
/**
* Last message was a text message
*/
newMessage = [JSQMessage messageWithSenderId:randomUserId
displayName:self.demoData.users[randomUserId]
text:copyMessage.text];
}
/**
* Upon receiving a message, you should:
*
* 1. Play sound (optional)
* 2. Add new id<JSQMessageData> object to your data source
* 3. Call `finishReceivingMessage`
*/
[JSQSystemSoundPlayer jsq_playMessageReceivedSound];
[self.demoData.messages addObject:newMessage];
[self finishReceivingMessageAnimated:YES];
if (newMessage.isMediaMessage) {
/**
* Simulate "downloading" media
*/
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
/**
* Media is "finished downloading", re-display visible cells
*
* If media cell is not visible, the next time it is dequeued the view controller will display its new attachment data
*
* Reload the specific item, or simply call `reloadData`
*/
if ([newMediaData isKindOfClass:[JSQPhotoMediaItem class]]) {
((JSQPhotoMediaItem *)newMediaData).image = newMediaAttachmentCopy;
[self.collectionView reloadData];
}
else if ([newMediaData isKindOfClass:[JSQLocationMediaItem class]]) {
[((JSQLocationMediaItem *)newMediaData)setLocation:newMediaAttachmentCopy withCompletionHandler:^{
[self.collectionView reloadData];
}];
}
else if ([newMediaData isKindOfClass:[JSQVideoMediaItem class]]) {
((JSQVideoMediaItem *)newMediaData).fileURL = newMediaAttachmentCopy;
((JSQVideoMediaItem *)newMediaData).isReadyToPlay = YES;
[self.collectionView reloadData];
}
else {
NSLog(@"%s error: unrecognized media item", __PRETTY_FUNCTION__);
}
});
}
});
}
- (void)closePressed:(UIBarButtonItem *)sender
{
[self.delegateModal didDismissJSQDemoViewController:self];
}
#pragma mark - JSQMessagesViewController method overrides
- (void)didPressSendButton:(UIButton *)button
withMessageText:(NSString *)text
senderId:(NSString *)senderId
senderDisplayName:(NSString *)senderDisplayName
date:(NSDate *)date
{
/**
* Sending a message. Your implementation of this method should do *at least* the following:
*
* 1. Play sound (optional)
* 2. Add new id<JSQMessageData> object to your data source
* 3. Call `finishSendingMessage`
*/
[JSQSystemSoundPlayer jsq_playMessageSentSound];
JSQMessage *message = [[JSQMessage alloc] initWithSenderId:senderId
senderDisplayName:senderDisplayName
date:date
text:text];
[self.demoData.messages addObject:message];
[self finishSendingMessageAnimated:YES];
}
- (void)didPressAccessoryButton:(UIButton *)sender
{
UIActionSheet *sheet = [[UIActionSheet alloc] initWithTitle:@"Media messages"
delegate:self
cancelButtonTitle:@"Cancel"
destructiveButtonTitle:nil
otherButtonTitles:@"Send photo", @"Send location", @"Send video", nil];
[sheet showFromToolbar:self.inputToolbar];
}
- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex
{
if (buttonIndex == actionSheet.cancelButtonIndex) {
return;
}
switch (buttonIndex) {
case 0:
[self.demoData addPhotoMediaMessage];
break;
case 1:
{
__weak UICollectionView *weakView = self.collectionView;
[self.demoData addLocationMediaMessageCompletion:^{
[weakView reloadData];
}];
}
break;
case 2:
[self.demoData addVideoMediaMessage];
break;
}
[JSQSystemSoundPlayer jsq_playMessageSentSound];
[self finishSendingMessageAnimated:YES];
}
#pragma mark - JSQMessages CollectionView DataSource
- (id<JSQMessageData>)collectionView:(JSQMessagesCollectionView *)collectionView messageDataForItemAtIndexPath:(NSIndexPath *)indexPath
{
return [self.demoData.messages objectAtIndex:indexPath.item];
}
- (id<JSQMessageBubbleImageDataSource>)collectionView:(JSQMessagesCollectionView *)collectionView messageBubbleImageDataForItemAtIndexPath:(NSIndexPath *)indexPath
{
/**
* You may return nil here if you do not want bubbles.
* In this case, you should set the background color of your collection view cell's textView.
*
* Otherwise, return your previously created bubble image data objects.
*/
JSQMessage *message = [self.demoData.messages objectAtIndex:indexPath.item];
if ([message.senderId isEqualToString:self.senderId]) {
return self.demoData.outgoingBubbleImageData;
}
return self.demoData.incomingBubbleImageData;
}
- (id<JSQMessageAvatarImageDataSource>)collectionView:(JSQMessagesCollectionView *)collectionView avatarImageDataForItemAtIndexPath:(NSIndexPath *)indexPath
{
/**
* Return `nil` here if you do not want avatars.
* If you do return `nil`, be sure to do the following in `viewDidLoad`:
*
* self.collectionView.collectionViewLayout.incomingAvatarViewSize = CGSizeZero;
* self.collectionView.collectionViewLayout.outgoingAvatarViewSize = CGSizeZero;
*
* It is possible to have only outgoing avatars or only incoming avatars, too.
*/
/**
* Return your previously created avatar image data objects.
*
* Note: these the avatars will be sized according to these values:
*
* self.collectionView.collectionViewLayout.incomingAvatarViewSize
* self.collectionView.collectionViewLayout.outgoingAvatarViewSize
*
* Override the defaults in `viewDidLoad`
*/
JSQMessage *message = [self.demoData.messages objectAtIndex:indexPath.item];
if (![message isKindOfClass:[JSQCall class]] || ![message isKindOfClass:[JSQErrorMessage class]] || ![message isKindOfClass:[JSQInfoMessage class]]) return nil;
if ([message.senderId isEqualToString:self.senderId]) {
if (![NSUserDefaults outgoingAvatarSetting]) {
return nil;
}
}
else {
if (![NSUserDefaults incomingAvatarSetting]) {
return nil;
}
}
return [self.demoData.avatars objectForKey:message.senderId];
}
- (NSAttributedString *)collectionView:(JSQMessagesCollectionView *)collectionView attributedTextForCellTopLabelAtIndexPath:(NSIndexPath *)indexPath
{
/**
* This logic should be consistent with what you return from `heightForCellTopLabelAtIndexPath:`
* The other label text delegate methods should follow a similar pattern.
*
* Show a timestamp for every 3rd message
*/
if (indexPath.item % 3 == 0) {
JSQMessage *message = [self.demoData.messages objectAtIndex:indexPath.item];
return [[JSQMessagesTimestampFormatter sharedFormatter] attributedTimestampForDate:message.date];
}
return nil;
}
- (NSAttributedString *)collectionView:(JSQMessagesCollectionView *)collectionView attributedTextForMessageBubbleTopLabelAtIndexPath:(NSIndexPath *)indexPath
{
JSQMessage *message = [self.demoData.messages objectAtIndex:indexPath.item];
/**
* iOS7-style sender name labels
*/
if ([message.senderId isEqualToString:self.senderId]) {
return nil;
}
if (indexPath.item - 1 > 0) {
JSQMessage *previousMessage = [self.demoData.messages objectAtIndex:indexPath.item - 1];
if ([[previousMessage senderId] isEqualToString:message.senderId]) {
return nil;
}
}
/**
* Don't specify attributes to use the defaults.
*/
return [[NSAttributedString alloc] initWithString:message.senderDisplayName];
}
- (NSAttributedString *)collectionView:(JSQMessagesCollectionView *)collectionView attributedTextForCellBottomLabelAtIndexPath:(NSIndexPath *)indexPath
{
return nil;
}
#pragma mark - UICollectionView DataSource
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return [self.demoData.messages count];
}
- (UICollectionViewCell *)collectionView:(JSQMessagesCollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
/**
* Override point for customizing cells
*/
JSQMessage *msg = [self.demoData.messages objectAtIndex:indexPath.item];
if ([msg isKindOfClass:[JSQMessage class]])
{
JSQMessagesCollectionViewCell *cell = (JSQMessagesCollectionViewCell *)[super collectionView:collectionView cellForItemAtIndexPath:indexPath];
if (!msg.isMediaMessage) {
if ([msg.senderId isEqualToString:self.senderId]) {
cell.textView.textColor = [UIColor blackColor];
}
else {
cell.textView.textColor = [UIColor whiteColor];
}
cell.textView.linkTextAttributes = @{ NSForegroundColorAttributeName : cell.textView.textColor,
NSUnderlineStyleAttributeName : @(NSUnderlineStyleSingle | NSUnderlinePatternSolid) };
}
return cell;
} else if ([msg isKindOfClass:[JSQCall class]]) {
JSQCallCollectionViewCell *cell = (JSQCallCollectionViewCell *)[super collectionView:collectionView cellForItemAtIndexPath:indexPath];
return cell;
} else {
JSQDisplayedMessageCollectionViewCell * cell = (JSQDisplayedMessageCollectionViewCell *)[super collectionView:collectionView cellForItemAtIndexPath:indexPath];
return cell;
}
}
#pragma mark - JSQMessages collection view flow layout delegate
#pragma mark - Adjusting cell label heights
- (CGFloat)collectionView:(JSQMessagesCollectionView *)collectionView
layout:(JSQMessagesCollectionViewFlowLayout *)collectionViewLayout heightForCellTopLabelAtIndexPath:(NSIndexPath *)indexPath
{
/**
* Each label in a cell has a `height` delegate method that corresponds to its text dataSource method
*/
/**
* This logic should be consistent with what you return from `attributedTextForCellTopLabelAtIndexPath:`
* The other label height delegate methods should follow similarly
*
* Show a timestamp for every 3rd message
*/
// if (indexPath.item % 3 == 0) {
// return kJSQMessagesCollectionViewCellLabelHeightDefault;
// }
return 0.0f;
}
- (CGFloat)collectionView:(JSQMessagesCollectionView *)collectionView
layout:(JSQMessagesCollectionViewFlowLayout *)collectionViewLayout heightForMessageBubbleTopLabelAtIndexPath:(NSIndexPath *)indexPath
{
/**
* iOS7-style sender name labels
*/
JSQMessage *currentMessage = [self.demoData.messages objectAtIndex:indexPath.item];
if ([[currentMessage senderId] isEqualToString:self.senderId]) {
return 0.0f;
}
if (indexPath.item - 1 > 0) {
JSQMessage *previousMessage = [self.demoData.messages objectAtIndex:indexPath.item - 1];
if ([[previousMessage senderId] isEqualToString:[currentMessage senderId]]) {
return 0.0f;
}
}
return kJSQMessagesCollectionViewCellLabelHeightDefault;
}
- (CGFloat)collectionView:(JSQMessagesCollectionView *)collectionView
layout:(JSQMessagesCollectionViewFlowLayout *)collectionViewLayout heightForCellBottomLabelAtIndexPath:(NSIndexPath *)indexPath
{
return 0.0f;
}
#pragma mark - Responding to collection view tap events
- (void)collectionView:(JSQMessagesCollectionView *)collectionView
header:(JSQMessagesLoadEarlierHeaderView *)headerView didTapLoadEarlierMessagesButton:(UIButton *)sender
{
NSLog(@"Load earlier messages!");
}
- (void)collectionView:(JSQMessagesCollectionView *)collectionView didTapAvatarImageView:(UIImageView *)avatarImageView atIndexPath:(NSIndexPath *)indexPath
{
NSLog(@"Tapped avatar!");
}
- (void)collectionView:(JSQMessagesCollectionView *)collectionView didTapMessageBubbleAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(@"Tapped message bubble!");
}
- (void)collectionView:(JSQMessagesCollectionView *)collectionView didTapCellAtIndexPath:(NSIndexPath *)indexPath touchLocation:(CGPoint)touchLocation
{
NSLog(@"Tapped cell at %@!", NSStringFromCGPoint(touchLocation));
}
@end

View File

@ -0,0 +1,61 @@
//
// Created by Jesse Squires
// http://www.jessesquires.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSQMessagesViewController
//
//
// GitHub
// https://github.com/jessesquires/JSQMessagesViewController
//
//
// License
// Copyright (c) 2014 Jesse Squires
// Released under an MIT license: http://opensource.org/licenses/MIT
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#import "JSQMessages.h"
/**
* This is for demo/testing purposes only.
* This object sets up some fake model data.
* Do not actually do anything like this.
*/
static NSString * const kJSQDemoAvatarDisplayNameSquires = @"Jesse Squires";
static NSString * const kJSQDemoAvatarDisplayNameCook = @"Tim Cook";
static NSString * const kJSQDemoAvatarDisplayNameJobs = @"Jobs";
static NSString * const kJSQDemoAvatarDisplayNameWoz = @"Steve Wozniak";
static NSString * const kJSQDemoAvatarIdSquires = @"053496-4509-289";
static NSString * const kJSQDemoAvatarIdCook = @"468-768355-23123";
static NSString * const kJSQDemoAvatarIdJobs = @"707-8956784-57";
static NSString * const kJSQDemoAvatarIdWoz = @"309-41802-93823";
@interface DemoModelData : NSObject
@property (strong, nonatomic) NSMutableArray *messages;
@property (strong, nonatomic) NSDictionary *avatars;
@property (strong, nonatomic) JSQMessagesBubbleImage *outgoingBubbleImageData;
@property (strong, nonatomic) JSQMessagesBubbleImage *incomingBubbleImageData;
@property (strong, nonatomic) NSDictionary *users;
- (void)addPhotoMediaMessage;
- (void)addLocationMediaMessageCompletion:(JSQLocationMediaItemCompletionBlock)completion;
- (void)addVideoMediaMessage;
@end

View File

@ -0,0 +1,236 @@
//
// Created by Jesse Squires
// http://www.jessesquires.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSQMessagesViewController
//
//
// GitHub
// https://github.com/jessesquires/JSQMessagesViewController
//
//
// License
// Copyright (c) 2014 Jesse Squires
// Released under an MIT license: http://opensource.org/licenses/MIT
//
#import "DemoModelData.h"
#import "NSUserDefaults+DemoSettings.h"
#import "JSQCall.h"
#import "JSQInfoMessage.h"
#import "JSQErrorMessage.h"
/**
* This is for demo/testing purposes only.
* This object sets up some fake model data.
* Do not actually do anything like this.
*/
@implementation DemoModelData
- (instancetype)init
{
self = [super init];
if (self) {
if ([NSUserDefaults emptyMessagesSetting]) {
self.messages = [NSMutableArray new];
}
else {
[self loadFakeMessages];
}
/**
* Create avatar images once.
*
* Be sure to create your avatars one time and reuse them for good performance.
*
* If you are not using avatars, ignore this.
*/
JSQMessagesAvatarImage *jsqImage = [JSQMessagesAvatarImageFactory avatarImageWithUserInitials:@"JSQ"
backgroundColor:[UIColor colorWithWhite:0.85f alpha:1.0f]
textColor:[UIColor colorWithWhite:0.60f alpha:1.0f]
font:[UIFont systemFontOfSize:14.0f]
diameter:kJSQMessagesCollectionViewAvatarSizeDefault];
JSQMessagesAvatarImage *cookImage = [JSQMessagesAvatarImageFactory avatarImageWithImage:[UIImage imageNamed:@"demo_avatar_cook"]
diameter:kJSQMessagesCollectionViewAvatarSizeDefault];
JSQMessagesAvatarImage *jobsImage = [JSQMessagesAvatarImageFactory avatarImageWithImage:[UIImage imageNamed:@"demo_avatar_jobs"]
diameter:kJSQMessagesCollectionViewAvatarSizeDefault];
JSQMessagesAvatarImage *wozImage = [JSQMessagesAvatarImageFactory avatarImageWithImage:[UIImage imageNamed:@"demo_avatar_woz"]
diameter:kJSQMessagesCollectionViewAvatarSizeDefault];
self.avatars = @{ kJSQDemoAvatarIdSquires : jsqImage,
kJSQDemoAvatarIdCook : cookImage,
kJSQDemoAvatarIdJobs : jobsImage,
kJSQDemoAvatarIdWoz : wozImage };
self.users = @{ kJSQDemoAvatarIdJobs : kJSQDemoAvatarDisplayNameJobs,
kJSQDemoAvatarIdCook : kJSQDemoAvatarDisplayNameCook,
kJSQDemoAvatarIdWoz : kJSQDemoAvatarDisplayNameWoz,
kJSQDemoAvatarIdSquires : kJSQDemoAvatarDisplayNameSquires };
/**
* Create message bubble images objects.
*
* Be sure to create your bubble images one time and reuse them for good performance.
*
*/
JSQMessagesBubbleImageFactory *bubbleFactory = [[JSQMessagesBubbleImageFactory alloc] init];
self.outgoingBubbleImageData = [bubbleFactory outgoingMessagesBubbleImageWithColor:[UIColor jsq_messageBubbleLightGrayColor]];
self.incomingBubbleImageData = [bubbleFactory incomingMessagesBubbleImageWithColor:[UIColor jsq_messageBubbleGreenColor]];
}
return self;
}
- (void)loadFakeMessages
{
/**
* Load some fake messages for demo.
*
* You should have a mutable array or orderedSet, or something.
*/
self.messages = [[NSMutableArray alloc] initWithObjects:
[[JSQCall alloc] initWithCallerId:kJSQDemoAvatarIdWoz
callerDisplayName:kJSQDemoAvatarDisplayNameWoz
date:[NSDate date]
status:kCallOutgoing],
[[JSQMessage alloc] initWithSenderId:kJSQDemoAvatarIdSquires
senderDisplayName:kJSQDemoAvatarDisplayNameSquires
date:[NSDate distantPast]
text:@"Welcome to JSQMessages: A messaging UI framework for iOS."],
[[JSQMessage alloc] initWithSenderId:kJSQDemoAvatarIdSquires
senderDisplayName:kJSQDemoAvatarDisplayNameSquires
date:[NSDate distantPast]
text:@"Welcome to JSQMessages: A messaging UI framework for iOS."],
[[JSQMessage alloc] initWithSenderId:kJSQDemoAvatarIdWoz
senderDisplayName:kJSQDemoAvatarDisplayNameWoz
date:[NSDate distantPast]
text:@"It is simple, elegant, and easy to use. There are super sweet default settings, but you can customize like crazy."],
[[JSQMessage alloc] initWithSenderId:kJSQDemoAvatarIdSquires
senderDisplayName:kJSQDemoAvatarDisplayNameSquires
date:[NSDate distantPast]
text:@"It even has data detectors. You can call me tonight. My cell number is 123-456-7890. My website is www.hexedbits.com."],
[[JSQMessage alloc] initWithSenderId:kJSQDemoAvatarIdJobs
senderDisplayName:kJSQDemoAvatarDisplayNameJobs
date:[NSDate date]
text:@"JSQMessagesViewController is nearly an exact replica of the iOS Messages App. And perhaps, better."],
[[JSQMessage alloc] initWithSenderId:kJSQDemoAvatarIdCook
senderDisplayName:kJSQDemoAvatarDisplayNameCook
date:[NSDate date]
text:@"It is unit-tested, free, open-source, and documented."],
[[JSQMessage alloc] initWithSenderId:kJSQDemoAvatarIdSquires
senderDisplayName:kJSQDemoAvatarDisplayNameSquires
date:[NSDate date]
text:@"Now with media messages!"],
[[JSQCall alloc] initWithCallerId:kJSQDemoAvatarIdWoz
callerDisplayName:kJSQDemoAvatarDisplayNameWoz
date:[NSDate date]
status:kCallIncoming],
[[JSQCall alloc] initWithCallerId:kJSQDemoAvatarIdWoz
callerDisplayName:kJSQDemoAvatarDisplayNameWoz
date:[NSDate date]
status:kCallMissed],
[[JSQInfoMessage alloc] initWithInfoType:JSQInfoMessageTypeSessionDidEnd
senderId:kJSQDemoAvatarIdCook
senderDisplayName:kJSQDemoAvatarDisplayNameCook
date:[NSDate date]],
[[JSQErrorMessage alloc] initWithErrorType:JSQErrorMessageMissingKeyId
senderId:kJSQDemoAvatarIdCook
senderDisplayName:kJSQDemoAvatarDisplayNameCook
date:[NSDate date]],
[[JSQErrorMessage alloc] initWithErrorType:JSQErrorMessageInvalidMessage
senderId:kJSQDemoAvatarIdCook
senderDisplayName:kJSQDemoAvatarDisplayNameCook
date:[NSDate date]],
[[JSQErrorMessage alloc] initWithErrorType:JSQErrorMessageInvalidVersion
senderId:kJSQDemoAvatarIdCook
senderDisplayName:kJSQDemoAvatarDisplayNameCook
date:[NSDate date]],
[[JSQErrorMessage alloc] initWithErrorType:JSQErrorMessageInvalidKeyException
senderId:kJSQDemoAvatarIdCook
senderDisplayName:kJSQDemoAvatarDisplayNameCook
date:[NSDate date]],
[[JSQErrorMessage alloc] initWithErrorType:JSQErrorMessageWrongTrustedIdentityKey
senderId:kJSQDemoAvatarIdCook
senderDisplayName:kJSQDemoAvatarDisplayNameCook
date:[NSDate date]],
nil];
[self addPhotoMediaMessage];
/**
* Setting to load extra messages for testing/demo
*/
if ([NSUserDefaults extraMessagesSetting]) {
NSArray *copyOfMessages = [self.messages copy];
for (NSUInteger i = 0; i < 4; i++) {
[self.messages addObjectsFromArray:copyOfMessages];
}
}
/**
* Setting to load REALLY long message for testing/demo
* You should see "END" twice
*/
if ([NSUserDefaults longMessageSetting]) {
JSQMessage *reallyLongMessage = [JSQMessage messageWithSenderId:kJSQDemoAvatarIdSquires
displayName:kJSQDemoAvatarDisplayNameSquires
text:@"Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur? END Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur? END"];
[self.messages addObject:reallyLongMessage];
}
}
- (void)addPhotoMediaMessage
{
JSQPhotoMediaItem *photoItem = [[JSQPhotoMediaItem alloc] initWithImage:[UIImage imageNamed:@"goldengate"]];
JSQMessage *photoMessage = [JSQMessage messageWithSenderId:kJSQDemoAvatarIdSquires
displayName:kJSQDemoAvatarDisplayNameSquires
media:photoItem];
[self.messages addObject:photoMessage];
}
- (void)addLocationMediaMessageCompletion:(JSQLocationMediaItemCompletionBlock)completion
{
CLLocation *ferryBuildingInSF = [[CLLocation alloc] initWithLatitude:37.795313 longitude:-122.393757];
JSQLocationMediaItem *locationItem = [[JSQLocationMediaItem alloc] init];
[locationItem setLocation:ferryBuildingInSF withCompletionHandler:completion];
JSQMessage *locationMessage = [JSQMessage messageWithSenderId:kJSQDemoAvatarIdSquires
displayName:kJSQDemoAvatarDisplayNameSquires
media:locationItem];
[self.messages addObject:locationMessage];
}
- (void)addVideoMediaMessage
{
// don't have a real video, just pretending
NSURL *videoURL = [NSURL URLWithString:@"file://"];
JSQVideoMediaItem *videoItem = [[JSQVideoMediaItem alloc] initWithFileURL:videoURL isReadyToPlay:YES];
JSQMessage *videoMessage = [JSQMessage messageWithSenderId:kJSQDemoAvatarIdSquires
displayName:kJSQDemoAvatarDisplayNameSquires
media:videoItem];
[self.messages addObject:videoMessage];
}
@end

View File

@ -0,0 +1,42 @@
//
// Created by Jesse Squires
// http://www.jessesquires.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSQMessagesViewController
//
//
// GitHub
// https://github.com/jessesquires/JSQMessagesViewController
//
//
// License
// Copyright (c) 2014 Jesse Squires
// Released under an MIT license: http://opensource.org/licenses/MIT
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
/**
* This is for demo/testing purposes only.
*
* This is a terrible idea for a real app.
*/
@interface DemoSettingsViewController : UITableViewController
@property (weak, nonatomic) IBOutlet UISwitch *extraMessagesSwitch;
@property (weak, nonatomic) IBOutlet UISwitch *longMessageSwitch;
@property (weak, nonatomic) IBOutlet UISwitch *emptySwitch;
@property (weak, nonatomic) IBOutlet UISwitch *incomingAvatarsSwitch;
@property (weak, nonatomic) IBOutlet UISwitch *outgoingAvatarsSwitch;
@property (weak, nonatomic) IBOutlet UISwitch *springySwitch;
@end

View File

@ -0,0 +1,72 @@
//
// Created by Jesse Squires
// http://www.jessesquires.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSQMessagesViewController
//
//
// GitHub
// https://github.com/jessesquires/JSQMessagesViewController
//
//
// License
// Copyright (c) 2014 Jesse Squires
// Released under an MIT license: http://opensource.org/licenses/MIT
//
#import "DemoSettingsViewController.h"
#import "NSUserDefaults+DemoSettings.h"
/**
* This is for demo/testing purposes only.
*
* This is a terrible idea for a real app.
*/
@implementation DemoSettingsViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.extraMessagesSwitch.on = [NSUserDefaults extraMessagesSetting];
self.longMessageSwitch.on = [NSUserDefaults longMessageSetting];
self.emptySwitch.on = [NSUserDefaults emptyMessagesSetting];
self.incomingAvatarsSwitch.on = [NSUserDefaults incomingAvatarSetting];
self.outgoingAvatarsSwitch.on = [NSUserDefaults outgoingAvatarSetting];
self.springySwitch.on = [NSUserDefaults springinessSetting];
}
- (IBAction)didTapSwitch:(UISwitch *)sender
{
if (sender == self.extraMessagesSwitch) {
[NSUserDefaults saveExtraMessagesSetting:sender.on];
}
else if (sender == self.longMessageSwitch) {
[NSUserDefaults saveLongMessageSetting:sender.on];
}
else if (sender == self.emptySwitch) {
[NSUserDefaults saveEmptyMessagesSetting:sender.on];
}
else if (sender == self.incomingAvatarsSwitch) {
[NSUserDefaults saveIncomingAvatarSetting:sender.on];
}
else if (sender == self.outgoingAvatarsSwitch) {
[NSUserDefaults saveOutgoingAvatarSetting:sender.on];
}
else if (sender == self.springySwitch) {
[NSUserDefaults saveSpringinessSetting:sender.on];
}
[[NSUserDefaults standardUserDefaults] synchronize];
}
@end

Some files were not shown because too many files have changed in this diff Show More