Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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

nakajijapan
December 15, 2015

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

nakajijapan

December 15, 2015
Tweet

More Decks by nakajijapan

Other Decks in Technology

Transcript

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

    View Slide

  2. About Me

    View Slide

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

    View Slide

  4. GitHub

    View Slide

  5. NKJMultiMovieCaptureView
    NKJMovieComposer
    NKJPagerViewController
    PhotoSlider
    etc
    Teiten

    View Slide

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

    View Slide

  7. minne
    iOS / Android

    View Slide

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

    View Slide

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

    View Slide

  10. ௠ँ

    View Slide

  11. Talk about

    View Slide

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

    View Slide

  13. ϝοηʔδ
    ػೳ

    View Slide

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

    View Slide

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

    View Slide

  16. ܦҢ

    View Slide

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

    View Slide

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

    View Slide

  19. վળ

    View Slide

  20. લఏ஌ࣝ

    View Slide

  21. Text Kit

    View Slide

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

    View Slide

  23. Text Kit
    • NSTextContainer
    • ςΩετͷ࠲ඪͱܗঢ়Λ؅ཧ
    • NSLayoutManager
    • ϨΠΞ΢τͱϨϯμϦϯάΛௐ੔
    • NSTextStorage
    • NSMutableAttributedStringͷαϒΫϥε

    View Slide

  24. ϝοηʔδೖྗ

    View Slide

  25. ϝοηʔδೖྗ
    • Growing Text View
    • ಈతʹTextྖҬΛ֦ு͢Δ
    • ϦΞϧλΠϜϓϨϏϡʔ
    • ૹ৴ͨ͠ঢ়ଶͱಉ͡΋ͷΛදࣔ͢Δ

    View Slide

  26. MITextInputView
    MITextView
    NSLayoutConstraint *heightConstraint;

    View Slide

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

    View Slide

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

    View Slide

  29. Growing Text View
    ςΩετ
    UITextView

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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];
    }
    ࠷େߦ਺Ҏ಺
    ࠷େߦ਺Ҏ্

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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];
    ը૾બ୒࣌

    View Slide

  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];
    ը૾બ୒࣌
    ը૾ͷૠೖ

    View Slide

  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];
    ը૾બ୒࣌
    Χʔιϧʹ͋ΔҐஔʹը૾Λૠೖ
    ը૾બ୒ޙͷಈ͖

    View Slide

  45. ը૾બ୒ޙͷಈ͖
    ݱࡏͷߴ͞Λࢉग़ͯ͠
    TextViewྖҬΛ޿͛Δ

    View Slide

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

    View Slide

  47. ొ࿥ॲཧ

    View Slide

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

    View Slide

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

    View Slide

  50. GCD

    View Slide

  51. GCD
    • dispatch queue ͱ͍͏ΩϡʔʹॲཧΛߦ͍ͨ
    ͍಺༰(λεΫ)Λ௥Ճͯ͠ߦ͘
    • Ωϡʔʹ௥Ճ͞ΕͨλεΫ͸FIFOͰॱ൪ʹ࣮
    ߦ͞ΕΔ
    • λεΫͷ࣮ߦ͸ผεϨου্Ͱ࣮ߦ͞ΕΔ
    IUUQTHJUIVCDPNNJYJJODJ045SBJOJOHXJLJ(SBOE$FOUSBM%JTQBUDI

    View Slide

  52. dispatch group
    • ͍͔ͭ͘ͷॲཧΛ෼ࢄͯ͠ߦ͍͍ͨɺ
    ͦͯ͠෼ࢄͯ͠ॲཧΛߦͬͨ݁ՌΛ
    ·ͱΊͯར༻͍ͨ͠ɺͱ͍ͬͨ࣌ʹ
    ར༻Ͱ͖Δ΋ͷ
    IUUQTHJUIVCDPNNJYJJODJ045SBJOJOHXJLJ(SBOE$FOUSBM%JTQBUDI

    View Slide

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

    View Slide

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

    View Slide

  55. άϧʔϓ։࢝
    ऴྃॲཧ
    ؔ࿈෇͚։࢝ [email protected]@FOUFS
    ؔ࿈෇͚ऴྃ
    ը૾ొ࿥
    ϝοηʔδૹ৴
    [email protected]@MFBWF
    [email protected]@OPUJGZ
    [email protected]@DSFBUF
    ϝοηʔδૹ৴ - վળޙ
    ը૾ͷ഑ஔҐஔΛอ͍࣋ͯ͠Ε͹
    ॱෆಉʹͳͬͯ΋໰୊ͳ͍

    View Slide

  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];
    άϧʔϓͷ࡞੒

    View Slide

  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];
    ؔ࿈෇͚։࢝
    ؔ࿈෇͚ऴྃ

    View Slide

  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];
    ϝοηʔδొ࿥ॲཧ

    View Slide

  59. ࠶ૹॲཧ

    View Slide

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

    View Slide

  61. Ұཡ

    View Slide

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

    View Slide

  63. ϖʔδϯά

    View Slide

  64. ௨ৗͷϖʔδϯά
    Loading

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  68. Կ͕໰୊͔
    • σʔλ্͕෦ʹ௥Ճ͞Ε͍ͯ͘
    • reloadData()࣌ʹcontentOffsetͷ஋͕
    ͣΕΔ
    • ׳ੑ͕࢒͍ͬͯΔঢ়گͩͱڧ੍తʹ
    Ҡಈ͞Εͯ͠·͏

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  72. ·ͱΊ

    View Slide

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

    View Slide

  74. ࢀߟ
    JSQMessagesViewController
    SlackTextViewController
    etc

    View Slide

  75. Thanks.

    View Slide