minneのメッセージ機能改善活動

A77456b262557e22986345f6d0555c58?s=47 nakajijapan
December 15, 2015

 minneのメッセージ機能改善活動

A77456b262557e22986345f6d0555c58?s=128

nakajijapan

December 15, 2015
Tweet

Transcript

  1. ϝοηʔδ ػೳվળ׆ಈ shibuya.swift #2 @nakajijapan

  2. About Me

  3. @nakajijapan Software Engineer GMO PEPABO inc. iOS / Web /

    OS X
  4. GitHub

  5. NKJMultiMovieCaptureView NKJMovieComposer NKJPagerViewController PhotoSlider etc Teiten

  6. NKJMultiMovieCaptureView NKJMovieComposer NKJPagerViewController PhotoSlider etc Teiten SwiftԽʂʂʂʂ

  7. minne iOS / Android

  8. 400 million app download ࠃ಺࠷େͷϋϯυϝΠυϚʔέοτ

  9. Google Play™ετΞ ʮ2015೥ϕετΞϓ Ϧʯʹબग़ʂ

  10. ௠ँ

  11. Talk about

  12. ϝοηʔδ ػೳվળ׆ಈ

  13. ϝοηʔδ ػೳ

  14. ϝοηʔδػೳ • 2014೥08݄ϦϦʔε • ࡞Ո͞ΜͱίϛϡχέʔγϣϯͰ͖Δ • ؾܰʹϝοηʔδަ׵ͯ͠΄͍͠

  15. ϝοηʔδػೳվળ׆ಈ • ܦҢ • มߋ಺༰ • ϝοηʔδೖྗ • Ұཡ •

    ·ͱΊ
  16. ܦҢ

  17. ܦҢ • όʔδϣϯΞοϓͰΠϯλʔϑΣʔε ΛҰ৽͕ͨ͠ɺΠϯλϥΫγϣϯͷ վળ͸์ஔؾຯͩͬͨ • ཁ๬΍όάͷ૿Ճ

  18. ܦҢ • όʔδϣϯΞοϓͰΠϯλʔϑΣʔε ΛҰ৽͕ͨ͠ɺΠϯλϥΫγϣϯͷ վળ͸์ஔؾຯͩͬͨ • ཁ๬΍όάͷ૿Ճ ΋ͬͱ҆৺͍ͤͨ͞ʂ

  19. վળ

  20. લఏ஌ࣝ

  21. Text Kit

  22. UITextView • ෳ਺ߦͷςΩετྖҬɾεΫϩʔϧΛ؅ཧ͢Δ • Πϯελϯεੜ੒࣌ʹҎԼͷΠϯελϯε΋ੜ੒ • NSTextContainer • NSLayoutManager •

    NSTextStorage • ͍ͣΕ΋readOnly
  23. Text Kit • NSTextContainer • ςΩετͷ࠲ඪͱܗঢ়Λ؅ཧ • NSLayoutManager • ϨΠΞ΢τͱϨϯμϦϯάΛௐ੔

    • NSTextStorage • NSMutableAttributedStringͷαϒΫϥε
  24. ϝοηʔδೖྗ

  25. ϝοηʔδೖྗ • Growing Text View • ಈతʹTextྖҬΛ֦ு͢Δ • ϦΞϧλΠϜϓϨϏϡʔ •

    ૹ৴ͨ͠ঢ়ଶͱಉ͡΋ͷΛදࣔ͢Δ
  26. MITextInputView MITextView NSLayoutConstraint *heightConstraint;

  27. Growing Text View • ೖྗͨ࣌͠ͷΠϕϯτΛܭଌ͢Δ • ߴ͞Λܭଌ͠ɺViewΛߋ৽͢Δ

  28. Growing Text View [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textDidChange:) name:UITextViewTextDidChangeNotification object:nil];

  29. Growing Text View ςΩετ UITextView

  30. roundf(textView.font.lineHeight * numberOfLines) textView.textContainerInset.bottom textView.textContainerInset.top Growing Text View ςΩετ UITextView

  31. Growing Text View • ྖҬ֦େ࣌ͷ࠷େ஋Λߦ਺Ͱࢦఆ • ࡉ͔͍ߴ͞ͷௐ੔͕Մೳ • ΫϥεΛར༻͢Δ΋ͷ͕ຖ౓ߴ͞Λ ҙࣝ͠ͳͯ͘ࡁΉ

  32. վߦҐஔͷݻఆ վߦ࣌͸ ΧʔιϧΛৗʹ࠷ԼҐʹ ഑ஔͰ͖ΔΑ͏ʹ͢Δ

  33. վߦҐஔͷݻఆ • ࠷େߦ਺ҎԼ • Viewͷ੍໿ʢߴ͞ʣΛߋ৽͢Δ • ৗʹ࠷্෦΁εΫϩʔϧͤ͞Δ • ࠷େߦ਺Λ௒͑ͨ •

    Viewͷ੍໿ʢߴ͞ʣΛߋ৽͢Δ • ৗʹ࠷Լ෦΁εΫϩʔϧͤ͞Δ
  34. վߦҐஔͷݻఆ [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]; } ࠷େߦ਺Ҏ಺ ࠷େߦ਺Ҏ্
  35. ϦΞϧλΠϜ ϓϨϏϡʔ

  36. ϦΞϧλΠϜϓϨϏϡʔ

  37. ϦΞϧλΠϜϓϨϏϡʔ

  38. ϦΞϧλΠϜϓϨϏϡʔ

  39. ϦΞϧλΠϜϓϨϏϡʔ

  40. ϦΞϧλΠϜϓϨϏϡʔ • ը૾౤ߘͷϩδοΫΛվળ • ࠓ·Ͱ • ը૾બ୒࣌ʹαʔόʹPOST • TextViewʹදࣔ͞ΕΔͷ͸ͦͷURL

  41. ϦΞϧλΠϜϓϨϏϡʔ • ը૾౤ߘͷϩδοΫΛվળ • ࠓ·Ͱ • ը૾બ୒࣌ʹαʔόʹPOST • TextViewʹදࣔ͞ΕΔͷ͸ͦͷURL ➡ϝοηʔδૹ৴࣌ʹ·ͱΊͯૹ৴

    ➡౤ߘͨ͠ը૾ΛϓϨϏϡʔͤ͞Δ
  42. ϦΞϧλΠϜϓϨϏϡʔ 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]; ը૾બ୒࣌
  43. ϦΞϧλΠϜϓϨϏϡʔ 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]; ը૾બ୒࣌ ը૾ͷૠೖ
  44. 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]; ը૾બ୒࣌ Χʔιϧʹ͋ΔҐஔʹը૾Λૠೖ ը૾બ୒ޙͷಈ͖
  45. ը૾બ୒ޙͷಈ͖ ݱࡏͷߴ͞Λࢉग़ͯ͠ TextViewྖҬΛ޿͛Δ

  46. TextViewͷྖҬΛ֦ு self.textViewHeightConstraint.constant = [self heightForLines:self.maxNumberOfLines];

  47. ొ࿥ॲཧ

  48. ϝοηʔδૹ৴ - վળલ • ը૾બ୒࣌ʹ௨৴ͯ͠ը૾Λొ࿥ • ͦͷޙɺURLΛऔಘ͢Δ • ϝοηʔδొ࿥ •

    ੔ܗ͞Εͨϝοηʔδͷૹ৴
  49. ϝοηʔδૹ৴ - վળޙ • ϝοηʔδొ࿥࣌ͷྲྀΕ • ొ࿥͞Εͨը૾ͷૹ৴ • औಘͨ͠ը૾URLͱը૾Λஔ׵ •

    ੔ܗ͞Εͨϝοηʔδͷૹ৴
  50. GCD

  51. GCD • dispatch queue ͱ͍͏ΩϡʔʹॲཧΛߦ͍ͨ ͍಺༰(λεΫ)Λ௥Ճͯ͠ߦ͘ • Ωϡʔʹ௥Ճ͞ΕͨλεΫ͸FIFOͰॱ൪ʹ࣮ ߦ͞ΕΔ •

    λεΫͷ࣮ߦ͸ผεϨου্Ͱ࣮ߦ͞ΕΔ IUUQTHJUIVCDPNNJYJJODJ045SBJOJOHXJLJ(SBOE$FOUSBM%JTQBUDI
  52. dispatch group • ͍͔ͭ͘ͷॲཧΛ෼ࢄͯ͠ߦ͍͍ͨɺ ͦͯ͠෼ࢄͯ͠ॲཧΛߦͬͨ݁ՌΛ ·ͱΊͯར༻͍ͨ͠ɺͱ͍ͬͨ࣌ʹ ར༻Ͱ͖Δ΋ͷ IUUQTHJUIVCDPNNJYJJODJ045SBJOJOHXJLJ(SBOE$FOUSBM%JTQBUDI

  53. ϝοηʔδૹ৴ ϝοηʔδૹ৴ - վળલ

  54. άϧʔϓ։࢝ ऴྃॲཧ ؔ࿈෇͚։࢝ EJTQBUDI@HSPVQ@FOUFS ؔ࿈෇͚ऴྃ ը૾ొ࿥ ϝοηʔδૹ৴ EJTQBUDI@HSPVQ@MFBWF EJTQBUDI@HSPVQ@OPUJGZ EJTQBUDI@HSPVQ@DSFBUF

    ϝοηʔδૹ৴ - վળޙ
  55. άϧʔϓ։࢝ ऴྃॲཧ ؔ࿈෇͚։࢝ EJTQBUDI@HSPVQ@FOUFS ؔ࿈෇͚ऴྃ ը૾ొ࿥ ϝοηʔδૹ৴ EJTQBUDI@HSPVQ@MFBWF EJTQBUDI@HSPVQ@OPUJGZ EJTQBUDI@HSPVQ@DSFBUF

    ϝοηʔδૹ৴ - վળޙ ը૾ͷ഑ஔҐஔΛอ͍࣋ͯ͠Ε͹ ॱෆಉʹͳͬͯ΋໰୊ͳ͍
  56. ϝοηʔδૹ৴ 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]; άϧʔϓͷ࡞੒
  57. ϝοηʔδૹ৴ 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]; ؔ࿈෇͚։࢝ ؔ࿈෇͚ऴྃ
  58. ϝοηʔδૹ৴ 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]; ϝοηʔδొ࿥ॲཧ
  59. ࠶ૹॲཧ

  60. ࠶ૹॲཧ • λΠϜΞ΢τ࣌ • ࣦഊͨ͜͠ͱΛ௨஌ • ࠶ૹͰ͖Δঢ়ଶʹ

  61. Ұཡ

  62. Ұཡ • ٯʹϖʔδϯά͢Δํ๏ • ௨ৗͷϖʔδϯά • ٯʹ͢Δͱͳʹ͕໰୊͔

  63. ϖʔδϯά

  64. ௨ৗͷϖʔδϯά Loading

  65. ௨ৗͷϖʔδϯά - (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section { if (section ==

    1) { self.indicatorView = [tableView dequeueReusableHeaderFooterViewWithIdentifier:@"MIIndicatorFooterView"]; [self getReviews]; return self.indicatorView; } return nil; } FooterView͕දࣔ͞ΕͨλΠϛϯάͰߦ͏
  66. ௨ৗͷϖʔδϯά - (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section { if (section ==

    1) { self.indicatorView = [tableView dequeueReusableHeaderFooterViewWithIdentifier:@"MIIndicatorFooterView"]; [self getReviews]; return self.indicatorView; } return nil; } FooterView͕දࣔ͞ΕͨλΠϛϯάͰߦ͏ ϔομʹҠಈ͢Ε͹͍͍ͷͰ͸ʁ
  67. ௨ৗͷϖʔδϯά - (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section { if (section ==

    1) { self.indicatorView = [tableView dequeueReusableHeaderFooterViewWithIdentifier:@"MIIndicatorFooterView"]; [self getReviews]; return self.indicatorView; } return nil; } FooterView͕දࣔ͞ΕͨλΠϛϯάͰߦ͏ ϔομʹҠಈ͢Ε͹͍͍ͷͰ͸ʁ
  68. Կ͕໰୊͔ • σʔλ্͕෦ʹ௥Ճ͞Ε͍ͯ͘ • reloadData()࣌ʹcontentOffsetͷ஋͕ ͣΕΔ • ׳ੑ͕࢒͍ͬͯΔঢ়گͩͱڧ੍తʹ Ҡಈ͞Εͯ͠·͏

  69. ௐࠪ • ඪ४ϝοηʔδΞϓϦͷಈ͖ • දࣔ࣌ʹϩʔσΟϯά։࢝ • ׳ੑεΫϩʔϧ͕ఀࢭͨ͠λΠϛϯ άͰऔಘ։࢝

  70. ௐࠪ • ඪ४ϝοηʔδΞϓϦͷಈ͖ • දࣔ࣌ʹϩʔσΟϯά։࢝ • ׳ੑεΫϩʔϧ͕ఀࢭͨ͠λΠϛϯ άͰऔಘ։࢝

  71. ղܾࡦ • ׳ੑεΫϩʔϧ͕ఀࢭͨ͠ • ScrollToTop࣌͸׳ੑεΫϩʔϧ͕ൃ ੜ͠ͳ͍ - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView -

    (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView
  72. ·ͱΊ

  73. ·ͱΊ • ࡉ͔͍UXͷվળ • ςΩετೖྗɺ௨৴ॲཧɺϖʔδϯά • ͍ΖΜͳΞϓϦͷUXΛૢ࡞ɾݕূɺϓ ϥάΠϯͷ࣮૷ΛಡΜͩΓ͢Δͱࢳޱ ͕ݟ͑Δ

  74. ࢀߟ JSQMessagesViewController SlackTextViewController etc

  75. Thanks.