Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
minneのメッセージ機能改善活動
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
nakajijapan
December 15, 2015
Technology
2.8k
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
minneのメッセージ機能改善活動
http://shibuya-swift.connpass.com/event/21979/
nakajijapan
December 15, 2015
More Decks by nakajijapan
See All by nakajijapan
サービスにおけるDesign Systemの構築
nakajijapan
11
4.2k
Markdownをリアルタイムに解析する
nakajijapan
5
4.6k
Firebase Authorization
nakajijapan
0
340
Intoducing Izumo
nakajijapan
1
2k
Practical CloudKit
nakajijapan
1
1.9k
Introducing to Ajimi - プロダクトを味見していこう
nakajijapan
0
2.4k
Shari
nakajijapan
1
3k
Japan Apple Pay Development
nakajijapan
0
340
業務で絶対必要にならない技術
nakajijapan
0
900
Other Decks in Technology
See All in Technology
貴方はどのエンジニアリングを磨くのか
hatyibei
0
110
Kotlin 開発のツラミを爆破した話! / Explode the difficulty of Kotlin dev!
eller86
0
160
Zoom2Youtube.Claude
kawaguti
PRO
3
480
アカウントが増えてからでは遅い? ~ マルチアカウント統制の勘所 ~
kenichinakamura
0
210
AWS Summit の片隅で、体育座りしながらコミュニティがにぎわう理由を考えた
k_adachi_01
2
360
実装だけじゃない! CCA-F取得エンジニアが教えるClaude Code開発プロセス活用術
diggymo
2
490
AI Agent SaaS を支える自社仮想化基盤への挑戦と実運用 / ai-agent-saas-virtualization
flatt_security
2
3.4k
なぜ人は自分のプロジェクトを 「なんちゃってアジャイル」と 自嘲するのか
kozotaira
0
270
『AIに負けない』より『AIと遊ぶ』」〜ワクワクが最強のテスト・QA学習戦略_公開用
odan611
2
550
【Claude Code】鹿野さんに聞く 私の推しの並行開発環境 大公開 / claude-code-parallel-2026-07-15
tonkotsuboy_com
6
1.6k
SREとQA 二人三脚で進めるSLO運用/sre-qa-slo
sugitak
0
110
生成AIの活用/high_school2026
okana2ki
0
110
Featured
See All Featured
Utilizing Notion as your number one productivity tool
mfonobong
4
350
Reflections from 52 weeks, 52 projects
jeffersonlam
356
21k
GitHub's CSS Performance
jonrohan
1033
470k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
128
56k
Bioeconomy Workshop: Dr. Julius Ecuru, Opportunities for a Bioeconomy in West Africa
akademiya2063
PRO
1
170
From π to Pie charts
rasagy
0
230
Sam Torres - BigQuery for SEOs
techseoconnect
PRO
0
300
Faster Mobile Websites
deanohume
310
32k
A Guide to Academic Writing Using Generative AI - A Workshop
ks91
PRO
1
350
The #1 spot is gone: here's how to win anyway
tamaranovitovic
3
1.1k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
27k
Thoughts on Productivity
jonyablonski
76
5.2k
Transcript
ϝοηʔδ ػೳվળ׆ಈ shibuya.swift #2 @nakajijapan
About Me
@nakajijapan Software Engineer GMO PEPABO inc. iOS / Web /
OS X
GitHub
NKJMultiMovieCaptureView NKJMovieComposer NKJPagerViewController PhotoSlider etc Teiten
NKJMultiMovieCaptureView NKJMovieComposer NKJPagerViewController PhotoSlider etc Teiten SwiftԽʂʂʂʂ
minne iOS / Android
400 million app download ࠃ࠷େͷϋϯυϝΠυϚʔέοτ
Google Play™ετΞ ʮ2015ϕετΞϓ Ϧʯʹબग़ʂ
ँ
Talk about
ϝοηʔδ ػೳվળ׆ಈ
ϝοηʔδ ػೳ
ϝοηʔδػೳ • 201408݄ϦϦʔε • ࡞Ո͞ΜͱίϛϡχέʔγϣϯͰ͖Δ • ؾܰʹϝοηʔδަͯ͠΄͍͠
ϝοηʔδػೳվળ׆ಈ • ܦҢ • มߋ༰ • ϝοηʔδೖྗ • Ұཡ •
·ͱΊ
ܦҢ
ܦҢ • όʔδϣϯΞοϓͰΠϯλʔϑΣʔε ΛҰ৽͕ͨ͠ɺΠϯλϥΫγϣϯͷ վળ์ஔؾຯͩͬͨ • ཁόάͷ૿Ճ
ܦҢ • όʔδϣϯΞοϓͰΠϯλʔϑΣʔε ΛҰ৽͕ͨ͠ɺΠϯλϥΫγϣϯͷ վળ์ஔؾຯͩͬͨ • ཁόάͷ૿Ճ ͬͱ҆৺͍ͤͨ͞ʂ
վળ
લఏࣝ
Text Kit
UITextView • ෳߦͷςΩετྖҬɾεΫϩʔϧΛཧ͢Δ • Πϯελϯεੜ࣌ʹҎԼͷΠϯελϯεੜ • NSTextContainer • NSLayoutManager •
NSTextStorage • ͍ͣΕreadOnly
Text Kit • NSTextContainer • ςΩετͷ࠲ඪͱܗঢ়Λཧ • NSLayoutManager • ϨΠΞτͱϨϯμϦϯάΛௐ
• NSTextStorage • NSMutableAttributedStringͷαϒΫϥε
ϝοηʔδೖྗ
ϝοηʔδೖྗ • Growing Text View • ಈతʹTextྖҬΛ֦ு͢Δ • ϦΞϧλΠϜϓϨϏϡʔ •
ૹ৴ͨ͠ঢ়ଶͱಉ͡ͷΛදࣔ͢Δ
MITextInputView MITextView NSLayoutConstraint *heightConstraint;
Growing Text View • ೖྗͨ࣌͠ͷΠϕϯτΛܭଌ͢Δ • ߴ͞Λܭଌ͠ɺViewΛߋ৽͢Δ
Growing Text View [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textDidChange:) name:UITextViewTextDidChangeNotification object:nil];
Growing Text View ςΩετ UITextView
roundf(textView.font.lineHeight * numberOfLines) textView.textContainerInset.bottom textView.textContainerInset.top Growing Text View ςΩετ UITextView
Growing Text View • ྖҬ֦େ࣌ͷ࠷େΛߦͰࢦఆ • ࡉ͔͍ߴ͞ͷௐ͕Մೳ • ΫϥεΛར༻͢Δͷ͕ຖߴ͞Λ ҙࣝ͠ͳͯ͘ࡁΉ
վߦҐஔͷݻఆ վߦ࣌ ΧʔιϧΛৗʹ࠷ԼҐʹ ஔͰ͖ΔΑ͏ʹ͢Δ
վߦҐஔͷݻఆ • ࠷େߦҎԼ • Viewͷ੍ʢߴ͞ʣΛߋ৽͢Δ • ৗʹ࠷্෦εΫϩʔϧͤ͞Δ • ࠷େߦΛ͑ͨ •
Viewͷ੍ʢߴ͞ʣΛߋ৽͢Δ • ৗʹ࠷Լ෦εΫϩʔϧͤ͞Δ
վߦҐஔͷݻఆ [self.textView setContentOffset:CGPointMake(0.f, 0.f) animated:NO]; if (self.textView.selectedRange.location == self.textView.text.length) {
NSRange range = NSMakeRange(self.textView.text.length, 0.f); self.textView.selectedRange = range; CGFloat scrollY = self.textView.contentSize.height - self.textView.bounds.size.height; if (scrollY < 0.f) { scrollY = 0.f; } CGPoint scrollPoint = CGPointMake(0.f, scrollY); [self.textView setContentOffset:scrollPoint animated:YES]; } ࠷େߦҎ ࠷େߦҎ্
ϦΞϧλΠϜ ϓϨϏϡʔ
ϦΞϧλΠϜϓϨϏϡʔ
ϦΞϧλΠϜϓϨϏϡʔ
ϦΞϧλΠϜϓϨϏϡʔ
ϦΞϧλΠϜϓϨϏϡʔ
ϦΞϧλΠϜϓϨϏϡʔ • ը૾ߘͷϩδοΫΛվળ • ࠓ·Ͱ • ը૾બ࣌ʹαʔόʹPOST • TextViewʹදࣔ͞ΕΔͷͦͷURL
ϦΞϧλΠϜϓϨϏϡʔ • ը૾ߘͷϩδοΫΛվળ • ࠓ·Ͱ • ը૾બ࣌ʹαʔόʹPOST • TextViewʹදࣔ͞ΕΔͷͦͷURL ➡ϝοηʔδૹ৴࣌ʹ·ͱΊͯૹ৴
➡ߘͨ͠ը૾ΛϓϨϏϡʔͤ͞Δ
ϦΞϧλΠϜϓϨϏϡʔ NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithAttributedString:self.textInputView.textView.attributedText]; NSTextAttachment *textAttachment =
[[NSTextAttachment alloc] init]; textAttachment.image = image; textAttachment.bounds = CGRectMake(0.f,0.f,100.f,100.f); NSAttributedString *attrStringWithImage = [NSAttributedString attributedStringWithAttachment:textAttachment]; [attributedString replaceCharactersInRange:self.textInputView.textView.selectedRange withAttributedString:attrStringWithImage]; [self.textInputView setAttributedText:attributedString]; ը૾બ࣌
ϦΞϧλΠϜϓϨϏϡʔ NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithAttributedString:self.textInputView.textView.attributedText]; NSTextAttachment *textAttachment =
[[NSTextAttachment alloc] init]; textAttachment.image = image; textAttachment.bounds = CGRectMake(0.f,0.f,100.f,100.f); NSAttributedString *attrStringWithImage = [NSAttributedString attributedStringWithAttachment:textAttachment]; [attributedString replaceCharactersInRange:self.textInputView.textView.selectedRange withAttributedString:attrStringWithImage]; [self.textInputView setAttributedText:attributedString]; ը૾બ࣌ ը૾ͷૠೖ
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithAttributedString:self.textInputView.textView.attributedText]; NSTextAttachment *textAttachment = [[NSTextAttachment
alloc] init]; textAttachment.image = image; textAttachment.bounds = CGRectMake(0.f,0.f,100.f,100.f); NSAttributedString *attrStringWithImage = [NSAttributedString attributedStringWithAttachment:textAttachment]; [attributedString replaceCharactersInRange:self.textInputView.textView.selectedRange withAttributedString:attrStringWithImage]; [self.textInputView setAttributedText:attributedString]; ը૾બ࣌ Χʔιϧʹ͋ΔҐஔʹը૾Λૠೖ ը૾બޙͷಈ͖
ը૾બޙͷಈ͖ ݱࡏͷߴ͞Λࢉग़ͯ͠ TextViewྖҬΛ͛Δ
TextViewͷྖҬΛ֦ு self.textViewHeightConstraint.constant = [self heightForLines:self.maxNumberOfLines];
ొॲཧ
ϝοηʔδૹ৴ - վળલ • ը૾બ࣌ʹ௨৴ͯ͠ը૾Λొ • ͦͷޙɺURLΛऔಘ͢Δ • ϝοηʔδొ •
ܗ͞Εͨϝοηʔδͷૹ৴
ϝοηʔδૹ৴ - վળޙ • ϝοηʔδొ࣌ͷྲྀΕ • ొ͞Εͨը૾ͷૹ৴ • औಘͨ͠ը૾URLͱը૾Λஔ •
ܗ͞Εͨϝοηʔδͷૹ৴
GCD
GCD • dispatch queue ͱ͍͏ΩϡʔʹॲཧΛߦ͍ͨ ͍༰(λεΫ)ΛՃͯ͠ߦ͘ • ΩϡʔʹՃ͞ΕͨλεΫFIFOͰॱ൪ʹ࣮ ߦ͞ΕΔ •
λεΫͷ࣮ߦผεϨου্Ͱ࣮ߦ͞ΕΔ IUUQTHJUIVCDPNNJYJJODJ045SBJOJOHXJLJ(SBOE$FOUSBM%JTQBUDI
dispatch group • ͍͔ͭ͘ͷॲཧΛࢄͯ͠ߦ͍͍ͨɺ ͦͯ͠ࢄͯ͠ॲཧΛߦͬͨ݁ՌΛ ·ͱΊͯར༻͍ͨ͠ɺͱ͍ͬͨ࣌ʹ ར༻Ͱ͖Δͷ IUUQTHJUIVCDPNNJYJJODJ045SBJOJOHXJLJ(SBOE$FOUSBM%JTQBUDI
ϝοηʔδૹ৴ ϝοηʔδૹ৴ - վળલ
άϧʔϓ։࢝ ऴྃॲཧ ؔ࿈͚։࢝ EJTQBUDI@HSPVQ@FOUFS ؔ࿈͚ऴྃ ը૾ొ ϝοηʔδૹ৴ EJTQBUDI@HSPVQ@MFBWF EJTQBUDI@HSPVQ@OPUJGZ EJTQBUDI@HSPVQ@DSFBUF
ϝοηʔδૹ৴ - վળޙ
άϧʔϓ։࢝ ऴྃॲཧ ؔ࿈͚։࢝ EJTQBUDI@HSPVQ@FOUFS ؔ࿈͚ऴྃ ը૾ొ ϝοηʔδૹ৴ EJTQBUDI@HSPVQ@MFBWF EJTQBUDI@HSPVQ@OPUJGZ EJTQBUDI@HSPVQ@DSFBUF
ϝοηʔδૹ৴ - վળޙ ը૾ͷஔҐஔΛอ͍࣋ͯ͠Ε ॱෆಉʹͳͬͯͳ͍
ϝοηʔδૹ৴ self.dispatchGroup = dispatch_group_create(); // attributedString͔Βը૾Λநग़ if (images.count > 0)
{ for (UIImage *tmpImage in images) { [self sendImage:tmpImage]; } } dispatch_group_notify(self.dispatchGroup, dispatch_get_main_queue(), ^{ [self operationDidFinishWithAttributedString:attributedString indexPath:indexPath]; }); dispatch_group_enter(self.dispatchGroup); // snip AFHTTPRequestOperation *operation = [client HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject) { // snip dispatch_group_leave(self.dispatchGroup); } failure:^(AFHTTPRequestOperation *operation, NSError *error) { dispatch_group_leave(self.dispatchGroup); }]; [self.messageQueue addOperation:operation]; άϧʔϓͷ࡞
ϝοηʔδૹ৴ self.dispatchGroup = dispatch_group_create(); self.savedImageTags = [NSMutableArray array]; // snip
if (images.count > 0) { for (UIImage *tmpImage in images) { [self sendImage:tmpImage]; } } dispatch_group_notify(self.dispatchGroup, dispatch_get_main_queue(), ^{ [self operationDidFinishWithAttributedString:attributedString indexPath:indexPath]; }); dispatch_group_enter(self.dispatchGroup); // snip AFHTTPRequestOperation *operation = [client HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject) { // snip dispatch_group_leave(self.dispatchGroup); } failure:^(AFHTTPRequestOperation *operation, NSError *error) { dispatch_group_leave(self.dispatchGroup); }]; [self.messageQueue addOperation:operation]; ؔ࿈͚։࢝ ؔ࿈͚ऴྃ
ϝοηʔδૹ৴ self.dispatchGroup = dispatch_group_create(); // attributedString͔Βը૾Λநग़ if (images.count > 0)
{ for (UIImage *tmpImage in images) { [self sendImage:tmpImage]; } } dispatch_group_notify(self.dispatchGroup, dispatch_get_main_queue(), ^{ [self operationDidFinishWithAttributedString:attributedString indexPath:indexPath]; }); dispatch_group_enter(self.dispatchGroup); // snip AFHTTPRequestOperation *operation = [client HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject) { // snip dispatch_group_leave(self.dispatchGroup); } failure:^(AFHTTPRequestOperation *operation, NSError *error) { dispatch_group_leave(self.dispatchGroup); }]; [self.messageQueue addOperation:operation]; ϝοηʔδొॲཧ
࠶ૹॲཧ
࠶ૹॲཧ • λΠϜΞτ࣌ • ࣦഊͨ͜͠ͱΛ௨ • ࠶ૹͰ͖Δঢ়ଶʹ
Ұཡ
Ұཡ • ٯʹϖʔδϯά͢Δํ๏ • ௨ৗͷϖʔδϯά • ٯʹ͢Δͱͳʹ͕͔
ϖʔδϯά
௨ৗͷϖʔδϯά Loading
௨ৗͷϖʔδϯά - (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section { if (section ==
1) { self.indicatorView = [tableView dequeueReusableHeaderFooterViewWithIdentifier:@"MIIndicatorFooterView"]; [self getReviews]; return self.indicatorView; } return nil; } FooterView͕දࣔ͞ΕͨλΠϛϯάͰߦ͏
௨ৗͷϖʔδϯά - (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section { if (section ==
1) { self.indicatorView = [tableView dequeueReusableHeaderFooterViewWithIdentifier:@"MIIndicatorFooterView"]; [self getReviews]; return self.indicatorView; } return nil; } FooterView͕දࣔ͞ΕͨλΠϛϯάͰߦ͏ ϔομʹҠಈ͢Ε͍͍ͷͰʁ
௨ৗͷϖʔδϯά - (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section { if (section ==
1) { self.indicatorView = [tableView dequeueReusableHeaderFooterViewWithIdentifier:@"MIIndicatorFooterView"]; [self getReviews]; return self.indicatorView; } return nil; } FooterView͕දࣔ͞ΕͨλΠϛϯάͰߦ͏ ϔομʹҠಈ͢Ε͍͍ͷͰʁ
Կ͕͔ • σʔλ্͕෦ʹՃ͞Ε͍ͯ͘ • reloadData()࣌ʹcontentOffsetͷ͕ ͣΕΔ • ׳ੑ͕͍ͬͯΔঢ়گͩͱڧ੍తʹ Ҡಈ͞Εͯ͠·͏
ௐࠪ • ඪ४ϝοηʔδΞϓϦͷಈ͖ • දࣔ࣌ʹϩʔσΟϯά։࢝ • ׳ੑεΫϩʔϧ͕ఀࢭͨ͠λΠϛϯ άͰऔಘ։࢝
ௐࠪ • ඪ४ϝοηʔδΞϓϦͷಈ͖ • දࣔ࣌ʹϩʔσΟϯά։࢝ • ׳ੑεΫϩʔϧ͕ఀࢭͨ͠λΠϛϯ άͰऔಘ։࢝
ղܾࡦ • ׳ੑεΫϩʔϧ͕ఀࢭͨ͠ • ScrollToTop࣌׳ੑεΫϩʔϧ͕ൃ ੜ͠ͳ͍ - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView -
(BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView
·ͱΊ
·ͱΊ • ࡉ͔͍UXͷվળ • ςΩετೖྗɺ௨৴ॲཧɺϖʔδϯά • ͍ΖΜͳΞϓϦͷUXΛૢ࡞ɾݕূɺϓ ϥάΠϯͷ࣮ΛಡΜͩΓ͢Δͱࢳޱ ͕ݟ͑Δ
ࢀߟ JSQMessagesViewController SlackTextViewController etc
Thanks.