Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Speaker Deck
PRO
Sign in
Sign up for free
minneのメッセージ機能改善活動
nakajijapan
December 15, 2015
Technology
1
2.2k
minneのメッセージ機能改善活動
http://shibuya-swift.connpass.com/event/21979/
nakajijapan
December 15, 2015
Tweet
Share
More Decks by nakajijapan
See All by nakajijapan
サービスにおけるDesign Systemの構築
nakajijapan
6
2.5k
Markdownをリアルタイムに解析する
nakajijapan
4
3.4k
Firebase Authorization
nakajijapan
0
98
Intoducing Izumo
nakajijapan
1
1.3k
Practical CloudKit
nakajijapan
1
1.2k
Introducing to Ajimi - プロダクトを味見していこう
nakajijapan
0
1.5k
Shari
nakajijapan
1
2.1k
Japan Apple Pay Development
nakajijapan
0
200
業務で絶対必要にならない技術
nakajijapan
0
520
Other Decks in Technology
See All in Technology
PUTとPOSTどっち使う?
hankehly
0
140
2022年度新卒技術研修「Docker」講義
excitejp
PRO
0
340
OPENLOGI Company Profile
hr01
0
220
Target SDK Versionを上げない Notification runtime permission対応
napplecomputer
0
110
Building smarter apps with machine learning, from magic to reality
picardparis
4
3.2k
1人目SETとして入社して2ヶ月の間におこなったこと
tarappo
3
390
スクラムのスケールとチームトポロジー / Scaled Scrum and Team Topologies
daiksy
1
370
JUnit5.7, 5.8の新機能紹介 #jjug_ccc #jjug_ccc_b / junit 5.7, 5.8 new features
kyonmm
PRO
2
390
音のような言葉 〜ちゃちゃっとチャットで楽しむちょっとしたコツ〜 / words like sounds
satoryu
1
1.3k
1人目QAエンジニアよもやま話 / QA Test Talk Vol.1
nametake
4
220
What's Data Lake ? Azure Data Lake best practice
ryomaru0825
2
710
マネージャーからみたスクラムと自己管理化
shibe23
0
940
Featured
See All Featured
Typedesign – Prime Four
hannesfritz
33
1.3k
A designer walks into a library…
pauljervisheath
196
16k
Practical Orchestrator
shlominoach
178
8.6k
Code Reviewing Like a Champion
maltzj
506
37k
Gamification - CAS2011
davidbonilla
75
3.9k
How GitHub Uses GitHub to Build GitHub
holman
465
280k
jQuery: Nuts, Bolts and Bling
dougneiner
56
6.4k
YesSQL, Process and Tooling at Scale
rocio
157
12k
Designing on Purpose - Digital PM Summit 2013
jponch
106
5.6k
Become a Pro
speakerdeck
PRO
3
820
The Mythical Team-Month
searls
209
39k
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
315
19k
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.