Compare commits
409 Commits
master
...
mkirk/adju
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6637483864 | ||
|
|
560e59f8e4 | ||
|
|
ef3bf46e9f | ||
|
|
5d7a5f99f9 | ||
|
|
521686c112 | ||
|
|
7054e4b13e | ||
|
|
fe656ae0c1 | ||
|
|
929f1d80cd | ||
|
|
23bf3f0b1e | ||
|
|
e4927be6a8 | ||
|
|
0622f6f4d7 | ||
|
|
ef5c87f0b4 | ||
|
|
ed54d6c50d | ||
|
|
e630340c48 | ||
|
|
fa6917e4a8 | ||
|
|
25769603b7 | ||
|
|
026b1e31f3 | ||
|
|
a430cbed21 | ||
|
|
d099c56c96 | ||
|
|
8c6e4081ff | ||
|
|
7fdc6393c0 | ||
|
|
ade4e7a95d | ||
|
|
9c435347af | ||
|
|
5cb6acf71e | ||
|
|
66a54186da | ||
|
|
b7f487cc92 | ||
|
|
8c29756211 | ||
|
|
2a46bd19b1 | ||
|
|
6f4d6cc90c | ||
|
|
547e216e82 | ||
|
|
c4143c59a5 | ||
|
|
359d576668 | ||
|
|
3b4dc8f238 | ||
|
|
40ae3b28c3 | ||
|
|
b53eef5dd8 | ||
|
|
d0fea3d377 | ||
|
|
c0928db9e1 | ||
|
|
942ec7a016 | ||
|
|
aecb14f9b7 | ||
|
|
943d8d90ec | ||
|
|
f867ce223f | ||
|
|
a6209a38f2 | ||
|
|
64dbe7f08b | ||
|
|
532eb513a0 | ||
|
|
e32c63283d | ||
|
|
b959f61f82 | ||
|
|
99ff104590 | ||
|
|
aa12e5d19b | ||
|
|
38e80c2696 | ||
|
|
d35bd59bce | ||
|
|
0fef325f6a | ||
|
|
a1779e4a33 | ||
|
|
959e07cceb | ||
|
|
8c6b123457 | ||
|
|
8cba6e5d7f | ||
|
|
f06c939b47 | ||
|
|
6873264627 | ||
|
|
d2e4a06115 | ||
|
|
747bf802f2 | ||
|
|
caeb91576f | ||
|
|
f656a7b129 | ||
|
|
5b27c16472 | ||
|
|
34d37e3e59 | ||
|
|
5a5614c3b9 | ||
|
|
3dabbbf1f2 | ||
|
|
c938362d7c | ||
|
|
932e61d0f1 | ||
|
|
028860b5cd | ||
|
|
17b848c152 | ||
|
|
39dc4e46de | ||
|
|
8b95f41c55 | ||
|
|
f72235a2bf | ||
|
|
bc7bef4c99 | ||
|
|
299dd01b54 | ||
|
|
b537f708d6 | ||
|
|
c1641d68bf | ||
|
|
444283922d | ||
|
|
637d4e1c40 | ||
|
|
cd0c9f61ed | ||
|
|
4b9a5ff5ad | ||
|
|
d616d11809 | ||
|
|
e5979c1ca4 | ||
|
|
59d04621c3 | ||
|
|
6b54c75bc9 | ||
|
|
72e91ffbd2 | ||
|
|
93c4de3ec4 | ||
|
|
fe251ef91c | ||
|
|
d922149efe | ||
|
|
7c248fdb4f | ||
|
|
13824b2264 | ||
|
|
d16852af96 | ||
|
|
aaa6fefc41 | ||
|
|
51beebb07f | ||
|
|
a9d34e8da1 | ||
|
|
3dd0bab1dc | ||
|
|
31ba236883 | ||
|
|
f3ad9887bf | ||
|
|
403745cbd3 | ||
|
|
ad7c3b8f99 | ||
|
|
3464c63f69 | ||
|
|
0a4a628c76 | ||
|
|
2a60b406a6 | ||
|
|
b9999066a9 | ||
|
|
9c1efe10a9 | ||
|
|
7c4b4f4639 | ||
|
|
3d9bb6d6a4 | ||
|
|
685ef7019e | ||
|
|
0f6095fc5b | ||
|
|
a9721495d4 | ||
|
|
01edde38e7 | ||
|
|
b461d5a3b3 | ||
|
|
0c8b939406 | ||
|
|
0c33d88198 | ||
|
|
6621f6b584 | ||
|
|
a29cbb7c79 | ||
|
|
f5f2e67e5b | ||
|
|
03473dfe0a | ||
|
|
2f33e3c9cd | ||
|
|
b49a69a487 | ||
|
|
ec601a6c62 | ||
|
|
be5dc2764c | ||
|
|
1f97141421 | ||
|
|
6dda0b041a | ||
|
|
239f5e6fe8 | ||
|
|
c296dac954 | ||
|
|
69b5779f6b | ||
|
|
5d5551b913 | ||
|
|
90471f36a5 | ||
|
|
2c1210c892 | ||
|
|
932b7da2d9 | ||
|
|
776c94c264 | ||
|
|
7f7b850890 | ||
|
|
e65c34ac78 | ||
|
|
b846d4b4a3 | ||
|
|
2891d192f4 | ||
|
|
04e0a43eb4 | ||
|
|
69e38bcbad | ||
|
|
11af29dcbb | ||
|
|
54fff888f2 | ||
|
|
70c3498c46 | ||
|
|
911b115768 | ||
|
|
d7e2748f3e | ||
|
|
7b6a6863bf | ||
|
|
ca70936656 | ||
|
|
5b529462fb | ||
|
|
9a9e28b35c | ||
|
|
f693689ca6 | ||
|
|
5bcd72ffce | ||
|
|
a04482c1e8 | ||
|
|
42aaff754b | ||
|
|
2393ea6c16 | ||
|
|
6f90c0e93a | ||
|
|
807b5fe9ea | ||
|
|
4c52e50b51 | ||
|
|
18703786e5 | ||
|
|
c980b20292 | ||
|
|
e935338e17 | ||
|
|
5d1ddb909a | ||
|
|
0d19bb201d | ||
|
|
8e5f36593e | ||
|
|
88f2a908c7 | ||
|
|
419a14210c | ||
|
|
75692259db | ||
|
|
59cd37a969 | ||
|
|
b99b2a3b38 | ||
|
|
506e82f9be | ||
|
|
2baed6f61d | ||
|
|
238ec1d13d | ||
|
|
a58d3e34d2 | ||
|
|
ea14a506e8 | ||
|
|
53f66cd8a4 | ||
|
|
4a009760da | ||
|
|
820ad7c9b4 | ||
|
|
f50127ca4e | ||
|
|
7485f3d1d3 | ||
|
|
cc678456dc | ||
|
|
1803ffbd6c | ||
|
|
f29f2e284d | ||
|
|
8716568a4d | ||
|
|
f419972a89 | ||
|
|
faf95defde | ||
|
|
ebc8e2f02c | ||
|
|
f82cb30939 | ||
|
|
5a01def7fb | ||
|
|
e0c53ac15b | ||
|
|
ef4f67d7d7 | ||
|
|
a00fa9dc52 | ||
|
|
6ab06a44a8 | ||
|
|
027ed01416 | ||
|
|
88b0301449 | ||
|
|
155990c63b | ||
|
|
487e15a163 | ||
|
|
af61aa9335 | ||
|
|
695f09f968 | ||
|
|
0c5c2b6bfb | ||
|
|
123199aa6a | ||
|
|
3a49d81fdc | ||
|
|
36e5d25146 | ||
|
|
7917c423cc | ||
|
|
49135c660a | ||
|
|
553f5ecbb8 | ||
|
|
25a58853b2 | ||
|
|
9f64e6a25a | ||
|
|
8ba29137e8 | ||
|
|
ac25bd4f2f | ||
|
|
611ef2dab2 | ||
|
|
1968ffcfa7 | ||
|
|
f65b452b93 | ||
|
|
e72bfeed1f | ||
|
|
1e7ded798b | ||
|
|
12e3dc6442 | ||
|
|
a735dd3da5 | ||
|
|
b95e606447 | ||
|
|
42d85c66d5 | ||
|
|
e63f45308a | ||
|
|
684aac33e7 | ||
|
|
6648fe06f9 | ||
|
|
1d4be68944 | ||
|
|
0730fe5ebe | ||
|
|
9ebbcb5d97 | ||
|
|
678d3348ed | ||
|
|
27ed04dae6 | ||
|
|
dad900b185 | ||
|
|
1d8ced57f4 | ||
|
|
e217043e06 | ||
|
|
875eab6a54 | ||
|
|
0317f7ff8c | ||
|
|
42d4d369e7 | ||
|
|
07cccf00ee | ||
|
|
031de6dd0a | ||
|
|
cd46b83b69 | ||
|
|
9042e961b0 | ||
|
|
375439060b | ||
|
|
f38934e03d | ||
|
|
052dfdcdd9 | ||
|
|
165adaef06 | ||
|
|
f5cdd957e9 | ||
|
|
f8cbc84e0f | ||
|
|
5291d8c5d1 | ||
|
|
9fa0e40397 | ||
|
|
3e14180439 | ||
|
|
ba77b23076 | ||
|
|
5124c1247e | ||
|
|
48dfb0ea03 | ||
|
|
f72c03cde3 | ||
|
|
e0141a3d7c | ||
|
|
ddc20c96ee | ||
|
|
a3652df8b6 | ||
|
|
d701cbff14 | ||
|
|
6405d7dad6 | ||
|
|
12dd5b2744 | ||
|
|
5b0badf923 | ||
|
|
cd17e0de51 | ||
|
|
6d7ceda2bd | ||
|
|
983fb5823d | ||
|
|
679ac60698 | ||
|
|
49f5d7ec9d | ||
|
|
9752be93cf | ||
|
|
9a89ba2d12 | ||
|
|
e6480b57ea | ||
|
|
b344f2dfa3 | ||
|
|
933f0dfe6c | ||
|
|
d5272c4283 | ||
|
|
c07c12e26a | ||
|
|
ec2e0a9a39 | ||
|
|
62385e451a | ||
|
|
08ff2cb0cb | ||
|
|
8dea72a056 | ||
|
|
c5c7050d60 | ||
|
|
3fad9a9548 | ||
|
|
da6136da26 | ||
|
|
3c964a4d83 | ||
|
|
586c9597c0 | ||
|
|
c28d4bfd26 | ||
|
|
a7335bc2c1 | ||
|
|
cbeb533a54 | ||
|
|
c4749a7991 | ||
|
|
7b2b238153 | ||
|
|
676dc75c14 | ||
|
|
1140cac96f | ||
|
|
91e74b0f7b | ||
|
|
3898476714 | ||
|
|
16ed8c1f88 | ||
|
|
efa3c2b365 | ||
|
|
8cd4b0be87 | ||
|
|
122983ddbb | ||
|
|
2870560006 | ||
|
|
a2f8b39891 | ||
|
|
b656f8adf9 | ||
|
|
867115b03c | ||
|
|
c2651dd0a4 | ||
|
|
ec8dcf66d5 | ||
|
|
acbb692b3d | ||
|
|
7606339770 | ||
|
|
9551159a20 | ||
|
|
c8831f0b30 | ||
|
|
566ddb3301 | ||
|
|
ea373b0130 | ||
|
|
9859d3ab52 | ||
|
|
8e8f0998be | ||
|
|
27ec773edc | ||
|
|
34f046c452 | ||
|
|
7e7edbf675 | ||
|
|
98368fc105 | ||
|
|
08567a4be6 | ||
|
|
78c46c0ac4 | ||
|
|
2f94744368 | ||
|
|
ad761def43 | ||
|
|
c5c9c20246 | ||
|
|
c4d38a8407 | ||
|
|
9db018bfce | ||
|
|
0db2ab9e74 | ||
|
|
6a5aeb5ffb | ||
|
|
74acd93417 | ||
|
|
3247b45b13 | ||
|
|
bccad24efc | ||
|
|
5e9e368f4d | ||
|
|
bff4ae7b86 | ||
|
|
4c05c28f70 | ||
|
|
6b58a211c5 | ||
|
|
cb901cb026 | ||
|
|
cd974aa015 | ||
|
|
a6a2884b07 | ||
|
|
a859a6862a | ||
|
|
72e7343908 | ||
|
|
45590c3405 | ||
|
|
cd085c683f | ||
|
|
853a3fe1c5 | ||
|
|
7610526965 | ||
|
|
ab831aaf67 | ||
|
|
5c57cee47f | ||
|
|
dcebef9d1e | ||
|
|
547cec7287 | ||
|
|
72540e2553 | ||
|
|
1c5811107c | ||
|
|
ea42262a9a | ||
|
|
483f36402a | ||
|
|
7edc1312d1 | ||
|
|
6086d26bc8 | ||
|
|
e75c2fb166 | ||
|
|
0ed53ae8c5 | ||
|
|
fb7119d3b2 | ||
|
|
71a7f519fc | ||
|
|
62c6844ce6 | ||
|
|
dd0e214d13 | ||
|
|
25a887a31e | ||
|
|
bd82c11af6 | ||
|
|
3e69f1fc1d | ||
|
|
acb047f25d | ||
|
|
fc581c8b0b | ||
|
|
007a945959 | ||
|
|
56f8a9c648 | ||
|
|
0f5cd6c2fd | ||
|
|
86746bd9d6 | ||
|
|
94b19ccb08 | ||
|
|
88d2fe4ef4 | ||
|
|
97bfef24c9 | ||
|
|
f09dc0230e | ||
|
|
a30f9bdee9 | ||
|
|
e23a248797 | ||
|
|
05c97700a2 | ||
|
|
1f64013617 | ||
|
|
a6437be1ab | ||
|
|
e0ae6cdb1f | ||
|
|
d9aea32ed0 | ||
|
|
09db017b87 | ||
|
|
a180dcbee6 | ||
|
|
2698a699d8 | ||
|
|
0aa0ae2237 | ||
|
|
1fb791f964 | ||
|
|
0b8fb267d1 | ||
|
|
b728ff3b4a | ||
|
|
fa7e42bfa5 | ||
|
|
82534ea75d | ||
|
|
08cc2d14d4 | ||
|
|
720999efe9 | ||
|
|
0157f1a47e | ||
|
|
fc340cd166 | ||
|
|
399544ff5d | ||
|
|
245551f8ce | ||
|
|
27d7a2e09c | ||
|
|
9e6e0f2524 | ||
|
|
9893d4208f | ||
|
|
3e80be42db | ||
|
|
e354981b49 | ||
|
|
08377bc153 | ||
|
|
1acb68119e | ||
|
|
f31b214c1b | ||
|
|
ddf11e62e4 | ||
|
|
fa668ef7da | ||
|
|
e3a2babfb2 | ||
|
|
ed81bb2977 | ||
|
|
6e7647d14a | ||
|
|
99977d4938 | ||
|
|
df26f3cc16 | ||
|
|
1b9814ee5c | ||
|
|
eec8ff4236 | ||
|
|
3779613da8 | ||
|
|
f55bdad953 | ||
|
|
741f53f793 | ||
|
|
9bbdffa568 | ||
|
|
db40c8b588 | ||
|
|
dbad9a9325 | ||
|
|
5d21d699f6 | ||
|
|
b5ca3df47e | ||
|
|
191d2c3700 | ||
|
|
bbbb242521 | ||
|
|
174cae9b6c | ||
|
|
7d6534eeae |
34
.codecov.yml
Normal file
34
.codecov.yml
Normal file
@ -0,0 +1,34 @@
|
||||
codecov:
|
||||
branch: develop
|
||||
|
||||
coverage:
|
||||
precision: 2
|
||||
round: nearest
|
||||
range: "60...100"
|
||||
ignore:
|
||||
- JSQMessagesDemo/*
|
||||
- Pods/*
|
||||
- JSQMessagesTests/*
|
||||
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
target: auto
|
||||
threshold: 2.0
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
|
||||
patch:
|
||||
default:
|
||||
target: auto
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
|
||||
comment:
|
||||
layout: "header, diff, changes, sunburst, uncovered"
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
behavior: default
|
||||
74
.github/CONDUCT.md
vendored
Normal file
74
.github/CONDUCT.md
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
# JSQMessagesViewController Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, gender identity and expression, level of experience,
|
||||
nationality, personal appearance, race, religion, or sexual identity and
|
||||
orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at **jesse.squires.developer [at] gmail [dot] com**. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at [http://contributor-covenant.org/version/1/4][version]
|
||||
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
||||
51
.github/CONTRIBUTING.md
vendored
Normal file
51
.github/CONTRIBUTING.md
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
# Contributing to JSQMessagesViewController
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
Please read our [Code of Conduct](https://github.com/jessesquires/JSQMessagesViewController/blob/develop/.github/CONDUCT.md). Intolerance, disrespect, and any of form of negativity will not be tolerated.
|
||||
|
||||
## Opening a new issue
|
||||
|
||||
1. Read *all* of the [`README`](https://github.com/jessesquires/JSQMessagesViewController/blob/develop/README.md) :speak_no_evil:
|
||||
* Search [open issues](https://github.com/jessesquires/JSQMessagesViewController/issues) *and* [closed issues](https://github.com/jessesquires/JSQMessagesViewController/issues?q=is%3Aissue+is%3Aclosed) to **avoid opening a duplicate issue!** :see_no_evil:
|
||||
* If your issue exists, please comment on its thread with your new information :hear_no_evil:
|
||||
* Otherwise, open a new issue with a good title and description :memo:
|
||||
* Provide **all** of the following information:
|
||||
- Library version(s) :octocat:
|
||||
- iOS version(s) :iphone:
|
||||
- Devices/Simulators affected :iphone:
|
||||
- Expected behavior vs actual behavior
|
||||
- Complete steps to reproduce the issue :warning:
|
||||
- Link to a project that exhibits the issue, if possible fork the repo and modify the provided demo project :construction:
|
||||
- Screenshots/GIFs/Videos showing the issue, if applicable :camera:
|
||||
- Full crash log, if applicable :boom:
|
||||
- Search for and list any issues that might be related :mag_right:
|
||||
|
||||
## Submitting a pull request
|
||||
|
||||
1. Link to the issue that the pull request resolves. If there isn't one, create one.
|
||||
2. Write unit tests that test your changes, if applicable.
|
||||
3. Update header docs, if needed.
|
||||
4. Follow existing coding style, and these [style guidelines](https://github.com/jessesquires/HowToContribute#style-guidelines).
|
||||
5. Resolve any merge conflicts.
|
||||
6. Squash your commits into a single commit.
|
||||
|
||||
## Questions and help
|
||||
|
||||
See the [Questions & Help](https://github.com/jessesquires/JSQMessagesViewController/blob/develop/README.md#questions--help), and [Documentation](https://github.com/jessesquires/JSQMessagesViewController/blob/develop/README.md#documentation) sections in the `README`.
|
||||
|
||||
## General guidelines
|
||||
|
||||
Please also read through these more general [contribution guidelines](https://github.com/jessesquires/HowToContribute).
|
||||
|
||||
## Did you read all of this?
|
||||
|
||||
You even followed the links? Congratulations! You deserve a high-five. :tada:
|
||||
|
||||

|
||||
|
||||
### New issue checklist
|
||||
|
||||
Now show me how awesome you are! :smile: When opening your new issue and filling out the checklist, you'll be asked for confirmation. Confirm that you've read this with these emoji: :muscle::sunglasses::facepunch:
|
||||
|
||||
> - [x] I have reviewed the contributing guidelines. Confirmation: :muscle::sunglasses::facepunch:
|
||||
36
.github/ISSUE_TEMPLATE.md
vendored
Normal file
36
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
## New issue checklist
|
||||
<!-- Before submitting this issue, make sure you have done the following -->
|
||||
|
||||
- [ ] I have read all of the [`README`](https://github.com/jessesquires/JSQMessagesViewController/blob/develop/README.md), [documentation](http://cocoadocs.org/docsets/JSQMessagesViewController/), and [FAQ](https://github.com/jessesquires/JSQMessagesViewController/blob/develop/Documentation/faq.md).
|
||||
- [ ] I have reviewed the [contributing guidelines](https://github.com/jessesquires/JSQMessagesViewController/blob/develop/.github/CONTRIBUTING.md). Confirmation: ____
|
||||
- [ ] I have searched [existing issues](https://github.com/jessesquires/JSQMessagesViewController/issues?q=is%3Aissue+sort%3Acreated-desc) and **this is not a duplicate**.
|
||||
|
||||
### General information
|
||||
|
||||
- Library version(s):
|
||||
- iOS version(s):
|
||||
- Devices/Simulators affected:
|
||||
- Reproducible in the demo project? (Yes/No):
|
||||
- Related issues:
|
||||
|
||||
## Bug report
|
||||
|
||||
#### Expected behavior
|
||||
|
||||
> ...
|
||||
|
||||
#### Actual behavior
|
||||
|
||||
> ...
|
||||
|
||||
#### Steps to reproduce
|
||||
|
||||
> ...
|
||||
|
||||
#### Crash log? Screenshots? Videos? Sample project?
|
||||
|
||||
>...
|
||||
|
||||
## Question or Feature Request
|
||||
|
||||
> ...
|
||||
11
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
11
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
## Pull request checklist
|
||||
|
||||
- [ ] All tests pass. Demo project builds and runs.
|
||||
- [ ] I have resolved any merge conflicts.
|
||||
- [ ] I have followed the [coding style](https://github.com/jessesquires/HowToContribute#style-guidelines), and reviewed the [contributing guidelines](https://github.com/jessesquires/JSQMessagesViewController/blob/develop/.github/CONTRIBUTING.md). Confirmation: ____
|
||||
|
||||
#### This fixes issue #___.
|
||||
|
||||
## What's in this pull request?
|
||||
|
||||
>...
|
||||
9
.gitignore
vendored
9
.gitignore
vendored
@ -1,6 +1,6 @@
|
||||
# Xcode
|
||||
|
||||
.DS_Store
|
||||
|
||||
# Xcode
|
||||
/build/*
|
||||
*/build/*
|
||||
*.pbxuser
|
||||
@ -18,8 +18,3 @@ DerivedData
|
||||
.idea/
|
||||
*.hmap
|
||||
*.xccheckout
|
||||
|
||||
|
||||
#CocoaPods
|
||||
|
||||
Pods
|
||||
48
.travis.yml
48
.travis.yml
@ -1,10 +1,44 @@
|
||||
language: objective-c
|
||||
osx_image: xcode7.3
|
||||
|
||||
before_install:
|
||||
- export LANG=en_US.UTF-8
|
||||
- gem install cocoapods
|
||||
- brew update
|
||||
- if brew outdated | grep -qx xctool; then brew upgrade xctool; fi
|
||||
env:
|
||||
global:
|
||||
- LANG=en_US.UTF-8
|
||||
- WORKSPACE="JSQMessages.xcworkspace"
|
||||
- IOS_SCHEME="JSQMessages"
|
||||
- IOS_SDK=iphonesimulator9.3
|
||||
|
||||
script:
|
||||
- xctool clean build test -workspace JSQMessages.xcworkspace -scheme JSQMessages -sdk iphonesimulator7.1 ONLY_ACTIVE_ARCH=NO
|
||||
matrix:
|
||||
- DESTINATION="OS=8.1,name=iPhone 5" SDK="$IOS_SDK" SCHEME="$IOS_SCHEME" RUN_TESTS="YES" BUILD_EXAMPLE="NO" POD_LINT="NO" COVERAGE="NO"
|
||||
- DESTINATION="OS=9.0,name=iPhone 6 Plus" SDK="$IOS_SDK" SCHEME="$IOS_SCHEME" RUN_TESTS="NO" BUILD_EXAMPLE="NO" POD_LINT="YES" COVERAGE="YES"
|
||||
|
||||
before_install:
|
||||
- gem install xcpretty --no-rdoc --no-ri --no-document --quiet
|
||||
|
||||
# skip pod install on travis-ci
|
||||
# since we check-in the pods folder, etc. this isn't needed
|
||||
install: true
|
||||
|
||||
script:
|
||||
|
||||
- if [ $POD_LINT == "YES" ]; then
|
||||
pod spec lint;
|
||||
pod lib lint;
|
||||
fi
|
||||
|
||||
- if [ $RUN_TESTS == "YES" ]; then
|
||||
xcodebuild clean build test -workspace "$WORKSPACE" -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION" -configuration Debug ONLY_ACTIVE_ARCH=NO | xcpretty -c;
|
||||
else
|
||||
xcodebuild clean build -workspace "$WORKSPACE" -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION" -configuration Debug ONLY_ACTIVE_ARCH=NO | xcpretty -c;
|
||||
fi
|
||||
|
||||
# Build for reporting test coverage
|
||||
- if [ $COVERAGE == "YES" ]; then
|
||||
xcodebuild clean build test -workspace "$WORKSPACE" -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION" -configuration Debug ONLY_ACTIVE_ARCH=NO GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES GCC_GENERATE_TEST_COVERAGE_FILES=YES;
|
||||
fi
|
||||
|
||||
after_success:
|
||||
|
||||
- if [ $COVERAGE == "YES" ]; then
|
||||
bash <(curl -s https://codecov.io/bash);
|
||||
fi
|
||||
|
||||
125
CHANGELOG.md
Normal file
125
CHANGELOG.md
Normal file
@ -0,0 +1,125 @@
|
||||
# CHANGELOG
|
||||
|
||||
The changelog for `JSQMessagesViewController`. Also see the [releases](https://github.com/jessesquires/JSQMessagesViewController/releases) on GitHub.
|
||||
|
||||
--------------------------------------
|
||||
|
||||
7.3.4
|
||||
-----
|
||||
|
||||
- [Milestone](https://github.com/jessesquires/JSQMessagesViewController/issues?q=milestone%3A7.3.4+is%3Aclosed)
|
||||
- [GitHub release notes](https://github.com/jessesquires/JSQMessagesViewController/releases/tag/7.3.4)
|
||||
|
||||
7.3.3
|
||||
-----
|
||||
|
||||
- [Milestone](https://github.com/jessesquires/JSQMessagesViewController/issues?q=milestone%3A7.3.3+is%3Aclosed)
|
||||
- [GitHub release notes](https://github.com/jessesquires/JSQMessagesViewController/releases/tag/7.3.3)
|
||||
|
||||
7.3.2
|
||||
-----
|
||||
|
||||
- [Milestone](https://github.com/jessesquires/JSQMessagesViewController/issues?q=milestone%3A7.3.2+is%3Aclosed)
|
||||
- [GitHub release notes](https://github.com/jessesquires/JSQMessagesViewController/releases/tag/7.3.2)
|
||||
|
||||
7.3.1
|
||||
-----
|
||||
|
||||
- [Milestone](https://github.com/jessesquires/JSQMessagesViewController/issues?q=milestone%3A7.3.1+is%3Aclosed)
|
||||
- [GitHub release notes](https://github.com/jessesquires/JSQMessagesViewController/releases/tag/7.3.1)
|
||||
|
||||
7.3.0
|
||||
-----
|
||||
|
||||
- [Milestone](https://github.com/jessesquires/JSQMessagesViewController/issues?q=milestone%3A7.3.0+is%3Aclosed)
|
||||
- [GitHub release notes](https://github.com/jessesquires/JSQMessagesViewController/releases/tag/7.3.0)
|
||||
|
||||
|
||||
7.2.0
|
||||
-----
|
||||
|
||||
- [Milestone](https://github.com/jessesquires/JSQMessagesViewController/issues?q=milestone%3A7.2.0+is%3Aclosed)
|
||||
- [GitHub release notes](https://github.com/jessesquires/JSQMessagesViewController/releases/tag/7.2.0)
|
||||
|
||||
7.1.0
|
||||
-----
|
||||
|
||||
- [Milestone](https://github.com/jessesquires/JSQMessagesViewController/issues?q=milestone%3A7.1.0+is%3Aclosed)
|
||||
- [GitHub release notes](https://github.com/jessesquires/JSQMessagesViewController/releases/tag/7.1.0)
|
||||
|
||||
7.0.2
|
||||
-----
|
||||
|
||||
- [Milestone](https://github.com/jessesquires/JSQMessagesViewController/issues?q=milestone%3A7.0.2+is%3Aclosed)
|
||||
- [GitHub release notes](https://github.com/jessesquires/JSQMessagesViewController/releases/tag/7.0.2)
|
||||
|
||||
7.0.1
|
||||
-----
|
||||
|
||||
- [Milestone](https://github.com/jessesquires/JSQMessagesViewController/issues?q=milestone%3A7.0.1+is%3Aclosed)
|
||||
- [GitHub release notes](https://github.com/jessesquires/JSQMessagesViewController/releases/tag/7.0.1)
|
||||
|
||||
7.0.0
|
||||
-----
|
||||
|
||||
- [Milestone](https://github.com/jessesquires/JSQMessagesViewController/issues?q=milestone%3A7.0.0+is%3Aclosed)
|
||||
- [GitHub release notes](https://github.com/jessesquires/JSQMessagesViewController/releases/tag/7.0.0)
|
||||
|
||||
6.1.3
|
||||
-----
|
||||
|
||||
- [GitHub release notes](https://github.com/jessesquires/JSQMessagesViewController/releases/tag/6.1.3)
|
||||
|
||||
6.1.2
|
||||
-----
|
||||
|
||||
- [Milestone](https://github.com/jessesquires/JSQMessagesViewController/issues?q=milestone%3A6.1.2+is%3Aclosed)
|
||||
- [GitHub release notes](https://github.com/jessesquires/JSQMessagesViewController/releases/tag/6.1.2)
|
||||
|
||||
6.1.1
|
||||
-----
|
||||
|
||||
- [Milestone](https://github.com/jessesquires/JSQMessagesViewController/issues?q=milestone%3A6.1.1+is%3Aclosed)
|
||||
- [GitHub release notes](https://github.com/jessesquires/JSQMessagesViewController/releases/tag/6.1.1)
|
||||
|
||||
6.0.0
|
||||
-----
|
||||
|
||||
- [Milestone](https://github.com/jessesquires/JSQMessagesViewController/issues?q=milestone%3A6.0.0+is%3Aclosed)
|
||||
- [GitHub release notes](https://github.com/jessesquires/JSQMessagesViewController/releases/tag/6.0.0)
|
||||
|
||||
5.3.0
|
||||
-----
|
||||
|
||||
- [GitHub release notes](https://github.com/jessesquires/JSQMessagesViewController/releases/tag/5.3.0)
|
||||
|
||||
5.2.0
|
||||
-----
|
||||
|
||||
- [GitHub release notes](https://github.com/jessesquires/JSQMessagesViewController/releases/tag/5.2.0)
|
||||
|
||||
5.1.0
|
||||
-----
|
||||
|
||||
- [GitHub release notes](https://github.com/jessesquires/JSQMessagesViewController/releases/tag/5.1.0)
|
||||
|
||||
5.0.3
|
||||
-----
|
||||
|
||||
- [GitHub release notes](https://github.com/jessesquires/JSQMessagesViewController/releases/tag/5.0.3)
|
||||
|
||||
5.0.0
|
||||
-----
|
||||
|
||||
- [Milestone](https://github.com/jessesquires/JSQMessagesViewController/issues?q=milestone%3A5.0.0+is%3Aclosed)
|
||||
- [GitHub release notes](https://github.com/jessesquires/JSQMessagesViewController/releases/tag/5.0.0)
|
||||
|
||||
4.0.0
|
||||
-----
|
||||
|
||||
- [GitHub release notes](https://github.com/jessesquires/JSQMessagesViewController/releases/tag/4.0.0)
|
||||
|
||||
All previous versions
|
||||
---------------------
|
||||
|
||||
Unfortunately, release notes are not available for earlier versions of the library.
|
||||
@ -1,3 +0,0 @@
|
||||
## How To Contribute
|
||||
|
||||
Please follow these sweet [contribution guidelines](https://github.com/jessesquires/HowToContribute).
|
||||
46
Documentation/apps_using_this_library.md
Normal file
46
Documentation/apps_using_this_library.md
Normal file
@ -0,0 +1,46 @@
|
||||
# Apps using this library
|
||||
|
||||
These are the (known) apps that use `JSQMessagesViewController`. Submit a [pull request](https://github.com/jessesquires/JSQMessagesViewController/compare) to join the list! :smile:
|
||||
|
||||
-----------------
|
||||
|
||||
* [Hemoglobe](http://bit.ly/hemoglobeapp)
|
||||
* [PocketSuite](https://itunes.apple.com/us/app/pocketsuite/id721795146)
|
||||
* [FireChat](https://itunes.apple.com/us/app/firechat/id719829352)
|
||||
* [Signal](https://github.com/WhisperSystems/Signal-iOS)
|
||||
* [ClassDojo](https://itunes.apple.com/us/app/classdojo/id552602056)
|
||||
* [Schools App](https://itunes.apple.com/us/app/schools-app/id495845755)
|
||||
* [ChatSecure](https://chatsecure.org)
|
||||
* [Bryx 911](https://itunes.apple.com/us/app/bryx-911/id813078029)
|
||||
* [Kytt](https://itunes.apple.com/de/app/kytt-neue-leute-in-der-umgebung/id848959696)
|
||||
* [Spark Social](https://itunes.apple.com/us/app/spark-social/id823785892)
|
||||
* [Spabbit](https://itunes.apple.com/us/app/spabbit/id737363908)
|
||||
* [Elodie](https://itunes.apple.com/app/elodie/id821610181)
|
||||
* [Instaply](https://itunes.apple.com/us/app/instaply/id558562920)
|
||||
* [Loopse](https://itunes.apple.com/us/app/loopse-spots-friends-sessions/id704783915)
|
||||
* [Oxwall Messenger](https://github.com/tochman/OxwallMessenger)
|
||||
* [FourChat](https://itunes.apple.com/us/app/fourchat/id650833730)
|
||||
* [vCinity](https://itunes.apple.com/us/app/vcinity-chat-without-internet/id875395391)
|
||||
* [Quick Text Message](https://itunes.apple.com/us/app/quick-text-message-fast-sms/id583729997)
|
||||
* [Libraries for developers](https://itunes.apple.com/us/app/libraries-for-developers/id653427112)
|
||||
* [Buhz|Hyve](https://itunes.apple.com/us/app/buhz-hyve/id818568956)
|
||||
* [Ringring.io](https://github.com/ringring-io/ringring-ios)
|
||||
* [gDecide](https://itunes.apple.com/ca/app/gdecide/id716801285)
|
||||
* [AwesomeChat](https://github.com/relatedcode/RealtimeChat)
|
||||
* [ParseChat](https://github.com/relatedcode/ParseChat)
|
||||
* [Jib](http://jibapp.com)
|
||||
* [Onvolo](https://itunes.apple.com/us/app/onvolo/id869332351)
|
||||
* [EVCloudKitDao](https://github.com/evermeer/EVCloudKitDao)
|
||||
* [Fluky Chat](https://itunes.apple.com/us/app/fluky-chat-secure-anonymous/id958605886)
|
||||
* [VillageUnity](https://itunes.apple.com/us/app/village-unity/id919972368)
|
||||
* [Pine](https://itunes.apple.com/us/app/pine-innovation-product-life/id946589228)
|
||||
* [NotificationChat](https://github.com/relatedcode/EncryptedChat)
|
||||
* [RealtimeChat](https://github.com/relatedcode/RealtimeChat)
|
||||
* [Bazar](https://itunes.apple.com/ru/app/bazar-talk-about-everything/id885453058)
|
||||
* [Roomie](https://itunes.apple.com/us/app/roomie-find-your-roomie/id962585201)
|
||||
* [PimpMyCall](https://itunes.apple.com/us/app/pimp-my-call/id990167537)
|
||||
* [Yellow Partner](https://itunes.apple.com/us/app/yellow-partner/id1062994361?ls=1&mt=8)
|
||||
* [Radiate](https://itunes.apple.com/us/app/radiate/id939284774?mt=8)
|
||||
* [Criptext](https://itunes.apple.com/us/app/criptext-secure-messenger/id848647361?mt=8)
|
||||
* [multipeer-chat](https://github.com/J4awesome/multipeer-chat)
|
||||
* *Your app here, submit a [pull request](https://github.com/jessesquires/JSQMessagesViewController/compare)!*
|
||||
130
Documentation/contributor_onboarding.md
Normal file
130
Documentation/contributor_onboarding.md
Normal file
@ -0,0 +1,130 @@
|
||||
# Contributor Onboarding
|
||||
|
||||
*Contributor onboarding guide for JSQMessagesViewController*
|
||||
|
||||
This guide is intended to bring new core contributors up-to-speed on the project, organization, expectations, and best practices.
|
||||
|
||||
-------------------------
|
||||
|
||||
## Introduction
|
||||
|
||||
Welcome! :smile: If you are reading this, then you are (or are about to be) a core contributor! :tada: The goal of this document is to cover everything you need to know about helping to maintain this project. If you are not familiar with the code, the docs, the demo project, and everything else in the repo, then that should be your first step. Otherwise, continue on!
|
||||
|
||||
## Getting push access
|
||||
|
||||
Being a **contributor** means submitting pull requests, opening issues, etc. Being a **core contributor** means getting push access and other permissions.
|
||||
|
||||
We love freely giving push access to great contributors, and always err on trusting contributors with this responsibility. However, before granting you push access we would like to see a few things:
|
||||
|
||||
- An interest and dedication to the project
|
||||
- Helping to triage issues, review pull requests, and diagnose bugs
|
||||
- Submitting a couple great pull requests
|
||||
|
||||
We really prefer to grant push access to contributors who have a decent amount of time to share each week or month. If you cannot be extremely active on the project — that's ok! You can still be an :sparkles: awesome contributor :sparkles: without getting push access!
|
||||
|
||||
The rationale behind all of this is that we do not want to accumulate a *huge* list of **core contributors** that are *not* regularly active.
|
||||
|
||||
Remember, *your time* is *your time* — there is absolutely no pressure on you to spend a lot of time on this project, although it is greatly appreciated! :smile:
|
||||
|
||||
> **Note:** the rest of this document applies to both **contributors** and **core contributors**, but there are some details that would require having push access.
|
||||
|
||||
## Core team
|
||||
|
||||
### Project lead
|
||||
|
||||
Jesse Squires ([**@jessesquires**](https://github.com/jessesquires)) serves as the lead for `JSQMessagesViewController`.
|
||||
|
||||
Responsibilities include:
|
||||
- Managing releases and CocoaPods distributions
|
||||
- Merging code into `master`
|
||||
- Overall guidance on design, architecture, and implementation
|
||||
- Strategic direction for the library
|
||||
- Onboarding new core contributors
|
||||
- Everything under **Core Contributors** :smile:
|
||||
- Anything not covered by **Core Contributors** :smile:
|
||||
|
||||
As core contributors grow and take on more repsonsibility, they can become a lead.
|
||||
|
||||
### Core contributors
|
||||
|
||||
Core contributors have push access and are responsible for:
|
||||
|
||||
- Bug fixes
|
||||
- New features
|
||||
- Triaging issues (managing, organizing)
|
||||
- Reviewing pull requests
|
||||
- Answering questions from the community on [issues](https://github.com/jessesquires/JSQMessagesViewController/issues?utf8=✓&q=is%3Aissue+label%3A%22questions+%26+help%22+) and [StackOverflow](http://stackoverflow.com/questions/tagged/jsqmessagesviewcontroller)
|
||||
- Documentation
|
||||
|
||||
Current core contributors:
|
||||
- Harlan Haskans ([**@harlanhaskins**](https://github.com/harlanhaskins))
|
||||
- Eli Burke ([**@eliburke**](https://github.com/eliburke))
|
||||
|
||||
## Pushing code
|
||||
|
||||
Although you have permissions to push code directly to `develop` as a core contributor, we ask that you *always* submit a pull request for code changes. After a code review and approval, you may merge your diff. For minor changes, like formatting or typos, pushing directly to `develop` is acceptable.
|
||||
|
||||
Always merge work to `develop` unless otherwise specified. The project lead will manage the `master` branch.
|
||||
|
||||
For now, Jesse ([**@jessesquires**](https://github.com/jessesquires)) should provide the final approval for *all* pull requests. However, as core contributors grow and establish themselves in the project, they can take on this responsibility as well.
|
||||
|
||||
## Project managment
|
||||
|
||||
### General guidelines
|
||||
|
||||
Above all, abide by our [code of conduct](https://github.com/jessesquires/JSQMessagesViewController/blob/develop/.github/CONDUCT.md) at all times. Be welcoming, kind, and inclusive.
|
||||
|
||||
Often, users do not follow our [contributing guidelines](https://github.com/jessesquires/JSQMessagesViewController/blob/develop/.github/CONTRIBUTING.md), fail to complete the [issue template](https://github.com/jessesquires/JSQMessagesViewController/blob/develop/.github/ISSUE_TEMPLATE.md), or fail to complete the [pull request template](https://github.com/jessesquires/JSQMessagesViewController/blob/develop/.github/PULL_REQUEST_TEMPLATE.md). This is frustrating, but the best response is to kindly remind and encourage them to follow the correct procedures next time.
|
||||
|
||||
When first responding to a newly opened issue or pull request, *always* thank the contributor and add some sweet emoji. Any positive emoji will work. (:+1:, :smile:, :sunglasses:, etc.) Choose your favorite.
|
||||
|
||||
> Thanks **@jessesquires**! :smile_cat:
|
||||
|
||||
Then continue on with the rest of your comment. There will be times where we simply cannot accept a patch for various reasons. In this case, kindly explain why it is not the right approach for the library, thank them for their time and effort, and encourage them to keep contributing.
|
||||
|
||||
In any situation, when in doubt, tag the project lead in a comment to get feedback.
|
||||
|
||||
### Development
|
||||
|
||||
- All work for minor and patch releases should happen on `develop`. For example, release 7.x.x.
|
||||
- All work for major releases should happen on a release branch. For example, `release_8.0`.
|
||||
- The project lead will manage the `master` branch.
|
||||
|
||||
For core contributors, always assign issues or pull requests to the appropriate team member. If you are working on an issue, assign it to yourself. If you would like someone to review a pull request, assign it to them.
|
||||
|
||||
### Managing issues
|
||||
|
||||
- Always add the appropriate label(s). There may be more than one.
|
||||
- Assign to a release milestone, if applicable.
|
||||
- Ask for more information from the user, if needed.
|
||||
- Verify bugs. Leave comments on your findings as necessary.
|
||||
- If it's a duplicate, label and close.
|
||||
- Follow the general guidelines above.
|
||||
|
||||
##### Special labels
|
||||
|
||||
- [`needs review`](https://github.com/jessesquires/JSQMessagesViewController/issues?q=is%3Aissue+label%3A%22needs+review%22): These issues need to be triaged and confirmed. They are typically bugs or pull requests, but do not have to be. Once verified, `needs review` should be removed and any other appropriate labels should be added.
|
||||
- [`new release roadmap`](https://github.com/jessesquires/JSQMessagesViewController/issues?utf8=✓&q=is%3Aissue+label%3A%22new+release+roadmap%22+): For communicating new releases to the community.
|
||||
- [`in-progress`](https://github.com/jessesquires/JSQMessagesViewController/issues?q=is%3Aissue+label%3Ain-progress): Specifies a task that is currently being worked on. Remove this label after closing a task.
|
||||
- [`duplicate`](https://github.com/jessesquires/JSQMessagesViewController/issues?utf8=✓&q=label%3Aduplicate+): For duplicate isses. When closing an issue as a duplicate be sure to leave a comment with the original issue number. *"Closing as duplicate of #6."*
|
||||
- [`questions & help`](https://github.com/jessesquires/JSQMessagesViewController/issues?q=is%3Aissue+label%3A%22questions+%26+help%22): For community questions and help. Note that we are trying to refer questions to [StackOverflow](http://stackoverflow.com/questions/tagged/jsqmessagesviewcontroller) instead.
|
||||
|
||||
### Managing pull requests
|
||||
|
||||
- Review the code for correctness, performance, style, etc. Leave comments as needed.
|
||||
- Always add the appropriate label(s). There may be more than one.
|
||||
- Assign to a release milestone, if applicable.
|
||||
- Follow the general guidelines above.
|
||||
- If you think it's ready to go, tag the project lead to get the final :+1:
|
||||
|
||||
### Managing releases
|
||||
|
||||
All releases are organized using [milestones](https://github.com/jessesquires/JSQMessagesViewController/milestones). Use these to prioritize work and figure out what's next.
|
||||
|
||||
Issues and pull requests included in the next milestone release should be the highest priorty. Once a milestone is 100% complete, the project lead will merge `develop` or other release branches into `master`. The project lead will close the milestone, tag the release, and submit to CocoaPods.
|
||||
|
||||
### Managing documentation
|
||||
|
||||
Having high quality documentation and 100% coverage has a significant impact on the project's success.
|
||||
|
||||
Always add new docs for new public APIs and keep them up-to-date. Use existing docs and Apple's docs for Cocoa as guidelines for writing great documentation.
|
||||
109
Documentation/faq.md
Normal file
109
Documentation/faq.md
Normal file
@ -0,0 +1,109 @@
|
||||
# FAQ
|
||||
|
||||
*Frequently asked questions for JSQMessagesViewController.*
|
||||
|
||||
Contributions are welcome! Please submit a [pull request](https://github.com/jessesquires/JSQMessagesViewController/compare).
|
||||
|
||||
------------------------------------
|
||||
|
||||
## For 7.x.x
|
||||
|
||||
#### Using `UITabBar` ?
|
||||
|
||||
Is the library compatible with `UITabBarController` and `UITabBar`? Yes and no. For the history on this issue, see [#179](https://github.com/jessesquires/JSQMessagesViewController/issues/179) and [#94](https://github.com/jessesquires/JSQMessagesViewController/issues/94). This seems to be the best workaround:
|
||||
|
||||
````objective-c
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
self.edgesForExtendedLayout = UIRectEdgeNone;
|
||||
}
|
||||
````
|
||||
|
||||
#### *Springy bubbles?*
|
||||
|
||||
:warning: Note: this feature is still experimental.
|
||||
|
||||
````objective-c
|
||||
- (void)viewDidAppear:(BOOL)animated
|
||||
{
|
||||
[super viewDidAppear:animated];
|
||||
self.collectionView.collectionViewLayout.springinessEnabled = YES;
|
||||
}
|
||||
````
|
||||
|
||||
#### *Remove avatars?*
|
||||
````objective-c
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
self.collectionView.collectionViewLayout.incomingAvatarViewSize = CGSizeZero;
|
||||
self.collectionView.collectionViewLayout.outgoingAvatarViewSize = CGSizeZero;
|
||||
}
|
||||
|
||||
- (id<JSQMessageAvatarImageDataSource>)collectionView:(JSQMessagesCollectionView *)collectionView avatarImageDataForItemAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
````
|
||||
|
||||
#### *Need customize your collection view cells?*
|
||||
|
||||
There are 2 approaches to this, which one you choose depends on your needs.
|
||||
|
||||
1. Customize appearance and behavior of existing cells. (Easy)
|
||||
2. Provide your own completely custom cell prototypes. (Hard)
|
||||
|
||||
> Also see [previous issues](https://github.com/jessesquires/JSQMessagesViewController/issues?utf8=✓&q=custom+cell+in%3Atitle).
|
||||
|
||||
##### (1) Customizing existing cells
|
||||
|
||||
If you only need to make minor changes to the existing cells (colors, data detectors, etc.), then you simply need to override the following method. You have access to all properties on the cell. ([docs](http://cocoadocs.org/docsets/JSQMessagesViewController/7.2.0/Classes/JSQMessagesCollectionViewCell.html))
|
||||
|
||||
````objective-c
|
||||
- (UICollectionViewCell *)collectionView:(JSQMessagesCollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
JSQMessagesCollectionViewCell *cell = (JSQMessagesCollectionViewCell *)[super collectionView:collectionView cellForItemAtIndexPath:indexPath];
|
||||
|
||||
// Customize the shit out of this cell
|
||||
// See the docs for JSQMessagesCollectionViewCell
|
||||
|
||||
return cell;
|
||||
}
|
||||
````
|
||||
|
||||
##### (2) Providing your own cell prototypes
|
||||
|
||||
This approach is more involved, but gives you greater flexibility. If you need to add or modify subviews of the cell, use this approach. ([docs](http://cocoadocs.org/docsets/JSQMessagesViewController/7.2.0/Classes/JSQMessagesViewController.html))
|
||||
|
||||
1. You need to provide your own cell subclasses, similar to the library's `JSQMessagesCollectionViewCell`, `JSQMessagesCollectionViewCellIncoming`, `JSQMessagesCollectionViewCellOutgoing`.
|
||||
2. On your `JSQMessagesViewController` subclass, set the following properties according to your classes:
|
||||
- `outgoingCellIdentifier`
|
||||
- `outgoingMediaCellIdentifier`
|
||||
- `incomingCellIdentifier`
|
||||
- `incomingMediaCellIdentifier`
|
||||
3. Register your cell classes/nibs with the collection view and the identifiers above
|
||||
4. Override `-collectionView: cellForItemAtIndexPath:`. Do not call `super`. Since you are providing your own cells, calling `super` will perform a bunch of unnecessary work.
|
||||
5. (Optional) For your model objects, implement `JSQMessageData` or subclass `JSQMessage` and extend to your needs.
|
||||
|
||||
#### *Customize your toolbar buttons?*
|
||||
````objective-c
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
// This button will call the `didPressAccessoryButton:` selector on your JSQMessagesViewController subclass
|
||||
self.inputToolbar.contentView.leftBarButtonItem = /* custom button or nil to remove */
|
||||
|
||||
// This button will call the `didPressSendButton:` selector on your JSQMessagesViewController subclass
|
||||
self.inputToolbar.contentView.rightBarButtonItem = /* custom button or nil to remove */
|
||||
|
||||
// Swap buttons, move send button to the LEFT side and the attachment button to the RIGHT
|
||||
// For RTL language support
|
||||
self.inputToolbar.contentView.leftBarButtonItem = [JSQMessagesToolbarButtonFactory defaultSendButtonItem];
|
||||
self.inputToolbar.contentView.rightBarButtonItem = [JSQMessagesToolbarButtonFactory defaultAccessoryButtonItem];
|
||||
|
||||
// The library will call the correct selector for each button, based on this value
|
||||
self.inputToolbar.sendButtonOnRight = NO;
|
||||
}
|
||||
````
|
||||
56
Documentation/getting_started.md
Normal file
56
Documentation/getting_started.md
Normal file
@ -0,0 +1,56 @@
|
||||
# Getting Started
|
||||
|
||||
*Getting started guide for JSQMessagesViewController*
|
||||
|
||||
-----------------------------
|
||||
|
||||
## For versions 6.x and 7.x
|
||||
|
||||
````objective-c
|
||||
#import <JSQMessagesViewController/JSQMessages.h> // import all the things
|
||||
````
|
||||
|
||||
* **Tutorials and blogs**
|
||||
* Read the [blog post](http://www.jessesquires.com/introducing-jsqmessagesvc-6-0/) about the 6.0 release!
|
||||
* Ray Wenderlich has a [great tutorial](http://www.raywenderlich.com/122148/firebase-tutorial-real-time-chat), written by [David East](https://twitter.com/_davideast). (For 7.x releases)
|
||||
|
||||
* **Demo Project**
|
||||
* There's a sweet demo project: `JSQMessages.xcworkspace`.
|
||||
* Run `pod install` first.
|
||||
* Swift Example can be found in the SwiftExample folder just open the `SwiftExample.xcworkspace`.
|
||||
* Run `pod install` first.
|
||||
* [Firebase](https://www.firebase.com) also has a sweet [demo project](https://github.com/firebase/ios-swift-chat-example), and it's in Swift!
|
||||
|
||||
|
||||
* **Message Model**
|
||||
* Your message model objects should conform to the `JSQMessageData` protocol.
|
||||
* However, you may use the provided `JSQMessage` class.
|
||||
|
||||
* **Media Attachment Model**
|
||||
* Your media attachment model objects should conform to the `JSQMessageMediaData` protocol.
|
||||
* However, you may use the provided classes: `JSQPhotoMediaItem`, `JSQLocationMediaItem`, `JSQVideoMediaItem`.
|
||||
* Creating your own custom media items is easy! Simply follow the pattern used by the built-in media types.
|
||||
* Also see `JSQMessagesMediaViewBubbleImageMasker` for masking your custom media views as message bubbles.
|
||||
|
||||
* **Avatar Model**
|
||||
* Your avatar model objects should conform to the `JSQMessageAvatarImageDataSource` protocol.
|
||||
* However, you may use the provided `JSQMessagesAvatarImage` class.
|
||||
* Also see `JSQMessagesAvatarImageFactory` for easily generating custom avatars.
|
||||
|
||||
* **Message Bubble Model**
|
||||
* Your message bubble model objects should conform to the `JSQMessageBubbleImageDataSource` protocol.
|
||||
* However, you may use the provided `JSQMessagesBubbleImage` class.
|
||||
* Also see `JSQMessagesBubbleImageFactory` and `UIImage+JSQMessages.h` for easily generating custom bubbles.
|
||||
|
||||
* **View Controller**
|
||||
* Subclass `JSQMessagesViewController`.
|
||||
* Implement the required methods in the `JSQMessagesCollectionViewDataSource` protocol.
|
||||
* Implement the required methods in the `JSQMessagesCollectionViewDelegateFlowLayout` protocol.
|
||||
* Set your `senderId` and `senderDisplayName`. These properties correspond to the methods found in `JSQMessageData` and determine which messages are incoming or outgoing.
|
||||
|
||||
* **Customizing**
|
||||
* The demo project is well-commented. Please use this as a guide.
|
||||
|
||||
## Previous versions
|
||||
|
||||
Sorry! Guides are not available for older versions of the library.
|
||||
17
Documentation/migration.md
Normal file
17
Documentation/migration.md
Normal file
@ -0,0 +1,17 @@
|
||||
# Migration Guide
|
||||
|
||||
*Migrating between major versions of JSQMessagesViewController?*
|
||||
|
||||
-----------------------------
|
||||
|
||||
## From `6.x` to `7.x`
|
||||
|
||||
See the [7.0 release notes](https://github.com/jessesquires/JSQMessagesViewController/releases/tag/7.0.0) for details about API changes.
|
||||
|
||||
## From `5.x` to `6.x`
|
||||
|
||||
See the [6.0 release notes](https://github.com/jessesquires/JSQMessagesViewController/releases/tag/6.0.0) for details about API changes.
|
||||
|
||||
## Previous versions
|
||||
|
||||
Unfortunately, versions prior to `5.0` outdate this document, and guides are not available.
|
||||
@ -7,8 +7,10 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
36CF33BD29CF36EB06D0CCFD /* libPods-JSQMessagesTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 782026E9E518622532ED474D /* libPods-JSQMessagesTests.a */; };
|
||||
77CC17A895E6E12BC9CB549A /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 97E6750B77E8A7042BA0754B /* libPods.a */; };
|
||||
54271E3B1C90469100294290 /* jsq_messages_sample.m4a in Resources */ = {isa = PBXBuildFile; fileRef = 54271E3A1C90469100294290 /* jsq_messages_sample.m4a */; };
|
||||
54271E3E1C905B9200294290 /* JSQAudioMediaItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 54271E3D1C905B9200294290 /* JSQAudioMediaItem.m */; };
|
||||
54271E401C905D1600294290 /* JSQAudioMediaItemTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 54271E3F1C905D1600294290 /* JSQAudioMediaItemTests.m */; };
|
||||
544A32211CB2EE380084BFC0 /* JSQAudioMediaViewAttributes.m in Sources */ = {isa = PBXBuildFile; fileRef = 544A32201CB2EE380084BFC0 /* JSQAudioMediaViewAttributes.m */; };
|
||||
88078A9D19D8FEB5005B4595 /* JSQMessagesMediaPlaceholderView.m in Sources */ = {isa = PBXBuildFile; fileRef = 88078A9C19D8FEB5005B4595 /* JSQMessagesMediaPlaceholderView.m */; };
|
||||
88324C3419F6301C00BC732D /* JSQMessagesMediaViewBubbleImageMaskerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 88324C3319F6301C00BC732D /* JSQMessagesMediaViewBubbleImageMaskerTests.m */; };
|
||||
883C11781A09FB100092A16D /* JSQMessagesCellTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 883C11771A09FB100092A16D /* JSQMessagesCellTextView.m */; };
|
||||
@ -30,6 +32,8 @@
|
||||
886C33FD19F4371E006B4997 /* JSQVideoMediaItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 886C33FC19F4371E006B4997 /* JSQVideoMediaItem.m */; };
|
||||
886C33FF19F45E30006B4997 /* JSQMessagesViewController.podspec in Resources */ = {isa = PBXBuildFile; fileRef = 886C33FE19F45E30006B4997 /* JSQMessagesViewController.podspec */; };
|
||||
886FFD2E19E9A65D00EB8485 /* UIDevice+JSQMessages.m in Sources */ = {isa = PBXBuildFile; fileRef = 886FFD2D19E9A65D00EB8485 /* UIDevice+JSQMessages.m */; };
|
||||
8873B60C1AB7B244006DF9AC /* NSBundle+JSQMessages.m in Sources */ = {isa = PBXBuildFile; fileRef = 8873B60B1AB7B244006DF9AC /* NSBundle+JSQMessages.m */; };
|
||||
8873B60E1AB7B63E006DF9AC /* JSQMessagesNSBundleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8873B60D1AB7B63E006DF9AC /* JSQMessagesNSBundleTests.m */; };
|
||||
8885734A19DE540400E89D20 /* DemoSettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8885734919DE540400E89D20 /* DemoSettingsViewController.m */; };
|
||||
8885734D19DE55D000E89D20 /* NSUserDefaults+DemoSettings.m in Sources */ = {isa = PBXBuildFile; fileRef = 8885734C19DE55D000E89D20 /* NSUserDefaults+DemoSettings.m */; };
|
||||
88A25F3719D8DF2500924534 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 88A25F2D19D8DF2500924534 /* AppDelegate.m */; };
|
||||
@ -99,12 +103,14 @@
|
||||
88A2601919D8E18400924534 /* JSQMessagesTypingIndicatorFooterViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 88A2600019D8E18400924534 /* JSQMessagesTypingIndicatorFooterViewTests.m */; };
|
||||
88A2601B19D8E45600924534 /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 88A2601A19D8E45600924534 /* Info.plist */; };
|
||||
88A901B619F618B100F99777 /* JSQMediaItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 88A901B519F618B100F99777 /* JSQMediaItem.m */; };
|
||||
88B5C41F1B7C422900EC79D4 /* JSQMessagesBubblesSizeCalculator.m in Sources */ = {isa = PBXBuildFile; fileRef = 88B5C41E1B7C422900EC79D4 /* JSQMessagesBubblesSizeCalculator.m */; };
|
||||
88C00A4E1A44D4C600B004B3 /* JSQLocationMediaItemTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 88C00A4D1A44D4C600B004B3 /* JSQLocationMediaItemTests.m */; };
|
||||
88C00A501A44D4D800B004B3 /* JSQPhotoMediaItemTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 88C00A4F1A44D4D800B004B3 /* JSQPhotoMediaItemTests.m */; };
|
||||
88C00A521A44D4E500B004B3 /* JSQVideoMediaItemTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 88C00A511A44D4E500B004B3 /* JSQVideoMediaItemTests.m */; };
|
||||
88C4583019F5F7A0008FD427 /* JSQMessagesMediaViewBubbleImageMasker.m in Sources */ = {isa = PBXBuildFile; fileRef = 88C4582F19F5F7A0008FD427 /* JSQMessagesMediaViewBubbleImageMasker.m */; };
|
||||
88E4D7131A0DBD6B000CC061 /* JSQMessages.strings in Resources */ = {isa = PBXBuildFile; fileRef = 8841B88719F4988800EA16B6 /* JSQMessages.strings */; };
|
||||
94A4FA20C2FBD0D62614D5A8 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 97E6750B77E8A7042BA0754B /* libPods.a */; };
|
||||
BF6DA766BD3B8893A67FB2BD /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C3F882AA48978C11F64DC2DF /* libPods.a */; };
|
||||
C8994EF7DDBE74B9E3F1A16C /* libPods-JSQMessagesTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E23238C8D8DF79244DEE1787 /* libPods-JSQMessagesTests.a */; };
|
||||
E8877F1947CE8050ECCD9539 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C3F882AA48978C11F64DC2DF /* libPods.a */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@ -118,30 +124,20 @@
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
0844AD596023C7658D39E241 /* Pods-JSQMessagesTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JSQMessagesTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-JSQMessagesTests/Pods-JSQMessagesTests.release.xcconfig"; sourceTree = "<group>"; };
|
||||
223FBACE0F24ADEF8B7F3F24 /* Pods-JSQMessagesTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JSQMessagesTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-JSQMessagesTests/Pods-JSQMessagesTests.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
27B7FD1B722B36B26CB3460B /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = "<group>"; };
|
||||
782026E9E518622532ED474D /* libPods-JSQMessagesTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-JSQMessagesTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
0B4D05069814EB50FB0F4229 /* Pods-JSQMessagesTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JSQMessagesTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-JSQMessagesTests/Pods-JSQMessagesTests.release.xcconfig"; sourceTree = "<group>"; };
|
||||
1D4D3B82D90888BCEAF890D3 /* Pods-JSQMessagesTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JSQMessagesTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-JSQMessagesTests/Pods-JSQMessagesTests.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
3BA6237809BE0D008CFE3697 /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
54271E3A1C90469100294290 /* jsq_messages_sample.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = jsq_messages_sample.m4a; sourceTree = "<group>"; };
|
||||
54271E3C1C905B9200294290 /* JSQAudioMediaItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSQAudioMediaItem.h; sourceTree = "<group>"; };
|
||||
54271E3D1C905B9200294290 /* JSQAudioMediaItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSQAudioMediaItem.m; sourceTree = "<group>"; };
|
||||
54271E3F1C905D1600294290 /* JSQAudioMediaItemTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSQAudioMediaItemTests.m; sourceTree = "<group>"; };
|
||||
544A321F1CB2EE380084BFC0 /* JSQAudioMediaViewAttributes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSQAudioMediaViewAttributes.h; sourceTree = "<group>"; };
|
||||
544A32201CB2EE380084BFC0 /* JSQAudioMediaViewAttributes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSQAudioMediaViewAttributes.m; sourceTree = "<group>"; };
|
||||
88078A9B19D8FEB5005B4595 /* JSQMessagesMediaPlaceholderView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSQMessagesMediaPlaceholderView.h; sourceTree = "<group>"; };
|
||||
88078A9C19D8FEB5005B4595 /* JSQMessagesMediaPlaceholderView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSQMessagesMediaPlaceholderView.m; sourceTree = "<group>"; };
|
||||
88324C3319F6301C00BC732D /* JSQMessagesMediaViewBubbleImageMaskerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSQMessagesMediaViewBubbleImageMaskerTests.m; sourceTree = "<group>"; };
|
||||
883C11761A09FB100092A16D /* JSQMessagesCellTextView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSQMessagesCellTextView.h; sourceTree = "<group>"; };
|
||||
883C11771A09FB100092A16D /* JSQMessagesCellTextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSQMessagesCellTextView.m; sourceTree = "<group>"; };
|
||||
8841B88619F4988800EA16B6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/JSQMessages.strings; sourceTree = "<group>"; };
|
||||
8841B88819F4988900EA16B6 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/JSQMessages.strings; sourceTree = "<group>"; };
|
||||
8841B88919F4988A00EA16B6 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/JSQMessages.strings; sourceTree = "<group>"; };
|
||||
8841B88A19F4988B00EA16B6 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/JSQMessages.strings; sourceTree = "<group>"; };
|
||||
8841B88B19F4988C00EA16B6 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/JSQMessages.strings"; sourceTree = "<group>"; };
|
||||
8841B88C19F4988F00EA16B6 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/JSQMessages.strings"; sourceTree = "<group>"; };
|
||||
8841B88D19F4989000EA16B6 /* ro */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ro; path = ro.lproj/JSQMessages.strings; sourceTree = "<group>"; };
|
||||
8841B88E19F4989100EA16B6 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/JSQMessages.strings; sourceTree = "<group>"; };
|
||||
8841B88F19F4989200EA16B6 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/JSQMessages.strings; sourceTree = "<group>"; };
|
||||
8841B89019F4989200EA16B6 /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt; path = pt.lproj/JSQMessages.strings; sourceTree = "<group>"; };
|
||||
8841B89119F4989300EA16B6 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/JSQMessages.strings; sourceTree = "<group>"; };
|
||||
8841B89219F4989400EA16B6 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/JSQMessages.strings; sourceTree = "<group>"; };
|
||||
8841B89319F4989500EA16B6 /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/JSQMessages.strings; sourceTree = "<group>"; };
|
||||
8841B89419F4989500EA16B6 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/JSQMessages.strings; sourceTree = "<group>"; };
|
||||
8841B89519F4989600EA16B6 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/JSQMessages.strings; sourceTree = "<group>"; };
|
||||
88445B3019E0AE3F0014F889 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
|
||||
88445B3219E0AE450014F889 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
|
||||
88445B3419E0AE4A0014F889 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
|
||||
@ -157,6 +153,9 @@
|
||||
886C33FE19F45E30006B4997 /* JSQMessagesViewController.podspec */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = JSQMessagesViewController.podspec; sourceTree = "<group>"; };
|
||||
886FFD2C19E9A65D00EB8485 /* UIDevice+JSQMessages.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIDevice+JSQMessages.h"; sourceTree = "<group>"; };
|
||||
886FFD2D19E9A65D00EB8485 /* UIDevice+JSQMessages.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIDevice+JSQMessages.m"; sourceTree = "<group>"; };
|
||||
8873B60A1AB7B244006DF9AC /* NSBundle+JSQMessages.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSBundle+JSQMessages.h"; sourceTree = "<group>"; };
|
||||
8873B60B1AB7B244006DF9AC /* NSBundle+JSQMessages.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSBundle+JSQMessages.m"; sourceTree = "<group>"; };
|
||||
8873B60D1AB7B63E006DF9AC /* JSQMessagesNSBundleTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSQMessagesNSBundleTests.m; sourceTree = "<group>"; };
|
||||
8885734819DE540400E89D20 /* DemoSettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoSettingsViewController.h; sourceTree = "<group>"; };
|
||||
8885734919DE540400E89D20 /* DemoSettingsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoSettingsViewController.m; sourceTree = "<group>"; };
|
||||
8885734B19DE55D000E89D20 /* NSUserDefaults+DemoSettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSUserDefaults+DemoSettings.h"; sourceTree = "<group>"; };
|
||||
@ -271,13 +270,17 @@
|
||||
88A2601A19D8E45600924534 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
88A901B419F618B100F99777 /* JSQMediaItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSQMediaItem.h; sourceTree = "<group>"; };
|
||||
88A901B519F618B100F99777 /* JSQMediaItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSQMediaItem.m; sourceTree = "<group>"; };
|
||||
88B5C41D1B7C422900EC79D4 /* JSQMessagesBubblesSizeCalculator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSQMessagesBubblesSizeCalculator.h; sourceTree = "<group>"; };
|
||||
88B5C41E1B7C422900EC79D4 /* JSQMessagesBubblesSizeCalculator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSQMessagesBubblesSizeCalculator.m; sourceTree = "<group>"; };
|
||||
88B5C4201B7C424700EC79D4 /* JSQMessagesBubbleSizeCalculating.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSQMessagesBubbleSizeCalculating.h; sourceTree = "<group>"; };
|
||||
88C00A4D1A44D4C600B004B3 /* JSQLocationMediaItemTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSQLocationMediaItemTests.m; sourceTree = "<group>"; };
|
||||
88C00A4F1A44D4D800B004B3 /* JSQPhotoMediaItemTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSQPhotoMediaItemTests.m; sourceTree = "<group>"; };
|
||||
88C00A511A44D4E500B004B3 /* JSQVideoMediaItemTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSQVideoMediaItemTests.m; sourceTree = "<group>"; };
|
||||
88C4582E19F5F7A0008FD427 /* JSQMessagesMediaViewBubbleImageMasker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSQMessagesMediaViewBubbleImageMasker.h; sourceTree = "<group>"; };
|
||||
88C4582F19F5F7A0008FD427 /* JSQMessagesMediaViewBubbleImageMasker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSQMessagesMediaViewBubbleImageMasker.m; sourceTree = "<group>"; };
|
||||
97E6750B77E8A7042BA0754B /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
FC5C727E4CCDA2B95A7BA30C /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
AD6E75315517DE46FE495B65 /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = "<group>"; };
|
||||
C3F882AA48978C11F64DC2DF /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
E23238C8D8DF79244DEE1787 /* libPods-JSQMessagesTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-JSQMessagesTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@ -291,7 +294,7 @@
|
||||
88445B3519E0AE4A0014F889 /* CoreGraphics.framework in Frameworks */,
|
||||
88445B3319E0AE450014F889 /* Foundation.framework in Frameworks */,
|
||||
88445B3119E0AE3F0014F889 /* UIKit.framework in Frameworks */,
|
||||
77CC17A895E6E12BC9CB549A /* libPods.a in Frameworks */,
|
||||
E8877F1947CE8050ECCD9539 /* libPods.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -306,47 +309,39 @@
|
||||
88445B3B19E0C0B10014F889 /* XCTest.framework in Frameworks */,
|
||||
88445B3919E0C0AC0014F889 /* Foundation.framework in Frameworks */,
|
||||
88445B3819E0C0A70014F889 /* UIKit.framework in Frameworks */,
|
||||
94A4FA20C2FBD0D62614D5A8 /* libPods.a in Frameworks */,
|
||||
36CF33BD29CF36EB06D0CCFD /* libPods-JSQMessagesTests.a in Frameworks */,
|
||||
C8994EF7DDBE74B9E3F1A16C /* libPods-JSQMessagesTests.a in Frameworks */,
|
||||
BF6DA766BD3B8893A67FB2BD /* libPods.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
2BBEF3CD91C31A49E5FF9E3C /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FC5C727E4CCDA2B95A7BA30C /* Pods.debug.xcconfig */,
|
||||
27B7FD1B722B36B26CB3460B /* Pods.release.xcconfig */,
|
||||
223FBACE0F24ADEF8B7F3F24 /* Pods-JSQMessagesTests.debug.xcconfig */,
|
||||
0844AD596023C7658D39E241 /* Pods-JSQMessagesTests.release.xcconfig */,
|
||||
);
|
||||
name = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
636A8663AEEE5C37B65C515D /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
88445B4319E1B5110014F889 /* MapKit.framework */,
|
||||
88445B4119E1B50B0014F889 /* CoreLocation.framework */,
|
||||
88445B3419E0AE4A0014F889 /* CoreGraphics.framework */,
|
||||
88445B4119E1B50B0014F889 /* CoreLocation.framework */,
|
||||
88445B3219E0AE450014F889 /* Foundation.framework */,
|
||||
782026E9E518622532ED474D /* libPods-JSQMessagesTests.a */,
|
||||
97E6750B77E8A7042BA0754B /* libPods.a */,
|
||||
88445B4319E1B5110014F889 /* MapKit.framework */,
|
||||
88445B3619E0AE5C0014F889 /* QuartzCore.framework */,
|
||||
88445B3019E0AE3F0014F889 /* UIKit.framework */,
|
||||
88445B3A19E0C0B10014F889 /* XCTest.framework */,
|
||||
C3F882AA48978C11F64DC2DF /* libPods.a */,
|
||||
E23238C8D8DF79244DEE1787 /* libPods-JSQMessagesTests.a */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8841B88219F4983C00EA16B6 /* Strings */ = {
|
||||
842892590A65F355D8619D29 /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8841B88719F4988800EA16B6 /* JSQMessages.strings */,
|
||||
3BA6237809BE0D008CFE3697 /* Pods.debug.xcconfig */,
|
||||
AD6E75315517DE46FE495B65 /* Pods.release.xcconfig */,
|
||||
1D4D3B82D90888BCEAF890D3 /* Pods-JSQMessagesTests.debug.xcconfig */,
|
||||
0B4D05069814EB50FB0F4229 /* Pods-JSQMessagesTests.release.xcconfig */,
|
||||
);
|
||||
path = Strings;
|
||||
name = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
88A25EF919D8DEC400924534 = {
|
||||
@ -357,8 +352,8 @@
|
||||
636A8663AEEE5C37B65C515D /* Frameworks */,
|
||||
88A25F2B19D8DF2500924534 /* JSQMessagesDemo */,
|
||||
88A25F1E19D8DEC500924534 /* JSQMessagesTests */,
|
||||
2BBEF3CD91C31A49E5FF9E3C /* Pods */,
|
||||
88A25F0319D8DEC400924534 /* Products */,
|
||||
842892590A65F355D8619D29 /* Pods */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
@ -396,6 +391,7 @@
|
||||
88A25F2B19D8DF2500924534 /* JSQMessagesDemo */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
54271E3A1C90469100294290 /* jsq_messages_sample.m4a */,
|
||||
88A25F2C19D8DF2500924534 /* AppDelegate.h */,
|
||||
88A25F2D19D8DF2500924534 /* AppDelegate.m */,
|
||||
88A25FD919D8E0C400924534 /* DemoMessagesViewController.h */,
|
||||
@ -434,7 +430,6 @@
|
||||
88A25F3F19D8E01A00924534 /* Assets */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8841B88219F4983C00EA16B6 /* Strings */,
|
||||
8861666C19F492B70025B958 /* JSQMessagesAssets.bundle */,
|
||||
);
|
||||
path = Assets;
|
||||
@ -445,6 +440,8 @@
|
||||
children = (
|
||||
88A25F5519D8E01A00924534 /* JSQSystemSoundPlayer+JSQMessages.h */,
|
||||
88A25F5619D8E01A00924534 /* JSQSystemSoundPlayer+JSQMessages.m */,
|
||||
8873B60A1AB7B244006DF9AC /* NSBundle+JSQMessages.h */,
|
||||
8873B60B1AB7B244006DF9AC /* NSBundle+JSQMessages.m */,
|
||||
88A25F5719D8E01A00924534 /* NSString+JSQMessages.h */,
|
||||
88A25F5819D8E01A00924534 /* NSString+JSQMessages.m */,
|
||||
88A25F5919D8E01A00924534 /* UIColor+JSQMessages.h */,
|
||||
@ -491,6 +488,11 @@
|
||||
88A25F6F19D8E01A00924534 /* Layout */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
544A321F1CB2EE380084BFC0 /* JSQAudioMediaViewAttributes.h */,
|
||||
544A32201CB2EE380084BFC0 /* JSQAudioMediaViewAttributes.m */,
|
||||
88B5C4201B7C424700EC79D4 /* JSQMessagesBubbleSizeCalculating.h */,
|
||||
88B5C41D1B7C422900EC79D4 /* JSQMessagesBubblesSizeCalculator.h */,
|
||||
88B5C41E1B7C422900EC79D4 /* JSQMessagesBubblesSizeCalculator.m */,
|
||||
88A25F7019D8E01A00924534 /* JSQMessagesCollectionViewFlowLayout.h */,
|
||||
88A25F7119D8E01A00924534 /* JSQMessagesCollectionViewFlowLayout.m */,
|
||||
88A25F7219D8E01A00924534 /* JSQMessagesCollectionViewFlowLayoutInvalidationContext.h */,
|
||||
@ -504,6 +506,8 @@
|
||||
88A25F7619D8E01A00924534 /* Model */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
54271E3C1C905B9200294290 /* JSQAudioMediaItem.h */,
|
||||
54271E3D1C905B9200294290 /* JSQAudioMediaItem.m */,
|
||||
88445B3E19E1B4470014F889 /* JSQLocationMediaItem.h */,
|
||||
88445B3F19E1B4470014F889 /* JSQLocationMediaItem.m */,
|
||||
88A901B419F618B100F99777 /* JSQMediaItem.h */,
|
||||
@ -567,6 +571,7 @@
|
||||
88A25FE219D8E18400924534 /* CategoryTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8873B60D1AB7B63E006DF9AC /* JSQMessagesNSBundleTests.m */,
|
||||
88A25FE319D8E18400924534 /* JSQMessagesNSStringTests.m */,
|
||||
88A25FE419D8E18400924534 /* JSQMessagesUIColorTests.m */,
|
||||
88A25FE519D8E18400924534 /* JSQMessagesUIImageTests.m */,
|
||||
@ -608,6 +613,7 @@
|
||||
88A25FF219D8E18400924534 /* ModelTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
54271E3F1C905D1600294290 /* JSQAudioMediaItemTests.m */,
|
||||
88C00A4D1A44D4C600B004B3 /* JSQLocationMediaItemTests.m */,
|
||||
88A25FF319D8E18400924534 /* JSQMessageMediaTests.m */,
|
||||
88A25FF419D8E18400924534 /* JSQMessagesAvatarImageTests.m */,
|
||||
@ -641,11 +647,12 @@
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 88A25F2519D8DEC500924534 /* Build configuration list for PBXNativeTarget "JSQMessages" */;
|
||||
buildPhases = (
|
||||
3AF3068570D5C74873D84E30 /* Check Pods Manifest.lock */,
|
||||
90945CFDCD14BCFE538D66C9 /* Check Pods Manifest.lock */,
|
||||
88A25EFE19D8DEC400924534 /* Sources */,
|
||||
88A25EFF19D8DEC400924534 /* Frameworks */,
|
||||
88A25F0019D8DEC400924534 /* Resources */,
|
||||
4CCCD7A86E86CB86C48E303C /* Copy Pods Resources */,
|
||||
C8D68BB894C4DD38C5615198 /* Embed Pods Frameworks */,
|
||||
908FA537E0B2FD4A94A60411 /* Copy Pods Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@ -660,11 +667,12 @@
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 88A25F2819D8DEC500924534 /* Build configuration list for PBXNativeTarget "JSQMessagesTests" */;
|
||||
buildPhases = (
|
||||
F4044DAC71D69462CA8CAE98 /* Check Pods Manifest.lock */,
|
||||
95BD949B70ECF8FE9CDDB707 /* Check Pods Manifest.lock */,
|
||||
88A25F1719D8DEC400924534 /* Sources */,
|
||||
88A25F1819D8DEC400924534 /* Frameworks */,
|
||||
88A25F1919D8DEC400924534 /* Resources */,
|
||||
F6B484334A138916FC111868 /* Copy Pods Resources */,
|
||||
78F522A7ECBD8D8774672696 /* Embed Pods Frameworks */,
|
||||
B85CD3AA4EFCF4765BE7FAD9 /* Copy Pods Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@ -682,7 +690,7 @@
|
||||
88A25EFA19D8DEC400924534 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0600;
|
||||
LastUpgradeCheck = 0700;
|
||||
ORGANIZATIONNAME = "Hexed Bits";
|
||||
TargetAttributes = {
|
||||
88A25F0119D8DEC400924534 = {
|
||||
@ -731,13 +739,13 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
88E4D7131A0DBD6B000CC061 /* JSQMessages.strings in Resources */,
|
||||
886C33FF19F45E30006B4997 /* JSQMessagesViewController.podspec in Resources */,
|
||||
8861666D19F492B70025B958 /* JSQMessagesAssets.bundle in Resources */,
|
||||
88A25FCF19D8E01A00924534 /* JSQMessagesCollectionViewCellOutgoing.xib in Resources */,
|
||||
88A25FCD19D8E01A00924534 /* JSQMessagesCollectionViewCellIncoming.xib in Resources */,
|
||||
88A25FD619D8E01A00924534 /* JSQMessagesToolbarContentView.xib in Resources */,
|
||||
88A25F3A19D8DF2500924534 /* Images.xcassets in Resources */,
|
||||
54271E3B1C90469100294290 /* jsq_messages_sample.m4a in Resources */,
|
||||
88A25FBC19D8E01A00924534 /* JSQMessagesViewController.xib in Resources */,
|
||||
88A25FD819D8E01A00924534 /* JSQMessagesTypingIndicatorFooterView.xib in Resources */,
|
||||
88A25F3919D8DF2500924534 /* Main.storyboard in Resources */,
|
||||
@ -756,22 +764,22 @@
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
3AF3068570D5C74873D84E30 /* Check Pods Manifest.lock */ = {
|
||||
78F522A7ECBD8D8774672696 /* Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Check Pods Manifest.lock";
|
||||
name = "Embed Pods Frameworks";
|
||||
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";
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
4CCCD7A86E86CB86C48E303C /* Copy Pods Resources */ = {
|
||||
908FA537E0B2FD4A94A60411 /* Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
@ -786,7 +794,7 @@
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
F4044DAC71D69462CA8CAE98 /* Check Pods Manifest.lock */ = {
|
||||
90945CFDCD14BCFE538D66C9 /* Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
@ -801,7 +809,22 @@
|
||||
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;
|
||||
};
|
||||
F6B484334A138916FC111868 /* Copy Pods Resources */ = {
|
||||
95BD949B70ECF8FE9CDDB707 /* 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;
|
||||
};
|
||||
B85CD3AA4EFCF4765BE7FAD9 /* Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
@ -816,6 +839,21 @@
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-JSQMessagesTests/Pods-JSQMessagesTests-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
C8D68BB894C4DD38C5615198 /* Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Embed Pods Frameworks";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
@ -829,6 +867,7 @@
|
||||
88A25FCB19D8E01A00924534 /* JSQMessagesCollectionViewCell.m in Sources */,
|
||||
88A25FBB19D8E01A00924534 /* JSQMessagesViewController.m in Sources */,
|
||||
8885734D19DE55D000E89D20 /* NSUserDefaults+DemoSettings.m in Sources */,
|
||||
544A32211CB2EE380084BFC0 /* JSQAudioMediaViewAttributes.m in Sources */,
|
||||
883C11781A09FB100092A16D /* JSQMessagesCellTextView.m in Sources */,
|
||||
88A25FB919D8E01A00924534 /* UIView+JSQMessages.m in Sources */,
|
||||
88A25FCA19D8E01A00924534 /* JSQMessagesCollectionView.m in Sources */,
|
||||
@ -849,7 +888,9 @@
|
||||
88A25FE019D8E0C400924534 /* DemoModelData.m in Sources */,
|
||||
88A25F3C19D8DF2500924534 /* main.m in Sources */,
|
||||
88A25F3719D8DF2500924534 /* AppDelegate.m in Sources */,
|
||||
54271E3E1C905B9200294290 /* JSQAudioMediaItem.m in Sources */,
|
||||
886FFD2E19E9A65D00EB8485 /* UIDevice+JSQMessages.m in Sources */,
|
||||
88B5C41F1B7C422900EC79D4 /* JSQMessagesBubblesSizeCalculator.m in Sources */,
|
||||
88A25FB619D8E01A00924534 /* NSString+JSQMessages.m in Sources */,
|
||||
88A901B619F618B100F99777 /* JSQMediaItem.m in Sources */,
|
||||
88A25FCC19D8E01A00924534 /* JSQMessagesCollectionViewCellIncoming.m in Sources */,
|
||||
@ -863,6 +904,7 @@
|
||||
88A25FE119D8E0C400924534 /* TableViewController.m in Sources */,
|
||||
88A25FBD19D8E01A00924534 /* JSQMessagesAvatarImageFactory.m in Sources */,
|
||||
88A25FB519D8E01A00924534 /* JSQSystemSoundPlayer+JSQMessages.m in Sources */,
|
||||
8873B60C1AB7B244006DF9AC /* NSBundle+JSQMessages.m in Sources */,
|
||||
88A25FD019D8E01A00924534 /* JSQMessagesComposerTextView.m in Sources */,
|
||||
88A25FC319D8E01A00924534 /* JSQMessagesCollectionViewLayoutAttributes.m in Sources */,
|
||||
);
|
||||
@ -877,6 +919,7 @@
|
||||
88C00A521A44D4E500B004B3 /* JSQVideoMediaItemTests.m in Sources */,
|
||||
88A2601519D8E18400924534 /* JSQMessagesInputToolbarTests.m in Sources */,
|
||||
88A2601719D8E18400924534 /* JSQMessagesLoadEarlierHeaderViewTests.m in Sources */,
|
||||
54271E401C905D1600294290 /* JSQAudioMediaItemTests.m in Sources */,
|
||||
88A2601219D8E18400924534 /* JSQMessagesCollectionViewCellTests.m in Sources */,
|
||||
88A2601619D8E18400924534 /* JSQMessagesLabelTests.m in Sources */,
|
||||
88A2600B19D8E18400924534 /* JSQMessagesCollectionViewFlowLayoutTests.m in Sources */,
|
||||
@ -896,6 +939,7 @@
|
||||
88A2601919D8E18400924534 /* JSQMessagesTypingIndicatorFooterViewTests.m in Sources */,
|
||||
88A2600319D8E18400924534 /* JSQMessagesUIImageTests.m in Sources */,
|
||||
88C00A4E1A44D4C600B004B3 /* JSQLocationMediaItemTests.m in Sources */,
|
||||
8873B60E1AB7B63E006DF9AC /* JSQMessagesNSBundleTests.m in Sources */,
|
||||
88A2600C19D8E18400924534 /* JSQMessagesCollectionViewLayoutAttributesTests.m in Sources */,
|
||||
88A2600619D8E18400924534 /* JSQMessagesViewControllerTests.m in Sources */,
|
||||
88A2600519D8E18400924534 /* JSQMessagesKeyboardControllerTests.m in Sources */,
|
||||
@ -914,28 +958,6 @@
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
8841B88719F4988800EA16B6 /* JSQMessages.strings */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
8841B88619F4988800EA16B6 /* Base */,
|
||||
8841B88819F4988900EA16B6 /* en */,
|
||||
8841B88919F4988A00EA16B6 /* es */,
|
||||
8841B88A19F4988B00EA16B6 /* de */,
|
||||
8841B88B19F4988C00EA16B6 /* zh-Hans */,
|
||||
8841B88C19F4988F00EA16B6 /* zh-Hant */,
|
||||
8841B88D19F4989000EA16B6 /* ro */,
|
||||
8841B88E19F4989100EA16B6 /* pl */,
|
||||
8841B88F19F4989200EA16B6 /* ru */,
|
||||
8841B89019F4989200EA16B6 /* pt */,
|
||||
8841B89119F4989300EA16B6 /* fr */,
|
||||
8841B89219F4989400EA16B6 /* it */,
|
||||
8841B89319F4989500EA16B6 /* he */,
|
||||
8841B89419F4989500EA16B6 /* nl */,
|
||||
8841B89519F4989600EA16B6 /* tr */,
|
||||
);
|
||||
name = JSQMessages.strings;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
88A25F3019D8DF2500924534 /* Main.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
@ -967,6 +989,7 @@
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
@ -1027,7 +1050,7 @@
|
||||
};
|
||||
88A25F2619D8DEC500924534 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = FC5C727E4CCDA2B95A7BA30C /* Pods.debug.xcconfig */;
|
||||
baseConfigurationReference = 3BA6237809BE0D008CFE3697 /* Pods.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
|
||||
@ -1038,6 +1061,7 @@
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
OTHER_LDFLAGS = "$(inherited)";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.hexedbits.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE = "";
|
||||
};
|
||||
@ -1045,7 +1069,7 @@
|
||||
};
|
||||
88A25F2719D8DEC500924534 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 27B7FD1B722B36B26CB3460B /* Pods.release.xcconfig */;
|
||||
baseConfigurationReference = AD6E75315517DE46FE495B65 /* Pods.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
|
||||
@ -1056,6 +1080,7 @@
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
OTHER_LDFLAGS = "$(inherited)";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.hexedbits.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE = "";
|
||||
};
|
||||
@ -1063,20 +1088,18 @@
|
||||
};
|
||||
88A25F2919D8DEC500924534 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 223FBACE0F24ADEF8B7F3F24 /* Pods-JSQMessagesTests.debug.xcconfig */;
|
||||
baseConfigurationReference = 1D4D3B82D90888BCEAF890D3 /* Pods-JSQMessagesTests.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(SDKROOT)/Developer/Library/Frameworks",
|
||||
"$(inherited)",
|
||||
);
|
||||
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
INFOPLIST_FILE = JSQMessagesTests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.hexedbits.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/JSQMessages.app/JSQMessages";
|
||||
};
|
||||
@ -1084,16 +1107,14 @@
|
||||
};
|
||||
88A25F2A19D8DEC500924534 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 0844AD596023C7658D39E241 /* Pods-JSQMessagesTests.release.xcconfig */;
|
||||
baseConfigurationReference = 0B4D05069814EB50FB0F4229 /* Pods-JSQMessagesTests.release.xcconfig */;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(SDKROOT)/Developer/Library/Frameworks",
|
||||
"$(inherited)",
|
||||
);
|
||||
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
|
||||
INFOPLIST_FILE = JSQMessagesTests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.hexedbits.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/JSQMessages.app/JSQMessages";
|
||||
};
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0600"
|
||||
LastUpgradeVersion = "0730"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@ -14,7 +14,7 @@
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "4F6B28C25B7B1259BD8C5B8B"
|
||||
BlueprintIdentifier = "D14DE867BBE115B80620BD22C0E35F79"
|
||||
BuildableName = "libPods.a"
|
||||
BlueprintName = "Pods"
|
||||
ReferencedContainer = "container:Pods/Pods.xcodeproj">
|
||||
@ -51,10 +51,10 @@
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
buildConfiguration = "Debug">
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
@ -76,17 +76,21 @@
|
||||
ReferencedContainer = "container:JSQMessages.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Debug"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable>
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "88A25F0119D8DEC400924534"
|
||||
@ -99,12 +103,13 @@
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Release"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable>
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "88A25F0119D8DEC400924534"
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
<?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">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="9060" systemVersion="15B42" 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"/>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9051"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Root View Controller-->
|
||||
@ -12,10 +12,11 @@
|
||||
<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"/>
|
||||
<animations/>
|
||||
<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"/>
|
||||
<rect key="frame" x="0.0" y="114" 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"/>
|
||||
@ -24,12 +25,15 @@
|
||||
<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"/>
|
||||
<animations/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="18"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<animations/>
|
||||
</tableViewCellContentView>
|
||||
<animations/>
|
||||
</tableViewCell>
|
||||
</prototypes>
|
||||
</tableView>
|
||||
@ -51,17 +55,21 @@
|
||||
<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"/>
|
||||
<animations/>
|
||||
<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">
|
||||
<rect key="frame" x="0.0" y="114" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Eii-ro-1yZ" id="qU9-o3-MWC">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="43"/>
|
||||
<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"/>
|
||||
<animations/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="49" id="A6s-Sr-u8U"/>
|
||||
<constraint firstAttribute="height" constant="31" id="z9i-uJ-MBq"/>
|
||||
@ -72,29 +80,34 @@
|
||||
</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"/>
|
||||
<animations/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<animations/>
|
||||
<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="centerY" secondItem="K7N-os-fuc" secondAttribute="centerY" id="j7N-59-vRk"/>
|
||||
<constraint firstAttribute="bottom" secondItem="bSS-CD-nfD" secondAttribute="bottom" constant="9" id="poL-wE-Eir"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<animations/>
|
||||
</tableViewCell>
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="bZI-G1-eYA">
|
||||
<rect key="frame" x="0.0" y="158" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="bZI-G1-eYA" id="1Ho-Zz-KN0">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="43"/>
|
||||
<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"/>
|
||||
<animations/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="31" id="gM4-dK-cl7"/>
|
||||
<constraint firstAttribute="width" constant="49" id="hvu-Yy-NTG"/>
|
||||
@ -105,29 +118,34 @@
|
||||
</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"/>
|
||||
<animations/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<animations/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="centerY" secondItem="hMq-Ee-EJK" secondAttribute="centerY" id="CcO-KD-31y"/>
|
||||
<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>
|
||||
<animations/>
|
||||
</tableViewCell>
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="2Th-gL-oAN">
|
||||
<rect key="frame" x="0.0" y="202" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="2Th-gL-oAN" id="OFq-Mz-mbl">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="43"/>
|
||||
<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"/>
|
||||
<animations/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="49" id="SaV-RG-DVL"/>
|
||||
<constraint firstAttribute="height" constant="31" id="dXb-pb-etA"/>
|
||||
@ -138,33 +156,38 @@
|
||||
</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"/>
|
||||
<animations/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<animations/>
|
||||
<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"/>
|
||||
<constraint firstAttribute="centerY" secondItem="baL-Tb-bTJ" secondAttribute="centerY" id="hx4-cn-W0h"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<animations/>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection headerTitle="Avatars" id="ns0-OO-PGu">
|
||||
<cells>
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="hYS-4f-iL6">
|
||||
<rect key="frame" x="0.0" y="289" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="hYS-4f-iL6" id="dk1-tc-gux">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="43"/>
|
||||
<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"/>
|
||||
<animations/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="49" id="1lf-6y-hgz"/>
|
||||
<constraint firstAttribute="height" constant="31" id="rdQ-ol-d20"/>
|
||||
@ -175,29 +198,34 @@
|
||||
</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"/>
|
||||
<animations/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<animations/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="centerY" secondItem="fLZ-NC-aPO" secondAttribute="centerY" id="17e-cC-bP4"/>
|
||||
<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>
|
||||
<animations/>
|
||||
</tableViewCell>
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="VF7-uo-6Pc">
|
||||
<rect key="frame" x="0.0" y="333" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="VF7-uo-6Pc" id="zad-JQ-TRI">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="43"/>
|
||||
<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"/>
|
||||
<animations/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="31" id="QXv-ty-g0B"/>
|
||||
<constraint firstAttribute="width" constant="49" id="rZ3-cO-f8t"/>
|
||||
@ -208,33 +236,38 @@
|
||||
</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"/>
|
||||
<animations/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<animations/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="LuM-mk-Zj6" secondAttribute="bottom" constant="6" id="1G7-8S-rYC"/>
|
||||
<constraint firstAttribute="centerY" secondItem="LuM-mk-Zj6" secondAttribute="centerY" id="2oC-JM-LVI"/>
|
||||
<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>
|
||||
<animations/>
|
||||
</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">
|
||||
<rect key="frame" x="0.0" y="420" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="q07-lZ-YdI" id="btE-Mk-fSE">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="43"/>
|
||||
<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"/>
|
||||
<animations/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="31" id="5bI-xQ-njj"/>
|
||||
<constraint firstAttribute="width" constant="49" id="PG2-X2-FdD"/>
|
||||
@ -245,21 +278,23 @@
|
||||
</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"/>
|
||||
<animations/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<animations/>
|
||||
<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="centerY" secondItem="uXC-2j-cgi" secondAttribute="centerY" id="zJf-Xj-C1X"/>
|
||||
<constraint firstAttribute="trailing" secondItem="uXC-2j-cgi" secondAttribute="trailing" constant="8" id="zzu-aP-ZiL"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<animations/>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
@ -289,6 +324,7 @@
|
||||
<navigationController definesPresentationContext="YES" id="s4z-xn-r6C" sceneMemberID="viewController">
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" id="G3q-Gy-0Lf">
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<animations/>
|
||||
</navigationBar>
|
||||
<connections>
|
||||
<segue destination="2Qx-iu-03V" kind="relationship" relationship="rootViewController" id="cuu-7c-yOr"/>
|
||||
@ -309,6 +345,7 @@
|
||||
<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"/>
|
||||
<animations/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
<navigationItem key="navigationItem" id="URv-4c-d6h"/>
|
||||
@ -323,6 +360,7 @@
|
||||
<navigationController definesPresentationContext="YES" id="JRd-Be-psV" sceneMemberID="viewController">
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" id="YZb-OI-WKd">
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<animations/>
|
||||
</navigationBar>
|
||||
<connections>
|
||||
<segue destination="rXs-qR-ns2" kind="relationship" relationship="rootViewController" id="8xe-QC-QaY"/>
|
||||
@ -343,6 +381,7 @@
|
||||
<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"/>
|
||||
<animations/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
<navigationItem key="navigationItem" id="gBw-Dh-o4F"/>
|
||||
@ -352,9 +391,4 @@
|
||||
<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>
|
||||
|
||||
@ -35,7 +35,7 @@
|
||||
|
||||
|
||||
|
||||
@interface DemoMessagesViewController : JSQMessagesViewController <UIActionSheetDelegate>
|
||||
@interface DemoMessagesViewController : JSQMessagesViewController <UIActionSheetDelegate, JSQMessagesComposerTextViewPasteDelegate>
|
||||
|
||||
@property (weak, nonatomic) id<JSQDemoViewControllerDelegate> delegateModal;
|
||||
|
||||
|
||||
@ -18,7 +18,6 @@
|
||||
|
||||
#import "DemoMessagesViewController.h"
|
||||
|
||||
|
||||
@implementation DemoMessagesViewController
|
||||
|
||||
#pragma mark - View lifecycle
|
||||
@ -44,6 +43,7 @@
|
||||
self.senderId = kJSQDemoAvatarIdSquires;
|
||||
self.senderDisplayName = kJSQDemoAvatarDisplayNameSquires;
|
||||
|
||||
self.inputToolbar.contentView.textView.pasteDelegate = self;
|
||||
|
||||
/**
|
||||
* Load up our fake data for the demo
|
||||
@ -68,13 +68,30 @@
|
||||
style:UIBarButtonItemStyleBordered
|
||||
target:self
|
||||
action:@selector(receiveMessagePressed:)];
|
||||
|
||||
|
||||
/**
|
||||
* Register custom menu actions for cells.
|
||||
*/
|
||||
[JSQMessagesCollectionViewCell registerMenuAction:@selector(customAction:)];
|
||||
|
||||
|
||||
/**
|
||||
* OPT-IN: allow cells to be deleted
|
||||
*/
|
||||
[JSQMessagesCollectionViewCell registerMenuAction:@selector(delete:)];
|
||||
|
||||
/**
|
||||
* Customize your toolbar buttons
|
||||
*
|
||||
* self.inputToolbar.contentView.leftBarButtonItem = custom button or nil to remove
|
||||
* self.inputToolbar.contentView.rightBarButtonItem = custom button or nil to remove
|
||||
*/
|
||||
|
||||
/**
|
||||
* Set a maximum height for the input toolbar
|
||||
*
|
||||
* self.inputToolbar.maximumHeight = 150;
|
||||
*/
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated
|
||||
@ -102,6 +119,19 @@
|
||||
|
||||
|
||||
|
||||
#pragma mark - Custom menu actions for cells
|
||||
|
||||
- (void)didReceiveMenuWillShowNotification:(NSNotification *)notification
|
||||
{
|
||||
/**
|
||||
* Display custom menu actions for cells.
|
||||
*/
|
||||
UIMenuController *menu = [notification object];
|
||||
menu.menuItems = @[ [[UIMenuItem alloc] initWithTitle:@"Custom Action" action:@selector(customAction:)] ];
|
||||
}
|
||||
|
||||
|
||||
|
||||
#pragma mark - Testing
|
||||
|
||||
- (void)pushMainViewController
|
||||
@ -202,6 +232,18 @@
|
||||
|
||||
newMediaData = videoItemCopy;
|
||||
}
|
||||
else if ([copyMediaData isKindOfClass:[JSQAudioMediaItem class]]) {
|
||||
JSQAudioMediaItem *audioItemCopy = [((JSQAudioMediaItem *)copyMediaData) copy];
|
||||
audioItemCopy.appliesMediaViewMaskAsOutgoing = NO;
|
||||
newMediaAttachmentCopy = [audioItemCopy.audioData copy];
|
||||
|
||||
/**
|
||||
* Reset audio item to simulate "downloading" the audio
|
||||
*/
|
||||
audioItemCopy.audioData = nil;
|
||||
|
||||
newMediaData = audioItemCopy;
|
||||
}
|
||||
else {
|
||||
NSLog(@"%s error: unrecognized media item", __PRETTY_FUNCTION__);
|
||||
}
|
||||
@ -258,6 +300,10 @@
|
||||
((JSQVideoMediaItem *)newMediaData).isReadyToPlay = YES;
|
||||
[self.collectionView reloadData];
|
||||
}
|
||||
else if ([newMediaData isKindOfClass:[JSQAudioMediaItem class]]) {
|
||||
((JSQAudioMediaItem *)newMediaData).audioData = newMediaAttachmentCopy;
|
||||
[self.collectionView reloadData];
|
||||
}
|
||||
else {
|
||||
NSLog(@"%s error: unrecognized media item", __PRETTY_FUNCTION__);
|
||||
}
|
||||
@ -305,11 +351,13 @@
|
||||
|
||||
- (void)didPressAccessoryButton:(UIButton *)sender
|
||||
{
|
||||
[self.inputToolbar.contentView.textView resignFirstResponder];
|
||||
|
||||
UIActionSheet *sheet = [[UIActionSheet alloc] initWithTitle:@"Media messages"
|
||||
delegate:self
|
||||
cancelButtonTitle:@"Cancel"
|
||||
destructiveButtonTitle:nil
|
||||
otherButtonTitles:@"Send photo", @"Send location", @"Send video", nil];
|
||||
otherButtonTitles:@"Send photo", @"Send location", @"Send video", @"Send audio", nil];
|
||||
|
||||
[sheet showFromToolbar:self.inputToolbar];
|
||||
}
|
||||
@ -317,6 +365,7 @@
|
||||
- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex
|
||||
{
|
||||
if (buttonIndex == actionSheet.cancelButtonIndex) {
|
||||
[self.inputToolbar.contentView.textView becomeFirstResponder];
|
||||
return;
|
||||
}
|
||||
|
||||
@ -338,6 +387,10 @@
|
||||
case 2:
|
||||
[self.demoData addVideoMediaMessage];
|
||||
break;
|
||||
|
||||
case 3:
|
||||
[self.demoData addAudioMediaMessage];
|
||||
break;
|
||||
}
|
||||
|
||||
[JSQSystemSoundPlayer jsq_playMessageSentSound];
|
||||
@ -354,6 +407,11 @@
|
||||
return [self.demoData.messages objectAtIndex:indexPath.item];
|
||||
}
|
||||
|
||||
- (void)collectionView:(JSQMessagesCollectionView *)collectionView didDeleteMessageAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
[self.demoData.messages removeObjectAtIndex:indexPath.item];
|
||||
}
|
||||
|
||||
- (id<JSQMessageBubbleImageDataSource>)collectionView:(JSQMessagesCollectionView *)collectionView messageBubbleImageDataForItemAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
/**
|
||||
@ -504,6 +562,43 @@
|
||||
|
||||
|
||||
|
||||
#pragma mark - UICollectionView Delegate
|
||||
|
||||
#pragma mark - Custom menu items
|
||||
|
||||
- (BOOL)collectionView:(UICollectionView *)collectionView canPerformAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender
|
||||
{
|
||||
if (action == @selector(customAction:)) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
return [super collectionView:collectionView canPerformAction:action forItemAtIndexPath:indexPath withSender:sender];
|
||||
}
|
||||
|
||||
- (void)collectionView:(UICollectionView *)collectionView performAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender
|
||||
{
|
||||
if (action == @selector(customAction:)) {
|
||||
[self customAction:sender];
|
||||
return;
|
||||
}
|
||||
|
||||
[super collectionView:collectionView performAction:action forItemAtIndexPath:indexPath withSender:sender];
|
||||
}
|
||||
|
||||
- (void)customAction:(id)sender
|
||||
{
|
||||
NSLog(@"Custom action received! Sender: %@", sender);
|
||||
|
||||
[[[UIAlertView alloc] initWithTitle:@"Custom Action"
|
||||
message:nil
|
||||
delegate:nil
|
||||
cancelButtonTitle:@"OK"
|
||||
otherButtonTitles:nil]
|
||||
show];
|
||||
}
|
||||
|
||||
|
||||
|
||||
#pragma mark - JSQMessages collection view flow layout delegate
|
||||
|
||||
#pragma mark - Adjusting cell label heights
|
||||
@ -578,4 +673,23 @@
|
||||
NSLog(@"Tapped cell at %@!", NSStringFromCGPoint(touchLocation));
|
||||
}
|
||||
|
||||
#pragma mark - JSQMessagesComposerTextViewPasteDelegate methods
|
||||
|
||||
|
||||
- (BOOL)composerTextView:(JSQMessagesComposerTextView *)textView shouldPasteWithSender:(id)sender
|
||||
{
|
||||
if ([UIPasteboard generalPasteboard].image) {
|
||||
// If there's an image in the pasteboard, construct a media item with that image and `send` it.
|
||||
JSQPhotoMediaItem *item = [[JSQPhotoMediaItem alloc] initWithImage:[UIPasteboard generalPasteboard].image];
|
||||
JSQMessage *message = [[JSQMessage alloc] initWithSenderId:self.senderId
|
||||
senderDisplayName:self.senderDisplayName
|
||||
date:[NSDate date]
|
||||
media:item];
|
||||
[self.demoData.messages addObject:message];
|
||||
[self finishSendingMessage];
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@ -58,4 +58,6 @@ static NSString * const kJSQDemoAvatarIdWoz = @"309-41802-93823";
|
||||
|
||||
- (void)addVideoMediaMessage;
|
||||
|
||||
- (void)addAudioMediaMessage;
|
||||
|
||||
@end
|
||||
|
||||
@ -131,6 +131,7 @@
|
||||
nil];
|
||||
|
||||
[self addPhotoMediaMessage];
|
||||
[self addAudioMediaMessage];
|
||||
|
||||
/**
|
||||
* Setting to load extra messages for testing/demo
|
||||
@ -156,6 +157,17 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (void)addAudioMediaMessage
|
||||
{
|
||||
NSString * sample = [[NSBundle mainBundle] pathForResource:@"jsq_messages_sample" ofType:@"m4a"];
|
||||
NSData * audioData = [NSData dataWithContentsOfFile:sample];
|
||||
JSQAudioMediaItem *audioItem = [[JSQAudioMediaItem alloc] initWithData:audioData];
|
||||
JSQMessage *audioMessage = [JSQMessage messageWithSenderId:kJSQDemoAvatarIdSquires
|
||||
displayName:kJSQDemoAvatarDisplayNameSquires
|
||||
media:audioItem];
|
||||
[self.messages addObject:audioMessage];
|
||||
}
|
||||
|
||||
- (void)addPhotoMediaMessage
|
||||
{
|
||||
JSQPhotoMediaItem *photoItem = [[JSQPhotoMediaItem alloc] initWithImage:[UIImage imageNamed:@"goldengate"]];
|
||||
|
||||
@ -71,6 +71,12 @@
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-76@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "83.5x83.5",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "icon167.png",
|
||||
"scale" : "2x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
||||
BIN
JSQMessagesDemo/Images.xcassets/AppIcon.appiconset/icon167.png
Normal file
BIN
JSQMessagesDemo/Images.xcassets/AppIcon.appiconset/icon167.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.0 KiB |
@ -7,7 +7,7 @@
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.hexedbits.$(PRODUCT_NAME:rfc1034identifier)</string>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
@ -15,11 +15,11 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>6.1.1</string>
|
||||
<string>7.3.4</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>6.1.1</string>
|
||||
<string>7.3.4</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
|
||||
@ -38,16 +38,23 @@
|
||||
|
||||
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
|
||||
{
|
||||
return 3;
|
||||
return 4;
|
||||
}
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
|
||||
{
|
||||
if (section == 2) {
|
||||
return 1;
|
||||
switch (section) {
|
||||
case 0:
|
||||
return 2;
|
||||
case 1:
|
||||
return 2;
|
||||
case 2:
|
||||
return 1;
|
||||
case 3:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
@ -58,7 +65,7 @@
|
||||
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
|
||||
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
|
||||
}
|
||||
|
||||
|
||||
if (indexPath.section == 0) {
|
||||
switch (indexPath.row) {
|
||||
case 0:
|
||||
@ -86,7 +93,14 @@
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
else if (indexPath.section == 3) {
|
||||
switch (indexPath.row) {
|
||||
case 0:
|
||||
cell.textLabel.text = @"Push view 2 levels";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
@ -97,6 +111,8 @@
|
||||
return @"Presentation";
|
||||
case 2:
|
||||
return @"Demo options";
|
||||
case 3:
|
||||
return @"Other testing";
|
||||
default:
|
||||
return nil;
|
||||
}
|
||||
@ -104,7 +120,7 @@
|
||||
|
||||
- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section
|
||||
{
|
||||
return (section == [tableView numberOfSections] - 1) ? @"Copyright © 2014\nJesse Squires\nMIT License" : nil;
|
||||
return (section == 2) ? @"Copyright © 2015\nJesse Squires\nMIT License" : nil;
|
||||
}
|
||||
|
||||
#pragma mark - Table view delegate
|
||||
@ -146,6 +162,19 @@
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (indexPath.section == 3) {
|
||||
switch (indexPath.row) {
|
||||
case 0:
|
||||
{
|
||||
UIViewController *blank = [[UIViewController alloc] initWithNibName:nil bundle:nil];
|
||||
blank.title = @"Blank";
|
||||
blank.view.backgroundColor = [UIColor lightGrayColor];
|
||||
[self.navigationController pushViewController:blank animated:NO];
|
||||
DemoMessagesViewController *vc = [DemoMessagesViewController messagesViewController];
|
||||
[self.navigationController pushViewController:vc animated:YES];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Segues
|
||||
|
||||
BIN
JSQMessagesDemo/jsq_messages_sample.m4a
Normal file
BIN
JSQMessagesDemo/jsq_messages_sample.m4a
Normal file
Binary file not shown.
46
JSQMessagesTests/CategoryTests/JSQMessagesNSBundleTests.m
Normal file
46
JSQMessagesTests/CategoryTests/JSQMessagesNSBundleTests.m
Normal file
@ -0,0 +1,46 @@
|
||||
//
|
||||
// Created by Jesse Squires
|
||||
// http://www.jessesquires.com
|
||||
//
|
||||
//
|
||||
// MIT License
|
||||
// Copyright (c) 2014 Jesse Squires
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import "NSBundle+JSQMessages.h"
|
||||
|
||||
|
||||
@interface JSQMessagesNSBundleTests : XCTestCase
|
||||
@end
|
||||
|
||||
|
||||
@implementation JSQMessagesNSBundleTests
|
||||
|
||||
- (void)testMessagesBundle
|
||||
{
|
||||
XCTAssertNotNil([NSBundle jsq_messagesBundle]);
|
||||
}
|
||||
|
||||
- (void)testAssetBundle
|
||||
{
|
||||
NSBundle *bundle = [NSBundle jsq_messagesAssetBundle];
|
||||
XCTAssertNotNil(bundle);
|
||||
XCTAssertEqualObjects(bundle.bundlePath.lastPathComponent, @"JSQMessagesAssets.bundle");
|
||||
}
|
||||
|
||||
- (void)testLocalizedStringForKey
|
||||
{
|
||||
XCTAssertNotNil([NSBundle jsq_localizedStringForKey:@"send"]);
|
||||
XCTAssertNotEqualObjects([NSBundle jsq_localizedStringForKey:@"send"], @"send");
|
||||
|
||||
XCTAssertNotNil([NSBundle jsq_localizedStringForKey:@"load_earlier_messages"]);
|
||||
XCTAssertNotEqualObjects([NSBundle jsq_localizedStringForKey:@"load_earlier_messages"], @"load_earlier_messages");
|
||||
|
||||
XCTAssertNotNil([NSBundle jsq_localizedStringForKey:@"new_message"]);
|
||||
XCTAssertNotEqualObjects([NSBundle jsq_localizedStringForKey:@"new_message"], @"new_message");
|
||||
}
|
||||
|
||||
@end
|
||||
@ -10,8 +10,6 @@
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import <OCMock/OCMock.h>
|
||||
|
||||
#import "JSQMessagesViewController.h"
|
||||
#import "DemoMessagesViewController.h"
|
||||
|
||||
@ -48,14 +46,17 @@
|
||||
XCTAssertNotNil(nib, @"Nib should not be nil");
|
||||
|
||||
JSQMessagesViewController *vc = [JSQMessagesViewController messagesViewController];
|
||||
[vc view];
|
||||
vc.senderId = @"senderId";
|
||||
vc.senderDisplayName = @"senderDisplayName";
|
||||
|
||||
[vc beginAppearanceTransition:YES animated:NO];
|
||||
[vc endAppearanceTransition];
|
||||
|
||||
XCTAssertNotNil(vc, @"View controller should not be nil");
|
||||
XCTAssertNotNil(vc.view, @"View should not be nil");
|
||||
XCTAssertNotNil(vc.collectionView, @"Collection view should not be nil");
|
||||
XCTAssertNotNil(vc.inputToolbar, @"Input toolbar should not be nil");
|
||||
|
||||
XCTAssertEqualObjects(vc.senderId, @"JSQDefaultSender", @"Property should be equal to default value");
|
||||
XCTAssertEqualObjects(vc.senderDisplayName, @"JSQDefaultSender", @"Property should be equal to default value");
|
||||
|
||||
XCTAssertEqual(vc.automaticallyAdjustsScrollViewInsets, YES, @"Property should be equal to default value");
|
||||
|
||||
XCTAssertEqualObjects(vc.incomingCellIdentifier, [JSQMessagesCollectionViewCellIncoming cellReuseIdentifier], @"Property should be equal to default value");
|
||||
@ -68,7 +69,12 @@
|
||||
- (void)testJSQMessagesViewControllerSubclassInitProgramatically
|
||||
{
|
||||
DemoMessagesViewController *demoVC = [DemoMessagesViewController messagesViewController];
|
||||
[demoVC view];
|
||||
demoVC.senderId = @"senderId";
|
||||
demoVC.senderDisplayName = @"senderDisplayName";
|
||||
|
||||
[demoVC beginAppearanceTransition:YES animated:NO];
|
||||
[demoVC endAppearanceTransition];
|
||||
|
||||
XCTAssertNotNil(demoVC, @"View controller should not be nil");
|
||||
XCTAssertTrue([demoVC isKindOfClass:[DemoMessagesViewController class]], @"View controller should be kind of class: %@", [DemoMessagesViewController class]);
|
||||
XCTAssertNotNil(demoVC.view, @"View should not be nil");
|
||||
@ -82,7 +88,12 @@
|
||||
XCTAssertNotNil(mainSB, @"Storyboard should not be nil");
|
||||
|
||||
DemoMessagesViewController *demoVC = [mainSB instantiateViewControllerWithIdentifier:@"DemoVC"];
|
||||
[demoVC view];
|
||||
demoVC.senderId = @"senderId";
|
||||
demoVC.senderDisplayName = @"senderDisplayName";
|
||||
|
||||
[demoVC beginAppearanceTransition:YES animated:NO];
|
||||
[demoVC endAppearanceTransition];
|
||||
|
||||
XCTAssertNotNil(demoVC, @"View controller should not be nil");
|
||||
XCTAssertTrue([demoVC isKindOfClass:[DemoMessagesViewController class]], @"View controller should be kind of class: %@", [DemoMessagesViewController class]);
|
||||
XCTAssertNotNil(demoVC.view, @"View should not be nil");
|
||||
@ -90,14 +101,4 @@
|
||||
XCTAssertNotNil(demoVC.inputToolbar, @"Input toolbar should not be nil");
|
||||
}
|
||||
|
||||
- (void)testViewConfiguration
|
||||
{
|
||||
JSQMessagesViewController *vc = [JSQMessagesViewController messagesViewController];
|
||||
|
||||
id mockVC = [OCMockObject partialMockForObject:vc];
|
||||
[[mockVC expect] jsq_configureMessagesViewController];
|
||||
[vc view];
|
||||
[mockVC verify];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@ -98,7 +98,7 @@
|
||||
|
||||
NSString *relativeDateString = [[JSQMessagesTimestampFormatter sharedFormatter] relativeDateForDate:date];
|
||||
|
||||
XCTAssertEqualObjects(relativeDateString, @"Today", @"Relative date string shoudl return expected value");
|
||||
XCTAssertEqualObjects(relativeDateString, @"Today", @"Relative date string should return expected value");
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.hexedbits.$(PRODUCT_NAME:rfc1034identifier)</string>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
|
||||
77
JSQMessagesTests/ModelTests/JSQAudioMediaItemTests.m
Normal file
77
JSQMessagesTests/ModelTests/JSQAudioMediaItemTests.m
Normal file
@ -0,0 +1,77 @@
|
||||
//
|
||||
// Created by Jesse Squires
|
||||
// http://www.jessesquires.com
|
||||
//
|
||||
//
|
||||
// MIT License
|
||||
// Copyright (c) 2014 Jesse Squires
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import "JSQAudioMediaItem.h"
|
||||
|
||||
@interface JSQAudioMediaItemTests : XCTestCase
|
||||
|
||||
@end
|
||||
|
||||
@implementation JSQAudioMediaItemTests
|
||||
|
||||
- (void)setUp
|
||||
{
|
||||
[super setUp];
|
||||
}
|
||||
|
||||
- (void)tearDown
|
||||
{
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
- (void)testAudioItemInit
|
||||
{
|
||||
JSQAudioMediaItem *item = [[JSQAudioMediaItem alloc] initWithData:[NSData data]];
|
||||
XCTAssertNotNil(item);
|
||||
}
|
||||
|
||||
- (void)testAudioItemIsEqual
|
||||
{
|
||||
NSString * sample = [[NSBundle mainBundle] pathForResource:@"jsq_messages_sample" ofType:@"m4a"];
|
||||
JSQAudioMediaItem *item = [[JSQAudioMediaItem alloc] initWithData:[NSData dataWithContentsOfFile:sample]];
|
||||
|
||||
JSQAudioMediaItem *copy = [item copy];
|
||||
|
||||
XCTAssertEqualObjects(item, copy, @"Copied items should be equal");
|
||||
|
||||
XCTAssertEqual([item hash], [copy hash], @"Copied item hashes should be equal");
|
||||
|
||||
XCTAssertEqualObjects(item, item, @"Item should be equal to itself");
|
||||
}
|
||||
|
||||
- (void)testAudioItemArchiving
|
||||
{
|
||||
NSString * sample = [[NSBundle mainBundle] pathForResource:@"jsq_messages_sample" ofType:@"m4a"];
|
||||
JSQAudioMediaItem *item = [[JSQAudioMediaItem alloc] initWithData:[NSData dataWithContentsOfFile:sample]];
|
||||
|
||||
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:item];
|
||||
|
||||
JSQAudioMediaItem *unarchivedItem = [NSKeyedUnarchiver unarchiveObjectWithData:data];
|
||||
|
||||
XCTAssertEqualObjects(item, unarchivedItem);
|
||||
}
|
||||
|
||||
- (void)testMediaDataProtocol
|
||||
{
|
||||
JSQAudioMediaItem *item = [[JSQAudioMediaItem alloc] init];
|
||||
|
||||
XCTAssertTrue(!CGSizeEqualToSize([item mediaViewDisplaySize], CGSizeZero));
|
||||
XCTAssertNotNil([item mediaPlaceholderView]);
|
||||
XCTAssertNil([item mediaView], @"Media view should be nil if image is nil");
|
||||
|
||||
NSString * sample = [[NSBundle mainBundle] pathForResource:@"jsq_messages_sample" ofType:@"m4a"];
|
||||
item.audioData = [NSData dataWithContentsOfFile:sample];
|
||||
|
||||
XCTAssertNotNil([item mediaView], @"Media view should NOT be nil once item has media data");
|
||||
}
|
||||
|
||||
@end
|
||||
@ -54,7 +54,7 @@
|
||||
[expectation fulfill];
|
||||
}];
|
||||
|
||||
[self waitForExpectationsWithTimeout:5 handler:^(NSError *error) {
|
||||
[self waitForExpectationsWithTimeout:15 handler:^(NSError *error) {
|
||||
XCTAssertNil(error, @"Expectation should not error");
|
||||
}];
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import <OCMock/OCMock.h>
|
||||
//#import <OCMock/OCMock.h>
|
||||
|
||||
#import "JSQMessage.h"
|
||||
|
||||
@ -30,6 +30,10 @@
|
||||
|
||||
- (BOOL)isEqual:(id)object { return YES; }
|
||||
|
||||
- (NSUInteger)hash { return 10000; }
|
||||
|
||||
- (NSUInteger)mediaHash { return self.hash; }
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@ -40,7 +44,7 @@
|
||||
@property (strong, nonatomic) NSString *senderId;
|
||||
@property (strong, nonatomic) NSString *senderDisplayName;
|
||||
@property (strong, nonatomic) NSDate *date;
|
||||
@property (strong, nonatomic) id<JSQMessageMediaData> mockMediaData;
|
||||
@property (strong, nonatomic) FakeMedia *fakeMediaData;
|
||||
|
||||
@end
|
||||
|
||||
@ -53,7 +57,7 @@
|
||||
self.senderId = @"324543-43556-212343";
|
||||
self.senderDisplayName = @"Jesse Squires";
|
||||
self.date = [NSDate date];
|
||||
self.mockMediaData = [OCMockObject mockForProtocol:@protocol(JSQMessageMediaData)];
|
||||
self.fakeMediaData = [FakeMedia new];
|
||||
}
|
||||
|
||||
- (void)tearDown
|
||||
@ -61,7 +65,7 @@
|
||||
self.senderId = nil;
|
||||
self.senderDisplayName = nil;
|
||||
self.date = nil;
|
||||
self.mockMediaData = nil;
|
||||
self.fakeMediaData = nil;
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
@ -70,13 +74,12 @@
|
||||
JSQMessage *msg = [[JSQMessage alloc] initWithSenderId:self.senderId
|
||||
senderDisplayName:self.senderDisplayName
|
||||
date:self.date
|
||||
media:self.mockMediaData];
|
||||
media:self.fakeMediaData];
|
||||
XCTAssertNotNil(msg, @"Message should not be nil");
|
||||
}
|
||||
|
||||
- (void)testMediaMessageInvalidInit
|
||||
{
|
||||
XCTAssertThrows([[JSQMessage alloc] init], @"Invalid init should throw");
|
||||
XCTAssertThrows([[JSQMessage alloc] initWithSenderId:nil senderDisplayName:nil date:nil media:nil], @"Invalid init should throw");
|
||||
}
|
||||
|
||||
@ -85,7 +88,7 @@
|
||||
JSQMessage *msg = [[JSQMessage alloc] initWithSenderId:self.senderId
|
||||
senderDisplayName:self.senderDisplayName
|
||||
date:self.date
|
||||
media:self.mockMediaData];
|
||||
media:self.fakeMediaData];
|
||||
JSQMessage *copy = [msg copy];
|
||||
|
||||
XCTAssertEqualObjects(msg, copy, @"Copied messages should be equal");
|
||||
|
||||
@ -60,7 +60,6 @@
|
||||
|
||||
- (void)testTextMessageInvalidInit
|
||||
{
|
||||
XCTAssertThrows([[JSQMessage alloc] init], @"Invalid init should throw");
|
||||
XCTAssertThrows([[JSQMessage alloc] initWithSenderId:nil senderDisplayName:nil date:nil text:nil], @"Invalid init should throw");
|
||||
}
|
||||
|
||||
|
||||
@ -32,7 +32,6 @@
|
||||
|
||||
- (void)testInitInvalid
|
||||
{
|
||||
XCTAssertThrows([[JSQMessagesAvatarImage alloc] init], @"Invalid init should throw");
|
||||
XCTAssertThrows([JSQMessagesAvatarImage avatarImageWithPlaceholder:nil], @"Invalid init should throw");
|
||||
XCTAssertThrows([[JSQMessagesAvatarImage alloc] initWithAvatarImage:nil highlightedImage:nil placeholderImage:nil], @"Invalid init should throw");
|
||||
}
|
||||
|
||||
@ -34,7 +34,6 @@
|
||||
|
||||
- (void)testInitInvalid
|
||||
{
|
||||
XCTAssertThrows([[JSQMessagesBubbleImage alloc] init], @"Invalid init should throw");
|
||||
XCTAssertThrows([[JSQMessagesBubbleImage alloc] initWithMessageBubbleImage:nil highlightedImage:nil], @"Invalid init should throw");
|
||||
}
|
||||
|
||||
|
||||
@ -39,7 +39,7 @@
|
||||
|
||||
XCTAssertNotNil(view, @"Collection view should not be nil");
|
||||
XCTAssertEqualObjects(view.backgroundColor, [UIColor whiteColor], @"Property should be equal to default value");
|
||||
XCTAssertEqual(view.keyboardDismissMode, UIScrollViewKeyboardDismissModeInteractive, @"Property should be equal to default value");
|
||||
XCTAssertEqual(view.keyboardDismissMode, UIScrollViewKeyboardDismissModeNone, @"Property should be equal to default value");
|
||||
XCTAssertEqual(view.alwaysBounceVertical, YES, @"Property should be equal to default value");
|
||||
XCTAssertEqual(view.bounces, YES, @"Property should be equal to default value");
|
||||
}
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
|
||||
#import "JSQMessagesViewController.h"
|
||||
#import "JSQMessagesInputToolbar.h"
|
||||
#import "DemoMessagesViewController.h"
|
||||
|
||||
|
||||
@interface JSQMessagesInputToolbarTests : XCTestCase
|
||||
@ -34,11 +35,36 @@
|
||||
{
|
||||
JSQMessagesViewController *vc = [JSQMessagesViewController messagesViewController];
|
||||
[vc loadView];
|
||||
|
||||
|
||||
JSQMessagesInputToolbar *toolbar = vc.inputToolbar;
|
||||
XCTAssertNotNil(toolbar, @"Toolbar should not be nil");
|
||||
XCTAssertNotNil(toolbar.contentView, @"Toolbar content view should not be nil");
|
||||
XCTAssertEqual(toolbar.sendButtonOnRight, YES, @"Property should be equal to default value");
|
||||
}
|
||||
|
||||
- (void)testSetMaximumHeight
|
||||
{
|
||||
UIStoryboard *mainSB = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
|
||||
XCTAssertNotNil(mainSB, @"Storyboard should not be nil");
|
||||
|
||||
DemoMessagesViewController *demoVC = [mainSB instantiateViewControllerWithIdentifier:@"DemoVC"];
|
||||
[demoVC beginAppearanceTransition:YES animated:NO];
|
||||
[demoVC endAppearanceTransition];
|
||||
|
||||
XCTAssertEqual(demoVC.inputToolbar.maximumHeight, NSNotFound, @"maximumInputToolbarHeight should equal default value");
|
||||
|
||||
demoVC.inputToolbar.maximumHeight = 54;
|
||||
|
||||
CGRect newBounds = demoVC.inputToolbar.bounds;
|
||||
newBounds.size.height = 100;
|
||||
demoVC.inputToolbar.bounds = newBounds;
|
||||
XCTAssertEqual(CGRectGetHeight(demoVC.inputToolbar.bounds), 100);
|
||||
|
||||
[demoVC.view setNeedsUpdateConstraints];
|
||||
[demoVC.view setNeedsLayout];
|
||||
[demoVC.view layoutIfNeeded];
|
||||
|
||||
XCTAssertLessThanOrEqual(CGRectGetHeight(demoVC.inputToolbar.frame), 54, @"Toolbar height should be <= to maximumInputToolbarHeight");
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@ -1,21 +1,26 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = 'JSQMessagesViewController'
|
||||
s.version = '6.1.1'
|
||||
s.summary = 'An elegant messages UI library for iOS.'
|
||||
s.homepage = 'http://jessesquires.github.io/JSQMessagesViewController'
|
||||
s.license = 'MIT'
|
||||
s.screenshots = ['https://raw.githubusercontent.com/jessesquires/JSQMessagesViewController/develop/Screenshots/screenshot0.png',
|
||||
'https://raw.githubusercontent.com/jessesquires/JSQMessagesViewController/develop/Screenshots/screenshot1.png',
|
||||
'https://raw.githubusercontent.com/jessesquires/JSQMessagesViewController/develop/Screenshots/screenshot2.png',
|
||||
'https://raw.githubusercontent.com/jessesquires/JSQMessagesViewController/develop/Screenshots/screenshot3.png']
|
||||
s.author = { 'Jesse Squires' => 'jesse.squires.developer@gmail.com' }
|
||||
s.social_media_url = 'https://twitter.com/jesse_squires'
|
||||
s.source = { :git => 'https://github.com/jessesquires/JSQMessagesViewController.git', :tag => s.version.to_s }
|
||||
s.platform = :ios, '7.0'
|
||||
s.source_files = 'JSQMessagesViewController/**/*.{h,m}'
|
||||
s.resources = 'JSQMessagesViewController/Assets/JSQMessagesAssets.bundle', 'JSQMessagesViewController/Assets/Strings/*.lproj', 'JSQMessagesViewController/**/*.{xib}',
|
||||
s.frameworks = 'QuartzCore', 'CoreGraphics', 'CoreLocation', 'MapKit', 'UIKit', 'Foundation'
|
||||
s.requires_arc = true
|
||||
s.name = 'JSQMessagesViewController'
|
||||
s.version = '7.3.4'
|
||||
s.summary = 'An elegant messages UI library for iOS.'
|
||||
s.homepage = 'http://jessesquires.github.io/JSQMessagesViewController'
|
||||
s.license = 'MIT'
|
||||
s.platform = :ios, '7.0'
|
||||
|
||||
s.dependency 'JSQSystemSoundPlayer', '~> 2.0.0'
|
||||
s.author = 'Jesse Squires'
|
||||
s.social_media_url = 'https://twitter.com/jesse_squires'
|
||||
|
||||
s.screenshots = ['https://raw.githubusercontent.com/jessesquires/JSQMessagesViewController/develop/Screenshots/screenshot0.png',
|
||||
'https://raw.githubusercontent.com/jessesquires/JSQMessagesViewController/develop/Screenshots/screenshot1.png',
|
||||
'https://raw.githubusercontent.com/jessesquires/JSQMessagesViewController/develop/Screenshots/screenshot2.png',
|
||||
'https://raw.githubusercontent.com/jessesquires/JSQMessagesViewController/develop/Screenshots/screenshot3.png']
|
||||
|
||||
s.source = { :git => 'https://github.com/jessesquires/JSQMessagesViewController.git', :tag => s.version }
|
||||
s.source_files = 'JSQMessagesViewController/**/*.{h,m}'
|
||||
|
||||
s.resources = ['JSQMessagesViewController/Assets/JSQMessagesAssets.bundle', 'JSQMessagesViewController/**/*.{xib}']
|
||||
|
||||
s.frameworks = 'QuartzCore', 'CoreGraphics', 'CoreLocation', 'MapKit', 'AVFoundation'
|
||||
s.requires_arc = true
|
||||
|
||||
s.dependency 'JSQSystemSoundPlayer', '~> 2.0.1'
|
||||
end
|
||||
|
||||
@ -0,0 +1,37 @@
|
||||
//
|
||||
// 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
|
||||
//
|
||||
|
||||
// ********************************
|
||||
// Special thanks to the localization contributors!
|
||||
//
|
||||
// https://github.com/jessesquires/JSQMessagesViewController/issues/237
|
||||
// ********************************
|
||||
|
||||
"load_earlier_messages" = "Load Earlier Messages";
|
||||
|
||||
"send" = "Send";
|
||||
|
||||
"new_message" = "New Message";
|
||||
|
||||
"text_message_accessibility_label" = "%@: %@";
|
||||
|
||||
"media_message_accessibility_label" = "%@: media message";
|
||||
|
||||
"accessory_button_accessibility_label" = "Share media";
|
||||
|
||||
"new_message_received_accessibility_announcement" = "New message received";
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.3 KiB |
@ -0,0 +1,29 @@
|
||||
//
|
||||
// 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
|
||||
//
|
||||
|
||||
// ********************************
|
||||
// Special thanks to the localization contributors!
|
||||
//
|
||||
// https://github.com/jessesquires/JSQMessagesViewController/issues/237
|
||||
// ********************************
|
||||
|
||||
"load_earlier_messages" = "تحميل الرسائل السابقة";
|
||||
|
||||
"send" = "أرسال";
|
||||
|
||||
"new_message" = "رسالة جديدة";
|
||||
@ -0,0 +1,37 @@
|
||||
//
|
||||
// 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
|
||||
//
|
||||
|
||||
// ********************************
|
||||
// Special thanks to the localization contributors!
|
||||
//
|
||||
// https://github.com/jessesquires/JSQMessagesViewController/issues/237
|
||||
// ********************************
|
||||
|
||||
"load_earlier_messages" = "Ältere Nachrichten laden";
|
||||
|
||||
"send" = "Senden";
|
||||
|
||||
"new_message" = "Neue Nachricht";
|
||||
|
||||
"text_message_accessibility_label" = "%@: %@";
|
||||
|
||||
"media_message_accessibility_label" = "%@: media Nachricht";
|
||||
|
||||
"accessory_button_accessibility_label" = "Aktien media";
|
||||
|
||||
"new_message_received_accessibility_announcement" = "Neue Nachricht empfangen";
|
||||
@ -0,0 +1,37 @@
|
||||
//
|
||||
// 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
|
||||
//
|
||||
|
||||
// ********************************
|
||||
// Special thanks to the localization contributors!
|
||||
//
|
||||
// https://github.com/jessesquires/JSQMessagesViewController/issues/237
|
||||
// ********************************
|
||||
|
||||
"load_earlier_messages" = "Load Earlier Messages";
|
||||
|
||||
"send" = "Send";
|
||||
|
||||
"new_message" = "New Message";
|
||||
|
||||
"text_message_accessibility_label" = "%@: %@";
|
||||
|
||||
"media_message_accessibility_label" = "%@: media message";
|
||||
|
||||
"accessory_button_accessibility_label" = "Share media";
|
||||
|
||||
"new_message_received_accessibility_announcement" = "New message received";
|
||||
@ -0,0 +1,37 @@
|
||||
//
|
||||
// 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
|
||||
//
|
||||
|
||||
// ********************************
|
||||
// Special thanks to the localization contributors!
|
||||
//
|
||||
// https://github.com/jessesquires/JSQMessagesViewController/issues/237
|
||||
// ********************************
|
||||
|
||||
"load_earlier_messages" = "Cargar mensajes anteriores";
|
||||
|
||||
"send" = "Enviar";
|
||||
|
||||
"new_message" = "Nuevo mensaje";
|
||||
|
||||
"text_message_accessibility_label" = "%@: %@";
|
||||
|
||||
"media_message_accessibility_label" = "%@: imagen";
|
||||
|
||||
"accessory_button_accessibility_label" = "Intercambio de archivos";
|
||||
|
||||
"new_message_received_accessibility_announcement" = "mensaje recibido";
|
||||
@ -22,8 +22,8 @@
|
||||
// https://github.com/jessesquires/JSQMessagesViewController/issues/237
|
||||
// ********************************
|
||||
|
||||
"Load Earlier Messages" = "Laad eerdere berichten";
|
||||
"load_earlier_messages" = "Lataa aiempia viestejä";
|
||||
|
||||
"Send" = "Stuur";
|
||||
"send" = "Lähetä";
|
||||
|
||||
"New Message" = "Nieuw bericht";
|
||||
"new_message" = "Uusi viesti";
|
||||
@ -0,0 +1,37 @@
|
||||
//
|
||||
// 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
|
||||
//
|
||||
|
||||
// ********************************
|
||||
// Special thanks to the localization contributors!
|
||||
//
|
||||
// https://github.com/jessesquires/JSQMessagesViewController/issues/237
|
||||
// ********************************
|
||||
|
||||
"load_earlier_messages" = "Messages précedents";
|
||||
|
||||
"send" = "Envoyer";
|
||||
|
||||
"new_message" = "Nouveau message";
|
||||
|
||||
"text_message_accessibility_label" = "%@: %@";
|
||||
|
||||
"media_message_accessibility_label" = "%@: image";
|
||||
|
||||
"accessory_button_accessibility_label" = "Partager fichier";
|
||||
|
||||
"new_message_received_accessibility_announcement" = "Nouveau message reçu";
|
||||
@ -0,0 +1,29 @@
|
||||
//
|
||||
// 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
|
||||
//
|
||||
|
||||
// ********************************
|
||||
// Special thanks to the localization contributors!
|
||||
//
|
||||
// https://github.com/jessesquires/JSQMessagesViewController/issues/237
|
||||
// ********************************
|
||||
|
||||
"load_earlier_messages" = "טען הודעות קודמות";
|
||||
|
||||
"send" = "שלח";
|
||||
|
||||
"new_message" = "הודעה חדשה";
|
||||
@ -0,0 +1,29 @@
|
||||
//
|
||||
// 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
|
||||
//
|
||||
|
||||
// ********************************
|
||||
// Special thanks to the localization contributors!
|
||||
//
|
||||
// https://github.com/jessesquires/JSQMessagesViewController/issues/237
|
||||
// ********************************
|
||||
|
||||
"load_earlier_messages" = "Učitaj ranije poruke";
|
||||
|
||||
"send" = "Šalji";
|
||||
|
||||
"new_message" = "Nova poruka";
|
||||
@ -22,8 +22,8 @@
|
||||
// https://github.com/jessesquires/JSQMessagesViewController/issues/237
|
||||
// ********************************
|
||||
|
||||
"Load Earlier Messages" = "載入之前的訊息";
|
||||
"load_earlier_messages" = "Muat Pesan Sebelumnya";
|
||||
|
||||
"Send" = "傳送";
|
||||
"send" = "Kirim";
|
||||
|
||||
"New Message" = "新信息";
|
||||
"new_message" = "Pesan Baru";
|
||||
@ -22,8 +22,8 @@
|
||||
// https://github.com/jessesquires/JSQMessagesViewController/issues/237
|
||||
// ********************************
|
||||
|
||||
"Load Earlier Messages" = "Cargar mensajes anteriores";
|
||||
"load_earlier_messages" = "Carica messaggi precedenti";
|
||||
|
||||
"Send" = "Enviar";
|
||||
"send" = "Invia";
|
||||
|
||||
"New Message" = "Nuevo mensaje";
|
||||
"new_message" = "Nuovo Messaggio";
|
||||
@ -0,0 +1,29 @@
|
||||
//
|
||||
// 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
|
||||
//
|
||||
|
||||
// ********************************
|
||||
// Special thanks to the localization contributors!
|
||||
//
|
||||
// https://github.com/jessesquires/JSQMessagesViewController/issues/237
|
||||
// ********************************
|
||||
|
||||
"load_earlier_messages" = "古いメッセージを読み込む";
|
||||
|
||||
"send" = "送信";
|
||||
|
||||
"new_message" = "新しいメッセージ";
|
||||
@ -0,0 +1,29 @@
|
||||
//
|
||||
// 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
|
||||
//
|
||||
|
||||
// ********************************
|
||||
// Special thanks to the localization contributors!
|
||||
//
|
||||
// https://github.com/jessesquires/JSQMessagesViewController/issues/237
|
||||
// ********************************
|
||||
|
||||
"load_earlier_messages" = "이전 메시지 불러오기";
|
||||
|
||||
"send" = "전송";
|
||||
|
||||
"new_message" = "새로운 메시지";
|
||||
@ -22,8 +22,8 @@
|
||||
// https://github.com/jessesquires/JSQMessagesViewController/issues/237
|
||||
// ********************************
|
||||
|
||||
"Load Earlier Messages" = "Load Earlier Messages";
|
||||
"load_earlier_messages" = "Muat Turun Mesej Lama";
|
||||
|
||||
"Send" = "Send";
|
||||
"send" = "Hantar";
|
||||
|
||||
"New Message" = "New Message";
|
||||
"new_message" = "Mesej Baru";
|
||||
@ -0,0 +1,37 @@
|
||||
//
|
||||
// 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
|
||||
//
|
||||
|
||||
// ********************************
|
||||
// Special thanks to the localization contributors!
|
||||
//
|
||||
// https://github.com/jessesquires/JSQMessagesViewController/issues/237
|
||||
// ********************************
|
||||
|
||||
"load_earlier_messages" = "Last tidligere beskjeder";
|
||||
|
||||
"send" = "Send";
|
||||
|
||||
"new_message" = "Ny melding";
|
||||
|
||||
"text_message_accessibility_label" = "%@: %@";
|
||||
|
||||
"media_message_accessibility_label" = "%@: mediamelding";
|
||||
|
||||
"accessory_button_accessibility_label" = "Del media";
|
||||
|
||||
"new_message_received_accessibility_announcement" = "Ny melding mottatt";
|
||||
@ -22,8 +22,8 @@
|
||||
// https://github.com/jessesquires/JSQMessagesViewController/issues/237
|
||||
// ********************************
|
||||
|
||||
"Load Earlier Messages" = "Messages précedents";
|
||||
"load_earlier_messages" = "Laad eerdere berichten";
|
||||
|
||||
"Send" = "Envoi";
|
||||
"send" = "Stuur";
|
||||
|
||||
"New Message" = "Nouveau message";
|
||||
"new_message" = "Nieuw bericht";
|
||||
@ -0,0 +1,29 @@
|
||||
//
|
||||
// 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
|
||||
//
|
||||
|
||||
// ********************************
|
||||
// Special thanks to the localization contributors!
|
||||
//
|
||||
// https://github.com/jessesquires/JSQMessagesViewController/issues/237
|
||||
// ********************************
|
||||
|
||||
"load_earlier_messages" = "Otwórz wcześniejsze wiadomości";
|
||||
|
||||
"send" = "Wyślij";
|
||||
|
||||
"new_message" = "Nowa wiadomość";
|
||||
@ -22,8 +22,8 @@
|
||||
// https://github.com/jessesquires/JSQMessagesViewController/issues/237
|
||||
// ********************************
|
||||
|
||||
"Load Earlier Messages" = "Ältere Nachrichten laden";
|
||||
"load_earlier_messages" = "Carregar mensagens anteriores";
|
||||
|
||||
"Send" = "Senden";
|
||||
"send" = "Enviar";
|
||||
|
||||
"New Message" = "Neue Nachricht";
|
||||
"new_message" = "Nova Mensagem";
|
||||
@ -0,0 +1,29 @@
|
||||
//
|
||||
// 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
|
||||
//
|
||||
|
||||
// ********************************
|
||||
// Special thanks to the localization contributors!
|
||||
//
|
||||
// https://github.com/jessesquires/JSQMessagesViewController/issues/237
|
||||
// ********************************
|
||||
|
||||
"load_earlier_messages" = "Încărcați mesajele anterioare";
|
||||
|
||||
"send" = "Trimiteți";
|
||||
|
||||
"new_message" = "Mesaj nou";
|
||||
@ -22,8 +22,8 @@
|
||||
// https://github.com/jessesquires/JSQMessagesViewController/issues/237
|
||||
// ********************************
|
||||
|
||||
"Load Earlier Messages" = "Предыдущие сообщения";
|
||||
"load_earlier_messages" = "Предыдущие сообщения";
|
||||
|
||||
"Send" = "Отпр";
|
||||
"send" = "Отпр";
|
||||
|
||||
"New Message" = "Сообщение";
|
||||
"new_message" = "Сообщение";
|
||||
@ -0,0 +1,29 @@
|
||||
//
|
||||
// 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
|
||||
//
|
||||
|
||||
// ********************************
|
||||
// Special thanks to the localization contributors!
|
||||
//
|
||||
// https://github.com/jessesquires/JSQMessagesViewController/issues/237
|
||||
// ********************************
|
||||
|
||||
"load_earlier_messages" = "โหลดข้อความก่อนหน้า";
|
||||
|
||||
"send" = "ส่ง";
|
||||
|
||||
"new_message" = "ข้อความใหม่";
|
||||
@ -22,8 +22,8 @@
|
||||
// https://github.com/jessesquires/JSQMessagesViewController/issues/237
|
||||
// ********************************
|
||||
|
||||
"Load Earlier Messages" = "Eski mesajları yükle";
|
||||
"load_earlier_messages" = "Eski mesajları yükle";
|
||||
|
||||
"Send" = "Gönder";
|
||||
"send" = "Gönder";
|
||||
|
||||
"New Message" = "Yeni Mesaj";
|
||||
"new_message" = "Yeni Mesaj";
|
||||
@ -0,0 +1,29 @@
|
||||
//
|
||||
// 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
|
||||
//
|
||||
|
||||
// ********************************
|
||||
// Special thanks to the localization contributors!
|
||||
//
|
||||
// https://github.com/jessesquires/JSQMessagesViewController/issues/237
|
||||
// ********************************
|
||||
|
||||
"load_earlier_messages" = "Tải thêm tin nhắn";
|
||||
|
||||
"send" = "Gửi";
|
||||
|
||||
"new_message" = "Tin nhắn mới";
|
||||
@ -22,8 +22,8 @@
|
||||
// https://github.com/jessesquires/JSQMessagesViewController/issues/237
|
||||
// ********************************
|
||||
|
||||
"Load Earlier Messages" = "Load Earlier Messages";
|
||||
"load_earlier_messages" = "载入较早的信息";
|
||||
|
||||
"Send" = "Send";
|
||||
"send" = "发送";
|
||||
|
||||
"New Message" = "New Message";
|
||||
"new_message" = "新信息";
|
||||
@ -22,8 +22,8 @@
|
||||
// https://github.com/jessesquires/JSQMessagesViewController/issues/237
|
||||
// ********************************
|
||||
|
||||
"Load Earlier Messages" = "载入较早的信息";
|
||||
"load_earlier_messages" = "載入之前的訊息";
|
||||
|
||||
"Send" = "发送";
|
||||
"send" = "傳送";
|
||||
|
||||
"New Message" = "新信息";
|
||||
"new_message" = "新信息";
|
||||
@ -1,29 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
//
|
||||
|
||||
// ********************************
|
||||
// Special thanks to the localization contributors!
|
||||
//
|
||||
// https://github.com/jessesquires/JSQMessagesViewController/issues/237
|
||||
// ********************************
|
||||
|
||||
"Load Earlier Messages" = "טען הודעות קודמות";
|
||||
|
||||
"Send" = "שלח";
|
||||
|
||||
"New Message" = "הודעה חדשה";
|
||||
@ -1,29 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
//
|
||||
|
||||
// ********************************
|
||||
// Special thanks to the localization contributors!
|
||||
//
|
||||
// https://github.com/jessesquires/JSQMessagesViewController/issues/237
|
||||
// ********************************
|
||||
|
||||
"Load Earlier Messages" = "Carica messaggi precedenti";
|
||||
|
||||
"Send" = "Invia";
|
||||
|
||||
"New Message" = "Nuovo Messaggio";
|
||||
@ -1,29 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
//
|
||||
|
||||
// ********************************
|
||||
// Special thanks to the localization contributors!
|
||||
//
|
||||
// https://github.com/jessesquires/JSQMessagesViewController/issues/237
|
||||
// ********************************
|
||||
|
||||
"Load Earlier Messages" = "Otwórz wcześniejsze wiadomości";
|
||||
|
||||
"Send" = "Wyślij";
|
||||
|
||||
"New Message" = "Nowa wiadomość";
|
||||
@ -1,29 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
//
|
||||
|
||||
// ********************************
|
||||
// Special thanks to the localization contributors!
|
||||
//
|
||||
// https://github.com/jessesquires/JSQMessagesViewController/issues/237
|
||||
// ********************************
|
||||
|
||||
"Load Earlier Messages" = "Carregar mensagens anteriore";
|
||||
|
||||
"Send" = "Enviar";
|
||||
|
||||
"New Message" = "Nova Mensagem";
|
||||
@ -1,29 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
//
|
||||
|
||||
// ********************************
|
||||
// Special thanks to the localization contributors!
|
||||
//
|
||||
// https://github.com/jessesquires/JSQMessagesViewController/issues/237
|
||||
// ********************************
|
||||
|
||||
"Load Earlier Messages" = "Încărcați mesajele anterioare";
|
||||
|
||||
"Send" = "Trimiteți";
|
||||
|
||||
"New Message" = "Mesaj nou";
|
||||
@ -18,34 +18,58 @@
|
||||
|
||||
#import "JSQSystemSoundPlayer+JSQMessages.h"
|
||||
|
||||
static NSString * const kJSQMessageReceivedSoundName = @"JSQMessagesAssets.bundle/Sounds/message_received";
|
||||
static NSString * const kJSQMessageSentSoundName = @"JSQMessagesAssets.bundle/Sounds/message_sent";
|
||||
#import "NSBundle+JSQMessages.h"
|
||||
|
||||
|
||||
static NSString * const kJSQMessageReceivedSoundName = @"message_received";
|
||||
static NSString * const kJSQMessageSentSoundName = @"message_sent";
|
||||
|
||||
|
||||
@implementation JSQSystemSoundPlayer (JSQMessages)
|
||||
|
||||
#pragma mark - Public
|
||||
|
||||
+ (void)jsq_playMessageReceivedSound
|
||||
{
|
||||
[[JSQSystemSoundPlayer sharedPlayer] playSoundWithFilename:kJSQMessageReceivedSoundName
|
||||
fileExtension:kJSQSystemSoundTypeAIFF];
|
||||
[self jsq_playSoundFromJSQMessagesBundleWithName:kJSQMessageReceivedSoundName asAlert:NO];
|
||||
}
|
||||
|
||||
+ (void)jsq_playMessageReceivedAlert
|
||||
{
|
||||
[[JSQSystemSoundPlayer sharedPlayer] playAlertSoundWithFilename:kJSQMessageReceivedSoundName
|
||||
fileExtension:kJSQSystemSoundTypeAIFF];
|
||||
[self jsq_playSoundFromJSQMessagesBundleWithName:kJSQMessageReceivedSoundName asAlert:YES];
|
||||
}
|
||||
|
||||
+ (void)jsq_playMessageSentSound
|
||||
{
|
||||
[[JSQSystemSoundPlayer sharedPlayer] playSoundWithFilename:kJSQMessageSentSoundName
|
||||
fileExtension:kJSQSystemSoundTypeAIFF];
|
||||
[self jsq_playSoundFromJSQMessagesBundleWithName:kJSQMessageSentSoundName asAlert:NO];
|
||||
}
|
||||
|
||||
+ (void)jsq_playMessageSentAlert
|
||||
{
|
||||
[[JSQSystemSoundPlayer sharedPlayer] playAlertSoundWithFilename:kJSQMessageSentSoundName
|
||||
fileExtension:kJSQSystemSoundTypeAIFF];
|
||||
[self jsq_playSoundFromJSQMessagesBundleWithName:kJSQMessageSentSoundName asAlert:YES];
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
+ (void)jsq_playSoundFromJSQMessagesBundleWithName:(NSString *)soundName asAlert:(BOOL)asAlert
|
||||
{
|
||||
// save sound player original bundle
|
||||
NSString *originalPlayerBundleIdentifier = [JSQSystemSoundPlayer sharedPlayer].bundle.bundleIdentifier;
|
||||
|
||||
// search for sounds in this library's bundle
|
||||
[JSQSystemSoundPlayer sharedPlayer].bundle = [NSBundle jsq_messagesBundle];
|
||||
|
||||
NSString *fileName = [NSString stringWithFormat:@"JSQMessagesAssets.bundle/Sounds/%@", soundName];
|
||||
|
||||
if (asAlert) {
|
||||
[[JSQSystemSoundPlayer sharedPlayer] playAlertSoundWithFilename:fileName fileExtension:kJSQSystemSoundTypeAIFF];
|
||||
}
|
||||
else {
|
||||
[[JSQSystemSoundPlayer sharedPlayer] playSoundWithFilename:fileName fileExtension:kJSQSystemSoundTypeAIFF];
|
||||
}
|
||||
|
||||
// restore original bundle
|
||||
[JSQSystemSoundPlayer sharedPlayer].bundle = [NSBundle bundleWithIdentifier:originalPlayerBundleIdentifier];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
42
JSQMessagesViewController/Categories/NSBundle+JSQMessages.h
Normal file
42
JSQMessagesViewController/Categories/NSBundle+JSQMessages.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>
|
||||
|
||||
@interface NSBundle (JSQMessages)
|
||||
|
||||
/**
|
||||
* @return The bundle for JSQMessagesViewController.
|
||||
*/
|
||||
+ (NSBundle *)jsq_messagesBundle;
|
||||
|
||||
/**
|
||||
* @return The bundle for assets in JSQMessagesViewController.
|
||||
*/
|
||||
+ (NSBundle *)jsq_messagesAssetBundle;
|
||||
|
||||
/**
|
||||
* Returns a localized version of the string designated by the specified key and residing in the JSQMessages table.
|
||||
*
|
||||
* @param key The key for a string in the JSQMessages table.
|
||||
*
|
||||
* @return A localized version of the string designated by key in the JSQMessages table.
|
||||
*/
|
||||
+ (NSString *)jsq_localizedStringForKey:(NSString *)key;
|
||||
|
||||
@end
|
||||
44
JSQMessagesViewController/Categories/NSBundle+JSQMessages.m
Normal file
44
JSQMessagesViewController/Categories/NSBundle+JSQMessages.m
Normal file
@ -0,0 +1,44 @@
|
||||
//
|
||||
// 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 "NSBundle+JSQMessages.h"
|
||||
|
||||
#import "JSQMessagesViewController.h"
|
||||
|
||||
@implementation NSBundle (JSQMessages)
|
||||
|
||||
+ (NSBundle *)jsq_messagesBundle
|
||||
{
|
||||
return [NSBundle bundleForClass:[JSQMessagesViewController class]];
|
||||
}
|
||||
|
||||
+ (NSBundle *)jsq_messagesAssetBundle
|
||||
{
|
||||
NSString *bundleResourcePath = [NSBundle jsq_messagesBundle].resourcePath;
|
||||
NSString *assetPath = [bundleResourcePath stringByAppendingPathComponent:@"JSQMessagesAssets.bundle"];
|
||||
return [NSBundle bundleWithPath:assetPath];
|
||||
}
|
||||
|
||||
+ (NSString *)jsq_localizedStringForKey:(NSString *)key
|
||||
{
|
||||
// In Signal, we prefer to use our own translations to the incomplete translations in this bundle
|
||||
// return NSLocalizedStringFromTableInBundle(key, @"JSQMessages", [NSBundle jsq_messagesAssetBundle], nil);
|
||||
return NSLocalizedString(key, nil);
|
||||
}
|
||||
|
||||
@end
|
||||
@ -25,4 +25,9 @@
|
||||
*/
|
||||
+ (BOOL)jsq_isCurrentDeviceBeforeiOS8;
|
||||
|
||||
/**
|
||||
* @return Whether or not the current device is running a version of iOS after 9.0.
|
||||
*/
|
||||
+ (BOOL)jsq_isCurrentDeviceAfteriOS9;
|
||||
|
||||
@end
|
||||
|
||||
@ -23,7 +23,13 @@
|
||||
+ (BOOL)jsq_isCurrentDeviceBeforeiOS8
|
||||
{
|
||||
// iOS < 8.0
|
||||
return [[UIDevice currentDevice].systemVersion compare:@"8.0.0" options:NSNumericSearch] == NSOrderedAscending;
|
||||
return [[UIDevice currentDevice].systemVersion compare:@"8.0" options:NSNumericSearch] == NSOrderedAscending;
|
||||
}
|
||||
|
||||
+ (BOOL)jsq_isCurrentDeviceAfteriOS9
|
||||
{
|
||||
// iOS > 9.0
|
||||
return [[UIDevice currentDevice].systemVersion compare:@"9.0" options:NSNumericSearch] == NSOrderedDescending;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@ -52,7 +52,7 @@
|
||||
/**
|
||||
* @return The compact message bubble image.
|
||||
*
|
||||
* @disscussion This is the default bubble image used by `JSQMessagesBubbleImageFactory`.
|
||||
* @discussion This is the default bubble image used by `JSQMessagesBubbleImageFactory`.
|
||||
*/
|
||||
+ (UIImage *)jsq_bubbleCompactImage;
|
||||
|
||||
@ -76,4 +76,9 @@
|
||||
*/
|
||||
+ (UIImage *)jsq_defaultPlayImage;
|
||||
|
||||
/**
|
||||
* @return The default pause icon image.
|
||||
*/
|
||||
+ (UIImage *)jsq_defaultPauseImage;
|
||||
|
||||
@end
|
||||
|
||||
@ -18,6 +18,9 @@
|
||||
|
||||
#import "UIImage+JSQMessages.h"
|
||||
|
||||
#import "NSBundle+JSQMessages.h"
|
||||
|
||||
|
||||
@implementation UIImage (JSQMessages)
|
||||
|
||||
- (UIImage *)jsq_imageMaskedWithColor:(UIColor *)maskColor
|
||||
@ -47,7 +50,9 @@
|
||||
|
||||
+ (UIImage *)jsq_bubbleImageFromBundleWithName:(NSString *)name
|
||||
{
|
||||
return [UIImage imageNamed:[NSString stringWithFormat:@"JSQMessagesAssets.bundle/Images/%@", name]];
|
||||
NSBundle *bundle = [NSBundle jsq_messagesAssetBundle];
|
||||
NSString *path = [bundle pathForResource:name ofType:@"png" inDirectory:@"Images"];
|
||||
return [UIImage imageWithContentsOfFile:path];
|
||||
}
|
||||
|
||||
+ (UIImage *)jsq_bubbleRegularImage
|
||||
@ -95,4 +100,9 @@
|
||||
return [UIImage jsq_bubbleImageFromBundleWithName:@"play"];
|
||||
}
|
||||
|
||||
+ (UIImage *)jsq_defaultPauseImage
|
||||
{
|
||||
return [UIImage jsq_bubbleImageFromBundleWithName:@"pause"];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@ -59,13 +59,6 @@ FOUNDATION_EXPORT NSString * const JSQMessagesKeyboardControllerUserInfoKeyKeybo
|
||||
*/
|
||||
- (void)keyboardController:(JSQMessagesKeyboardController *)keyboardController keyboardDidChangeFrame:(CGRect)keyboardFrame;
|
||||
|
||||
/**
|
||||
* Tells the delegate that the keyboard has been hidden.
|
||||
*
|
||||
* @param keyboardController The keyboard controller that is notifying the delegate.
|
||||
*/
|
||||
- (void)keyboardControllerKeyboardDidHide:(JSQMessagesKeyboardController *)keyboardController;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@ -117,6 +110,11 @@ FOUNDATION_EXPORT NSString * const JSQMessagesKeyboardControllerUserInfoKeyKeybo
|
||||
*/
|
||||
@property (assign, nonatomic, readonly) CGRect currentKeyboardFrame;
|
||||
|
||||
/**
|
||||
* Not a valid initializer.
|
||||
*/
|
||||
- (id)init NS_UNAVAILABLE;
|
||||
|
||||
/**
|
||||
* Creates a new keyboard controller object with the specified textView, contextView, panGestureRecognizer, and delegate.
|
||||
*
|
||||
@ -130,7 +128,7 @@ FOUNDATION_EXPORT NSString * const JSQMessagesKeyboardControllerUserInfoKeyKeybo
|
||||
- (instancetype)initWithTextView:(UITextView *)textView
|
||||
contextView:(UIView *)contextView
|
||||
panGestureRecognizer:(UIPanGestureRecognizer *)panGestureRecognizer
|
||||
delegate:(id<JSQMessagesKeyboardControllerDelegate>)delegate;
|
||||
delegate:(id<JSQMessagesKeyboardControllerDelegate>)delegate NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
* Tells the keyboard controller that it should begin listening for system keyboard notifications.
|
||||
|
||||
@ -39,24 +39,7 @@ typedef void (^JSQAnimationCompletionBlock)(BOOL finished);
|
||||
|
||||
@property (assign, nonatomic) BOOL jsq_isObserving;
|
||||
|
||||
@property (weak, nonatomic) UIView *keyboardView;
|
||||
|
||||
- (void)jsq_registerForNotifications;
|
||||
- (void)jsq_unregisterForNotifications;
|
||||
|
||||
- (void)jsq_didReceiveKeyboardDidShowNotification:(NSNotification *)notification;
|
||||
- (void)jsq_didReceiveKeyboardWillChangeFrameNotification:(NSNotification *)notification;
|
||||
- (void)jsq_didReceiveKeyboardDidChangeFrameNotification:(NSNotification *)notification;
|
||||
- (void)jsq_didReceiveKeyboardDidHideNotification:(NSNotification *)notification;
|
||||
- (void)jsq_handleKeyboardNotification:(NSNotification *)notification completion:(JSQAnimationCompletionBlock)completion;
|
||||
|
||||
- (void)jsq_setKeyboardViewHidden:(BOOL)hidden;
|
||||
|
||||
- (void)jsq_notifyKeyboardFrameNotificationForFrame:(CGRect)frame;
|
||||
|
||||
- (void)jsq_removeKeyboardFrameObserver;
|
||||
|
||||
- (void)jsq_handlePanGestureRecognizer:(UIPanGestureRecognizer *)pan;
|
||||
@property (strong, nonatomic) UIView *keyboardView;
|
||||
|
||||
@end
|
||||
|
||||
@ -75,7 +58,7 @@ typedef void (^JSQAnimationCompletionBlock)(BOOL finished);
|
||||
NSParameterAssert(textView != nil);
|
||||
NSParameterAssert(contextView != nil);
|
||||
NSParameterAssert(panGestureRecognizer != nil);
|
||||
|
||||
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_textView = textView;
|
||||
@ -91,11 +74,8 @@ typedef void (^JSQAnimationCompletionBlock)(BOOL finished);
|
||||
{
|
||||
[self jsq_removeKeyboardFrameObserver];
|
||||
[self jsq_unregisterForNotifications];
|
||||
_textView = nil;
|
||||
_contextView = nil;
|
||||
_panGestureRecognizer = nil;
|
||||
_delegate = nil;
|
||||
_keyboardView = nil;
|
||||
}
|
||||
|
||||
#pragma mark - Setters
|
||||
@ -105,15 +85,15 @@ typedef void (^JSQAnimationCompletionBlock)(BOOL finished);
|
||||
if (_keyboardView) {
|
||||
[self jsq_removeKeyboardFrameObserver];
|
||||
}
|
||||
|
||||
|
||||
_keyboardView = keyboardView;
|
||||
|
||||
|
||||
if (keyboardView && !_jsq_isObserving) {
|
||||
[_keyboardView addObserver:self
|
||||
forKeyPath:NSStringFromSelector(@selector(frame))
|
||||
options:(NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew)
|
||||
context:kJSQMessagesKeyboardControllerKeyValueObservingContext];
|
||||
|
||||
|
||||
_jsq_isObserving = YES;
|
||||
}
|
||||
}
|
||||
@ -130,7 +110,7 @@ typedef void (^JSQAnimationCompletionBlock)(BOOL finished);
|
||||
if (!self.keyboardIsVisible) {
|
||||
return CGRectNull;
|
||||
}
|
||||
|
||||
|
||||
return self.keyboardView.frame;
|
||||
}
|
||||
|
||||
@ -141,16 +121,18 @@ typedef void (^JSQAnimationCompletionBlock)(BOOL finished);
|
||||
if (self.textView.inputAccessoryView == nil) {
|
||||
self.textView.inputAccessoryView = [[UIView alloc] init];
|
||||
}
|
||||
|
||||
|
||||
[self jsq_registerForNotifications];
|
||||
}
|
||||
|
||||
- (void)endListeningForKeyboard
|
||||
{
|
||||
[self jsq_unregisterForNotifications];
|
||||
|
||||
|
||||
[self jsq_setKeyboardViewHidden:NO];
|
||||
self.keyboardView = nil;
|
||||
// No need to set keyboardView to nil here
|
||||
// Plus we need it for future "dismiss keyboard" gesture recognition.
|
||||
// self.keyboardView = nil;
|
||||
}
|
||||
|
||||
#pragma mark - Notifications
|
||||
@ -158,22 +140,22 @@ typedef void (^JSQAnimationCompletionBlock)(BOOL finished);
|
||||
- (void)jsq_registerForNotifications
|
||||
{
|
||||
[self jsq_unregisterForNotifications];
|
||||
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(jsq_didReceiveKeyboardDidShowNotification:)
|
||||
name:UIKeyboardDidShowNotification
|
||||
object:nil];
|
||||
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(jsq_didReceiveKeyboardWillChangeFrameNotification:)
|
||||
name:UIKeyboardWillChangeFrameNotification
|
||||
object:nil];
|
||||
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(jsq_didReceiveKeyboardDidChangeFrameNotification:)
|
||||
name:UIKeyboardDidChangeFrameNotification
|
||||
object:nil];
|
||||
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(jsq_didReceiveKeyboardDidHideNotification:)
|
||||
name:UIKeyboardDidHideNotification
|
||||
@ -187,9 +169,24 @@ typedef void (^JSQAnimationCompletionBlock)(BOOL finished);
|
||||
|
||||
- (void)jsq_didReceiveKeyboardDidShowNotification:(NSNotification *)notification
|
||||
{
|
||||
self.keyboardView = self.textView.inputAccessoryView.superview;
|
||||
UIView *keyboardViewProxy = self.textView.inputAccessoryView.superview;
|
||||
if ([UIDevice jsq_isCurrentDeviceAfteriOS9]) {
|
||||
NSPredicate *windowPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", NSClassFromString(@"UIRemoteKeyboardWindow")];
|
||||
UIWindow *keyboardWindow = [[UIApplication sharedApplication].windows filteredArrayUsingPredicate:windowPredicate].firstObject;
|
||||
|
||||
for (UIView *subview in keyboardWindow.subviews) {
|
||||
for (UIView *hostview in subview.subviews) {
|
||||
if ([hostview isMemberOfClass:NSClassFromString(@"UIInputSetHostView")]) {
|
||||
keyboardViewProxy = hostview;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.keyboardView = keyboardViewProxy;
|
||||
}
|
||||
|
||||
[self jsq_setKeyboardViewHidden:NO];
|
||||
|
||||
|
||||
[self jsq_handleKeyboardNotification:notification completion:^(BOOL finished) {
|
||||
[self.panGestureRecognizer addTarget:self action:@selector(jsq_handlePanGestureRecognizer:)];
|
||||
}];
|
||||
@ -203,38 +200,36 @@ typedef void (^JSQAnimationCompletionBlock)(BOOL finished);
|
||||
- (void)jsq_didReceiveKeyboardDidChangeFrameNotification:(NSNotification *)notification
|
||||
{
|
||||
[self jsq_setKeyboardViewHidden:NO];
|
||||
|
||||
|
||||
[self jsq_handleKeyboardNotification:notification completion:nil];
|
||||
}
|
||||
|
||||
- (void)jsq_didReceiveKeyboardDidHideNotification:(NSNotification *)notification
|
||||
{
|
||||
self.keyboardView = nil;
|
||||
|
||||
|
||||
[self jsq_handleKeyboardNotification:notification completion:^(BOOL finished) {
|
||||
[self.panGestureRecognizer removeTarget:self action:NULL];
|
||||
|
||||
[self.delegate keyboardControllerKeyboardDidHide:self];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)jsq_handleKeyboardNotification:(NSNotification *)notification completion:(JSQAnimationCompletionBlock)completion
|
||||
{
|
||||
NSDictionary *userInfo = [notification userInfo];
|
||||
|
||||
|
||||
CGRect keyboardEndFrame = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
|
||||
|
||||
|
||||
if (CGRectIsNull(keyboardEndFrame)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
UIViewAnimationCurve animationCurve = [userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue];
|
||||
NSInteger animationCurveOption = (animationCurve << 16);
|
||||
|
||||
|
||||
double animationDuration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
|
||||
|
||||
|
||||
CGRect keyboardEndFrameConverted = [self.contextView convertRect:keyboardEndFrame fromView:nil];
|
||||
|
||||
|
||||
[UIView animateWithDuration:animationDuration
|
||||
delay:0.0
|
||||
options:animationCurveOption
|
||||
@ -259,31 +254,37 @@ typedef void (^JSQAnimationCompletionBlock)(BOOL finished);
|
||||
- (void)jsq_notifyKeyboardFrameNotificationForFrame:(CGRect)frame
|
||||
{
|
||||
[self.delegate keyboardController:self keyboardDidChangeFrame:frame];
|
||||
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:JSQMessagesKeyboardControllerNotificationKeyboardDidChangeFrame
|
||||
object:self
|
||||
userInfo:@{ JSQMessagesKeyboardControllerUserInfoKeyKeyboardDidChangeFrame : [NSValue valueWithCGRect:frame] }];
|
||||
}
|
||||
|
||||
- (void)jsq_resetKeyboardAndTextView
|
||||
{
|
||||
[self jsq_setKeyboardViewHidden:YES];
|
||||
[self jsq_removeKeyboardFrameObserver];
|
||||
[self.textView resignFirstResponder];
|
||||
}
|
||||
|
||||
#pragma mark - Key-value observing
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
|
||||
{
|
||||
if (context == kJSQMessagesKeyboardControllerKeyValueObservingContext) {
|
||||
|
||||
|
||||
if (object == self.keyboardView && [keyPath isEqualToString:NSStringFromSelector(@selector(frame))]) {
|
||||
|
||||
|
||||
CGRect oldKeyboardFrame = [[change objectForKey:NSKeyValueChangeOldKey] CGRectValue];
|
||||
CGRect newKeyboardFrame = [[change objectForKey:NSKeyValueChangeNewKey] CGRectValue];
|
||||
|
||||
|
||||
if (CGRectEqualToRect(newKeyboardFrame, oldKeyboardFrame) || CGRectIsNull(newKeyboardFrame)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// do not convert frame to contextView coordinates here
|
||||
// KVO is triggered during panning (see below)
|
||||
// panning occurs in contextView coordinates already
|
||||
[self jsq_notifyKeyboardFrameNotificationForFrame:newKeyboardFrame];
|
||||
|
||||
CGRect keyboardEndFrameConverted = [self.contextView convertRect:newKeyboardFrame
|
||||
fromView:self.keyboardView.superview];
|
||||
[self jsq_notifyKeyboardFrameNotificationForFrame:keyboardEndFrameConverted];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -293,14 +294,14 @@ typedef void (^JSQAnimationCompletionBlock)(BOOL finished);
|
||||
if (!_jsq_isObserving) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@try {
|
||||
[_keyboardView removeObserver:self
|
||||
forKeyPath:NSStringFromSelector(@selector(frame))
|
||||
context:kJSQMessagesKeyboardControllerKeyValueObservingContext];
|
||||
}
|
||||
@catch (NSException * __unused exception) { }
|
||||
|
||||
|
||||
_jsq_isObserving = NO;
|
||||
}
|
||||
|
||||
@ -308,42 +309,42 @@ typedef void (^JSQAnimationCompletionBlock)(BOOL finished);
|
||||
|
||||
- (void)jsq_handlePanGestureRecognizer:(UIPanGestureRecognizer *)pan
|
||||
{
|
||||
CGPoint touch = [pan locationInView:self.contextView];
|
||||
|
||||
CGPoint touch = [pan locationInView:self.contextView.window];
|
||||
|
||||
// system keyboard is added to a new UIWindow, need to operate in window coordinates
|
||||
// also, keyboard always slides from bottom of screen, not the bottom of a view
|
||||
CGFloat contextViewWindowHeight = CGRectGetHeight(self.contextView.window.frame);
|
||||
|
||||
|
||||
if ([UIDevice jsq_isCurrentDeviceBeforeiOS8]) {
|
||||
// handle iOS 7 bug when rotating to landscape
|
||||
if (UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)) {
|
||||
contextViewWindowHeight = CGRectGetWidth(self.contextView.window.frame);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CGFloat keyboardViewHeight = CGRectGetHeight(self.keyboardView.frame);
|
||||
|
||||
|
||||
CGFloat dragThresholdY = (contextViewWindowHeight - keyboardViewHeight - self.keyboardTriggerPoint.y);
|
||||
|
||||
|
||||
CGRect newKeyboardViewFrame = self.keyboardView.frame;
|
||||
|
||||
|
||||
BOOL userIsDraggingNearThresholdForDismissing = (touch.y > dragThresholdY);
|
||||
|
||||
|
||||
self.keyboardView.userInteractionEnabled = !userIsDraggingNearThresholdForDismissing;
|
||||
|
||||
|
||||
switch (pan.state) {
|
||||
case UIGestureRecognizerStateChanged:
|
||||
{
|
||||
newKeyboardViewFrame.origin.y = touch.y + self.keyboardTriggerPoint.y;
|
||||
|
||||
|
||||
// bound frame between bottom of view and height of keyboard
|
||||
newKeyboardViewFrame.origin.y = MIN(newKeyboardViewFrame.origin.y, contextViewWindowHeight);
|
||||
newKeyboardViewFrame.origin.y = MAX(newKeyboardViewFrame.origin.y, contextViewWindowHeight - keyboardViewHeight);
|
||||
|
||||
|
||||
if (CGRectGetMinY(newKeyboardViewFrame) == CGRectGetMinY(self.keyboardView.frame)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
[UIView animateWithDuration:0.0
|
||||
delay:0.0
|
||||
options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionTransitionNone
|
||||
@ -353,22 +354,23 @@ typedef void (^JSQAnimationCompletionBlock)(BOOL finished);
|
||||
completion:nil];
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case UIGestureRecognizerStateEnded:
|
||||
case UIGestureRecognizerStateCancelled:
|
||||
case UIGestureRecognizerStateFailed:
|
||||
{
|
||||
BOOL keyboardViewIsHidden = (CGRectGetMinY(self.keyboardView.frame) >= contextViewWindowHeight);
|
||||
if (keyboardViewIsHidden) {
|
||||
[self jsq_resetKeyboardAndTextView];
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
CGPoint velocity = [pan velocityInView:self.contextView];
|
||||
BOOL userIsScrollingDown = (velocity.y > 0.0f);
|
||||
BOOL shouldHide = (userIsScrollingDown && userIsDraggingNearThresholdForDismissing);
|
||||
|
||||
|
||||
newKeyboardViewFrame.origin.y = shouldHide ? contextViewWindowHeight : (contextViewWindowHeight - keyboardViewHeight);
|
||||
|
||||
|
||||
[UIView animateWithDuration:0.25
|
||||
delay:0.0
|
||||
options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationCurveEaseOut
|
||||
@ -377,11 +379,9 @@ typedef void (^JSQAnimationCompletionBlock)(BOOL finished);
|
||||
}
|
||||
completion:^(BOOL finished) {
|
||||
self.keyboardView.userInteractionEnabled = !shouldHide;
|
||||
|
||||
|
||||
if (shouldHide) {
|
||||
[self jsq_setKeyboardViewHidden:YES];
|
||||
[self jsq_removeKeyboardFrameObserver];
|
||||
[self.textView resignFirstResponder];
|
||||
[self jsq_resetKeyboardAndTextView];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
@ -21,6 +21,7 @@
|
||||
#import "JSQMessagesCollectionView.h"
|
||||
#import "JSQMessagesCollectionViewFlowLayout.h"
|
||||
#import "JSQMessagesInputToolbar.h"
|
||||
#import "JSQMessagesKeyboardController.h"
|
||||
|
||||
/**
|
||||
* The `JSQMessagesViewController` class is an abstract class that represents a view controller whose content consists of
|
||||
@ -33,40 +34,43 @@
|
||||
UITextViewDelegate>
|
||||
|
||||
/**
|
||||
* Returns the collection view object managed by this view controller.
|
||||
* Returns the collection view object managed by this view controller.
|
||||
* This view controller is the collection view's data source and delegate.
|
||||
*/
|
||||
@property (weak, nonatomic, readonly) JSQMessagesCollectionView *collectionView;
|
||||
|
||||
/**
|
||||
* Returns the input toolbar view object managed by this view controller.
|
||||
* Returns the input toolbar view object managed by this view controller.
|
||||
* This view controller is the toolbar's delegate.
|
||||
*/
|
||||
@property (weak, nonatomic, readonly) JSQMessagesInputToolbar *inputToolbar;
|
||||
|
||||
/**
|
||||
* Returns the keyboard controller object used to manage the software keyboard.
|
||||
*/
|
||||
@property (strong, nonatomic) JSQMessagesKeyboardController *keyboardController;
|
||||
|
||||
/**
|
||||
* The display name of the current user who is sending messages.
|
||||
* This value does not have to be unique.
|
||||
*
|
||||
* @discussion This value must not be `nil`. The default value is `@"JSQDefaultSender"`.
|
||||
* @discussion This value does not have to be unique. This value must not be `nil`.
|
||||
*/
|
||||
@property (copy, nonatomic) NSString *senderDisplayName;
|
||||
|
||||
/**
|
||||
* The string identifier that uniquely identifies the current user sending messages.
|
||||
*
|
||||
*
|
||||
* @discussion This property is used to determine if a message is incoming or outgoing.
|
||||
* All message data objects returned by `collectionView:messageDataForItemAtIndexPath:` are
|
||||
* checked against this identifier.
|
||||
* This value must not be `nil`. The default value is `@"JSQDefaultSender"`.
|
||||
* checked against this identifier. This value must not be `nil`.
|
||||
*/
|
||||
@property (copy, nonatomic) NSString *senderId;
|
||||
|
||||
/**
|
||||
* Specifies whether or not the view controller should automatically scroll to the most recent message
|
||||
* Specifies whether or not the view controller should automatically scroll to the most recent message
|
||||
* when the view appears and when sending, receiving, and composing a new message.
|
||||
*
|
||||
* @discussion The default value is `YES`, which allows the view controller to scroll automatically to the most recent message.
|
||||
* @discussion The default value is `YES`, which allows the view controller to scroll automatically to the most recent message.
|
||||
* Set to `NO` if you want to manage scrolling yourself.
|
||||
*/
|
||||
@property (assign, nonatomic) BOOL automaticallyScrollsToMostRecentMessage;
|
||||
@ -78,7 +82,7 @@
|
||||
* @discussion This cell identifier is used for outgoing text message data items.
|
||||
* The default value is the string returned by `[JSQMessagesCollectionViewCellOutgoing cellReuseIdentifier]`.
|
||||
* This value must not be `nil`.
|
||||
*
|
||||
*
|
||||
* @see JSQMessagesCollectionViewCellOutgoing.
|
||||
*
|
||||
* @warning Overriding this property's default value is *not* recommended.
|
||||
@ -90,7 +94,7 @@
|
||||
@property (copy, nonatomic) NSString *outgoingCellIdentifier;
|
||||
|
||||
/**
|
||||
* The collection view cell identifier to use for dequeuing outgoing message collection view cells
|
||||
* The collection view cell identifier to use for dequeuing outgoing message collection view cells
|
||||
* in the collectionView for media messages.
|
||||
*
|
||||
* @discussion This cell identifier is used for outgoing media message data items.
|
||||
@ -173,7 +177,7 @@
|
||||
/**
|
||||
* Returns the `UINib` object initialized for a `JSQMessagesViewController`.
|
||||
*
|
||||
* @return The initialized `UINib` object or `nil` if there were errors during initialization
|
||||
* @return The initialized `UINib` object or `nil` if there were errors during initialization
|
||||
* or the nib file could not be located.
|
||||
*
|
||||
* @discussion You may override this method to provide a customized nib. If you do,
|
||||
@ -184,7 +188,7 @@
|
||||
|
||||
/**
|
||||
* Creates and returns a new `JSQMessagesViewController` object.
|
||||
*
|
||||
*
|
||||
* @discussion This is the designated initializer for programmatic instantiation.
|
||||
*
|
||||
* @return An initialized `JSQMessagesViewController` object if successful, `nil` otherwise.
|
||||
@ -264,4 +268,60 @@
|
||||
*/
|
||||
- (void)scrollToBottomAnimated:(BOOL)animated;
|
||||
|
||||
/**
|
||||
* Used to decide if a message is incoming or outgoing.
|
||||
*
|
||||
* @discussion The default implementation of this method compares the `senderId` of the message to the
|
||||
* value of the `senderId` property and returns `YES` if they are equal. Subclasses can override
|
||||
* this method to specialize the decision logic.
|
||||
*/
|
||||
- (BOOL)isOutgoingMessage:(id<JSQMessageData>)messageItem;
|
||||
|
||||
/**
|
||||
* Scrolls the collection view so that the cell at the specified indexPath is completely visible above the `inputToolbar`.
|
||||
*
|
||||
* @param indexPath The indexPath for the cell that will be visible.
|
||||
* @param animated Pass `YES` if you want to animate scrolling, `NO` otherwise.
|
||||
*/
|
||||
- (void)scrollToIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated;
|
||||
|
||||
/**
|
||||
Call to super required.
|
||||
*/
|
||||
- (void)viewDidLoad NS_REQUIRES_SUPER;
|
||||
|
||||
/**
|
||||
Call to super required.
|
||||
*/
|
||||
- (void)viewWillAppear:(BOOL)animated NS_REQUIRES_SUPER;
|
||||
|
||||
/**
|
||||
Call to super required.
|
||||
*/
|
||||
- (void)viewDidAppear:(BOOL)animated NS_REQUIRES_SUPER;
|
||||
|
||||
/**
|
||||
Call to super required.
|
||||
*/
|
||||
- (void)viewWillDisappear:(BOOL)animated NS_REQUIRES_SUPER;
|
||||
|
||||
/**
|
||||
Call to super required.
|
||||
*/
|
||||
- (void)viewDidDisappear:(BOOL)animated NS_REQUIRES_SUPER;
|
||||
|
||||
/**
|
||||
Called when `UIMenuControllerWillShowMenuNotification` is posted.
|
||||
|
||||
@param notification The posted notification.
|
||||
*/
|
||||
- (void)didReceiveMenuWillShowNotification:(NSNotification *)notification;
|
||||
|
||||
/**
|
||||
Called when `UIMenuControllerWillHideMenuNotification` is posted.
|
||||
|
||||
@param notification The posted notification.
|
||||
*/
|
||||
- (void)didReceiveMenuWillHideNotification:(NSNotification *)notification;
|
||||
|
||||
@end
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="6245" systemVersion="13F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="9531" systemVersion="15C50" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6238"/>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9529"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="JSQMessagesViewController">
|
||||
@ -45,9 +46,4 @@
|
||||
<nil key="simulatedStatusBarMetrics"/>
|
||||
</view>
|
||||
</objects>
|
||||
<simulatedMetricsContainer key="defaultSimulatedMetrics">
|
||||
<simulatedStatusBarMetrics key="statusBar"/>
|
||||
<simulatedOrientationMetrics key="orientation"/>
|
||||
<simulatedScreenMetrics key="destination" type="retina4"/>
|
||||
</simulatedMetricsContainer>
|
||||
</document>
|
||||
|
||||
@ -21,22 +21,6 @@
|
||||
#import "UIColor+JSQMessages.h"
|
||||
|
||||
|
||||
@interface JSQMessagesAvatarImageFactory ()
|
||||
|
||||
+ (UIImage *)jsq_circularImage:(UIImage *)image
|
||||
withDiamter:(NSUInteger)diameter
|
||||
highlightedColor:(UIColor *)highlightedColor;
|
||||
|
||||
+ (UIImage *)jsq_imageWitInitials:(NSString *)initials
|
||||
backgroundColor:(UIColor *)backgroundColor
|
||||
textColor:(UIColor *)textColor
|
||||
font:(UIFont *)font
|
||||
diameter:(NSUInteger)diameter;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
@implementation JSQMessagesAvatarImageFactory
|
||||
|
||||
#pragma mark - Public
|
||||
@ -44,9 +28,9 @@
|
||||
+ (JSQMessagesAvatarImage *)avatarImageWithPlaceholder:(UIImage *)placeholderImage diameter:(NSUInteger)diameter
|
||||
{
|
||||
UIImage *circlePlaceholderImage = [JSQMessagesAvatarImageFactory jsq_circularImage:placeholderImage
|
||||
withDiamter:diameter
|
||||
withDiameter:diameter
|
||||
highlightedColor:nil];
|
||||
|
||||
|
||||
return [JSQMessagesAvatarImage avatarImageWithPlaceholder:circlePlaceholderImage];
|
||||
}
|
||||
|
||||
@ -54,7 +38,7 @@
|
||||
{
|
||||
UIImage *avatar = [JSQMessagesAvatarImageFactory circularAvatarImage:image withDiameter:diameter];
|
||||
UIImage *highlightedAvatar = [JSQMessagesAvatarImageFactory circularAvatarHighlightedImage:image withDiameter:diameter];
|
||||
|
||||
|
||||
return [[JSQMessagesAvatarImage alloc] initWithAvatarImage:avatar
|
||||
highlightedImage:highlightedAvatar
|
||||
placeholderImage:avatar];
|
||||
@ -63,14 +47,14 @@
|
||||
+ (UIImage *)circularAvatarImage:(UIImage *)image withDiameter:(NSUInteger)diameter
|
||||
{
|
||||
return [JSQMessagesAvatarImageFactory jsq_circularImage:image
|
||||
withDiamter:diameter
|
||||
withDiameter:diameter
|
||||
highlightedColor:nil];
|
||||
}
|
||||
|
||||
+ (UIImage *)circularAvatarHighlightedImage:(UIImage *)image withDiameter:(NSUInteger)diameter
|
||||
{
|
||||
return [JSQMessagesAvatarImageFactory jsq_circularImage:image
|
||||
withDiamter:diameter
|
||||
withDiameter:diameter
|
||||
highlightedColor:[UIColor colorWithWhite:0.1f alpha:0.3f]];
|
||||
}
|
||||
|
||||
@ -85,11 +69,11 @@
|
||||
textColor:textColor
|
||||
font:font
|
||||
diameter:diameter];
|
||||
|
||||
|
||||
UIImage *avatarHighlightedImage = [JSQMessagesAvatarImageFactory jsq_circularImage:avatarImage
|
||||
withDiamter:diameter
|
||||
withDiameter:diameter
|
||||
highlightedColor:[UIColor colorWithWhite:0.1f alpha:0.3f]];
|
||||
|
||||
|
||||
return [[JSQMessagesAvatarImage alloc] initWithAvatarImage:avatarImage
|
||||
highlightedImage:avatarHighlightedImage
|
||||
placeholderImage:avatarImage];
|
||||
@ -108,70 +92,64 @@
|
||||
NSParameterAssert(textColor != nil);
|
||||
NSParameterAssert(font != nil);
|
||||
NSParameterAssert(diameter > 0);
|
||||
|
||||
|
||||
CGRect frame = CGRectMake(0.0f, 0.0f, diameter, diameter);
|
||||
|
||||
NSString *text = [initials uppercaseStringWithLocale:[NSLocale currentLocale]];
|
||||
|
||||
|
||||
NSDictionary *attributes = @{ NSFontAttributeName : font,
|
||||
NSForegroundColorAttributeName : textColor };
|
||||
|
||||
CGRect textFrame = [text boundingRectWithSize:frame.size
|
||||
options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading)
|
||||
attributes:attributes
|
||||
context:nil];
|
||||
|
||||
|
||||
CGRect textFrame = [initials boundingRectWithSize:frame.size
|
||||
options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading)
|
||||
attributes:attributes
|
||||
context:nil];
|
||||
|
||||
CGPoint frameMidPoint = CGPointMake(CGRectGetMidX(frame), CGRectGetMidY(frame));
|
||||
CGPoint textFrameMidPoint = CGPointMake(CGRectGetMidX(textFrame), CGRectGetMidY(textFrame));
|
||||
|
||||
|
||||
CGFloat dx = frameMidPoint.x - textFrameMidPoint.x;
|
||||
CGFloat dy = frameMidPoint.y - textFrameMidPoint.y;
|
||||
CGPoint drawPoint = CGPointMake(dx, dy);
|
||||
UIImage *image = nil;
|
||||
|
||||
|
||||
UIGraphicsBeginImageContextWithOptions(frame.size, NO, [UIScreen mainScreen].scale);
|
||||
{
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
CGContextSaveGState(context);
|
||||
|
||||
|
||||
CGContextSetFillColorWithColor(context, backgroundColor.CGColor);
|
||||
CGContextFillRect(context, frame);
|
||||
[text drawAtPoint:drawPoint withAttributes:attributes];
|
||||
|
||||
[initials drawAtPoint:drawPoint withAttributes:attributes];
|
||||
|
||||
image = UIGraphicsGetImageFromCurrentImageContext();
|
||||
|
||||
CGContextRestoreGState(context);
|
||||
|
||||
}
|
||||
UIGraphicsEndImageContext();
|
||||
|
||||
return [JSQMessagesAvatarImageFactory jsq_circularImage:image withDiamter:diameter highlightedColor:nil];
|
||||
|
||||
return [JSQMessagesAvatarImageFactory jsq_circularImage:image withDiameter:diameter highlightedColor:nil];
|
||||
}
|
||||
|
||||
+ (UIImage *)jsq_circularImage:(UIImage *)image withDiamter:(NSUInteger)diameter highlightedColor:(UIColor *)highlightedColor
|
||||
+ (UIImage *)jsq_circularImage:(UIImage *)image withDiameter:(NSUInteger)diameter highlightedColor:(UIColor *)highlightedColor
|
||||
{
|
||||
NSParameterAssert(image != nil);
|
||||
NSParameterAssert(diameter > 0);
|
||||
|
||||
|
||||
CGRect frame = CGRectMake(0.0f, 0.0f, diameter, diameter);
|
||||
UIImage *newImage = nil;
|
||||
|
||||
|
||||
UIGraphicsBeginImageContextWithOptions(frame.size, NO, [UIScreen mainScreen].scale);
|
||||
{
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
CGContextSaveGState(context);
|
||||
|
||||
|
||||
UIBezierPath *imgPath = [UIBezierPath bezierPathWithOvalInRect:frame];
|
||||
[imgPath addClip];
|
||||
[image drawInRect:frame];
|
||||
|
||||
|
||||
if (highlightedColor != nil) {
|
||||
CGContextSetFillColorWithColor(context, highlightedColor.CGColor);
|
||||
CGContextFillEllipseInRect(context, frame);
|
||||
}
|
||||
|
||||
|
||||
newImage = UIGraphicsGetImageFromCurrentImageContext();
|
||||
|
||||
CGContextRestoreGState(context);
|
||||
}
|
||||
UIGraphicsEndImageContext();
|
||||
|
||||
|
||||
@ -27,14 +27,6 @@
|
||||
@property (strong, nonatomic, readonly) UIImage *bubbleImage;
|
||||
@property (assign, nonatomic, readonly) UIEdgeInsets capInsets;
|
||||
|
||||
- (UIEdgeInsets)jsq_centerPointEdgeInsetsForImageSize:(CGSize)bubbleImageSize;
|
||||
|
||||
- (JSQMessagesBubbleImage *)jsq_messagesBubbleImageWithColor:(UIColor *)color flippedForIncoming:(BOOL)flippedForIncoming;
|
||||
|
||||
- (UIImage *)jsq_horizontallyFlippedImageFromImage:(UIImage *)image;
|
||||
|
||||
- (UIImage *)jsq_stretchableImageFromImage:(UIImage *)image withCapInsets:(UIEdgeInsets)capInsets;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@ -66,11 +58,6 @@
|
||||
return [self initWithBubbleImage:[UIImage jsq_bubbleCompactImage] capInsets:UIEdgeInsetsZero];
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
_bubbleImage = nil;
|
||||
}
|
||||
|
||||
#pragma mark - Public
|
||||
|
||||
- (JSQMessagesBubbleImage *)outgoingMessagesBubbleImageWithColor:(UIColor *)color
|
||||
|
||||
@ -61,7 +61,7 @@
|
||||
* @see JSQMessagesBubbleImageFactory.
|
||||
* @see JSQMessagesBubbleImage.
|
||||
*/
|
||||
- (instancetype)initWithBubbleImageFactory:(JSQMessagesBubbleImageFactory *)bubbleImageFactory;
|
||||
- (instancetype)initWithBubbleImageFactory:(JSQMessagesBubbleImageFactory *)bubbleImageFactory NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
* Applies an outgoing bubble image mask to the specified mediaView.
|
||||
|
||||
@ -21,13 +21,6 @@
|
||||
#import "JSQMessagesBubbleImageFactory.h"
|
||||
|
||||
|
||||
@interface JSQMessagesMediaViewBubbleImageMasker ()
|
||||
|
||||
- (void)jsq_maskView:(UIView *)view withImage:(UIImage *)image;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation JSQMessagesMediaViewBubbleImageMasker
|
||||
|
||||
#pragma mark - Initialization
|
||||
|
||||
@ -66,13 +66,6 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
_dateFormatter = nil;
|
||||
_dateTextAttributes = nil;
|
||||
_timeTextAttributes = nil;
|
||||
}
|
||||
|
||||
#pragma mark - Formatter
|
||||
|
||||
- (NSString *)timestampForDate:(NSDate *)date
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
|
||||
#import "UIColor+JSQMessages.h"
|
||||
#import "UIImage+JSQMessages.h"
|
||||
#import "NSBundle+JSQMessages.h"
|
||||
|
||||
|
||||
@implementation JSQMessagesToolbarButtonFactory
|
||||
@ -29,47 +30,49 @@
|
||||
UIImage *accessoryImage = [UIImage jsq_defaultAccessoryImage];
|
||||
UIImage *normalImage = [accessoryImage jsq_imageMaskedWithColor:[UIColor lightGrayColor]];
|
||||
UIImage *highlightedImage = [accessoryImage jsq_imageMaskedWithColor:[UIColor darkGrayColor]];
|
||||
|
||||
|
||||
UIButton *accessoryButton = [[UIButton alloc] initWithFrame:CGRectMake(0.0f, 0.0f, accessoryImage.size.width, 32.0f)];
|
||||
[accessoryButton setImage:normalImage forState:UIControlStateNormal];
|
||||
[accessoryButton setImage:highlightedImage forState:UIControlStateHighlighted];
|
||||
|
||||
|
||||
accessoryButton.contentMode = UIViewContentModeScaleAspectFit;
|
||||
accessoryButton.backgroundColor = [UIColor clearColor];
|
||||
accessoryButton.tintColor = [UIColor lightGrayColor];
|
||||
|
||||
accessoryButton.accessibilityLabel = [NSBundle jsq_localizedStringForKey:@"accessory_button_accessibility_label"];
|
||||
|
||||
return accessoryButton;
|
||||
}
|
||||
|
||||
+ (UIButton *)defaultSendButtonItem
|
||||
{
|
||||
NSString *sendTitle = NSLocalizedStringFromTable(@"Send", @"JSQMessages", @"Text for the send button on the messages view toolbar");
|
||||
|
||||
NSString *sendTitle = [NSBundle jsq_localizedStringForKey:@"send"];
|
||||
|
||||
UIButton *sendButton = [[UIButton alloc] initWithFrame:CGRectZero];
|
||||
[sendButton setTitle:sendTitle forState:UIControlStateNormal];
|
||||
[sendButton setTitleColor:[UIColor jsq_messageBubbleBlueColor] forState:UIControlStateNormal];
|
||||
[sendButton setTitleColor:[[UIColor jsq_messageBubbleBlueColor] jsq_colorByDarkeningColorWithValue:0.1f] forState:UIControlStateHighlighted];
|
||||
[sendButton setTitleColor:[UIColor lightGrayColor] forState:UIControlStateDisabled];
|
||||
|
||||
|
||||
sendButton.titleLabel.font = [UIFont boldSystemFontOfSize:17.0f];
|
||||
sendButton.titleLabel.adjustsFontSizeToFitWidth = YES;
|
||||
sendButton.titleLabel.minimumScaleFactor = 0.85f;
|
||||
sendButton.contentMode = UIViewContentModeCenter;
|
||||
sendButton.backgroundColor = [UIColor clearColor];
|
||||
sendButton.tintColor = [UIColor jsq_messageBubbleBlueColor];
|
||||
|
||||
|
||||
CGFloat maxHeight = 32.0f;
|
||||
|
||||
|
||||
CGRect sendTitleRect = [sendTitle boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, maxHeight)
|
||||
options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
|
||||
attributes:@{ NSFontAttributeName : sendButton.titleLabel.font }
|
||||
context:nil];
|
||||
|
||||
|
||||
sendButton.frame = CGRectMake(0.0f,
|
||||
0.0f,
|
||||
CGRectGetWidth(CGRectIntegral(sendTitleRect)),
|
||||
maxHeight);
|
||||
|
||||
|
||||
return sendButton;
|
||||
}
|
||||
|
||||
|
||||
@ -29,9 +29,12 @@
|
||||
#import "JSQMessagesLoadEarlierHeaderView.h"
|
||||
|
||||
// Layout
|
||||
#import "JSQMessagesBubbleSizeCalculating.h"
|
||||
#import "JSQMessagesBubblesSizeCalculator.h"
|
||||
#import "JSQMessagesCollectionViewFlowLayout.h"
|
||||
#import "JSQMessagesCollectionViewLayoutAttributes.h"
|
||||
#import "JSQMessagesCollectionViewFlowLayoutInvalidationContext.h"
|
||||
#import "JSQAudioMediaViewAttributes.h"
|
||||
|
||||
// Toolbar
|
||||
#import "JSQMessagesComposerTextView.h"
|
||||
@ -42,6 +45,7 @@
|
||||
#import "JSQMessage.h"
|
||||
|
||||
#import "JSQMediaItem.h"
|
||||
#import "JSQAudioMediaItem.h"
|
||||
#import "JSQPhotoMediaItem.h"
|
||||
#import "JSQLocationMediaItem.h"
|
||||
#import "JSQVideoMediaItem.h"
|
||||
@ -49,6 +53,8 @@
|
||||
#import "JSQMessagesBubbleImage.h"
|
||||
#import "JSQMessagesAvatarImage.h"
|
||||
|
||||
#import "JSQAudioMediaViewAttributes.h"
|
||||
|
||||
// Protocols
|
||||
#import "JSQMessageData.h"
|
||||
#import "JSQMessageMediaData.h"
|
||||
@ -70,5 +76,6 @@
|
||||
#import "UIColor+JSQMessages.h"
|
||||
#import "UIImage+JSQMessages.h"
|
||||
#import "UIView+JSQMessages.h"
|
||||
#import "NSBundle+JSQMessages.h"
|
||||
|
||||
#endif
|
||||
|
||||
117
JSQMessagesViewController/Layout/JSQAudioMediaViewAttributes.h
Normal file
117
JSQMessagesViewController/Layout/JSQAudioMediaViewAttributes.h
Normal file
@ -0,0 +1,117 @@
|
||||
//
|
||||
// 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 <AVFoundation/AVFoundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
An instance of `JSQAudioMediaViewAttributes` specifies the appearance configuration of a `JSQAudioMediaItem`.
|
||||
Use this class to customize the appearance of `JSQAudioMediaItem`.
|
||||
*/
|
||||
@interface JSQAudioMediaViewAttributes : NSObject
|
||||
|
||||
/**
|
||||
* The image for the play button. The default is a play icon.
|
||||
*/
|
||||
@property (nonatomic, strong) UIImage *playButtonImage;
|
||||
|
||||
/**
|
||||
* The image for the pause button. The default is a pause icon.
|
||||
*/
|
||||
@property (nonatomic, strong) UIImage *pauseButtonImage;
|
||||
|
||||
/**
|
||||
* The font for the elapsed time label. The default is a system font.
|
||||
*/
|
||||
@property (strong, nonatomic) UIFont *labelFont;
|
||||
|
||||
/**
|
||||
* Specifies whether to show fractions of a second for audio files with a duration of less than 1 minute.
|
||||
*/
|
||||
@property (nonatomic, assign) BOOL showFractionalSeconds;
|
||||
|
||||
/**
|
||||
* The background color for the player.
|
||||
*/
|
||||
@property (nonatomic, strong) UIColor *backgroundColor;
|
||||
|
||||
/**
|
||||
* The tint color for the player.
|
||||
*/
|
||||
@property (nonatomic, strong) UIColor *tintColor;
|
||||
|
||||
/**
|
||||
* Insets that sepcify the padding around the play/pause button and time label.
|
||||
*/
|
||||
@property (nonatomic, assign) UIEdgeInsets controlInsets;
|
||||
|
||||
/**
|
||||
* Specifies the padding between the button, progress bar, and label.
|
||||
*/
|
||||
@property (nonatomic, assign) CGFloat controlPadding;
|
||||
|
||||
/**
|
||||
* Specifies the audio category set prior to playback.
|
||||
*/
|
||||
@property (nonatomic, copy) NSString *audioCategory;
|
||||
|
||||
/**
|
||||
* Specifies the audio category options set prior to playback.
|
||||
*/
|
||||
@property (nonatomic) AVAudioSessionCategoryOptions audioCategoryOptions;
|
||||
|
||||
/**
|
||||
Initializes and returns a `JSQAudioMediaViewAttributes` instance having the specified attributes.
|
||||
|
||||
@param playButtonImage The image for the play button.
|
||||
@param pauseButtonImage The image for the pause button.
|
||||
@param labelFont The font for the elapsed time label.
|
||||
@param showFractionalSeconds Specifies whether to show fractions of a second for audio files with a duration of less than 1 minute.
|
||||
@param backgroundColor The background color for the player.
|
||||
@param tintColor The tint color for the player.
|
||||
@param controlInsets Insets that sepcify the padding around the play/pause button and time label.
|
||||
@param controlPadding Specifies the padding between the button, progress bar, and label.
|
||||
@param audioCategory Specifies the audio category set prior to playback.
|
||||
@param audioCategoryOptions Specifies the audio category options set prior to playback.
|
||||
|
||||
@return A new `JSQAudioMediaViewAttributes` instance
|
||||
*/
|
||||
- (instancetype)initWithPlayButtonImage:(UIImage *)playButtonImage
|
||||
pauseButtonImage:(UIImage *)pauseButtonImage
|
||||
labelFont:(UIFont *)labelFont
|
||||
showFractionalSecodns:(BOOL)showFractionalSeconds
|
||||
backgroundColor:(UIColor *)backgroundColor
|
||||
tintColor:(UIColor *)tintColor
|
||||
controlInsets:(UIEdgeInsets)controlInsets
|
||||
controlPadding:(CGFloat)controlPadding
|
||||
audioCategory:(NSString *)audioCategory
|
||||
audioCategoryOptions:(AVAudioSessionCategoryOptions)audioCategoryOptions NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
Initializes and returns a default `JSQAudioMediaViewAttributes` instance.
|
||||
|
||||
@return A new `JSQAudioMediaViewAttributes` instance
|
||||
*/
|
||||
- (instancetype)init;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
114
JSQMessagesViewController/Layout/JSQAudioMediaViewAttributes.m
Normal file
114
JSQMessagesViewController/Layout/JSQAudioMediaViewAttributes.m
Normal file
@ -0,0 +1,114 @@
|
||||
//
|
||||
// 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 "JSQAudioMediaViewAttributes.h"
|
||||
|
||||
#import "UIImage+JSQMessages.h"
|
||||
#import "UIColor+JSQMessages.h"
|
||||
|
||||
@implementation JSQAudioMediaViewAttributes
|
||||
|
||||
- (instancetype)initWithPlayButtonImage:(UIImage *)playButtonImage
|
||||
pauseButtonImage:(UIImage *)pauseButtonImage
|
||||
labelFont:(UIFont *)labelFont
|
||||
showFractionalSecodns:(BOOL)showFractionalSeconds
|
||||
backgroundColor:(UIColor *)backgroundColor
|
||||
tintColor:(UIColor *)tintColor
|
||||
controlInsets:(UIEdgeInsets)controlInsets
|
||||
controlPadding:(CGFloat)controlPadding
|
||||
audioCategory:(NSString *)audioCategory
|
||||
audioCategoryOptions:(AVAudioSessionCategoryOptions)audioCategoryOptions {
|
||||
NSParameterAssert(playButtonImage != nil);
|
||||
NSParameterAssert(pauseButtonImage != nil);
|
||||
NSParameterAssert(labelFont != nil);
|
||||
NSParameterAssert(backgroundColor != nil);
|
||||
NSParameterAssert(tintColor != nil);
|
||||
NSParameterAssert(audioCategory != nil);
|
||||
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_playButtonImage = playButtonImage;
|
||||
_pauseButtonImage = pauseButtonImage;
|
||||
_labelFont = labelFont;
|
||||
_showFractionalSeconds = showFractionalSeconds;
|
||||
_backgroundColor = backgroundColor;
|
||||
_tintColor = tintColor;
|
||||
_controlInsets = controlInsets;
|
||||
_controlPadding = controlPadding;
|
||||
_audioCategory = audioCategory;
|
||||
_audioCategoryOptions = audioCategoryOptions;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
UIColor *tintColor = [UIColor jsq_messageBubbleBlueColor];
|
||||
AVAudioSessionCategoryOptions options = AVAudioSessionCategoryOptionDuckOthers
|
||||
| AVAudioSessionCategoryOptionDefaultToSpeaker
|
||||
| AVAudioSessionCategoryOptionAllowBluetooth;
|
||||
|
||||
return [self initWithPlayButtonImage:[[UIImage jsq_defaultPlayImage] jsq_imageMaskedWithColor:tintColor]
|
||||
pauseButtonImage:[[UIImage jsq_defaultPauseImage] jsq_imageMaskedWithColor:tintColor]
|
||||
labelFont:[UIFont systemFontOfSize:12]
|
||||
showFractionalSecodns:NO
|
||||
backgroundColor:[UIColor jsq_messageBubbleLightGrayColor]
|
||||
tintColor:tintColor
|
||||
controlInsets:UIEdgeInsetsMake(6, 6, 6, 18)
|
||||
controlPadding:6
|
||||
audioCategory:@"AVAudioSessionCategoryPlayback"
|
||||
audioCategoryOptions:options];
|
||||
}
|
||||
|
||||
- (void)setPlayButtonImage:(UIImage *)playButtonImage
|
||||
{
|
||||
NSParameterAssert(playButtonImage != nil);
|
||||
_playButtonImage = playButtonImage;
|
||||
}
|
||||
|
||||
- (void)setPauseButtonImage:(UIImage *)pauseButtonImage
|
||||
{
|
||||
NSParameterAssert(pauseButtonImage != nil);
|
||||
_pauseButtonImage = pauseButtonImage;
|
||||
}
|
||||
|
||||
- (void)setLabelFont:(UIFont *)labelFont
|
||||
{
|
||||
NSParameterAssert(labelFont != nil);
|
||||
_labelFont = labelFont;
|
||||
}
|
||||
|
||||
- (void)setBackgroundColor:(UIColor *)backgroundColor
|
||||
{
|
||||
NSParameterAssert(backgroundColor != nil);
|
||||
_backgroundColor = backgroundColor;
|
||||
}
|
||||
|
||||
- (void)setTintColor:(UIColor *)tintColor
|
||||
{
|
||||
NSParameterAssert(tintColor != nil);
|
||||
_tintColor = tintColor;
|
||||
}
|
||||
|
||||
- (void)setAudioCategory:(NSString *)audioCategory
|
||||
{
|
||||
NSParameterAssert(audioCategory != nil);
|
||||
_audioCategory = audioCategory;
|
||||
}
|
||||
|
||||
@end
|
||||
@ -0,0 +1,57 @@
|
||||
//
|
||||
// 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>
|
||||
|
||||
@class JSQMessagesCollectionViewFlowLayout;
|
||||
@protocol JSQMessageData;
|
||||
|
||||
/**
|
||||
* The `JSQMessagesBubbleSizeCalculating` protocol defines the common interface through which
|
||||
* an object provides layout information to an instance of `JSQMessagesCollectionViewFlowLayout`.
|
||||
*
|
||||
* A concrete class that conforms to this protocol is provided in the library.
|
||||
* See `JSQMessagesBubbleSizeCalculator`.
|
||||
*/
|
||||
@protocol JSQMessagesBubbleSizeCalculating <NSObject>
|
||||
|
||||
/**
|
||||
* Computes and returns the size of the `messageBubbleImageView` property
|
||||
* of a `JSQMessagesCollectionViewCell` for the specified messageData at indexPath.
|
||||
*
|
||||
* @param messageData A message data object.
|
||||
* @param indexPath The index path at which messageData is located.
|
||||
* @param layout The layout object asking for this information.
|
||||
*
|
||||
* @return A sizes that specifies the required dimensions to display the entire message contents.
|
||||
* Note, this is *not* the entire cell, but only its message bubble.
|
||||
*/
|
||||
- (CGSize)messageBubbleSizeForMessageData:(id<JSQMessageData>)messageData
|
||||
atIndexPath:(NSIndexPath *)indexPath
|
||||
withLayout:(JSQMessagesCollectionViewFlowLayout *)layout;
|
||||
|
||||
/**
|
||||
* Notifies the receiver that the layout will be reset.
|
||||
* Use this method to clear any cached layout information, if necessary.
|
||||
*
|
||||
* @param layout The layout object notifying the receiver.
|
||||
*/
|
||||
- (void)prepareForResettingLayout:(JSQMessagesCollectionViewFlowLayout *)layout;
|
||||
|
||||
@end
|
||||
@ -0,0 +1,43 @@
|
||||
//
|
||||
// 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 "JSQMessagesBubbleSizeCalculating.h"
|
||||
|
||||
/**
|
||||
* An instance of `JSQMessagesBubblesSizeCalculator` is responsible for calculating
|
||||
* message bubble sizes for an instance of `JSQMessagesCollectionViewFlowLayout`.
|
||||
*/
|
||||
@interface JSQMessagesBubblesSizeCalculator : NSObject <JSQMessagesBubbleSizeCalculating>
|
||||
|
||||
/**
|
||||
* Initializes and returns a bubble size calculator with the given cache and minimumBubbleWidth.
|
||||
*
|
||||
* @param cache A cache object used to store layout information.
|
||||
* @param minimumBubbleWidth The minimum width for any given message bubble.
|
||||
* @param usesFixedWidthBubbles Specifies whether or not to use fixed-width bubbles.
|
||||
* If `NO` (the default), then bubbles will resize when rotating to landscape.
|
||||
*
|
||||
* @return An initialized `JSQMessagesBubblesSizeCalculator` object if successful, `nil` otherwise.
|
||||
*/
|
||||
- (instancetype)initWithCache:(NSCache *)cache
|
||||
minimumBubbleWidth:(NSUInteger)minimumBubbleWidth
|
||||
usesFixedWidthBubbles:(BOOL)usesFixedWidthBubbles NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
@end
|
||||
@ -0,0 +1,179 @@
|
||||
//
|
||||
// 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 "JSQMessagesBubblesSizeCalculator.h"
|
||||
|
||||
#import "JSQMessagesCollectionView.h"
|
||||
#import "JSQMessagesCollectionViewDataSource.h"
|
||||
#import "JSQMessagesCollectionViewFlowLayout.h"
|
||||
#import "JSQMessageData.h"
|
||||
|
||||
#import "UIImage+JSQMessages.h"
|
||||
|
||||
|
||||
@interface JSQMessagesBubblesSizeCalculator ()
|
||||
|
||||
@property (strong, nonatomic, readonly) NSCache *cache;
|
||||
|
||||
@property (assign, nonatomic, readonly) NSUInteger minimumBubbleWidth;
|
||||
|
||||
@property (assign, nonatomic, readonly) BOOL usesFixedWidthBubbles;
|
||||
|
||||
@property (assign, nonatomic, readonly) NSInteger additionalInset;
|
||||
|
||||
@property (assign, nonatomic) CGFloat layoutWidthForFixedWidthBubbles;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation JSQMessagesBubblesSizeCalculator
|
||||
|
||||
#pragma mark - Init
|
||||
|
||||
- (instancetype)initWithCache:(NSCache *)cache
|
||||
minimumBubbleWidth:(NSUInteger)minimumBubbleWidth
|
||||
usesFixedWidthBubbles:(BOOL)usesFixedWidthBubbles
|
||||
{
|
||||
NSParameterAssert(cache != nil);
|
||||
NSParameterAssert(minimumBubbleWidth > 0);
|
||||
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_cache = cache;
|
||||
_minimumBubbleWidth = minimumBubbleWidth;
|
||||
_usesFixedWidthBubbles = usesFixedWidthBubbles;
|
||||
_layoutWidthForFixedWidthBubbles = 0.0f;
|
||||
|
||||
// this extra inset value is needed because `boundingRectWithSize:` is slightly off
|
||||
// see comment below
|
||||
_additionalInset = 2;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
NSCache *cache = [NSCache new];
|
||||
cache.name = @"JSQMessagesBubblesSizeCalculator.cache";
|
||||
cache.countLimit = 200;
|
||||
return [self initWithCache:cache
|
||||
minimumBubbleWidth:[UIImage jsq_bubbleCompactImage].size.width
|
||||
usesFixedWidthBubbles:NO];
|
||||
}
|
||||
|
||||
#pragma mark - NSObject
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
return [NSString stringWithFormat:@"<%@: cache=%@, minimumBubbleWidth=%@ usesFixedWidthBubbles=%@>",
|
||||
[self class], self.cache, @(self.minimumBubbleWidth), @(self.usesFixedWidthBubbles)];
|
||||
}
|
||||
|
||||
#pragma mark - JSQMessagesBubbleSizeCalculating
|
||||
|
||||
- (void)prepareForResettingLayout:(JSQMessagesCollectionViewFlowLayout *)layout
|
||||
{
|
||||
[self.cache removeAllObjects];
|
||||
}
|
||||
|
||||
- (CGSize)messageBubbleSizeForMessageData:(id<JSQMessageData>)messageData
|
||||
atIndexPath:(NSIndexPath *)indexPath
|
||||
withLayout:(JSQMessagesCollectionViewFlowLayout *)layout
|
||||
{
|
||||
NSValue *cachedSize = [self.cache objectForKey:@([messageData messageHash])];
|
||||
if (cachedSize != nil) {
|
||||
return [cachedSize CGSizeValue];
|
||||
}
|
||||
|
||||
CGSize finalSize = CGSizeZero;
|
||||
|
||||
if ([messageData isMediaMessage]) {
|
||||
finalSize = [[messageData media] mediaViewDisplaySize];
|
||||
}
|
||||
else {
|
||||
CGSize avatarSize = [self jsq_avatarSizeForMessageData:messageData withLayout:layout];
|
||||
|
||||
// from the cell xibs, there is a 2 point space between avatar and bubble
|
||||
CGFloat spacingBetweenAvatarAndBubble = 2.0f;
|
||||
CGFloat horizontalContainerInsets = layout.messageBubbleTextViewTextContainerInsets.left + layout.messageBubbleTextViewTextContainerInsets.right;
|
||||
CGFloat horizontalFrameInsets = layout.messageBubbleTextViewFrameInsets.left + layout.messageBubbleTextViewFrameInsets.right;
|
||||
|
||||
CGFloat horizontalInsetsTotal = horizontalContainerInsets + horizontalFrameInsets + spacingBetweenAvatarAndBubble;
|
||||
CGFloat maximumTextWidth = [self textBubbleWidthForLayout:layout] - avatarSize.width - layout.messageBubbleLeftRightMargin - horizontalInsetsTotal;
|
||||
|
||||
CGRect stringRect = [[messageData text] boundingRectWithSize:CGSizeMake(maximumTextWidth, CGFLOAT_MAX)
|
||||
options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading)
|
||||
attributes:@{ NSFontAttributeName : layout.messageBubbleFont }
|
||||
context:nil];
|
||||
|
||||
CGSize stringSize = CGRectIntegral(stringRect).size;
|
||||
|
||||
CGFloat verticalContainerInsets = layout.messageBubbleTextViewTextContainerInsets.top + layout.messageBubbleTextViewTextContainerInsets.bottom;
|
||||
CGFloat verticalFrameInsets = layout.messageBubbleTextViewFrameInsets.top + layout.messageBubbleTextViewFrameInsets.bottom;
|
||||
|
||||
// add extra 2 points of space (`self.additionalInset`), because `boundingRectWithSize:` is slightly off
|
||||
// not sure why. magix. (shrug) if you know, submit a PR
|
||||
CGFloat verticalInsets = verticalContainerInsets + verticalFrameInsets + self.additionalInset;
|
||||
|
||||
// same as above, an extra 2 points of magix
|
||||
CGFloat finalWidth = MAX(stringSize.width + horizontalInsetsTotal, self.minimumBubbleWidth) + self.additionalInset;
|
||||
|
||||
finalSize = CGSizeMake(finalWidth, stringSize.height + verticalInsets);
|
||||
}
|
||||
|
||||
[self.cache setObject:[NSValue valueWithCGSize:finalSize] forKey:@([messageData messageHash])];
|
||||
|
||||
return finalSize;
|
||||
}
|
||||
|
||||
- (CGSize)jsq_avatarSizeForMessageData:(id<JSQMessageData>)messageData
|
||||
withLayout:(JSQMessagesCollectionViewFlowLayout *)layout
|
||||
{
|
||||
NSString *messageSender = [messageData senderId];
|
||||
|
||||
if ([messageSender isEqualToString:[layout.collectionView.dataSource senderId]]) {
|
||||
return layout.outgoingAvatarViewSize;
|
||||
}
|
||||
|
||||
return layout.incomingAvatarViewSize;
|
||||
}
|
||||
|
||||
- (CGFloat)textBubbleWidthForLayout:(JSQMessagesCollectionViewFlowLayout *)layout
|
||||
{
|
||||
if (self.usesFixedWidthBubbles) {
|
||||
return [self widthForFixedWidthBubblesWithLayout:layout];
|
||||
}
|
||||
|
||||
return layout.itemWidth;
|
||||
}
|
||||
|
||||
- (CGFloat)widthForFixedWidthBubblesWithLayout:(JSQMessagesCollectionViewFlowLayout *)layout {
|
||||
if (self.layoutWidthForFixedWidthBubbles > 0.0f) {
|
||||
return self.layoutWidthForFixedWidthBubbles;
|
||||
}
|
||||
|
||||
// also need to add `self.additionalInset` here, see comment above
|
||||
NSInteger horizontalInsets = layout.sectionInset.left + layout.sectionInset.right + self.additionalInset;
|
||||
CGFloat width = CGRectGetWidth(layout.collectionView.bounds) - horizontalInsets;
|
||||
CGFloat height = CGRectGetHeight(layout.collectionView.bounds) - horizontalInsets;
|
||||
self.layoutWidthForFixedWidthBubbles = MIN(width, height);
|
||||
|
||||
return self.layoutWidthForFixedWidthBubbles;
|
||||
}
|
||||
|
||||
@end
|
||||
@ -23,6 +23,8 @@
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "JSQMessagesBubbleSizeCalculating.h"
|
||||
|
||||
@class JSQMessagesCollectionView;
|
||||
|
||||
|
||||
@ -56,7 +58,18 @@ FOUNDATION_EXPORT const CGFloat kJSQMessagesCollectionViewAvatarSizeDefault;
|
||||
/**
|
||||
* The collection view object currently using this layout object.
|
||||
*/
|
||||
|
||||
// TODO: fix, rename "messagesCollectionView", see #920
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wincompatible-property-type"
|
||||
@property (readonly, nonatomic) JSQMessagesCollectionView *collectionView;
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
/**
|
||||
* The object that the layout uses to calculate bubble sizes.
|
||||
* The default value is an instance of `JSQMessagesBubblesSizeCalculator`.
|
||||
*/
|
||||
@property (strong, nonatomic) id<JSQMessagesBubbleSizeCalculating> bubbleSizeCalculator;
|
||||
|
||||
/**
|
||||
* Specifies whether or not the layout should enable spring behavior dynamics for its items using `UIDynamics`.
|
||||
@ -176,12 +189,15 @@ FOUNDATION_EXPORT const CGFloat kJSQMessagesCollectionViewAvatarSizeDefault;
|
||||
|
||||
/**
|
||||
* Computes and returns the size of the `messageBubbleImageView` property of a `JSQMessagesCollectionViewCell`
|
||||
* at the specified indexPath. The returned size contains the required dimensions to display the entire message contents.
|
||||
* Note, this is *not* the entire cell, but only its message bubble.
|
||||
* at the specified indexPath.
|
||||
*
|
||||
* @param indexPath The index path of the item to be displayed.
|
||||
*
|
||||
* @return The size of the message bubble for the item displayed at indexPath.
|
||||
*
|
||||
* @discussion The layout uses its `bubbleSizeCalculator` object to perform this computation.
|
||||
* The returned size contains the required dimensions to display the entire message contents.
|
||||
* Note, this is *not* the entire cell, but only its message bubble.
|
||||
*/
|
||||
- (CGSize)messageBubbleSizeForItemAtIndexPath:(NSIndexPath *)indexPath;
|
||||
|
||||
|
||||
@ -30,6 +30,7 @@
|
||||
|
||||
#import "JSQMessagesCollectionViewLayoutAttributes.h"
|
||||
#import "JSQMessagesCollectionViewFlowLayoutInvalidationContext.h"
|
||||
#import "JSQMessagesBubblesSizeCalculator.h"
|
||||
|
||||
#import "UIImage+JSQMessages.h"
|
||||
|
||||
@ -40,37 +41,21 @@ const CGFloat kJSQMessagesCollectionViewAvatarSizeDefault = 30.0f;
|
||||
|
||||
@interface JSQMessagesCollectionViewFlowLayout ()
|
||||
|
||||
@property (strong, nonatomic) NSCache *messageBubbleCache;
|
||||
|
||||
@property (strong, nonatomic) UIDynamicAnimator *dynamicAnimator;
|
||||
@property (strong, nonatomic) NSMutableSet *visibleIndexPaths;
|
||||
|
||||
@property (assign, nonatomic) CGFloat latestDelta;
|
||||
|
||||
@property (assign, nonatomic, readonly) NSUInteger bubbleImageAssetWidth;
|
||||
|
||||
- (void)jsq_configureFlowLayout;
|
||||
|
||||
- (void)jsq_didReceiveApplicationMemoryWarningNotification:(NSNotification *)notification;
|
||||
- (void)jsq_didReceiveDeviceOrientationDidChangeNotification:(NSNotification *)notification;
|
||||
|
||||
- (void)jsq_resetLayout;
|
||||
- (void)jsq_resetDynamicAnimator;
|
||||
|
||||
- (void)jsq_configureMessageCellLayoutAttributes:(JSQMessagesCollectionViewLayoutAttributes *)layoutAttributes;
|
||||
- (CGSize)jsq_avatarSizeForIndexPath:(NSIndexPath *)indexPath;
|
||||
|
||||
- (UIAttachmentBehavior *)jsq_springBehaviorWithLayoutAttributesItem:(UICollectionViewLayoutAttributes *)item;
|
||||
- (void)jsq_addNewlyVisibleBehaviorsFromVisibleItems:(NSArray *)visibleItems;
|
||||
- (void)jsq_removeNoLongerVisibleBehaviorsFromVisibleItemsIndexPaths:(NSSet *)visibleItemsIndexPaths;
|
||||
- (void)jsq_adjustSpringBehavior:(UIAttachmentBehavior *)springBehavior forTouchLocation:(CGPoint)touchLocation;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
@implementation JSQMessagesCollectionViewFlowLayout
|
||||
|
||||
@dynamic collectionView;
|
||||
|
||||
@synthesize bubbleSizeCalculator = _bubbleSizeCalculator;
|
||||
|
||||
#pragma mark - Initialization
|
||||
|
||||
- (void)jsq_configureFlowLayout
|
||||
@ -79,12 +64,6 @@ const CGFloat kJSQMessagesCollectionViewAvatarSizeDefault = 30.0f;
|
||||
self.sectionInset = UIEdgeInsetsMake(10.0f, 4.0f, 10.0f, 4.0f);
|
||||
self.minimumLineSpacing = 4.0f;
|
||||
|
||||
_bubbleImageAssetWidth = [UIImage jsq_bubbleCompactImage].size.width;
|
||||
|
||||
_messageBubbleCache = [NSCache new];
|
||||
_messageBubbleCache.name = @"JSQMessagesCollectionViewFlowLayout.messageBubbleCache";
|
||||
_messageBubbleCache.countLimit = 200;
|
||||
|
||||
_messageBubbleFont = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
|
||||
|
||||
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
|
||||
@ -143,21 +122,16 @@ const CGFloat kJSQMessagesCollectionViewAvatarSizeDefault = 30.0f;
|
||||
- (void)dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
|
||||
_messageBubbleFont = nil;
|
||||
|
||||
[_messageBubbleCache removeAllObjects];
|
||||
_messageBubbleCache = nil;
|
||||
|
||||
[_dynamicAnimator removeAllBehaviors];
|
||||
_dynamicAnimator = nil;
|
||||
|
||||
[_visibleIndexPaths removeAllObjects];
|
||||
_visibleIndexPaths = nil;
|
||||
}
|
||||
|
||||
#pragma mark - Setters
|
||||
|
||||
- (void)setBubbleSizeCalculator:(id<JSQMessagesBubbleSizeCalculating>)bubbleSizeCalculator
|
||||
{
|
||||
NSParameterAssert(bubbleSizeCalculator != nil);
|
||||
_bubbleSizeCalculator = bubbleSizeCalculator;
|
||||
}
|
||||
|
||||
- (void)setSpringinessEnabled:(BOOL)springinessEnabled
|
||||
{
|
||||
if (_springinessEnabled == springinessEnabled) {
|
||||
@ -221,11 +195,6 @@ const CGFloat kJSQMessagesCollectionViewAvatarSizeDefault = 30.0f;
|
||||
[self invalidateLayoutWithContext:[JSQMessagesCollectionViewFlowLayoutInvalidationContext context]];
|
||||
}
|
||||
|
||||
- (void)setCacheLimit:(NSUInteger)cacheLimit
|
||||
{
|
||||
self.messageBubbleCache.countLimit = cacheLimit;
|
||||
}
|
||||
|
||||
#pragma mark - Getters
|
||||
|
||||
- (CGFloat)itemWidth
|
||||
@ -249,9 +218,13 @@ const CGFloat kJSQMessagesCollectionViewAvatarSizeDefault = 30.0f;
|
||||
return _visibleIndexPaths;
|
||||
}
|
||||
|
||||
- (NSUInteger)cacheLimit
|
||||
- (id<JSQMessagesBubbleSizeCalculating>)bubbleSizeCalculator
|
||||
{
|
||||
return self.messageBubbleCache.countLimit;
|
||||
if (_bubbleSizeCalculator == nil) {
|
||||
_bubbleSizeCalculator = [JSQMessagesBubblesSizeCalculator new];
|
||||
}
|
||||
|
||||
return _bubbleSizeCalculator;
|
||||
}
|
||||
|
||||
#pragma mark - Notifications
|
||||
@ -281,7 +254,7 @@ const CGFloat kJSQMessagesCollectionViewAvatarSizeDefault = 30.0f;
|
||||
[self jsq_resetDynamicAnimator];
|
||||
}
|
||||
|
||||
if (context.emptyCache) {
|
||||
if (context.invalidateFlowLayoutMessagesCache) {
|
||||
[self jsq_resetLayout];
|
||||
}
|
||||
|
||||
@ -308,7 +281,7 @@ const CGFloat kJSQMessagesCollectionViewAvatarSizeDefault = 30.0f;
|
||||
|
||||
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
|
||||
{
|
||||
NSArray *attributesInRect = [super layoutAttributesForElementsInRect:rect];
|
||||
NSArray *attributesInRect = [[super layoutAttributesForElementsInRect:rect] copy];
|
||||
|
||||
if (self.springinessEnabled) {
|
||||
NSMutableArray *attributesInRectCopy = [attributesInRect mutableCopy];
|
||||
@ -329,7 +302,7 @@ const CGFloat kJSQMessagesCollectionViewAvatarSizeDefault = 30.0f;
|
||||
}
|
||||
}
|
||||
|
||||
attributesInRect = attributesInRectCopy;
|
||||
attributesInRect = [attributesInRectCopy copy];
|
||||
}
|
||||
|
||||
[attributesInRect enumerateObjectsUsingBlock:^(JSQMessagesCollectionViewLayoutAttributes *attributesItem, NSUInteger idx, BOOL *stop) {
|
||||
@ -346,7 +319,7 @@ const CGFloat kJSQMessagesCollectionViewAvatarSizeDefault = 30.0f;
|
||||
|
||||
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
JSQMessagesCollectionViewLayoutAttributes *customAttributes = (JSQMessagesCollectionViewLayoutAttributes *)[super layoutAttributesForItemAtIndexPath:indexPath];
|
||||
JSQMessagesCollectionViewLayoutAttributes *customAttributes = (JSQMessagesCollectionViewLayoutAttributes *)[[super layoutAttributesForItemAtIndexPath:indexPath] copy];
|
||||
|
||||
if (customAttributes.representedElementCategory == UICollectionElementCategoryCell) {
|
||||
[self jsq_configureMessageCellLayoutAttributes:customAttributes];
|
||||
@ -415,7 +388,7 @@ const CGFloat kJSQMessagesCollectionViewAvatarSizeDefault = 30.0f;
|
||||
|
||||
- (void)jsq_resetLayout
|
||||
{
|
||||
[self.messageBubbleCache removeAllObjects];
|
||||
[self.bubbleSizeCalculator prepareForResettingLayout:self];
|
||||
[self jsq_resetDynamicAnimator];
|
||||
}
|
||||
|
||||
@ -431,52 +404,12 @@ const CGFloat kJSQMessagesCollectionViewAvatarSizeDefault = 30.0f;
|
||||
|
||||
- (CGSize)messageBubbleSizeForItemAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
id<JSQMessageData> messageItem = [self.collectionView.dataSource collectionView:self.collectionView messageDataForItemAtIndexPath:indexPath];
|
||||
|
||||
NSValue *cachedSize = [self.messageBubbleCache objectForKey:@(messageItem.hash)];
|
||||
if (cachedSize != nil) {
|
||||
return [cachedSize CGSizeValue];
|
||||
}
|
||||
|
||||
CGSize finalSize = CGSizeZero;
|
||||
|
||||
if ([messageItem isMediaMessage]) {
|
||||
finalSize = [[messageItem media] mediaViewDisplaySize];
|
||||
}
|
||||
else {
|
||||
CGSize avatarSize = [self jsq_avatarSizeForIndexPath:indexPath];
|
||||
|
||||
// from the cell xibs, there is a 2 point space between avatar and bubble
|
||||
CGFloat spacingBetweenAvatarAndBubble = 2.0f;
|
||||
CGFloat horizontalContainerInsets = self.messageBubbleTextViewTextContainerInsets.left + self.messageBubbleTextViewTextContainerInsets.right;
|
||||
CGFloat horizontalFrameInsets = self.messageBubbleTextViewFrameInsets.left + self.messageBubbleTextViewFrameInsets.right;
|
||||
|
||||
CGFloat horizontalInsetsTotal = horizontalContainerInsets + horizontalFrameInsets + spacingBetweenAvatarAndBubble;
|
||||
CGFloat maximumTextWidth = self.itemWidth - avatarSize.width - self.messageBubbleLeftRightMargin - horizontalInsetsTotal;
|
||||
|
||||
CGRect stringRect = [[messageItem text] boundingRectWithSize:CGSizeMake(maximumTextWidth, CGFLOAT_MAX)
|
||||
options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading)
|
||||
attributes:@{ NSFontAttributeName : self.messageBubbleFont }
|
||||
context:nil];
|
||||
|
||||
CGSize stringSize = CGRectIntegral(stringRect).size;
|
||||
|
||||
CGFloat verticalContainerInsets = self.messageBubbleTextViewTextContainerInsets.top + self.messageBubbleTextViewTextContainerInsets.bottom;
|
||||
CGFloat verticalFrameInsets = self.messageBubbleTextViewFrameInsets.top + self.messageBubbleTextViewFrameInsets.bottom;
|
||||
|
||||
// add extra 2 points of space, because `boundingRectWithSize:` is slightly off
|
||||
// not sure why. magix. (shrug) if you know, submit a PR
|
||||
CGFloat verticalInsets = verticalContainerInsets + verticalFrameInsets + 2.0f;
|
||||
|
||||
// same as above, an extra 2 points of magix
|
||||
CGFloat finalWidth = MAX(stringSize.width + horizontalInsetsTotal, self.bubbleImageAssetWidth) + 2.0f;
|
||||
|
||||
finalSize = CGSizeMake(finalWidth, stringSize.height + verticalInsets);
|
||||
}
|
||||
|
||||
[self.messageBubbleCache setObject:[NSValue valueWithCGSize:finalSize] forKey:@(messageItem.hash)];
|
||||
|
||||
return finalSize;
|
||||
id<JSQMessageData> messageItem = [self.collectionView.dataSource collectionView:self.collectionView
|
||||
messageDataForItemAtIndexPath:indexPath];
|
||||
|
||||
return [self.bubbleSizeCalculator messageBubbleSizeForMessageData:messageItem
|
||||
atIndexPath:indexPath
|
||||
withLayout:self];
|
||||
}
|
||||
|
||||
- (CGSize)sizeForItemAtIndexPath:(NSIndexPath *)indexPath
|
||||
@ -523,22 +456,15 @@ const CGFloat kJSQMessagesCollectionViewAvatarSizeDefault = 30.0f;
|
||||
heightForCellBottomLabelAtIndexPath:indexPath];
|
||||
}
|
||||
|
||||
- (CGSize)jsq_avatarSizeForIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
id<JSQMessageData> messageData = [self.collectionView.dataSource collectionView:self.collectionView messageDataForItemAtIndexPath:indexPath];
|
||||
NSString *messageSender = [messageData senderId];
|
||||
|
||||
if ([messageSender isEqualToString:[self.collectionView.dataSource senderId]]) {
|
||||
return self.outgoingAvatarViewSize;
|
||||
}
|
||||
|
||||
return self.incomingAvatarViewSize;
|
||||
}
|
||||
|
||||
#pragma mark - Spring behavior utilities
|
||||
|
||||
- (UIAttachmentBehavior *)jsq_springBehaviorWithLayoutAttributesItem:(UICollectionViewLayoutAttributes *)item
|
||||
{
|
||||
if (CGSizeEqualToSize(item.frame.size, CGSizeZero)) {
|
||||
// adding a spring behavior with zero size will fail in in -prepareForCollectionViewUpdates:
|
||||
return nil;
|
||||
}
|
||||
|
||||
UIAttachmentBehavior *springBehavior = [[UIAttachmentBehavior alloc] initWithItem:item attachedToAnchor:item.center];
|
||||
springBehavior.length = 1.0f;
|
||||
springBehavior.damping = 1.0f;
|
||||
@ -570,14 +496,14 @@ const CGFloat kJSQMessagesCollectionViewAvatarSizeDefault = 30.0f;
|
||||
NSArray *behaviors = self.dynamicAnimator.behaviors;
|
||||
|
||||
NSIndexSet *indexSet = [behaviors indexesOfObjectsPassingTest:^BOOL(UIAttachmentBehavior *springBehaviour, NSUInteger index, BOOL *stop) {
|
||||
UICollectionViewLayoutAttributes *layoutAttributes = [springBehaviour.items firstObject];
|
||||
UICollectionViewLayoutAttributes *layoutAttributes = (UICollectionViewLayoutAttributes *)[springBehaviour.items firstObject];
|
||||
return ![visibleItemsIndexPaths containsObject:layoutAttributes.indexPath];
|
||||
}];
|
||||
|
||||
NSArray *behaviorsToRemove = [self.dynamicAnimator.behaviors objectsAtIndexes:indexSet];
|
||||
|
||||
[behaviorsToRemove enumerateObjectsUsingBlock:^(UIAttachmentBehavior *springBehaviour, NSUInteger index, BOOL *stop) {
|
||||
UICollectionViewLayoutAttributes *layoutAttributes = [springBehaviour.items firstObject];
|
||||
UICollectionViewLayoutAttributes *layoutAttributes = (UICollectionViewLayoutAttributes *)[springBehaviour.items firstObject];
|
||||
[self.dynamicAnimator removeBehavior:springBehaviour];
|
||||
[self.visibleIndexPaths removeObject:layoutAttributes.indexPath];
|
||||
}];
|
||||
@ -585,12 +511,12 @@ const CGFloat kJSQMessagesCollectionViewAvatarSizeDefault = 30.0f;
|
||||
|
||||
- (void)jsq_adjustSpringBehavior:(UIAttachmentBehavior *)springBehavior forTouchLocation:(CGPoint)touchLocation
|
||||
{
|
||||
UICollectionViewLayoutAttributes *item = [springBehavior.items firstObject];
|
||||
UICollectionViewLayoutAttributes *item = (UICollectionViewLayoutAttributes *)[springBehavior.items firstObject];
|
||||
CGPoint center = item.center;
|
||||
|
||||
// if touch is not (0,0) -- adjust item center "in flight"
|
||||
if (!CGPointEqualToPoint(CGPointZero, touchLocation)) {
|
||||
CGFloat distanceFromTouch = fabsf(touchLocation.y - springBehavior.anchorPoint.y);
|
||||
CGFloat distanceFromTouch = fabs(touchLocation.y - springBehavior.anchorPoint.y);
|
||||
CGFloat scrollResistance = distanceFromTouch / self.springResistanceFactor;
|
||||
|
||||
if (self.latestDelta < 0.0f) {
|
||||
|
||||
@ -28,10 +28,10 @@
|
||||
@interface JSQMessagesCollectionViewFlowLayoutInvalidationContext : UICollectionViewFlowLayoutInvalidationContext
|
||||
|
||||
/**
|
||||
* A boolean indicating whether to empty the layout information cache for items and views in the layout.
|
||||
* A boolean indicating whether to empty the messages layout information cache for items and views in the layout.
|
||||
* The default value is `NO`.
|
||||
*/
|
||||
@property (nonatomic, assign) BOOL emptyCache;
|
||||
@property (nonatomic, assign) BOOL invalidateFlowLayoutMessagesCache;
|
||||
|
||||
/**
|
||||
* Creates and returns a new `JSQMessagesCollectionViewFlowLayoutInvalidationContext` object.
|
||||
|
||||
@ -28,7 +28,7 @@
|
||||
if (self) {
|
||||
self.invalidateFlowLayoutDelegateMetrics = NO;
|
||||
self.invalidateFlowLayoutAttributes = NO;
|
||||
_emptyCache = NO;
|
||||
_invalidateFlowLayoutMessagesCache = NO;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@ -45,8 +45,8 @@
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
return [NSString stringWithFormat:@"<%@: invalidateFlowLayoutDelegateMetrics=%@, invalidateFlowLayoutAttributes=%@, invalidateDataSourceCounts=%@, emptyCache=%@>",
|
||||
[self class], @(self.invalidateFlowLayoutDelegateMetrics), @(self.invalidateFlowLayoutAttributes), @(self.invalidateDataSourceCounts), @(self.emptyCache)];
|
||||
return [NSString stringWithFormat:@"<%@: invalidateFlowLayoutDelegateMetrics=%@, invalidateFlowLayoutAttributes=%@, invalidateDataSourceCounts=%@, invalidateFlowLayoutMessagesCache=%@>",
|
||||
[self class], @(self.invalidateFlowLayoutDelegateMetrics), @(self.invalidateFlowLayoutAttributes), @(self.invalidateDataSourceCounts), @(self.invalidateFlowLayoutMessagesCache)];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user