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
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.7k
Firebase Authorization
nakajijapan
0
340
Intoducing Izumo
nakajijapan
1
2.1k
Practical CloudKit
nakajijapan
1
1.9k
Introducing to Ajimi - プロダクトを味見していこう
nakajijapan
0
2.4k
Shari
nakajijapan
1
3k
Japan Apple Pay Development
nakajijapan
0
350
業務で絶対必要にならない技術
nakajijapan
0
910
Other Decks in Technology
See All in Technology
Data Hubグループ 紹介資料
sansan33
PRO
0
3.2k
わたしが知り合いゼロの勉強会に 行けるようになるまで
r5ni4
2
450
20260817_生成AIの動向と中学校・高等学校での活用を考える_v1.00_公開用
doradora09
PRO
0
210
Eight Engineering Unit 紹介資料
sansan33
PRO
3
8.3k
AWS Blocks が楽しい #ゆるWeb札幌
tacck
PRO
0
140
私がブラウザを自作したくなった理由
supurazako
1
280
Genie Codeハンズオン応用編
taka_aki
0
140
2027年のMetricKit
kantacky
0
170
AI開発に用いられるHPC技術について
gpuunite_official
0
270
【書籍出版記念】 10周回って、エージェント開発は RAGがすべてだった。〜RAGの歴史と開発現場で見えた実践知〜
akiratameto
20
7.1k
Kiro WebとCloud Sessions
nagisa53
2
180
形式手法特論:Hyperproperty とモデル検査 #kernelvm / Kernel VM Study Tokyo 19th
ytaka23
0
760
Featured
See All Featured
Design of three-dimensional binary manipulators for pick-and-place task avoiding obstacles (IECON2024)
konakalab
0
550
Noah Learner - AI + Me: how we built a GSC Bulk Export data pipeline
techseoconnect
PRO
0
400
How GitHub (no longer) Works
holman
316
150k
The B2B funnel & how to create a winning content strategy
katarinadahlin
PRO
1
480
HTML-Aware ERB: The Path to Reactive Rendering @ RubyCon 2026, Rimini, Italy
marcoroth
3
480
The Curious Case for Waylosing
cassininazir
1
480
A better future with KSS
kneath
240
18k
The Illustrated Children's Guide to Kubernetes
chrisshort
51
53k
The Web Performance Landscape in 2024 [PerfNow 2024]
tammyeverts
12
1.3k
What's in a price? How to price your products and services
michaelherold
247
13k
DBのスキルで生き残る技術 - AI時代におけるテーブル設計の勘所
soudai
PRO
68
57k
How to Build an AI Search Optimization Roadmap - Criteria and Steps to Take #SEOIRL
aleyda
1
2.1k
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.