Compare commits
771 Commits
iOS6_suppo
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
26fb5cbcf4 | ||
|
|
c8262eee36 | ||
|
|
184f53b07b | ||
|
|
ed9858bd6d | ||
|
|
9dffc05aa7 | ||
|
|
cb5c4facbc | ||
|
|
84d1b659f3 | ||
|
|
b73b5d9983 | ||
|
|
6a30411791 | ||
|
|
395b1342a9 | ||
|
|
ea3b8cc6fc | ||
|
|
697c839c80 | ||
|
|
d6c9dd0ce9 | ||
|
|
16c42a11e5 | ||
|
|
9980266b62 | ||
|
|
38c14c19e7 | ||
|
|
736d110eba | ||
|
|
7c57677738 | ||
|
|
69e2518345 | ||
|
|
3a50f7f9c2 | ||
|
|
6e4857ab8d | ||
|
|
260e51ce2c | ||
|
|
a81bfa2277 | ||
|
|
847f49e6e7 | ||
|
|
8703ce020c | ||
|
|
7d8cd7b867 | ||
|
|
8d9f241038 | ||
|
|
fcbbfdc43f | ||
|
|
66fb14161d | ||
|
|
bc976a04d9 | ||
|
|
4ab13ad7ac | ||
|
|
d5f8d4291b | ||
|
|
43f6e75bbf | ||
|
|
59ea2b6920 | ||
|
|
dee758a3ff | ||
|
|
17153bdd38 | ||
|
|
82482a1fc5 | ||
|
|
442b597c95 | ||
|
|
1bb258691b | ||
|
|
7c2cec63f9 | ||
|
|
91fbb4dce7 | ||
|
|
681b6d329b | ||
|
|
0d8fabe142 | ||
|
|
4049f54789 | ||
|
|
54ae40b07a | ||
|
|
bcf0ac6e1f | ||
|
|
e70cec697f | ||
|
|
7234f10f81 | ||
|
|
fd7f27b74b | ||
|
|
b22b7a4caf | ||
|
|
b8b3fb9122 | ||
|
|
5fc12f5cba | ||
|
|
4cad607ae2 | ||
|
|
276e9f94a6 | ||
|
|
6a04d7585d | ||
|
|
28b17896cc | ||
|
|
8f28bc513e | ||
|
|
512d2a9394 | ||
|
|
81488d5ee6 | ||
|
|
08ebf9b608 | ||
|
|
56f0d40214 | ||
|
|
5d83de37cd | ||
|
|
2626e8419b | ||
|
|
597670e446 | ||
|
|
881ddddeb4 | ||
|
|
32395453ff | ||
|
|
5c6dce5a04 | ||
|
|
3d248e4af0 | ||
|
|
6bbef1949b | ||
|
|
1031a89858 | ||
|
|
21d877492e | ||
|
|
5670accd2c | ||
|
|
d138d4f3a3 | ||
|
|
cddbbf2c75 | ||
|
|
104ba0e023 | ||
|
|
a27fa1091e | ||
|
|
a7a9cd7c64 | ||
|
|
7446b63cbb | ||
|
|
73482f6ace | ||
|
|
2dad6e3036 | ||
|
|
b20af9e524 | ||
|
|
5ba9d2ee09 | ||
|
|
fe0540167b | ||
|
|
45ce02baa6 | ||
|
|
7722bce9d8 | ||
|
|
ea74142adb | ||
|
|
ae9869d7ff | ||
|
|
36b066fbdc | ||
|
|
387a7544b3 | ||
|
|
484a4b59bd | ||
|
|
f6e96e9a07 | ||
|
|
cdc86cc0a3 | ||
|
|
03b74fe77e | ||
|
|
280ff293f1 | ||
|
|
3b8cf0fa35 | ||
|
|
591e9198bb | ||
|
|
0d20dadb8e | ||
|
|
72a4b5b970 | ||
|
|
b291898161 | ||
|
|
5627228464 | ||
|
|
4281751534 | ||
|
|
c81aa93726 | ||
|
|
468e102a7c | ||
|
|
b3a80d9afc | ||
|
|
dbfe58fc6d | ||
|
|
2070cde22d | ||
|
|
e99b127694 | ||
|
|
08a9ec95fa | ||
|
|
0d8667d14e | ||
|
|
5c8dee520c | ||
|
|
42c29ce4a1 | ||
|
|
66a7d47ff3 | ||
|
|
049035cdb6 | ||
|
|
4aedb062cc | ||
|
|
c0edc6f7aa | ||
|
|
9b579e91b9 | ||
|
|
e88501ed61 | ||
|
|
49f2c24533 | ||
|
|
74965225f0 | ||
|
|
99971a8d38 | ||
|
|
da7203dee2 | ||
|
|
88e16ac830 | ||
|
|
41dddfea3a | ||
|
|
ebea4ef0b8 | ||
|
|
f115194efa | ||
|
|
39393e4b8b | ||
|
|
06e7c2bec9 | ||
|
|
6dded2cce9 | ||
|
|
f03bac13a0 | ||
|
|
33b81ca6b0 | ||
|
|
752edc4f0f | ||
|
|
71acc85bb4 | ||
|
|
3868620d62 | ||
|
|
58e4ac6624 | ||
|
|
b5b6e8c2af | ||
|
|
065d60d997 | ||
|
|
3f0f52f704 | ||
|
|
188ab824b8 | ||
|
|
e2b62c4996 | ||
|
|
1af170b983 | ||
|
|
b590303a4f | ||
|
|
a38125e726 | ||
|
|
b75870a241 | ||
|
|
d4a5bc6d09 | ||
|
|
da92497d42 | ||
|
|
6f7f318bcd | ||
|
|
45549628ae | ||
|
|
02fdee07d0 | ||
|
|
005378f8f3 | ||
|
|
0638d8caf2 | ||
|
|
a023fee709 | ||
|
|
f201b085e3 | ||
|
|
a56ceebdd3 | ||
|
|
f914b81044 | ||
|
|
639df113ff | ||
|
|
3954411213 | ||
|
|
3e8a3cbfc1 | ||
|
|
833cf417f5 | ||
|
|
3684895342 | ||
|
|
dee7714d9e | ||
|
|
dd9458a70f | ||
|
|
d85c94e57f | ||
|
|
454be29f8d | ||
|
|
d0fd6e58ec | ||
|
|
3b159c1df9 | ||
|
|
33e780e535 | ||
|
|
ddca80232a | ||
|
|
1db3c14f6f | ||
|
|
f348e82249 | ||
|
|
6a2bda18c8 | ||
|
|
02e5927a1d | ||
|
|
41d295a6af | ||
|
|
ee51479736 | ||
|
|
933f11765c | ||
|
|
ae47a8350f | ||
|
|
1fb7f9f6cf | ||
|
|
31bf2b2fa9 | ||
|
|
e839b58a47 | ||
|
|
da2352c73b | ||
|
|
bde4ae92aa | ||
|
|
3da13a9fc1 | ||
|
|
423588cbc2 | ||
|
|
7ef5b4bbc2 | ||
|
|
18fc6ec9f6 | ||
|
|
65a691af96 | ||
|
|
a03344bacc | ||
|
|
a5ad7cfa18 | ||
|
|
d1781adc09 | ||
|
|
fc21561030 | ||
|
|
11b74c5113 | ||
|
|
11d25f1393 | ||
|
|
b696ea4117 | ||
|
|
2fdbc9f0f1 | ||
|
|
610b1c4e80 | ||
|
|
08b974cd7d | ||
|
|
b75bfdf54d | ||
|
|
cdd92be623 | ||
|
|
eb35a14807 | ||
|
|
8acf85ea08 | ||
|
|
e83b02a74d | ||
|
|
3ab7fd4918 | ||
|
|
5c850cf963 | ||
|
|
a1b2060796 | ||
|
|
f796b576d0 | ||
|
|
ac1660841d | ||
|
|
730927509b | ||
|
|
e9d5f6371b | ||
|
|
392ad6936d | ||
|
|
bc2ca5c0c5 | ||
|
|
b442c87ab8 | ||
|
|
2923ab1d03 | ||
|
|
d91f26d3e5 | ||
|
|
c3f8361ad3 | ||
|
|
51a17030cb | ||
|
|
e436e8d456 | ||
|
|
6ace8da92b | ||
|
|
4d01ea8a14 | ||
|
|
42b8f45252 | ||
|
|
27efc06859 | ||
|
|
2c9977e589 | ||
|
|
643b67b24b | ||
|
|
38fcfdb915 | ||
|
|
07f3aee64f | ||
|
|
7db7eea4fa | ||
|
|
00d8bd162c | ||
|
|
b3a1f75221 | ||
|
|
67c365a6b7 | ||
|
|
1e5247bce3 | ||
|
|
1052627f59 | ||
|
|
0b2280754f | ||
|
|
df55fd3e71 | ||
|
|
0892296fc3 | ||
|
|
91753a7169 | ||
|
|
23a5402f1f | ||
|
|
57fc67be2c | ||
|
|
b0be2b2da2 | ||
|
|
a93bd1b8aa | ||
|
|
aae37aec37 | ||
|
|
4af0466f98 | ||
|
|
85caf8535e | ||
|
|
1a809a0ba1 | ||
|
|
0c9825cbf9 | ||
|
|
6df0e07619 | ||
|
|
7f624691ee | ||
|
|
ef8c54597a | ||
|
|
fd192ba635 | ||
|
|
ddfd4f5d8c | ||
|
|
cf5ac56a04 | ||
|
|
54b9d85206 | ||
|
|
0ab88fb813 | ||
|
|
33bbfd7bd7 | ||
|
|
4e1047d815 | ||
|
|
e812298e34 | ||
|
|
efb47ff444 | ||
|
|
818c4af616 | ||
|
|
cddf5f4f0d | ||
|
|
202ce0aa0b | ||
|
|
d82f39ccb9 | ||
|
|
1dc1185baa | ||
|
|
4a33d94766 | ||
|
|
c144892a45 | ||
|
|
0a16732842 | ||
|
|
0facd44dbd | ||
|
|
7198f760aa | ||
|
|
ccb1a284fb | ||
|
|
375eda5d4f | ||
|
|
71c21c06bf | ||
|
|
dd5216d5e1 | ||
|
|
961190658b | ||
|
|
39ea9f6eef | ||
|
|
3bbb185777 | ||
|
|
c3376a1eb5 | ||
|
|
cf543f6110 | ||
|
|
8c0760e766 | ||
|
|
0c404b528f | ||
|
|
75ee09576a | ||
|
|
0f619f71a7 | ||
|
|
084be62933 | ||
|
|
a8fe8f961d | ||
|
|
d7b4e73d7f | ||
|
|
5045f04522 | ||
|
|
a68ddf1621 | ||
|
|
d3b7f3a1a1 | ||
|
|
af9117642c | ||
|
|
40771ad88a | ||
|
|
9591b89800 | ||
|
|
67e6650b3e | ||
|
|
eec8b614ee | ||
|
|
5020d5ad85 | ||
|
|
45ae2095c2 | ||
|
|
9156843e2e | ||
|
|
a434273f03 | ||
|
|
95fc8b453f | ||
|
|
39c88d90df | ||
|
|
7f4a2bab25 | ||
|
|
da83dcbe75 | ||
|
|
9de1f6f584 | ||
|
|
a97cf9dcbc | ||
|
|
00cf2719a7 | ||
|
|
027bdc3a30 | ||
|
|
4a140738e4 | ||
|
|
430853e7c8 | ||
|
|
1787b82224 | ||
|
|
f0916dc5e3 | ||
|
|
efca0b6bac | ||
|
|
751677394c | ||
|
|
832474648d | ||
|
|
3bfa3fd49b | ||
|
|
30bce9b446 | ||
|
|
92cd02669f | ||
|
|
dcb567e1a5 | ||
|
|
00a503f691 | ||
|
|
3c5725059c | ||
|
|
79abcca365 | ||
|
|
9ed3f977a0 | ||
|
|
b93e090fc9 | ||
|
|
c6f108b94b | ||
|
|
742dcb288a | ||
|
|
47d1c210c5 | ||
|
|
0ce760a61a | ||
|
|
13b83d0579 | ||
|
|
fd07b7baca | ||
|
|
8a616d430c | ||
|
|
2da245ed81 | ||
|
|
91e35809a1 | ||
|
|
ba298887b3 | ||
|
|
a94cf830db | ||
|
|
1d0c612ec4 | ||
|
|
d3bae33297 | ||
|
|
01484038b7 | ||
|
|
5d87411371 | ||
|
|
14b8045f5d | ||
|
|
3502a81989 | ||
|
|
b39f705d05 | ||
|
|
fe4e4e3f50 | ||
|
|
18ba36af8b | ||
|
|
8511641a24 | ||
|
|
bbecf44390 | ||
|
|
6298f32ecb | ||
|
|
2f9a222454 | ||
|
|
bdef43d27b | ||
|
|
f3cddcff6d | ||
|
|
d2a2594f28 | ||
|
|
752f0e20b7 | ||
|
|
b7f6f6a871 | ||
|
|
50179bb492 | ||
|
|
99412fe735 | ||
|
|
8bd4fe9ada | ||
|
|
aa658076bd | ||
|
|
6aade7b583 | ||
|
|
2c39325220 | ||
|
|
a98115d4cd | ||
|
|
988dd6fdfa | ||
|
|
e18465c03c | ||
|
|
57282750cc | ||
|
|
6d96064ebf | ||
|
|
f7e6f7f0e9 | ||
|
|
251e5afad8 | ||
|
|
51701ca5dd | ||
|
|
17c8b10007 | ||
|
|
5625d9d57b | ||
|
|
60aa9659c1 | ||
|
|
b4b61c2140 | ||
|
|
b1d764deb3 | ||
|
|
2e9729554c | ||
|
|
8913766cb7 | ||
|
|
49636a9c5c | ||
|
|
9772fc8a76 | ||
|
|
18849e9279 | ||
|
|
abdd2ddabc | ||
|
|
1ee911d36e | ||
|
|
4501c6e657 | ||
|
|
762ae3e581 | ||
|
|
7751ec3106 | ||
|
|
29cee9eeb5 | ||
|
|
ed0301fc14 | ||
|
|
fbaf58ecad | ||
|
|
c3e78b5ca8 | ||
|
|
178e1a5ab8 | ||
|
|
ff1fc2060a | ||
|
|
16aa5599d8 | ||
|
|
010b79008a | ||
|
|
6be7dc59f8 | ||
|
|
ffbaa0b0b4 | ||
|
|
239bba9832 | ||
|
|
22683e94a2 | ||
|
|
9538b4fdb0 | ||
|
|
6fd3209097 | ||
|
|
86f2ef29bb | ||
|
|
9bdf157a39 | ||
|
|
31d4e930d8 | ||
|
|
3ac741121e | ||
|
|
3f7b5ae86f | ||
|
|
3fcf0340fe | ||
|
|
948f2e29c6 | ||
|
|
211fb75afc | ||
|
|
d5a6d67638 | ||
|
|
18b773b68c | ||
|
|
3490051b77 | ||
|
|
f29354e039 | ||
|
|
9d5505624b | ||
|
|
8ce72ac3d3 | ||
|
|
9527f91a37 | ||
|
|
22f1bca66d | ||
|
|
299f9bf1a9 | ||
|
|
991f64ab8f | ||
|
|
4250240152 | ||
|
|
e77e2ebd03 | ||
|
|
597b2aee75 | ||
|
|
c4fdcd7a0a | ||
|
|
8f50457f5a | ||
|
|
72b9d050d6 | ||
|
|
fcb118c23e | ||
|
|
aa953e7e0f | ||
|
|
f321c473a2 | ||
|
|
74f9a28b80 | ||
|
|
0e0e90e3eb | ||
|
|
2bb26b7f7e | ||
|
|
ffbc01f626 | ||
|
|
3eb4c40a12 | ||
|
|
30d27ce745 | ||
|
|
42c0dc9973 | ||
|
|
b5e6204012 | ||
|
|
b8ded053e8 | ||
|
|
19e74fa0b1 | ||
|
|
9f493caf40 | ||
|
|
04e33af761 | ||
|
|
92188db5b7 | ||
|
|
c119d99625 | ||
|
|
553ab0168f | ||
|
|
02d89d1ae4 | ||
|
|
1894236342 | ||
|
|
e3b061457e | ||
|
|
ff42633d05 | ||
|
|
cf757b761b | ||
|
|
8cfe21d8e0 | ||
|
|
ec7e4d90d2 | ||
|
|
b8c1f8d7a5 | ||
|
|
1f48465acd | ||
|
|
3b8bf98776 | ||
|
|
8241aac750 | ||
|
|
f0f9d4caa5 | ||
|
|
1c1754168f | ||
|
|
9a2b37f854 | ||
|
|
f4654c882c | ||
|
|
607cae9b7c | ||
|
|
b30722b34d | ||
|
|
7342a94bf8 | ||
|
|
daa5c23630 | ||
|
|
4826acbbdb | ||
|
|
82050acc37 | ||
|
|
bea68885eb | ||
|
|
81d8357f4f | ||
|
|
f1544d239d | ||
|
|
821112a001 | ||
|
|
59a54f100b | ||
|
|
2e83e71fb5 | ||
|
|
175e7f8635 | ||
|
|
1d4bbd6f86 | ||
|
|
61a7b47d8d | ||
|
|
a60cf53e71 | ||
|
|
a039eec10e | ||
|
|
686cefb1ff | ||
|
|
acb32dc8ae | ||
|
|
547bc9127f | ||
|
|
b1af4a7925 | ||
|
|
6ec830f0d7 | ||
|
|
a33a34f01c | ||
|
|
f8213531a0 | ||
|
|
1fe27d5bb0 | ||
|
|
7c81a4a149 | ||
|
|
e57d3ac74c | ||
|
|
ef25a30119 | ||
|
|
325478726a | ||
|
|
03791ad38c | ||
|
|
7b3a14372d | ||
|
|
b8f171a745 | ||
|
|
a0e80726db | ||
|
|
eb291a60ee | ||
|
|
ca95d8789b | ||
|
|
7c8fc8afd8 | ||
|
|
7908ad0340 | ||
|
|
d55090df39 | ||
|
|
9d9d46936e | ||
|
|
b474f8d448 | ||
|
|
bda909b31a | ||
|
|
6cc981364d | ||
|
|
7728cc5a57 | ||
|
|
fc406d8a14 | ||
|
|
d6fb1d2f5a | ||
|
|
b22e30dba0 | ||
|
|
9b27b0d30a | ||
|
|
0046e2aa7b | ||
|
|
7a4fba830e | ||
|
|
bb66b56b5a | ||
|
|
3a231c0795 | ||
|
|
9aa5e525c0 | ||
|
|
08ed2d926b | ||
|
|
a78c6109b0 | ||
|
|
00571d8f11 | ||
|
|
1a6210d1fb | ||
|
|
db3886cc8e | ||
|
|
337154eead | ||
|
|
bbd0b93d26 | ||
|
|
ef5bd9055e | ||
|
|
9b9ba27f4b | ||
|
|
cfe5adc2b3 | ||
|
|
a765fd9897 | ||
|
|
6f265f68d1 | ||
|
|
95b927c903 | ||
|
|
f7cf63cfcf | ||
|
|
cc00acfb66 | ||
|
|
8db1e1d959 | ||
|
|
fb33e14894 | ||
|
|
a5f0533645 | ||
|
|
09d5f6f8cb | ||
|
|
454afd7947 | ||
|
|
2075b4b440 | ||
|
|
3d90d29578 | ||
|
|
16e2a54373 | ||
|
|
1ae002d148 | ||
|
|
56f405c3fe | ||
|
|
010de028f2 | ||
|
|
e7016347b7 | ||
|
|
97582097ff | ||
|
|
e087f7d427 | ||
|
|
eb066d0255 | ||
|
|
8d96dc19bf | ||
|
|
96c4fb004e | ||
|
|
c5b966cc98 | ||
|
|
dab5ab384b | ||
|
|
8a74118cdd | ||
|
|
a63089407c | ||
|
|
e18002f035 | ||
|
|
f7f9fa67a3 | ||
|
|
8462740e6a | ||
|
|
088bbd5b2e | ||
|
|
c48a374075 | ||
|
|
99475aef52 | ||
|
|
faf23dd004 | ||
|
|
96b13f1dc4 | ||
|
|
5255f69a12 | ||
|
|
ad3c9d219d | ||
|
|
6c711b5199 | ||
|
|
4553eb127b | ||
|
|
5ad9c62590 | ||
|
|
35d3dd1944 | ||
|
|
b3fe29290f | ||
|
|
b82c343fce | ||
|
|
c506538f99 | ||
|
|
8207ebd7ab | ||
|
|
315d960d7d | ||
|
|
2da57e27b3 | ||
|
|
15287977fe | ||
|
|
6210a16612 | ||
|
|
17d8dc9f44 | ||
|
|
2ef4d274d5 | ||
|
|
5c26d81a00 | ||
|
|
364f4ef462 | ||
|
|
8e141438de | ||
|
|
085354c527 | ||
|
|
c77a64e55e | ||
|
|
01e4c8afae | ||
|
|
d41f0a1de4 | ||
|
|
c65b015090 | ||
|
|
008a5c7810 | ||
|
|
83ef7e3555 | ||
|
|
a8fd950e82 | ||
|
|
729ae80933 | ||
|
|
b5621cce63 | ||
|
|
987a3bc5e6 | ||
|
|
5436bd3a7d | ||
|
|
5fd9fa22e7 | ||
|
|
fe1e6824a0 | ||
|
|
648376c273 | ||
|
|
2d8b14fef5 | ||
|
|
39c2d93cd6 | ||
|
|
0e2b157c7c | ||
|
|
994cc03580 | ||
|
|
a7d8303869 | ||
|
|
20a1d1ad9b | ||
|
|
d10f5cb477 | ||
|
|
c72cc1c97c | ||
|
|
20c913ab8d | ||
|
|
2500e90b92 | ||
|
|
de8a167929 | ||
|
|
b00fa9dbfc | ||
|
|
d3594d9067 | ||
|
|
ce8b11a8c2 | ||
|
|
b02e1aad6e | ||
|
|
98f1a16e9b | ||
|
|
edabf8086f | ||
|
|
d332e7b502 | ||
|
|
ad1a17f223 | ||
|
|
8fc2468a50 | ||
|
|
13bade879a | ||
|
|
910b3a1793 | ||
|
|
c173026407 | ||
|
|
f43090d873 | ||
|
|
c5931a8f34 | ||
|
|
34fbeea2ac | ||
|
|
dd75cb3ccd | ||
|
|
294b91104a | ||
|
|
cda9f0ffdc | ||
|
|
38f288a7cf | ||
|
|
cc47da1120 | ||
|
|
36de9c4cc7 | ||
|
|
9b7ab359b7 | ||
|
|
593260171e | ||
|
|
5016eca08a | ||
|
|
4659173e52 | ||
|
|
324e1b3532 | ||
|
|
5b0b560616 | ||
|
|
e1e7d296bc | ||
|
|
54a110660b | ||
|
|
86dfa19267 | ||
|
|
42bbcef8ac | ||
|
|
4c6876285c | ||
|
|
4dc36c2ca1 | ||
|
|
2b1edebb39 | ||
|
|
a948140f15 | ||
|
|
2f7f75e4d4 | ||
|
|
1a11ed10e7 | ||
|
|
8467c4cba8 | ||
|
|
5f91045fef | ||
|
|
61c74ae700 | ||
|
|
15d39a035c | ||
|
|
79b0a1bfab | ||
|
|
f2d673df6f | ||
|
|
3e0e77730a | ||
|
|
0513a7cc15 | ||
|
|
7872368ff2 | ||
|
|
34c8122fb0 | ||
|
|
80e7812e8d | ||
|
|
4303d7ac6d | ||
|
|
5b3503ca9e | ||
|
|
dca211964f | ||
|
|
e60a8a2f54 | ||
|
|
e44eab3b32 | ||
|
|
ff16f64b89 | ||
|
|
c0425b7e8c | ||
|
|
36de500d75 | ||
|
|
c77ca0d197 | ||
|
|
04fcb1cfba | ||
|
|
71710399a6 | ||
|
|
e173f3f6b7 | ||
|
|
254fbeb1e5 | ||
|
|
92858080ae | ||
|
|
ff93c0f3ac | ||
|
|
79a311685e | ||
|
|
4eeb6c0caf | ||
|
|
fde0bc7f28 | ||
|
|
d603c7153b | ||
|
|
2ce8e413a4 | ||
|
|
7a92308dc2 | ||
|
|
da13cc302c | ||
|
|
06e1502476 | ||
|
|
72ab4db082 | ||
|
|
7cbc4789dc | ||
|
|
cbd87be5ed | ||
|
|
08ae2259ae | ||
|
|
6e7e2a78ad | ||
|
|
680af644d7 | ||
|
|
d5eadfba63 | ||
|
|
cc324e7655 | ||
|
|
a58245de64 | ||
|
|
e9eaddef49 | ||
|
|
05671a56c8 | ||
|
|
3dc2d7546b | ||
|
|
42ae44b751 | ||
|
|
646f7b3c4e | ||
|
|
dcc364e126 | ||
|
|
2b10d0f2f9 | ||
|
|
72442413af | ||
|
|
7b79b8232a | ||
|
|
ee464c535a | ||
|
|
afc975e584 | ||
|
|
252eb6c19d | ||
|
|
6c663cd874 | ||
|
|
f8e9d7684f | ||
|
|
b82f5b44a4 | ||
|
|
36238e3c84 | ||
|
|
07e3a32d69 | ||
|
|
867cc72901 | ||
|
|
311015bc61 | ||
|
|
451ac87db7 | ||
|
|
18e7d316f2 | ||
|
|
5705f3fcff | ||
|
|
fbaa881504 | ||
|
|
f4ff3eb954 | ||
|
|
c9718c9b0c | ||
|
|
1318713503 | ||
|
|
5c74d16e74 | ||
|
|
8697e7fb78 | ||
|
|
cf0fc8f8e5 | ||
|
|
99446f2f3b | ||
|
|
bd2635fb73 | ||
|
|
39bbc898c6 | ||
|
|
df00e3404c | ||
|
|
ce1da7bcee | ||
|
|
54e37e0cac | ||
|
|
97f8555229 | ||
|
|
b00805719b | ||
|
|
94f229a539 | ||
|
|
b3e26a7498 | ||
|
|
d26545b112 | ||
|
|
05c3c30f91 | ||
|
|
7616ce5f28 | ||
|
|
46a5022939 | ||
|
|
d718020d25 | ||
|
|
3e70a95831 | ||
|
|
8082519e71 | ||
|
|
38217f56eb | ||
|
|
6bc7e4b169 | ||
|
|
143dbcf434 | ||
|
|
ff45ec0d06 | ||
|
|
b203ff3854 | ||
|
|
e7e6aad748 | ||
|
|
0dc7c5bb21 | ||
|
|
51ed4773ff | ||
|
|
e80240cc55 | ||
|
|
051d5b63e9 | ||
|
|
17282ef553 | ||
|
|
3587591c41 | ||
|
|
f0ca89cf93 | ||
|
|
fe8dd1b750 | ||
|
|
85a65f7906 | ||
|
|
890c4e1408 | ||
|
|
52ea59c680 | ||
|
|
dba6bd6a71 | ||
|
|
933b0bcbac | ||
|
|
f66b041915 | ||
|
|
d76647a773 | ||
|
|
06c961ad60 | ||
|
|
6a70a141b9 | ||
|
|
d387abc3d9 | ||
|
|
cb2ea02cbc | ||
|
|
c4f3b2ba11 | ||
|
|
a1acbd36d9 | ||
|
|
2ecfd327bf | ||
|
|
429db56bc0 | ||
|
|
90a364ef29 | ||
|
|
514a4b36b9 | ||
|
|
9aed5929ef | ||
|
|
8ce8d9ca69 | ||
|
|
75974c13b1 | ||
|
|
7e37f91ad8 | ||
|
|
b7510b3bbe | ||
|
|
12b8d9a011 | ||
|
|
ae105c22fe | ||
|
|
8182bc26aa | ||
|
|
26108b8ddd | ||
|
|
b76bfec86e | ||
|
|
2a213001ff | ||
|
|
9c889e582a | ||
|
|
32b0ede235 | ||
|
|
802d4dbd66 | ||
|
|
d755624bc5 | ||
|
|
451dcbb78a | ||
|
|
5f0e36023a | ||
|
|
830e8a6238 | ||
|
|
ae606e10e0 | ||
|
|
e15937838b | ||
|
|
ef2f09c3b2 | ||
|
|
4c67b4999e | ||
|
|
6fdb742e9c | ||
|
|
35e606c1ff | ||
|
|
b8e76ed136 | ||
|
|
8efa202e47 | ||
|
|
cd23e61945 | ||
|
|
dbc0d09083 |
7
.cocoadocs.yml
Normal 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"
|
||||
@ -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
|
After Width: | Height: | Size: 6.9 KiB |
BIN
Assets/jsq_message_chat_icon.png
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
BIN
Assets/jsq_messages_banner.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
Assets/jsq_messages_banner.psd
Normal file
BIN
Assets/jsq_messages_icon.psd
Normal file
BIN
Assets/jsq_messages_splash.psd
Normal file
BIN
Assets/jsq_messages_splash_ipad.psd
Normal file
3
CONTRIBUTING.md
Normal file
@ -0,0 +1,3 @@
|
||||
## How To Contribute
|
||||
|
||||
Please follow these sweet [contribution guidelines](https://github.com/jessesquires/HowToContribute).
|
||||
@ -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 */;
|
||||
}
|
||||
@ -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>
|
||||
@ -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>
|
||||
@ -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>
|
||||
|
Before Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 91 KiB |
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -1,2 +0,0 @@
|
||||
/* Localized versions of Info.plist keys */
|
||||
|
||||
@ -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]));
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
@ -1,2 +0,0 @@
|
||||
/* Localized versions of Info.plist keys */
|
||||
|
||||
@ -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', '1.5.2'
|
||||
end
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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 application’s 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 application’s 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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -1,546 +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;
|
||||
@property (assign, nonatomic) BOOL isObserving;
|
||||
|
||||
- (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;
|
||||
}
|
||||
|
||||
if ([self respondsToSelector:@selector(automaticallyAdjustsScrollViewInsets)]) {
|
||||
self.automaticallyAdjustsScrollViewInsets = 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]];
|
||||
self.isObserving = NO;
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
if (!self.isObserving) {
|
||||
[[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];
|
||||
|
||||
self.isObserving = YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated
|
||||
{
|
||||
[super viewWillDisappear:animated];
|
||||
|
||||
[self.messageInputView resignFirstResponder];
|
||||
[self setEditing:NO animated:YES];
|
||||
|
||||
if (self.isObserving) {
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
|
||||
|
||||
[self.messageInputView.textView removeObserver:self forKeyPath:@"contentSize"];
|
||||
|
||||
self.isObserving = NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (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.messageInputView.textView.undoManager removeAllActions];
|
||||
[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
|
||||
|
Before Width: | Height: | Size: 9.8 KiB |
|
Before Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 5.8 KiB |
|
Before Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 421 B |
|
Before Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 1015 B |
|
Before Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 3.9 KiB |
1224
JSQMessages.xcodeproj/project.pbxproj
Normal file
@ -2,6 +2,6 @@
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:JSMessagesDemo.xcodeproj">
|
||||
location = "self:JSQMessages.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@ -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>
|
||||
10
JSQMessages.xcworkspace/contents.xcworkspacedata
generated
Normal 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>
|
||||
26
JSQMessagesDemo/AppDelegate.h
Normal 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
|
||||
35
JSQMessagesDemo/AppDelegate.m
Normal 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
|
||||
360
JSQMessagesDemo/Base.lproj/Main.storyboard
Normal 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>
|
||||
48
JSQMessagesDemo/DemoMessagesViewController.h
Normal 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
|
||||
584
JSQMessagesDemo/DemoMessagesViewController.m
Normal 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
|
||||
61
JSQMessagesDemo/DemoModelData.h
Normal 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
|
||||
236
JSQMessagesDemo/DemoModelData.m
Normal 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
|
||||
42
JSQMessagesDemo/DemoSettingsViewController.h
Normal 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
|
||||
72
JSQMessagesDemo/DemoSettingsViewController.m
Normal 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
|
||||