diff --git a/Yep.xcodeproj/project.pbxproj b/Yep.xcodeproj/project.pbxproj index 9cb5d45f..019fec80 100644 --- a/Yep.xcodeproj/project.pbxproj +++ b/Yep.xcodeproj/project.pbxproj @@ -90,6 +90,12 @@ 502AE5541AB93215005BD199 /* ProfileFooterCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 502AE5521AB93215005BD199 /* ProfileFooterCell.xib */; }; 502AE5581AB932D5005BD199 /* ProfileSectionHeaderReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 502AE5561AB932D5005BD199 /* ProfileSectionHeaderReusableView.swift */; }; 502AE5591AB932D5005BD199 /* ProfileSectionHeaderReusableView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 502AE5571AB932D5005BD199 /* ProfileSectionHeaderReusableView.xib */; }; + 502B605A1BD1102B00D9C17E /* TitleCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 502B60581BD1102B00D9C17E /* TitleCell.swift */; settings = {ASSET_TAGS = (); }; }; + 502B605B1BD1102B00D9C17E /* TitleCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 502B60591BD1102B00D9C17E /* TitleCell.xib */; settings = {ASSET_TAGS = (); }; }; + 502B605F1BD112E500D9C17E /* QuickPickPhotosCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 502B605D1BD112E500D9C17E /* QuickPickPhotosCell.swift */; settings = {ASSET_TAGS = (); }; }; + 502B60601BD112E500D9C17E /* QuickPickPhotosCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 502B605E1BD112E500D9C17E /* QuickPickPhotosCell.xib */; settings = {ASSET_TAGS = (); }; }; + 502B60641BD1208E00D9C17E /* CameraCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 502B60621BD1208E00D9C17E /* CameraCell.swift */; settings = {ASSET_TAGS = (); }; }; + 502B60651BD1208E00D9C17E /* CameraCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 502B60631BD1208E00D9C17E /* CameraCell.xib */; settings = {ASSET_TAGS = (); }; }; 503315971AC1017E0008A209 /* ChatLeftTextCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 503315951AC1017E0008A209 /* ChatLeftTextCell.swift */; }; 503315981AC1017E0008A209 /* ChatLeftTextCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 503315961AC1017E0008A209 /* ChatLeftTextCell.xib */; }; 5033159C1AC101C80008A209 /* ChatRightTextCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5033159A1AC101C80008A209 /* ChatRightTextCell.swift */; }; @@ -214,6 +220,7 @@ 50AFBF4E1BB3A594007D19DA /* bub3.caf in Resources */ = {isa = PBXBuildFile; fileRef = 50AFBF4D1BB3A594007D19DA /* bub3.caf */; settings = {ASSET_TAGS = (); }; }; 50AFBF5F1BB4F332007D19DA /* FeedsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50AFBF5E1BB4F332007D19DA /* FeedsViewController.swift */; settings = {ASSET_TAGS = (); }; }; 50B2CA921BD0BF3800668AC6 /* FeedTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B2CA911BD0BF3800668AC6 /* FeedTextView.swift */; settings = {ASSET_TAGS = (); }; }; + 50B2CA941BD0EF7900668AC6 /* MoreMessageTypesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B2CA931BD0EF7900668AC6 /* MoreMessageTypesView.swift */; settings = {ASSET_TAGS = (); }; }; 50B38FB71B69CD960064C132 /* Array+Yep.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B38FB61B69CD960064C132 /* Array+Yep.swift */; }; 50B38FCC1B6B37F00064C132 /* FeedbackViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B38FCB1B6B37F00064C132 /* FeedbackViewController.swift */; }; 50B8E6161B40F9E700C17EA1 /* YepDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B8E6151B40F9E700C17EA1 /* YepDownloader.swift */; }; @@ -386,6 +393,12 @@ 502AE5521AB93215005BD199 /* ProfileFooterCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = ProfileFooterCell.xib; path = Views/Cells/ProfileFooter/ProfileFooterCell.xib; sourceTree = ""; }; 502AE5561AB932D5005BD199 /* ProfileSectionHeaderReusableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ProfileSectionHeaderReusableView.swift; path = Views/ReusableViews/ProfileSectionHeader/ProfileSectionHeaderReusableView.swift; sourceTree = ""; }; 502AE5571AB932D5005BD199 /* ProfileSectionHeaderReusableView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = ProfileSectionHeaderReusableView.xib; path = Views/ReusableViews/ProfileSectionHeader/ProfileSectionHeaderReusableView.xib; sourceTree = ""; }; + 502B60581BD1102B00D9C17E /* TitleCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TitleCell.swift; path = Views/Cells/Title/TitleCell.swift; sourceTree = ""; }; + 502B60591BD1102B00D9C17E /* TitleCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = TitleCell.xib; path = Views/Cells/Title/TitleCell.xib; sourceTree = ""; }; + 502B605D1BD112E500D9C17E /* QuickPickPhotosCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = QuickPickPhotosCell.swift; path = Views/Cells/QuickPickPhotos/QuickPickPhotosCell.swift; sourceTree = ""; }; + 502B605E1BD112E500D9C17E /* QuickPickPhotosCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = QuickPickPhotosCell.xib; path = Views/Cells/QuickPickPhotos/QuickPickPhotosCell.xib; sourceTree = ""; }; + 502B60621BD1208E00D9C17E /* CameraCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CameraCell.swift; path = Views/Cells/Camera/CameraCell.swift; sourceTree = ""; }; + 502B60631BD1208E00D9C17E /* CameraCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = CameraCell.xib; path = Views/Cells/Camera/CameraCell.xib; sourceTree = ""; }; 503315951AC1017E0008A209 /* ChatLeftTextCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ChatLeftTextCell.swift; path = Views/Cells/ChatLeftText/ChatLeftTextCell.swift; sourceTree = ""; }; 503315961AC1017E0008A209 /* ChatLeftTextCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = ChatLeftTextCell.xib; path = Views/Cells/ChatLeftText/ChatLeftTextCell.xib; sourceTree = ""; }; 5033159A1AC101C80008A209 /* ChatRightTextCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ChatRightTextCell.swift; path = Views/Cells/ChatRightText/ChatRightTextCell.swift; sourceTree = ""; }; @@ -511,6 +524,7 @@ 50AFBF4D1BB3A594007D19DA /* bub3.caf */ = {isa = PBXFileReference; lastKnownFileType = file; name = bub3.caf; path = Sounds/bub3.caf; sourceTree = ""; }; 50AFBF5E1BB4F332007D19DA /* FeedsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedsViewController.swift; path = ViewControllers/Feeds/FeedsViewController.swift; sourceTree = ""; }; 50B2CA911BD0BF3800668AC6 /* FeedTextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedTextView.swift; path = Views/TextViews/FeedTextView.swift; sourceTree = ""; }; + 50B2CA931BD0EF7900668AC6 /* MoreMessageTypesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MoreMessageTypesView.swift; path = Views/MoreMessageTypes/MoreMessageTypesView.swift; sourceTree = ""; }; 50B38FB61B69CD960064C132 /* Array+Yep.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Array+Yep.swift"; path = "Extensions/Array+Yep.swift"; sourceTree = ""; }; 50B38FCB1B6B37F00064C132 /* FeedbackViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedbackViewController.swift; path = ViewControllers/Feedback/FeedbackViewController.swift; sourceTree = ""; }; 50B8E6151B40F9E700C17EA1 /* YepDownloader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = YepDownloader.swift; path = Services/YepDownloader.swift; sourceTree = ""; }; @@ -974,6 +988,7 @@ 0AC25F041B1100F3009E6E13 /* ScrollView */, 50E18BB81B180F8E0076C3C6 /* SayHi */, 5019F62F1B39BFCB0000D993 /* ConversationMore */, + 50B2CA951BD0EF8A00668AC6 /* MoreMessageTypes */, 500A0B201B451A7400D49358 /* Badge */, 504F85AD1B4D21E400F909BE /* DiscoverFilter */, 8409137E1B7DB178001C89D6 /* FriendRequest */, @@ -1037,6 +1052,9 @@ 501138331BCB62A70055A014 /* FeedConversation */, 6A82150A1BCBE8F40011F36C /* DiscoverSkill */, 50535D251BCE4A0A002FE1A4 /* Photo */, + 502B605C1BD1102F00D9C17E /* Title */, + 502B60611BD112EA00D9C17E /* QuickPickPhotos */, + 502B60661BD1209300D9C17E /* Camera */, ); name = Cells; sourceTree = ""; @@ -1199,6 +1217,33 @@ name = ProfileSectionHeader; sourceTree = ""; }; + 502B605C1BD1102F00D9C17E /* Title */ = { + isa = PBXGroup; + children = ( + 502B60581BD1102B00D9C17E /* TitleCell.swift */, + 502B60591BD1102B00D9C17E /* TitleCell.xib */, + ); + name = Title; + sourceTree = ""; + }; + 502B60611BD112EA00D9C17E /* QuickPickPhotos */ = { + isa = PBXGroup; + children = ( + 502B605D1BD112E500D9C17E /* QuickPickPhotosCell.swift */, + 502B605E1BD112E500D9C17E /* QuickPickPhotosCell.xib */, + ); + name = QuickPickPhotos; + sourceTree = ""; + }; + 502B60661BD1209300D9C17E /* Camera */ = { + isa = PBXGroup; + children = ( + 502B60621BD1208E00D9C17E /* CameraCell.swift */, + 502B60631BD1208E00D9C17E /* CameraCell.xib */, + ); + name = Camera; + sourceTree = ""; + }; 503315991AC101830008A209 /* ChatLeftText */ = { isa = PBXGroup; children = ( @@ -1693,6 +1738,14 @@ name = Feed; sourceTree = ""; }; + 50B2CA951BD0EF8A00668AC6 /* MoreMessageTypes */ = { + isa = PBXGroup; + children = ( + 50B2CA931BD0EF7900668AC6 /* MoreMessageTypesView.swift */, + ); + name = MoreMessageTypes; + sourceTree = ""; + }; 50B38FCD1B6B37F90064C132 /* Feedback */ = { isa = PBXGroup; children = ( @@ -2116,6 +2169,7 @@ 502AE54F1AB93175005BD199 /* ProfileHeaderCell.xib in Resources */, 50F5AF9A1BBBD02A0033C9BC /* FeedMediaCell.xib in Resources */, 50A640EA1B6F45FA0050E4A3 /* DoNotDisturbPeriodCell.xib in Resources */, + 502B60651BD1208E00D9C17E /* CameraCell.xib in Resources */, 0A8F54BB1AB67D11004AD60E /* LaunchScreen.xib in Resources */, 5053AD511AF82A1700B3CBFA /* PickLocationCell.xib in Resources */, 5011382D1BCB5E150055A014 /* FeedConversationDockCell.xib in Resources */, @@ -2123,6 +2177,7 @@ 0A8F54B81AB67D11004AD60E /* Images.xcassets in Resources */, 50EB8CC51AE89F12001AC1EE /* ChatRightVideoCell.xib in Resources */, 502AE5191AB70D72005BD199 /* Intro.storyboard in Resources */, + 502B60601BD112E500D9C17E /* QuickPickPhotosCell.xib in Resources */, 504BDC151B4528A500DBD35D /* Badges.xcassets in Resources */, 50F5AF961BBBCDA30033C9BC /* FeedMediaAddCell.xib in Resources */, 50894D081AEDFDA2000981AF /* EditProfileMoreInfoCell.xib in Resources */, @@ -2135,6 +2190,7 @@ 501138321BCB62A20055A014 /* FeedConversationCell.xib in Resources */, 848806B41B7B4E2E000C5424 /* Show.storyboard in Resources */, 0AD7EA771AC3EEB000617758 /* PushConfig.plist in Resources */, + 502B605B1BD1102B00D9C17E /* TitleCell.xib in Resources */, 5064F65A1B0B14420089FAD4 /* AddFriendMoreCell.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2242,6 +2298,7 @@ 50F5AF991BBBD02A0033C9BC /* FeedMediaCell.swift in Sources */, 5023DE331AB688F600B3EE96 /* UIColor+Yep.swift in Sources */, 503315971AC1017E0008A209 /* ChatLeftTextCell.swift in Sources */, + 50B2CA941BD0EF7900668AC6 /* MoreMessageTypesView.swift in Sources */, 0A8F54B11AB67D11004AD60E /* AppDelegate.swift in Sources */, 508F8FC81B0161EF00461B0B /* ProfileSeparationLineCell.swift in Sources */, 500A0B1C1B450C2100D49358 /* EditNicknameAndBadgeViewController.swift in Sources */, @@ -2293,6 +2350,7 @@ 505EC81C1ADFBE1B001D27E0 /* SkillSelectionCell.swift in Sources */, 5023DE591AB6DBC100B3EE96 /* UIImage+Yep.swift in Sources */, 508F8FD41B01E3D100461B0B /* SocialWorkDribbbleViewController.swift in Sources */, + 502B605F1BD112E500D9C17E /* QuickPickPhotosCell.swift in Sources */, 50AFBF5F1BB4F332007D19DA /* FeedsViewController.swift in Sources */, 50EB8CBF1AE89ED8001AC1EE /* ChatLeftVideoCell.swift in Sources */, 502AE5331AB86020005BD199 /* RegisterVerifyMobileViewController.swift in Sources */, @@ -2360,6 +2418,7 @@ 50E1D7291B870D7500D90A82 /* NSParagraphStyle+Yep.swift in Sources */, 5023DE561AB6CA1000B3EE96 /* AvatarCache.swift in Sources */, 5023DE371AB6979600B3EE96 /* ConversationCell.swift in Sources */, + 502B60641BD1208E00D9C17E /* CameraCell.swift in Sources */, 502AE5351AB8649F005BD199 /* LoginByMobileViewController.swift in Sources */, 506BB7F91AE5069500C1A2A0 /* RegisterPickSkillsSelectSkillsTransitionManager.swift in Sources */, 50B8E6181B41340A00C17EA1 /* MessageLoadingProgressView.swift in Sources */, @@ -2395,6 +2454,7 @@ 6A5632931BC911CB00397B53 /* DiscoverNormalUserCell.swift in Sources */, 5053AD5A1AF83B4200B3CBFA /* ChatLeftLocationCell.swift in Sources */, 50A640E91B6F45FA0050E4A3 /* DoNotDisturbPeriodCell.swift in Sources */, + 502B605A1BD1102B00D9C17E /* TitleCell.swift in Sources */, 840CD23C1B7B59D5000D2680 /* ShowStepViewController.swift in Sources */, 508F8FD01B01D04400461B0B /* GithubRepoCell.swift in Sources */, 50F2B97B1B2EEAF800F840CC /* NavigationTitleLabel.swift in Sources */, diff --git a/Yep/Base.lproj/Main.storyboard b/Yep/Base.lproj/Main.storyboard index dce80bcd..87432dc4 100644 --- a/Yep/Base.lproj/Main.storyboard +++ b/Yep/Base.lproj/Main.storyboard @@ -1,5 +1,5 @@ - + @@ -117,91 +117,34 @@ - - - - - - - - - - - - - - - - - - - - - - - - - + + - + - - - - + - - - @@ -302,13 +245,13 @@ - + - + @@ -887,7 +830,7 @@ - + @@ -1031,7 +974,7 @@ - - - + @@ -1112,7 +1055,7 @@ @@ -1161,18 +1104,18 @@ - + diff --git a/Yep/Caches/AvatarCache.swift b/Yep/Caches/AvatarCache.swift index 3463bfda..8c7a0810 100644 --- a/Yep/Caches/AvatarCache.swift +++ b/Yep/Caches/AvatarCache.swift @@ -128,18 +128,22 @@ class AvatarCache { } } - private var avatarCompletionSet = Set() + private var avatarCompletions = [AvatarCompletion]() private func removeAvatarCompletionsByAvatarURLString(avatarURLString: String) { - let avatarCompletions = avatarCompletionSet.filter({ $0.avatarURLString == avatarURLString }) - avatarCompletions.forEach { - avatarCompletionSet.remove($0) - } + + let completionsToRemove = avatarCompletions.filter({ $0.avatarURLString == avatarURLString }) + + completionsToRemove.forEach({ + if let index = avatarCompletions.indexOf($0) { + avatarCompletions.removeAtIndex(index) + } + }) } private func completeWithImage(image: UIImage, avatarURLString: String) { - for avatarCompletion in avatarCompletionSet.filter({ $0.avatarURLString == avatarURLString }) { + for avatarCompletion in avatarCompletions.filter({ $0.avatarURLString == avatarURLString }) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) { let avatar = image.roundImageOfRadius(avatarCompletion.radius) @@ -203,9 +207,9 @@ class AvatarCache { removeAvatarCompletionsByAvatarURLString(avatarURLString) // NOTICE: 默认在主线程添加 - avatarCompletionSet.insert(avatarCompletion) + avatarCompletions.append(avatarCompletion) - if avatarCompletionSet.filter({ $0.avatarURLString == avatarURLString }).count > 1 { + if avatarCompletions.filter({ $0.avatarURLString == avatarURLString }).count > 1 { // 不用做事,等着具有同样 avatarURLString 的 avatarCompletion 帮忙完成 } else { @@ -360,9 +364,9 @@ class AvatarCache { removeAvatarCompletionsByAvatarURLString(avatarURLString) // NOTICE: 默认在主线程添加 - avatarCompletionSet.insert(avatarCompletion) + avatarCompletions.append(avatarCompletion) - if avatarCompletionSet.filter({ $0.avatarURLString == avatarURLString }).count > 1 { + if avatarCompletions.filter({ $0.avatarURLString == avatarURLString }).count > 1 { // 不用做事,等着具有同样 avatarURLString 的 avatarCompletion 帮忙完成 } else { diff --git a/Yep/Images.xcassets/icon_more_add_contact.imageset/Contents.json b/Yep/Images.xcassets/icon_more_add_contact.imageset/Contents.json deleted file mode 100644 index c9411d94..00000000 --- a/Yep/Images.xcassets/icon_more_add_contact.imageset/Contents.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "icon_more_add_contact.pdf" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Yep/Images.xcassets/icon_more_add_contact.imageset/icon_more_add_contact.pdf b/Yep/Images.xcassets/icon_more_add_contact.imageset/icon_more_add_contact.pdf deleted file mode 100644 index 3036ca1f..00000000 Binary files a/Yep/Images.xcassets/icon_more_add_contact.imageset/icon_more_add_contact.pdf and /dev/null differ diff --git a/Yep/Images.xcassets/icon_more_add_location.imageset/Contents.json b/Yep/Images.xcassets/icon_more_add_location.imageset/Contents.json deleted file mode 100644 index f74f7242..00000000 --- a/Yep/Images.xcassets/icon_more_add_location.imageset/Contents.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "icon_more_add_location.pdf" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Yep/Images.xcassets/icon_more_add_location.imageset/icon_more_add_location.pdf b/Yep/Images.xcassets/icon_more_add_location.imageset/icon_more_add_location.pdf deleted file mode 100644 index 13998c92..00000000 Binary files a/Yep/Images.xcassets/icon_more_add_location.imageset/icon_more_add_location.pdf and /dev/null differ diff --git a/Yep/Images.xcassets/icon_more_choose_photo.imageset/Contents.json b/Yep/Images.xcassets/icon_more_choose_photo.imageset/Contents.json deleted file mode 100644 index 5d0190f7..00000000 --- a/Yep/Images.xcassets/icon_more_choose_photo.imageset/Contents.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "icon_more_choose_photo.pdf" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Yep/Images.xcassets/icon_more_choose_photo.imageset/icon_more_choose_photo.pdf b/Yep/Images.xcassets/icon_more_choose_photo.imageset/icon_more_choose_photo.pdf deleted file mode 100644 index 99396182..00000000 Binary files a/Yep/Images.xcassets/icon_more_choose_photo.imageset/icon_more_choose_photo.pdf and /dev/null differ diff --git a/Yep/Images.xcassets/icon_more_take_photo.imageset/icon_more_take_photo.pdf b/Yep/Images.xcassets/icon_more_take_photo.imageset/icon_more_take_photo.pdf deleted file mode 100644 index 3a1b9c6c..00000000 Binary files a/Yep/Images.xcassets/icon_more_take_photo.imageset/icon_more_take_photo.pdf and /dev/null differ diff --git a/Yep/Images.xcassets/icon_more_take_photo.imageset/Contents.json b/Yep/Images.xcassets/icon_quick_camera.imageset/Contents.json similarity index 72% rename from Yep/Images.xcassets/icon_more_take_photo.imageset/Contents.json rename to Yep/Images.xcassets/icon_quick_camera.imageset/Contents.json index 8d0db4bf..86b39c35 100644 --- a/Yep/Images.xcassets/icon_more_take_photo.imageset/Contents.json +++ b/Yep/Images.xcassets/icon_quick_camera.imageset/Contents.json @@ -2,7 +2,7 @@ "images" : [ { "idiom" : "universal", - "filename" : "icon_more_take_photo.pdf" + "filename" : "icon_quick_camera.pdf" } ], "info" : { diff --git a/Yep/Images.xcassets/item_less.imageset/item_less.pdf b/Yep/Images.xcassets/icon_quick_camera.imageset/icon_quick_camera.pdf similarity index 77% rename from Yep/Images.xcassets/item_less.imageset/item_less.pdf rename to Yep/Images.xcassets/icon_quick_camera.imageset/icon_quick_camera.pdf index fce333f4..cd01c6ac 100644 Binary files a/Yep/Images.xcassets/item_less.imageset/item_less.pdf and b/Yep/Images.xcassets/icon_quick_camera.imageset/icon_quick_camera.pdf differ diff --git a/Yep/Images.xcassets/item_less.imageset/Contents.json b/Yep/Images.xcassets/item_less.imageset/Contents.json deleted file mode 100644 index 73b5368e..00000000 --- a/Yep/Images.xcassets/item_less.imageset/Contents.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "item_less.pdf" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - }, - "properties" : { - "template-rendering-intent" : "template" - } -} \ No newline at end of file diff --git a/Yep/Services/YepService.swift b/Yep/Services/YepService.swift index 157cacea..a41bbf99 100644 --- a/Yep/Services/YepService.swift +++ b/Yep/Services/YepService.swift @@ -1630,7 +1630,7 @@ enum TimeDirection { } } -func messagesFromRecipient(recipient: Recipient, withTimeDirection timeDirection: TimeDirection, failureHandler: ((Reason, String?) -> Void)?, completion: Bool -> Void) { +func messagesFromRecipient(recipient: Recipient, withTimeDirection timeDirection: TimeDirection, failureHandler: ((Reason, String?) -> Void)?, completion: (messageIDs: [String]) -> Void) { var requestParameters = [ "recipient_type": recipient.type.nameForServer, @@ -1646,32 +1646,25 @@ func messagesFromRecipient(recipient: Recipient, withTimeDirection timeDirection break } - let parse: JSONDictionary -> Bool? = { data in + let parse: JSONDictionary -> [String]? = { data in - guard let unreadMessagesData = data["messages"] as? [JSONDictionary] else { - return false + guard let + unreadMessagesData = data["messages"] as? [JSONDictionary], + realm = try? Realm() else { + return [] } - println("messagesFromRecipient: \(recipient), \(unreadMessagesData.count)") + //println("messagesFromRecipient: \(recipient), \(unreadMessagesData.count)") - dispatch_async(dispatch_get_main_queue()) { + var messageIDs = [String]() - guard let realm = try? Realm() else { - return + for messageInfo in unreadMessagesData { + syncMessageWithMessageInfo(messageInfo, messageAge: timeDirection.messageAge, inRealm: realm) { _messageIDs in + messageIDs += _messageIDs } - - var messageIDs = [String]() - - for messageInfo in unreadMessagesData { - syncMessageWithMessageInfo(messageInfo, messageAge: timeDirection.messageAge, inRealm: realm) { _messageIDs in - messageIDs += _messageIDs - } - } - - tryPostNewMessagesReceivedNotificationWithMessageIDs(messageIDs, withMessageAge: .New) } - return true + return messageIDs } let resource = authJsonResource(path: "/api/v1/\(recipient.type.nameForServer)/\(recipient.ID)/messages", method: .GET, requestParameters: requestParameters, parse: parse ) diff --git a/Yep/ViewControllers/Conversation/ConversationViewController.swift b/Yep/ViewControllers/Conversation/ConversationViewController.swift index 1cc7d222..40d604be 100644 --- a/Yep/ViewControllers/Conversation/ConversationViewController.swift +++ b/Yep/ViewControllers/Conversation/ConversationViewController.swift @@ -150,6 +150,57 @@ class ConversationViewController: BaseViewController { lazy var moreView: ConversationMoreView = ConversationMoreView() + lazy var moreMessageTypesView: MoreMessageTypesView = { + + let view = MoreMessageTypesView() + + view.alertCanNotAccessCameraRollAction = { [weak self] in + self?.alertCanNotAccessCameraRoll() + } + + view.sendImageAction = { [weak self] image in + self?.sendImage(image) + } + + view.takePhotoAction = { [weak self] in + + let openCamera: ProposerAction = { [weak self] in + if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.Camera) { + if let strongSelf = self { + strongSelf.imagePicker.sourceType = .Camera + strongSelf.presentViewController(strongSelf.imagePicker, animated: true, completion: nil) + } + } + } + + proposeToAccess(.Camera, agreed: openCamera, rejected: { + self?.alertCanNotOpenCamera() + }) + } + + view.choosePhotoAction = { [weak self] in + + let openCameraRoll: ProposerAction = { [weak self] in + if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.PhotoLibrary){ + if let strongSelf = self { + strongSelf.imagePicker.sourceType = .PhotoLibrary + strongSelf.presentViewController(strongSelf.imagePicker, animated: true, completion: nil) + } + } + } + + proposeToAccess(.Photos, agreed: openCameraRoll, rejected: { + self?.alertCanNotAccessCameraRoll() + }) + } + + view.pickLocationAction = { [weak self] in + self?.performSegueWithIdentifier("presentPickLocation", sender: nil) + } + + return view + }() + lazy var pullToRefreshView: PullToRefreshView = { let pullToRefreshView = PullToRefreshView() @@ -208,16 +259,18 @@ class ConversationViewController: BaseViewController { @IBOutlet weak var messageToolbar: MessageToolbar! @IBOutlet weak var messageToolbarBottomConstraint: NSLayoutConstraint! - @IBOutlet weak var moreMessageTypesView: UIView! - @IBOutlet weak var moreMessageTypesViewHeightConstraint: NSLayoutConstraint! - let moreMessageTypesViewDefaultHeight: CGFloat = 110 + //@IBOutlet weak var moreMessageTypesView: UIView! + //@IBOutlet weak var moreMessageTypesViewHeightConstraint: NSLayoutConstraint! + //let moreMessageTypesViewDefaultHeight: CGFloat = 110 - @IBOutlet weak var choosePhotoButton: MessageTypeButton! - @IBOutlet weak var takePhotoButton: MessageTypeButton! - @IBOutlet weak var addLocationButton: MessageTypeButton! + //@IBOutlet weak var choosePhotoButton: MessageTypeButton! + //@IBOutlet weak var takePhotoButton: MessageTypeButton! + //@IBOutlet weak var addLocationButton: MessageTypeButton! @IBOutlet weak var swipeUpView: UIView! @IBOutlet weak var swipeUpPromptLabel: UILabel! + + @IBOutlet weak var activityIndicator: UIActivityIndicatorView! var isTryingShowFriendRequestView = false @@ -357,7 +410,7 @@ class ConversationViewController: BaseViewController { conversationCollectionView.addGestureRecognizer(tap) messageToolbarBottomConstraint.constant = 0 - moreMessageTypesViewHeightConstraint.constant = moreMessageTypesViewDefaultHeight + //moreMessageTypesViewHeightConstraint.constant = moreMessageTypesViewDefaultHeight keyboardMan.animateWhenKeyboardAppear = { [weak self] appearPostIndex, keyboardHeight, keyboardHeightIncrement in @@ -369,7 +422,7 @@ class ConversationViewController: BaseViewController { // 注意第一次要减去已经有的高度偏移 if appearPostIndex == 0 { - strongSelf.conversationCollectionView.contentOffset.y += keyboardHeightIncrement - strongSelf.moreMessageTypesViewDefaultHeight + strongSelf.conversationCollectionView.contentOffset.y += keyboardHeightIncrement //- strongSelf.moreMessageTypesViewDefaultHeight } else { strongSelf.conversationCollectionView.contentOffset.y += keyboardHeightIncrement } @@ -395,20 +448,11 @@ class ConversationViewController: BaseViewController { if let strongSelf = self { - if strongSelf.messageToolbar.state == .MoreMessages { - strongSelf.conversationCollectionView.contentOffset.y -= keyboardHeight - strongSelf.moreMessageTypesViewDefaultHeight - strongSelf.conversationCollectionView.contentInset.bottom = strongSelf.messageToolbar.frame.height + strongSelf.moreMessageTypesViewDefaultHeight + strongSelf.conversationCollectionView.contentOffset.y -= keyboardHeight + strongSelf.conversationCollectionView.contentInset.bottom = strongSelf.messageToolbar.frame.height - strongSelf.messageToolbarBottomConstraint.constant = strongSelf.moreMessageTypesViewDefaultHeight - strongSelf.view.layoutIfNeeded() - - } else { - strongSelf.conversationCollectionView.contentOffset.y -= keyboardHeight - strongSelf.conversationCollectionView.contentInset.bottom = strongSelf.messageToolbar.frame.height - - strongSelf.messageToolbarBottomConstraint.constant = 0 - strongSelf.view.layoutIfNeeded() - } + strongSelf.messageToolbarBottomConstraint.constant = 0 + strongSelf.view.layoutIfNeeded() } } @@ -424,10 +468,19 @@ class ConversationViewController: BaseViewController { timeDirection = .Future(minMessageID: minMessageID) } else { timeDirection = .None + + self?.activityIndicator.startAnimating() } - messagesFromRecipient(recipient, withTimeDirection: timeDirection, failureHandler: nil, completion: { success in - println("messagesFromRecipient: \(success)") + messagesFromRecipient(recipient, withTimeDirection: timeDirection, failureHandler: nil, completion: { messageIDs in + println("messagesFromRecipient: \(messageIDs.count)") + + dispatch_async(dispatch_get_main_queue()) { [weak self] in + + tryPostNewMessagesReceivedNotificationWithMessageIDs(messageIDs, withMessageAge: timeDirection.messageAge) + + self?.activityIndicator.stopAnimating() + } }) } } @@ -480,61 +533,26 @@ class ConversationViewController: BaseViewController { messageToolbar.conversation = conversation + // MARK: MessageToolbar MoreMessageTypes + + messageToolbar.moreMessageTypesAction = { [weak self] in + + if let window = self?.view.window { + self?.moreMessageTypesView.showInView(window) + + delay(0.2) { + self?.imagePicker.hidesBarsOnTap = false + } + } + } + // MARK: MessageToolbar State Transitions messageToolbar.stateTransitionAction = { [weak self] (messageToolbar, previousState, currentState) in if let strongSelf = self { - - switch (previousState, currentState) { - - case (.MoreMessages, .Default): fallthrough - case (.MoreMessages, .VoiceRecord): - - dispatch_async(dispatch_get_main_queue()) { [weak self] in - - UIView.animateWithDuration(0.2, delay: 0.0, options: .CurveEaseInOut, animations: { _ in - - strongSelf.conversationCollectionView.contentOffset.y -= strongSelf.moreMessageTypesViewDefaultHeight - strongSelf.conversationCollectionView.contentInset.bottom = strongSelf.messageToolbar.frame.height - - strongSelf.messageToolbarBottomConstraint.constant = 0 - strongSelf.view.layoutIfNeeded() - - }, completion: { finished in - }) - } - - default: - - if currentState == .MoreMessages { - - if previousState != .BeginTextInput && previousState != .TextInputing { - - dispatch_async(dispatch_get_main_queue()) { [weak self] in - - UIView.animateWithDuration(0.2, delay: 0.0, options: .CurveEaseInOut, animations: { _ in - - strongSelf.conversationCollectionView.contentOffset.y += strongSelf.moreMessageTypesViewDefaultHeight - strongSelf.conversationCollectionView.contentInset.bottom = strongSelf.messageToolbar.frame.height + strongSelf.moreMessageTypesViewDefaultHeight - - strongSelf.messageToolbarBottomConstraint.constant = strongSelf.moreMessageTypesViewDefaultHeight - strongSelf.view.layoutIfNeeded() - - }, completion: { finished in - }) - } - } - - // touch to create (if need) for faster appear - delay(0.2) { - self?.imagePicker.hidesBarsOnTap = false - } - } - } - switch currentState { - case .BeginTextInput, .MoreMessages: + case .BeginTextInput: self?.tryFoldFeedView() default: break @@ -871,47 +889,6 @@ class ConversationViewController: BaseViewController { self?.swipeUpPromptLabel.text = text } - - // MARK: More Message Types - - choosePhotoButton.title = NSLocalizedString("Choose photo", comment: "") - choosePhotoButton.tapAction = { [weak self] in - - let openCameraRoll: ProposerAction = { [weak self] in - if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.PhotoLibrary){ - if let strongSelf = self { - strongSelf.imagePicker.sourceType = .PhotoLibrary - strongSelf.presentViewController(strongSelf.imagePicker, animated: true, completion: nil) - } - } - } - - proposeToAccess(.Photos, agreed: openCameraRoll, rejected: { - self?.alertCanNotAccessCameraRoll() - }) - } - - takePhotoButton.title = NSLocalizedString("Take photo", comment: "") - takePhotoButton.tapAction = { [weak self] in - - let openCamera: ProposerAction = { [weak self] in - if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.Camera) { - if let strongSelf = self { - strongSelf.imagePicker.sourceType = .Camera - strongSelf.presentViewController(strongSelf.imagePicker, animated: true, completion: nil) - } - } - } - - proposeToAccess(.Camera, agreed: openCamera, rejected: { - self?.alertCanNotOpenCamera() - }) - } - - addLocationButton.title = NSLocalizedString("Share location", comment: "") - addLocationButton.tapAction = { [weak self] in - self?.performSegueWithIdentifier("presentPickLocation", sender: nil) - } } } @@ -2931,7 +2908,7 @@ extension ConversationViewController: UICollectionViewDataSource, UICollectionVi switch messageToolbar.state { - case .BeginTextInput, .TextInputing, .MoreMessages: + case .BeginTextInput, .TextInputing: messageToolbar.state = .Default default: @@ -3015,11 +2992,15 @@ extension ConversationViewController: PullToRefreshViewDelegate { timeDirection = .None } - messagesFromRecipient(recipient, withTimeDirection: timeDirection, failureHandler: nil, completion: { success in - println("messagesFromRecipient: \(success)") + messagesFromRecipient(recipient, withTimeDirection: timeDirection, failureHandler: nil, completion: { messageIDs in + println("messagesFromRecipient: \(messageIDs.count)") dispatch_async(dispatch_get_main_queue()) { - pulllToRefreshView.endRefreshingAndDoFurtherAction() {} + pulllToRefreshView.endRefreshingAndDoFurtherAction() { + dispatch_async(dispatch_get_main_queue()) { + tryPostNewMessagesReceivedNotificationWithMessageIDs(messageIDs, withMessageAge: timeDirection.messageAge) + } + } } }) } @@ -3036,7 +3017,7 @@ extension ConversationViewController: PullToRefreshViewDelegate { var newMessagesCount = strongSelf.messagesBunchCount if (strongSelf.displayedMessagesRange.location - newMessagesCount) < 0 { - newMessagesCount = strongSelf.displayedMessagesRange.location - newMessagesCount + newMessagesCount = strongSelf.displayedMessagesRange.location } if newMessagesCount > 0 { diff --git a/Yep/ViewControllers/NewFeed/NewFeedViewController.swift b/Yep/ViewControllers/NewFeed/NewFeedViewController.swift index a0fc8b41..f4efc5ea 100644 --- a/Yep/ViewControllers/NewFeed/NewFeedViewController.swift +++ b/Yep/ViewControllers/NewFeed/NewFeedViewController.swift @@ -21,15 +21,6 @@ class NewFeedViewController: UIViewController { @IBOutlet weak var messageTextView: UITextView! @IBOutlet weak var mediaCollectionView: UICollectionView! - lazy var imagePicker: UIImagePickerController = { - let imagePicker = UIImagePickerController() - imagePicker.delegate = self - imagePicker.mediaTypes = [kUTTypeImage as String] - imagePicker.videoQuality = .TypeMedium - imagePicker.allowsEditing = false - return imagePicker - }() - var imageAssetSet: Set? var mediaImages = [UIImage]() { @@ -96,15 +87,6 @@ class NewFeedViewController: UIViewController { } } - override func viewDidAppear(animated: Bool) { - super.viewDidAppear(animated) - - // touch to create (if need) for faster appear - delay(0.2) { [weak self] in - self?.imagePicker.hidesBarsOnTap = false - } - } - // MARK: Navigation override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { @@ -265,21 +247,6 @@ extension NewFeedViewController: UICollectionViewDataSource, UICollectionViewDel switch indexPath.section { case 0: - /* - let openCameraRoll: ProposerAction = { [weak self] in - if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.PhotoLibrary) { - if let strongSelf = self { - strongSelf.imagePicker.sourceType = .SavedPhotosAlbum - strongSelf.presentViewController(strongSelf.imagePicker, animated: true, completion: nil) - } - } - } - - proposeToAccess(.Photos, agreed: openCameraRoll, rejected: { [weak self] in - self?.alertCanNotAccessCameraRoll() - }) - */ - proposeToAccess(.Photos, agreed: { [weak self] in self?.performSegueWithIdentifier("showPickPhotos", sender: nil) @@ -297,42 +264,3 @@ extension NewFeedViewController: UICollectionViewDataSource, UICollectionViewDel } } -extension NewFeedViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate { - - func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) { - - if let mediaType = info[UIImagePickerControllerMediaType] as? String { - - if mediaType == (kUTTypeImage as String) { - - if let image = info[UIImagePickerControllerOriginalImage] as? UIImage { - - let imageWidth = image.size.width - let imageHeight = image.size.height - - let fixedImageWidth: CGFloat - let fixedImageHeight: CGFloat - - if imageWidth > imageHeight { - fixedImageWidth = min(imageWidth, YepConfig.Media.imageWidth) - fixedImageHeight = imageHeight * (fixedImageWidth / imageWidth) - } else { - fixedImageHeight = min(imageHeight, YepConfig.Media.imageHeight) - fixedImageWidth = imageWidth * (fixedImageHeight / imageHeight) - } - - let fixedSize = CGSize(width: fixedImageWidth, height: fixedImageHeight) - - // resize to smaller, not need fixRotation - - if let fixedImage = image.resizeToSize(fixedSize, withInterpolationQuality: CGInterpolationQuality.Medium) { - mediaImages.append(fixedImage) - } - } - } - } - - dismissViewControllerAnimated(true, completion: nil) - } -} - diff --git a/Yep/ViewControllers/PickPhotos/PickPhotosViewController.swift b/Yep/ViewControllers/PickPhotos/PickPhotosViewController.swift index 4b8ae0e6..ab6d81f1 100644 --- a/Yep/ViewControllers/PickPhotos/PickPhotosViewController.swift +++ b/Yep/ViewControllers/PickPhotos/PickPhotosViewController.swift @@ -45,7 +45,11 @@ class PickPhotosViewController: UICollectionViewController, PHPhotoLibraryChange layout.sectionInset = UIEdgeInsets(top: gap, left: gap, bottom: gap, right: gap) } - images = PHAsset.fetchAssetsWithMediaType(.Image, options: nil) + let options = PHFetchOptions() + options.sortDescriptors = [ + NSSortDescriptor(key: "creationDate", ascending: false) + ] + images = PHAsset.fetchAssetsWithMediaType(.Image, options: options) imageCacheController = ImageCacheController(imageManager: imageManager, images: images, preheatSize: 1) PHPhotoLibrary.sharedPhotoLibrary().registerChangeObserver(self) diff --git a/Yep/Views/Cells/Camera/CameraCell.swift b/Yep/Views/Cells/Camera/CameraCell.swift new file mode 100644 index 00000000..c268015f --- /dev/null +++ b/Yep/Views/Cells/Camera/CameraCell.swift @@ -0,0 +1,19 @@ +// +// CameraCell.swift +// Yep +// +// Created by nixzhu on 15/10/16. +// Copyright © 2015年 Catch Inc. All rights reserved. +// + +import UIKit + +class CameraCell: UICollectionViewCell { + + override func awakeFromNib() { + super.awakeFromNib() + + contentView.backgroundColor = UIColor.yepTintColor() + } +} + diff --git a/Yep/Views/Cells/Camera/CameraCell.xib b/Yep/Views/Cells/Camera/CameraCell.xib new file mode 100644 index 00000000..d249c13c --- /dev/null +++ b/Yep/Views/Cells/Camera/CameraCell.xib @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Yep/Views/Cells/QuickPickPhotos/QuickPickPhotosCell.swift b/Yep/Views/Cells/QuickPickPhotos/QuickPickPhotosCell.swift new file mode 100644 index 00000000..ca7c7aaa --- /dev/null +++ b/Yep/Views/Cells/QuickPickPhotos/QuickPickPhotosCell.swift @@ -0,0 +1,151 @@ +// +// QuickPickPhotosCell.swift +// Yep +// +// Created by nixzhu on 15/10/16. +// Copyright © 2015年 Catch Inc. All rights reserved. +// + +import UIKit +import Photos +import Proposer + +class QuickPickPhotosCell: UITableViewCell { + + @IBOutlet weak var photosCollectionView: UICollectionView! + + var alertCanNotAccessCameraRollAction: (() -> Void)? + var takePhotoAction: (() -> Void)? + var pickedPhotosAction: (Set -> Void)? + + var images: PHFetchResult? + let imageManager = PHCachingImageManager() + var imageCacheController: ImageCacheController! + + var pickedImageSet = Set() { + didSet { + pickedPhotosAction?(pickedImageSet) + } + } + var completion: ((images: [UIImage], imageAssetSet: Set) -> Void)? + + let cameraCellID = "CameraCell" + let photoCellID = "PhotoCell" + + override func awakeFromNib() { + super.awakeFromNib() + + photosCollectionView.backgroundColor = UIColor.clearColor() + photosCollectionView.registerNib(UINib(nibName: cameraCellID, bundle: nil), forCellWithReuseIdentifier: cameraCellID) + photosCollectionView.registerNib(UINib(nibName: photoCellID, bundle: nil), forCellWithReuseIdentifier: photoCellID) + photosCollectionView.dataSource = self + photosCollectionView.delegate = self + + if let layout = photosCollectionView.collectionViewLayout as? UICollectionViewFlowLayout { + layout.itemSize = CGSize(width: 70, height: 70) + layout.minimumInteritemSpacing = 10 + layout.sectionInset = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 0) + + photosCollectionView.contentInset = UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 0) + } + + proposeToAccess(.Photos, agreed: { + dispatch_async(dispatch_get_main_queue()) { [weak self] in + if let strongSelf = self { + let options = PHFetchOptions() + options.sortDescriptors = [ + NSSortDescriptor(key: "creationDate", ascending: false) + ] + let images = PHAsset.fetchAssetsWithMediaType(.Image, options: options) + strongSelf.images = images + strongSelf.imageCacheController = ImageCacheController(imageManager: strongSelf.imageManager, images: images, preheatSize: 1) + + strongSelf.photosCollectionView.reloadData() + } + } + + }, rejected: { [weak self] in + self?.alertCanNotAccessCameraRollAction?() + }) + } + + override func setSelected(selected: Bool, animated: Bool) { + super.setSelected(selected, animated: animated) + + // Configure the view for the selected state + } +} + +extension QuickPickPhotosCell: UICollectionViewDataSource, UICollectionViewDelegate { + + func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int { + return 2 + } + + func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + switch section { + case 0: + return 1 + case 1: + return images?.count ?? 0 + default: + return 0 + } + } + + func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { + + switch indexPath.section { + + case 0: + let cell = collectionView.dequeueReusableCellWithReuseIdentifier(cameraCellID, forIndexPath: indexPath) as! CameraCell + return cell + + case 1: + let cell = collectionView.dequeueReusableCellWithReuseIdentifier(photoCellID, forIndexPath: indexPath) as! PhotoCell + return cell + + default: + return UICollectionViewCell() + } + } + + func collectionView(collectionView: UICollectionView, willDisplayCell cell: UICollectionViewCell, forItemAtIndexPath indexPath: NSIndexPath) { + + if let cell = cell as? PhotoCell { + cell.imageManager = imageManager + + if let imageAsset = images?[indexPath.item] as? PHAsset { + cell.imageAsset = imageAsset + cell.photoPickedImageView.hidden = !pickedImageSet.contains(imageAsset) + } + } + } + + func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) { + + switch indexPath.section { + + case 0: + takePhotoAction?() + + case 1: + if let imageAsset = images?[indexPath.item] as? PHAsset { + + if pickedImageSet.contains(imageAsset) { + pickedImageSet.remove(imageAsset) + + } else { + pickedImageSet.insert(imageAsset) + } + + let cell = collectionView.cellForItemAtIndexPath(indexPath) as! PhotoCell + cell.photoPickedImageView.hidden = !pickedImageSet.contains(imageAsset) + } + + default: + break + } + } +} + diff --git a/Yep/Views/Cells/QuickPickPhotos/QuickPickPhotosCell.xib b/Yep/Views/Cells/QuickPickPhotos/QuickPickPhotosCell.xib new file mode 100644 index 00000000..cecde387 --- /dev/null +++ b/Yep/Views/Cells/QuickPickPhotos/QuickPickPhotosCell.xib @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Yep/Views/Cells/Title/TitleCell.swift b/Yep/Views/Cells/Title/TitleCell.swift new file mode 100644 index 00000000..cd2a545a --- /dev/null +++ b/Yep/Views/Cells/Title/TitleCell.swift @@ -0,0 +1,32 @@ +// +// TitleCell.swift +// Yep +// +// Created by nixzhu on 15/10/16. +// Copyright © 2015年 Catch Inc. All rights reserved. +// + +import UIKit + +class TitleCell: UITableViewCell { + + @IBOutlet weak var singleTitleLabel: UILabel! + + var boldEnabled = false { + didSet { + singleTitleLabel.font = boldEnabled ? UIFont.boldSystemFontOfSize(17) : UIFont.systemFontOfSize(17) + } + } + + override func awakeFromNib() { + super.awakeFromNib() + // Initialization code + } + + override func setSelected(selected: Bool, animated: Bool) { + super.setSelected(selected, animated: animated) + + // Configure the view for the selected state + } +} + diff --git a/Yep/Views/Cells/Title/TitleCell.xib b/Yep/Views/Cells/Title/TitleCell.xib new file mode 100644 index 00000000..7048bd61 --- /dev/null +++ b/Yep/Views/Cells/Title/TitleCell.xib @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Yep/Views/MessageToolbar/MessageToolbar.swift b/Yep/Views/MessageToolbar/MessageToolbar.swift index 725bd450..4d8bf600 100644 --- a/Yep/Views/MessageToolbar/MessageToolbar.swift +++ b/Yep/Views/MessageToolbar/MessageToolbar.swift @@ -9,11 +9,11 @@ import UIKit enum MessageToolbarState: Int, CustomStringConvertible { + case Default case BeginTextInput case TextInputing case VoiceRecord - case MoreMessages var description: String { switch self { @@ -25,8 +25,6 @@ enum MessageToolbarState: Int, CustomStringConvertible { return "TextInputing" case .VoiceRecord: return "VoiceRecord" - case .MoreMessages: - return "MoreMessages" } } } @@ -113,10 +111,6 @@ class MessageToolbar: UIToolbar { moreButton.tintColor = UIColor.messageToolBarColor() showVoiceButtonAnimation() - - case .MoreMessages: - moreButton.setImage(UIImage(named: "item_less"), forState: .Normal) - moreButton.tintColor = UIColor.messageToolBarHighlightColor() } updateHeightOfMessageTextView() @@ -137,7 +131,7 @@ class MessageToolbar: UIToolbar { var textSendAction: ((messageToolBar: MessageToolbar) -> Void)? - var toggleMoreMessagesAction: ((messageToolBar: MessageToolbar) -> Void)? + var moreMessageTypesAction: (() -> Void)? var voiceRecordBeginAction: ((messageToolBar: MessageToolbar) -> Void)? @@ -209,7 +203,7 @@ class MessageToolbar: UIToolbar { button.setImage(UIImage(named: "item_more"), forState: .Normal) button.tintColor = UIColor.messageToolBarColor() button.tintAdjustmentMode = .Normal - button.addTarget(self, action: "toggleMoreMessages", forControlEvents: UIControlEvents.TouchUpInside) + button.addTarget(self, action: "moreMessageTypes", forControlEvents: UIControlEvents.TouchUpInside) return button }() @@ -394,14 +388,8 @@ class MessageToolbar: UIToolbar { } } - func toggleMoreMessages() { - - if state != .MoreMessages { - state = .MoreMessages - - } else { - state = .Default - } + func moreMessageTypes() { + moreMessageTypesAction?() } func tryVoiceRecordBegin() { diff --git a/Yep/Views/MoreMessageTypes/MoreMessageTypesView.swift b/Yep/Views/MoreMessageTypes/MoreMessageTypesView.swift new file mode 100644 index 00000000..8773db7d --- /dev/null +++ b/Yep/Views/MoreMessageTypes/MoreMessageTypesView.swift @@ -0,0 +1,345 @@ +// +// MoreMessageTypesView.swift +// Yep +// +// Created by nixzhu on 15/10/16. +// Copyright © 2015年 Catch Inc. All rights reserved. +// + +import UIKit +import Photos + +class MoreMessageTypesView: UIView { + + let totalHeight: CGFloat = 100 + 60 * 3 + + lazy var containerView: UIView = { + let view = UIView() + view.backgroundColor = UIColor.clearColor() + return view + }() + + let titleCellID = "TitleCell" + let quickPickPhotosCellID = "QuickPickPhotosCell" + + lazy var tableView: UITableView = { + let view = UITableView() + view.dataSource = self + view.delegate = self + view.rowHeight = 60 + view.scrollEnabled = false + + view.registerNib(UINib(nibName: self.quickPickPhotosCellID, bundle: nil), forCellReuseIdentifier: self.quickPickPhotosCellID) + view.registerNib(UINib(nibName: self.titleCellID, bundle: nil), forCellReuseIdentifier: self.titleCellID) + return view + }() + + var alertCanNotAccessCameraRollAction: (() -> Void)? + var takePhotoAction: (() -> Void)? + var choosePhotoAction: (() -> Void)? + var pickLocationAction: (() -> Void)? + var sendImageAction: (UIImage -> Void)? + + var quickPickedImageSet = Set() { + didSet { + tableView.reloadRowsAtIndexPaths([NSIndexPath(forRow: Row.PickPhotos.rawValue, inSection: 0)], withRowAnimation: .None) + } + } + + var tableViewBottomConstraint: NSLayoutConstraint? + + func showInView(view: UIView) { + + frame = view.bounds + + view.addSubview(self) + + layoutIfNeeded() + + containerView.alpha = 1 + + UIView.animateWithDuration(0.2, delay: 0.0, options: .CurveEaseIn, animations: { _ in + self.containerView.backgroundColor = UIColor.blackColor().colorWithAlphaComponent(0.3) + + }, completion: { _ in + }) + + UIView.animateWithDuration(0.2, delay: 0.1, options: .CurveEaseOut, animations: { _ in + self.tableViewBottomConstraint?.constant = 0 + + self.layoutIfNeeded() + + }, completion: { _ in + }) + } + + func hide() { + + UIView.animateWithDuration(0.2, delay: 0.0, options: .CurveEaseIn, animations: { _ in + self.tableViewBottomConstraint?.constant = self.totalHeight + + self.layoutIfNeeded() + + }, completion: { _ in + }) + + UIView.animateWithDuration(0.2, delay: 0.1, options: .CurveEaseOut, animations: { _ in + self.containerView.backgroundColor = UIColor.clearColor() + + }, completion: { _ in + self.removeFromSuperview() + }) + } + + func hideAndDo(afterHideAction: (() -> Void)?) { + + UIView.animateWithDuration(0.2, delay: 0.0, options: .CurveLinear, animations: { _ in + self.containerView.alpha = 0 + + self.tableViewBottomConstraint?.constant = self.totalHeight + + self.layoutIfNeeded() + + }, completion: { finished in + self.removeFromSuperview() + }) + + delay(0.1) { + afterHideAction?() + } + } + + var isFirstTimeBeenAddAsSubview = true + + override func didMoveToSuperview() { + super.didMoveToSuperview() + + if isFirstTimeBeenAddAsSubview { + isFirstTimeBeenAddAsSubview = false + + makeUI() + + let tap = UITapGestureRecognizer(target: self, action: "hide") + containerView.addGestureRecognizer(tap) + + tap.cancelsTouchesInView = true + tap.delegate = self + } + } + + func makeUI() { + + addSubview(containerView) + containerView.translatesAutoresizingMaskIntoConstraints = false + + containerView.addSubview(tableView) + tableView.translatesAutoresizingMaskIntoConstraints = false + + let viewsDictionary = [ + "containerView": containerView, + "tableView": tableView, + ] + + // layout for containerView + + let containerViewConstraintsH = NSLayoutConstraint.constraintsWithVisualFormat("H:|[containerView]|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: viewsDictionary) + let containerViewConstraintsV = NSLayoutConstraint.constraintsWithVisualFormat("V:|[containerView]|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: viewsDictionary) + + NSLayoutConstraint.activateConstraints(containerViewConstraintsH) + NSLayoutConstraint.activateConstraints(containerViewConstraintsV) + + // layout for tableView + + let tableViewConstraintsH = NSLayoutConstraint.constraintsWithVisualFormat("H:|[tableView]|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: viewsDictionary) + + let tableViewBottomConstraint = NSLayoutConstraint(item: tableView, attribute: .Bottom, relatedBy: .Equal, toItem: containerView, attribute: .Bottom, multiplier: 1.0, constant: self.totalHeight) + + self.tableViewBottomConstraint = tableViewBottomConstraint + + let tableViewHeightConstraint = NSLayoutConstraint(item: tableView, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: self.totalHeight) + + NSLayoutConstraint.activateConstraints(tableViewConstraintsH) + NSLayoutConstraint.activateConstraints([tableViewBottomConstraint, tableViewHeightConstraint]) + } +} + +// MARK: - UIGestureRecognizerDelegate + +extension MoreMessageTypesView: UIGestureRecognizerDelegate { + + func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool { + + if touch.view != containerView { + return false + } + + return true + } +} + +// MARK: - UITableViewDataSource, UITableViewDelegate + +extension MoreMessageTypesView: UITableViewDataSource, UITableViewDelegate { + + enum Row: Int { + + case PhotoGallery = 0 + case PickPhotos + case Location + case Cancel + + var normalTitle: String { + switch self { + case .PhotoGallery: + return "" + case .PickPhotos: + return NSLocalizedString("Pick Photos", comment: "") + case .Location: + return NSLocalizedString("Location", comment: "") + case .Cancel: + return NSLocalizedString("Cancel", comment: "") + } + } + } + + func numberOfSectionsInTableView(tableView: UITableView) -> Int { + return 1 + } + + func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return 4 + } + + func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { + + if let row = Row(rawValue: indexPath.row) { + if case .PhotoGallery = row { + let cell = tableView.dequeueReusableCellWithIdentifier(quickPickPhotosCellID) as! QuickPickPhotosCell + + cell.alertCanNotAccessCameraRollAction = { [weak self] in + self?.alertCanNotAccessCameraRollAction?() + } + + cell.takePhotoAction = { [weak self] in + self?.hideAndDo { + self?.takePhotoAction?() + } + } + + cell.pickedPhotosAction = { [weak self] pickedImageSet in + self?.quickPickedImageSet = pickedImageSet + } + + return cell + + } else { + let cell = tableView.dequeueReusableCellWithIdentifier(titleCellID) as! TitleCell + cell.singleTitleLabel.text = row.normalTitle + cell.boldEnabled = false + cell.singleTitleLabel.textColor = UIColor.yepTintColor() + + if case .PickPhotos = row { + if !quickPickedImageSet.isEmpty { + cell.singleTitleLabel.text = String(format: NSLocalizedString("Send Photos (%d)", comment: ""), quickPickedImageSet.count) + cell.boldEnabled = true + } + } + + return cell + } + } + + return UITableViewCell() + } + + func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { + if let row = Row(rawValue: indexPath.row) { + if case .PhotoGallery = row { + return 100 + + } else { + return 60 + } + } + + return 0 + } + + func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { + + tableView.deselectRowAtIndexPath(indexPath, animated: true) + + if let row = Row(rawValue: indexPath.row) { + switch row { + + case .PickPhotos: + if !quickPickedImageSet.isEmpty { + + var images = [UIImage]() + + let options = PHImageRequestOptions() + options.synchronous = true + + let imageManager = PHCachingImageManager() + for imageAsset in quickPickedImageSet { + + let maxSize: CGFloat = 512 + + let pixelWidth = CGFloat(imageAsset.pixelWidth) + let pixelHeight = CGFloat(imageAsset.pixelHeight) + + //println("pixelWidth: \(pixelWidth)") + //println("pixelHeight: \(pixelHeight)") + + let targetSize: CGSize + + if pixelWidth > pixelHeight { + let width = maxSize + let height = floor(maxSize * (pixelHeight / pixelWidth)) + targetSize = CGSize(width: width, height: height) + + } else { + let height = maxSize + let width = floor(maxSize * (pixelWidth / pixelHeight)) + targetSize = CGSize(width: width, height: height) + } + + //println("targetSize: \(targetSize)") + + imageManager.requestImageForAsset(imageAsset, targetSize: targetSize, contentMode: .AspectFill, options: options) { image, info in + if let image = image { + println("image.size: \(image.size)") + images.append(image) + } + } + } + + images.forEach { + sendImageAction?($0) + } + + quickPickedImageSet.removeAll() + + hide() + + } else { + hideAndDo { + choosePhotoAction?() + } + } + + case .Location: + hideAndDo { + pickLocationAction?() + } + + case .Cancel: + hide() + + default: + break + } + } + } +} + diff --git a/Yep/Views/PullToRefresh/PullToRefreshView.swift b/Yep/Views/PullToRefresh/PullToRefreshView.swift index 7b10a8e3..a3b930ce 100644 --- a/Yep/Views/PullToRefresh/PullToRefreshView.swift +++ b/Yep/Views/PullToRefresh/PullToRefreshView.swift @@ -71,16 +71,18 @@ class PullToRefreshView: UIView { // MARK: Actions func beginRefreshing() { + isRefreshing = true - UIView.animateWithDuration(0.3, delay: 0, options: .CurveEaseInOut, animations: { () -> Void in + UIView.animateWithDuration(0.25, delay: 0, options: .CurveEaseInOut, animations: { () -> Void in delegate?.scrollView().contentInset.top += sceneHeight }, completion: { (_) -> Void in }) } func endRefreshingAndDoFurtherAction(furtherAction: () -> Void) { - UIView.animateWithDuration(0.3, delay: 0, options: .CurveEaseInOut, animations: { () -> Void in + + UIView.animateWithDuration(0.25, delay: 0, options: .CurveEaseInOut, animations: { () -> Void in delegate?.scrollView().contentInset.top -= sceneHeight }, completion: { (_) -> Void in