diff --git a/Podfile.lock b/Podfile.lock index 5e6829b1..9078588b 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,6 +1,6 @@ PODS: - 1PasswordExtension (1.7) - - Alamofire (3.1.3) + - Alamofire (3.1.4) - APAddressBook/Core (0.2.1) - APAddressBook/Swift (0.2.1): - APAddressBook/Core @@ -8,7 +8,7 @@ PODS: - Base64 (1.0.1) - Crashlytics (3.4.1): - Fabric (~> 1.6.0) - - DeviceGuru (1.3.2) + - DeviceGuru (1.3.3) - Fabric (1.6.1) - FXBlurView (1.6.4) - JPush-iOS-SDK (1.8.8) @@ -21,11 +21,11 @@ PODS: - Navi (0.3.9) - pop (1.0.8) - Proposer (0.7.1) - - Realm (0.96.3): - - Realm/Headers (= 0.96.3) - - Realm/Headers (0.96.3) - - RealmSwift (0.96.3): - - Realm (= 0.96.3) + - Realm (0.97.0): + - Realm/Headers (= 0.97.0) + - Realm/Headers (0.97.0) + - RealmSwift (0.97.0): + - Realm (= 0.97.0) - Ruler (0.6) - SocketRocket (0.4.2) - TPKeyboardAvoiding (1.2.11) @@ -57,12 +57,12 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: 1PasswordExtension: a22573ad528dd63bd79c1f3b3813b17c2f86b91b - Alamofire: 9f93b56389e48def9220dd57d1f44b1927229a5a + Alamofire: fbc829692f351fa1d8a31dd75fd7f7f56fea31fb APAddressBook: 90c0fb07579a47e10ef1a2344e23cfd35f388bec Appsee: 77f144701aa1a53d854d319a46667561849e8cf5 Base64: 4924bf3ca6fa559a5161ef717291bd450eb7bd1a Crashlytics: 41574a245bc6ae10c55d57f385ebd2004e5bee05 - DeviceGuru: bbc2660d73211dd5d868bc85a1c2ca9607d01153 + DeviceGuru: 49c6178d77693f8459ace21cecaf4d1d26297d08 Fabric: bf0ceb04e7c924090efc5f8c55e4fbcca189e0a0 FXBlurView: db786c2561cb49a09ae98407f52460096ab8a44f JPush-iOS-SDK: d4c097d8abbdd0837e24d44b41aacf64a6feddfd @@ -73,8 +73,8 @@ SPEC CHECKSUMS: Navi: 24f5c81aeb46b89d27168ec1d459581230ae2675 pop: bb773ae2c791ca2629de13b347e7a8b450fa6a57 Proposer: e9759ad71a6702fd94315a0bff1f8c116edf6f07 - Realm: 145d3cdf78a03aee71bde5381ad7e542bd541720 - RealmSwift: aab84968aad662f61f36473d1aea019f63481505 + Realm: b498a6fb9d288b171ff98d0e2d4384c295be8990 + RealmSwift: d014810dec75c9a3f1a33926d2dabfed855c582c Ruler: b36ec3bbc5fc6c3b7f629a6052cc1c17ada4b280 SocketRocket: ffe08119b00ef982f6c37052a4705a057c8494ad TPKeyboardAvoiding: e5ce146b313de063957bfa75a13f1e9b0ff18ab7 diff --git a/Yep.xcodeproj/project.pbxproj b/Yep.xcodeproj/project.pbxproj index 6a1de0e0..2c97b9aa 100644 --- a/Yep.xcodeproj/project.pbxproj +++ b/Yep.xcodeproj/project.pbxproj @@ -60,9 +60,6 @@ 501690181BFB18D20096C4F9 /* ChatLeftSocialWorkCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 501690161BFB18D20096C4F9 /* ChatLeftSocialWorkCell.swift */; }; 501690191BFB18D20096C4F9 /* ChatLeftSocialWorkCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 501690171BFB18D20096C4F9 /* ChatLeftSocialWorkCell.xib */; }; 5016901C1BFB22180096C4F9 /* SocialWorkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5016901B1BFB22180096C4F9 /* SocialWorkService.swift */; }; - 5016901F1BFDCECB0096C4F9 /* FeedSocialWorkCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5016901D1BFDCECB0096C4F9 /* FeedSocialWorkCell.swift */; }; - 501690201BFDCECB0096C4F9 /* FeedSocialWorkCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5016901E1BFDCECB0096C4F9 /* FeedSocialWorkCell.xib */; }; - 501690231BFEA6C30096C4F9 /* FeedBasicCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 501690221BFEA6C30096C4F9 /* FeedBasicCell.swift */; }; 501813261BA07CED0025873C /* Activities.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 501813251BA07CED0025873C /* Activities.xcassets */; }; 5018132C1BA3CFEF0025873C /* WeChatActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5018132B1BA3CFEF0025873C /* WeChatActivity.swift */; }; 5019F6311B39BFE00000D993 /* ConversationMoreView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5019F6301B39BFE00000D993 /* ConversationMoreView.swift */; }; @@ -184,6 +181,8 @@ 507F65F71B2EBC2700A35FDC /* MediaPreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 507F65F61B2EBC2700A35FDC /* MediaPreviewView.swift */; }; 5080EB1C1B93F43A006F3C2D /* PodsHelpYepViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5080EB1B1B93F43A006F3C2D /* PodsHelpYepViewController.swift */; }; 5080EB1F1B93F932006F3C2D /* NSBundle+Yep.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5080EB1E1B93F932006F3C2D /* NSBundle+Yep.swift */; }; + 50858EBB1C22487900EDE492 /* FeedBasicCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50858EBA1C22487900EDE492 /* FeedBasicCell.swift */; }; + 50858EBF1C224DA900EDE492 /* FeedBiggerImageCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50858EBE1C224DA900EDE492 /* FeedBiggerImageCell.swift */; }; 50894CF11AEA4726000981AF /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50894CF01AEA4726000981AF /* SettingsViewController.swift */; }; 50894CF51AEA4A01000981AF /* SettingsUserCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50894CF31AEA4A01000981AF /* SettingsUserCell.swift */; }; 50894CF61AEA4A01000981AF /* SettingsUserCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 50894CF41AEA4A01000981AF /* SettingsUserCell.xib */; }; @@ -302,6 +301,12 @@ 50E3488E1B905C0100A8E699 /* AboutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E3488D1B905C0100A8E699 /* AboutViewController.swift */; }; 50E348921B9063A700A8E699 /* AboutCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E348901B9063A700A8E699 /* AboutCell.swift */; }; 50E348931B9063A700A8E699 /* AboutCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 50E348911B9063A700A8E699 /* AboutCell.xib */; }; + 50E392BC1C22752C00C0CECB /* FeedNormalImagesCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E392BB1C22752C00C0CECB /* FeedNormalImagesCell.swift */; }; + 50E392BE1C228BF000C0CECB /* FeedCellLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E392BD1C228BF000C0CECB /* FeedCellLayout.swift */; }; + 50E392C01C22B30300C0CECB /* FeedGithubRepoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E392BF1C22B30300C0CECB /* FeedGithubRepoCell.swift */; }; + 50E392C21C22B9E400C0CECB /* FeedDribbbleShotCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E392C11C22B9E400C0CECB /* FeedDribbbleShotCell.swift */; }; + 50E392C41C22C0F600C0CECB /* FeedVoiceCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E392C31C22C0F600C0CECB /* FeedVoiceCell.swift */; }; + 50E392C61C22C82800C0CECB /* FeedLocationCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E392C51C22C82800C0CECB /* FeedLocationCell.swift */; }; 50E61CEB1AF1F69D00908A9D /* ConversationTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E61CEA1AF1F69D00908A9D /* ConversationTitleView.swift */; }; 50EB8C5E1AE78AB9001AC1EE /* YepAsset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50EB8C5D1AE78AB9001AC1EE /* YepAsset.swift */; }; 50EB8CBF1AE89ED8001AC1EE /* ChatLeftVideoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50EB8CBD1AE89ED8001AC1EE /* ChatLeftVideoCell.swift */; }; @@ -315,8 +320,7 @@ 50F5AF831BBA67060033C9BC /* NewFeedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50F5AF821BBA67060033C9BC /* NewFeedViewController.swift */; }; 50F5AF861BBA676D0033C9BC /* NewFeed.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 50F5AF851BBA676D0033C9BC /* NewFeed.storyboard */; }; 50F5AF881BBBB2AE0033C9BC /* HorizontalLineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50F5AF871BBBB2AE0033C9BC /* HorizontalLineView.swift */; }; - 50F5AF8C1BBBB64C0033C9BC /* FeedCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50F5AF8A1BBBB64C0033C9BC /* FeedCell.swift */; }; - 50F5AF8D1BBBB64C0033C9BC /* FeedCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 50F5AF8B1BBBB64C0033C9BC /* FeedCell.xib */; }; + 50F5AF8C1BBBB64C0033C9BC /* FeedAnyImagesCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50F5AF8A1BBBB64C0033C9BC /* FeedAnyImagesCell.swift */; }; 50F5AF951BBBCDA30033C9BC /* FeedMediaAddCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50F5AF931BBBCDA30033C9BC /* FeedMediaAddCell.swift */; }; 50F5AF961BBBCDA30033C9BC /* FeedMediaAddCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 50F5AF941BBBCDA30033C9BC /* FeedMediaAddCell.xib */; }; 50F5AF991BBBD02A0033C9BC /* FeedMediaCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50F5AF971BBBD02A0033C9BC /* FeedMediaCell.swift */; }; @@ -423,9 +427,6 @@ 501690161BFB18D20096C4F9 /* ChatLeftSocialWorkCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ChatLeftSocialWorkCell.swift; path = Views/Cells/ChatLeftSocialWork/ChatLeftSocialWorkCell.swift; sourceTree = ""; }; 501690171BFB18D20096C4F9 /* ChatLeftSocialWorkCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = ChatLeftSocialWorkCell.xib; path = Views/Cells/ChatLeftSocialWork/ChatLeftSocialWorkCell.xib; sourceTree = ""; }; 5016901B1BFB22180096C4F9 /* SocialWorkService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocialWorkService.swift; path = Services/SocialWorkService.swift; sourceTree = ""; }; - 5016901D1BFDCECB0096C4F9 /* FeedSocialWorkCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedSocialWorkCell.swift; path = Views/Cells/FeedSocialWork/FeedSocialWorkCell.swift; sourceTree = ""; }; - 5016901E1BFDCECB0096C4F9 /* FeedSocialWorkCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = FeedSocialWorkCell.xib; path = Views/Cells/FeedSocialWork/FeedSocialWorkCell.xib; sourceTree = ""; }; - 501690221BFEA6C30096C4F9 /* FeedBasicCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedBasicCell.swift; path = Views/Cells/FeedBasic/FeedBasicCell.swift; sourceTree = ""; }; 501813251BA07CED0025873C /* Activities.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Activities.xcassets; sourceTree = ""; }; 5018132B1BA3CFEF0025873C /* WeChatActivity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WeChatActivity.swift; path = Activities/WeChatActivity.swift; sourceTree = ""; }; 5019F6301B39BFE00000D993 /* ConversationMoreView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ConversationMoreView.swift; path = Views/ConversationMore/ConversationMoreView.swift; sourceTree = ""; }; @@ -548,6 +549,8 @@ 507F65F61B2EBC2700A35FDC /* MediaPreviewView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MediaPreviewView.swift; path = Views/Media/MediaPreviewView.swift; sourceTree = ""; }; 5080EB1B1B93F43A006F3C2D /* PodsHelpYepViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PodsHelpYepViewController.swift; path = ViewControllers/PodsHelpYep/PodsHelpYepViewController.swift; sourceTree = ""; }; 5080EB1E1B93F932006F3C2D /* NSBundle+Yep.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "NSBundle+Yep.swift"; path = "Extensions/NSBundle+Yep.swift"; sourceTree = ""; }; + 50858EBA1C22487900EDE492 /* FeedBasicCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedBasicCell.swift; path = Views/Cells/Feed/FeedBasicCell.swift; sourceTree = ""; }; + 50858EBE1C224DA900EDE492 /* FeedBiggerImageCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedBiggerImageCell.swift; path = Views/Cells/Feed/FeedBiggerImageCell.swift; sourceTree = ""; }; 50894CF01AEA4726000981AF /* SettingsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SettingsViewController.swift; path = ViewControllers/Settings/SettingsViewController.swift; sourceTree = ""; }; 50894CF31AEA4A01000981AF /* SettingsUserCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SettingsUserCell.swift; path = Views/Cells/SettingsUser/SettingsUserCell.swift; sourceTree = ""; }; 50894CF41AEA4A01000981AF /* SettingsUserCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = SettingsUserCell.xib; path = Views/Cells/SettingsUser/SettingsUserCell.xib; sourceTree = ""; }; @@ -666,6 +669,12 @@ 50E3488D1B905C0100A8E699 /* AboutViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AboutViewController.swift; path = ViewControllers/About/AboutViewController.swift; sourceTree = ""; }; 50E348901B9063A700A8E699 /* AboutCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AboutCell.swift; path = Views/Cells/About/AboutCell.swift; sourceTree = ""; }; 50E348911B9063A700A8E699 /* AboutCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = AboutCell.xib; path = Views/Cells/About/AboutCell.xib; sourceTree = ""; }; + 50E392BB1C22752C00C0CECB /* FeedNormalImagesCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedNormalImagesCell.swift; path = Views/Cells/Feed/FeedNormalImagesCell.swift; sourceTree = ""; }; + 50E392BD1C228BF000C0CECB /* FeedCellLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedCellLayout.swift; path = Views/Cells/Feed/FeedCellLayout.swift; sourceTree = ""; }; + 50E392BF1C22B30300C0CECB /* FeedGithubRepoCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedGithubRepoCell.swift; path = Views/Cells/Feed/FeedGithubRepoCell.swift; sourceTree = ""; }; + 50E392C11C22B9E400C0CECB /* FeedDribbbleShotCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedDribbbleShotCell.swift; path = Views/Cells/Feed/FeedDribbbleShotCell.swift; sourceTree = ""; }; + 50E392C31C22C0F600C0CECB /* FeedVoiceCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedVoiceCell.swift; path = Views/Cells/Feed/FeedVoiceCell.swift; sourceTree = ""; }; + 50E392C51C22C82800C0CECB /* FeedLocationCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedLocationCell.swift; path = Views/Cells/Feed/FeedLocationCell.swift; sourceTree = ""; }; 50E61CEA1AF1F69D00908A9D /* ConversationTitleView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ConversationTitleView.swift; path = Views/ConversationTitle/ConversationTitleView.swift; sourceTree = ""; }; 50EB8C5D1AE78AB9001AC1EE /* YepAsset.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = YepAsset.swift; path = Helpers/YepAsset.swift; sourceTree = ""; }; 50EB8CBD1AE89ED8001AC1EE /* ChatLeftVideoCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ChatLeftVideoCell.swift; path = Views/Cells/ChatLeftVideo/ChatLeftVideoCell.swift; sourceTree = ""; }; @@ -679,8 +688,7 @@ 50F5AF821BBA67060033C9BC /* NewFeedViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NewFeedViewController.swift; path = ViewControllers/NewFeed/NewFeedViewController.swift; sourceTree = ""; }; 50F5AF851BBA676D0033C9BC /* NewFeed.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = NewFeed.storyboard; path = ViewControllers/NewFeed/NewFeed.storyboard; sourceTree = ""; }; 50F5AF871BBBB2AE0033C9BC /* HorizontalLineView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = HorizontalLineView.swift; path = Views/HorizontalLine/HorizontalLineView.swift; sourceTree = ""; }; - 50F5AF8A1BBBB64C0033C9BC /* FeedCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedCell.swift; path = Views/Cells/Feed/FeedCell.swift; sourceTree = ""; }; - 50F5AF8B1BBBB64C0033C9BC /* FeedCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = FeedCell.xib; path = Views/Cells/Feed/FeedCell.xib; sourceTree = ""; }; + 50F5AF8A1BBBB64C0033C9BC /* FeedAnyImagesCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedAnyImagesCell.swift; path = Views/Cells/Feed/FeedAnyImagesCell.swift; sourceTree = ""; }; 50F5AF931BBBCDA30033C9BC /* FeedMediaAddCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedMediaAddCell.swift; path = Views/Cells/FeedMediaAdd/FeedMediaAddCell.swift; sourceTree = ""; }; 50F5AF941BBBCDA30033C9BC /* FeedMediaAddCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = FeedMediaAddCell.xib; path = Views/Cells/FeedMediaAdd/FeedMediaAddCell.xib; sourceTree = ""; }; 50F5AF971BBBD02A0033C9BC /* FeedMediaCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedMediaCell.swift; path = Views/Cells/FeedMedia/FeedMediaCell.swift; sourceTree = ""; }; @@ -991,23 +999,6 @@ name = ChatLeftSocialWork; sourceTree = ""; }; - 501690211BFDCED10096C4F9 /* FeedSocialWork */ = { - isa = PBXGroup; - children = ( - 5016901D1BFDCECB0096C4F9 /* FeedSocialWorkCell.swift */, - 5016901E1BFDCECB0096C4F9 /* FeedSocialWorkCell.xib */, - ); - name = FeedSocialWork; - sourceTree = ""; - }; - 501690241BFEA6CA0096C4F9 /* FeedBasic */ = { - isa = PBXGroup; - children = ( - 501690221BFEA6C30096C4F9 /* FeedBasicCell.swift */, - ); - name = FeedBasic; - sourceTree = ""; - }; 5019F62F1B39BFCB0000D993 /* ConversationMore */ = { isa = PBXGroup; children = ( @@ -1243,9 +1234,7 @@ 5064F65B1B0B14470089FAD4 /* AddFriendMore */, 50E348941B9063AD00A8E699 /* About */, 503807251BDA180A0068345E /* FeedSkillUsers */, - 501690241BFEA6CA0096C4F9 /* FeedBasic */, 50AFBF651BB4FC37007D19DA /* Feed */, - 501690211BFDCED10096C4F9 /* FeedSocialWork */, 50F5AF921BBBCC960033C9BC /* FeedMediaAdd */, 50F5AF9B1BBBD0310033C9BC /* FeedMedia */, 6A5632901BC90A4900397B53 /* DiscoverCardUser */, @@ -1979,8 +1968,15 @@ 50AFBF651BB4FC37007D19DA /* Feed */ = { isa = PBXGroup; children = ( - 50F5AF8A1BBBB64C0033C9BC /* FeedCell.swift */, - 50F5AF8B1BBBB64C0033C9BC /* FeedCell.xib */, + 50E392BD1C228BF000C0CECB /* FeedCellLayout.swift */, + 50858EBA1C22487900EDE492 /* FeedBasicCell.swift */, + 50858EBE1C224DA900EDE492 /* FeedBiggerImageCell.swift */, + 50E392BB1C22752C00C0CECB /* FeedNormalImagesCell.swift */, + 50F5AF8A1BBBB64C0033C9BC /* FeedAnyImagesCell.swift */, + 50E392BF1C22B30300C0CECB /* FeedGithubRepoCell.swift */, + 50E392C11C22B9E400C0CECB /* FeedDribbbleShotCell.swift */, + 50E392C31C22C0F600C0CECB /* FeedVoiceCell.swift */, + 50E392C51C22C82800C0CECB /* FeedLocationCell.swift */, ); name = Feed; sourceTree = ""; @@ -2435,7 +2431,6 @@ 50E348931B9063A700A8E699 /* AboutCell.xib in Resources */, 5039594F1B05E6E000797A3E /* ProfileSocialAccountGithubCell.xib in Resources */, 502AE5591AB932D5005BD199 /* ProfileSectionHeaderReusableView.xib in Resources */, - 50F5AF8D1BBBB64C0033C9BC /* FeedCell.xib in Resources */, 5056AD6F1B05CB92003F9359 /* ProfileSocialAccountImagesCell.xib in Resources */, 506BB7F61AE4E0A500C1A2A0 /* SkillAnnotationHeader.xib in Resources */, 50AFBF4E1BB3A594007D19DA /* bub3.caf in Resources */, @@ -2485,7 +2480,6 @@ 50CDBE431ACA9B4200459CE0 /* ChatRightImageCell.xib in Resources */, 50E1CE8B1BE0BC940025B933 /* MessageMedia.storyboard in Resources */, 50DEE0471BEA030900015741 /* PodsHelpYep.storyboard in Resources */, - 501690201BFDCECB0096C4F9 /* FeedSocialWorkCell.xib in Resources */, 508C2AA51AD661B3002B8097 /* UserStateCell.xib in Resources */, 508F8FDF1B049ACF00461B0B /* InstagramMediaCell.xib in Resources */, 848806B11B789A2D000C5424 /* EditSkillCell.xib in Resources */, @@ -2547,7 +2541,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Fabric/Fabric.framework/run\" 3030ba006e21bcf8eb4a2127b6a7931ea6667486 1b6549ac285402b232e4718eda293559d8ed8df039017af3fa9593518395eef0"; + shellScript = "\"${PODS_ROOT}/Fabric/run\" 3030ba006e21bcf8eb4a2127b6a7931ea6667486 1b6549ac285402b232e4718eda293559d8ed8df039017af3fa9593518395eef0"; }; 39615BAC5D2C17889BD12838 /* Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; @@ -2615,6 +2609,7 @@ 500EED981BF07A3000C6BABC /* ConversationViewController+Feed.swift in Sources */, 508F8FDE1B049ACF00461B0B /* InstagramMediaCell.swift in Sources */, 502AE5211AB71C7E005BD199 /* BorderButton.swift in Sources */, + 50858EBB1C22487900EDE492 /* FeedBasicCell.swift in Sources */, 84C959891B731FD20017D3ED /* ChatBaseCell.swift in Sources */, 502AE54E1AB93175005BD199 /* ProfileHeaderCell.swift in Sources */, 50A640E01B6F38CF0050E4A3 /* NotificationsViewController.swift in Sources */, @@ -2664,6 +2659,7 @@ 502C24831C1034000074A1BA /* UIImageView+Yep.swift in Sources */, 5080EB1C1B93F43A006F3C2D /* PodsHelpYepViewController.swift in Sources */, 0A1CAC731AFA526200826B45 /* SkillHomeSectionButton.swift in Sources */, + 50E392BC1C22752C00C0CECB /* FeedNormalImagesCell.swift in Sources */, 50B38FCC1B6B37F00064C132 /* FeedbackViewController.swift in Sources */, 507F65F71B2EBC2700A35FDC /* MediaPreviewView.swift in Sources */, 501690181BFB18D20096C4F9 /* ChatLeftSocialWorkCell.swift in Sources */, @@ -2703,6 +2699,7 @@ 50DEE0411BE7626100015741 /* YepHelpers.swift in Sources */, 501138311BCB62A20055A014 /* FeedConversationCell.swift in Sources */, 502AE5401AB88862005BD199 /* SkillRankCell.swift in Sources */, + 50E392C41C22C0F600C0CECB /* FeedVoiceCell.swift in Sources */, 50C090A01ADE391600CC6389 /* AddSkillsReusableView.swift in Sources */, 84385C4A1B85BE200071130D /* ShowStepMeetViewController.swift in Sources */, 5023DE2D1AB685ED00B3EE96 /* DiscoverViewController.swift in Sources */, @@ -2725,6 +2722,7 @@ 502280F81B1C3F2800E984EE /* FriendsInContactsViewController.swift in Sources */, 50E348921B9063A700A8E699 /* AboutCell.swift in Sources */, 508A08501C16B810000A3305 /* RecordButton.swift in Sources */, + 50858EBF1C224DA900EDE492 /* FeedBiggerImageCell.swift in Sources */, 508D17401ACCD2740092D666 /* YepButton.swift in Sources */, 5019F6311B39BFE00000D993 /* ConversationMoreView.swift in Sources */, 506BB7F51AE4E0A500C1A2A0 /* SkillAnnotationHeader.swift in Sources */, @@ -2752,21 +2750,23 @@ 50DEE0311BE339B000015741 /* LoadMoreTableViewCell.swift in Sources */, 50DEE0361BE3720F00015741 /* LoadMoreCollectionViewCell.swift in Sources */, 504F41251ACE2D1D00FBB19A /* SampleView.swift in Sources */, + 50E392C01C22B30300C0CECB /* FeedGithubRepoCell.swift in Sources */, 50A640EC1B6F4E420050E4A3 /* DoNotDisturbPeriodViewController.swift in Sources */, 502AE5291AB81076005BD199 /* YepService.swift in Sources */, - 5016901F1BFDCECB0096C4F9 /* FeedSocialWorkCell.swift in Sources */, 50C090A41ADE46D500CC6389 /* RegisterSelectSkillsViewController.swift in Sources */, 50CC4FA21C1AC0EE00B6A263 /* UITableView+Yep.swift in Sources */, + 50E392C61C22C82800C0CECB /* FeedLocationCell.swift in Sources */, 502AE51E1AB712D2005BD199 /* RegisterPickNameViewController.swift in Sources */, 50B8E6161B40F9E700C17EA1 /* YepDownloader.swift in Sources */, 50894D101AEE3C25000981AF /* YepHUD.swift in Sources */, 50EB8CCE1AEA1B38001AC1EE /* MessageMediaViewController.swift in Sources */, 50E114F91ABC137A00F13000 /* YepServiceSync.swift in Sources */, + 50E392BE1C228BF000C0CECB /* FeedCellLayout.swift in Sources */, 506E241C1AE6254F00A33C1A /* MessageTypeButton.swift in Sources */, 505082351C212E9200643CEB /* FeedLocationContainerView.swift in Sources */, 0A3E60671ACCAECE003DC853 /* YepLocationService.swift in Sources */, 6A8215051BCBE5170011F36C /* MiniCardSkillLayout.swift in Sources */, - 50F5AF8C1BBBB64C0033C9BC /* FeedCell.swift in Sources */, + 50F5AF8C1BBBB64C0033C9BC /* FeedAnyImagesCell.swift in Sources */, 50894CF11AEA4726000981AF /* SettingsViewController.swift in Sources */, 5023DE2A1AB685BA00B3EE96 /* ContactsViewController.swift in Sources */, 50CDBE491ACBAD3200459CE0 /* ChatLeftImageCell.swift in Sources */, @@ -2819,9 +2819,9 @@ 840CD23C1B7B59D5000D2680 /* ShowStepViewController.swift in Sources */, 508F8FD01B01D04400461B0B /* GithubRepoCell.swift in Sources */, 50F2B97B1B2EEAF800F840CC /* NavigationTitleLabel.swift in Sources */, - 501690231BFEA6C30096C4F9 /* FeedBasicCell.swift in Sources */, 50A820241B969B0F007CD30E /* NSTimeZone+Yep.swift in Sources */, 50FD753D1C1FB4AC0050A382 /* FPSLabel.swift in Sources */, + 50E392C21C22B9E400C0CECB /* FeedDribbbleShotCell.swift in Sources */, 504F415A1AD5028200FBB19A /* SkillCell.swift in Sources */, 84385C481B85AC890071130D /* ShowStepMatchViewController.swift in Sources */, 0A0027D61B19CEC000AD4BE0 /* ConversationOperationQueue.swift in Sources */, diff --git a/Yep/Caches/ImageCache.swift b/Yep/Caches/ImageCache.swift index 017146b1..11418896 100644 --- a/Yep/Caches/ImageCache.swift +++ b/Yep/Caches/ImageCache.swift @@ -94,7 +94,7 @@ class ImageCache { let finalImage = storeImage.decodedImage() - println("Image Decode size \(storeImage.size)") + //println("Image Decode size \(storeImage.size)") dispatch_async(dispatch_get_main_queue()) { completion(url: attachmentURL, image: finalImage, cacheType: .None) diff --git a/Yep/Configs/YepConfig.swift b/Yep/Configs/YepConfig.swift index 2af6a1f2..34caeaff 100644 --- a/Yep/Configs/YepConfig.swift +++ b/Yep/Configs/YepConfig.swift @@ -226,6 +226,18 @@ class YepConfig { static let voiceTimeLengthTextAttributes:[String: NSObject] = [ NSFontAttributeName: UIFont.feedVoiceTimeLengthFont(), ] + + static let bottomLabelsTextAttributes:[String: NSObject] = [ + NSFontAttributeName: UIFont.feedBottomLabelsFont(), + ] + } + + struct FeedBiggerImageCell { + static let imageSize: CGSize = CGSize(width: 160, height: 160) + } + + struct FeedNormalImagesCell { + static let imageSize: CGSize = CGSize(width: 80, height: 80) } struct FeedView { diff --git a/Yep/Extensions/UIFont+Yep.swift b/Yep/Extensions/UIFont+Yep.swift index fdaca68c..8d3999b5 100644 --- a/Yep/Extensions/UIFont+Yep.swift +++ b/Yep/Extensions/UIFont+Yep.swift @@ -49,6 +49,10 @@ extension UIFont { return UIFont.systemFontOfSize(12) } + class func feedBottomLabelsFont() -> UIFont { + return UIFont.systemFontOfSize(14) + } + class func feedVoiceTimeLengthFont() -> UIFont { return UIFont.systemFontOfSize(15) } diff --git a/Yep/Extensions/UIImage+Yep.swift b/Yep/Extensions/UIImage+Yep.swift index 8cc2a2e8..7710498c 100644 --- a/Yep/Extensions/UIImage+Yep.swift +++ b/Yep/Extensions/UIImage+Yep.swift @@ -83,7 +83,7 @@ extension UIImage { let newHeight = pixelSideLength let newWidth = (pixelSideLength / pixelHeight) * pixelWidth - newSize = CGSize(width: newWidth, height: newHeight) + newSize = CGSize(width: floor(newWidth), height: floor(newHeight)) } else { @@ -93,16 +93,15 @@ extension UIImage { let newWidth = pixelSideLength let newHeight = (pixelSideLength / pixelWidth) * pixelHeight - newSize = CGSize(width: newWidth, height: newHeight) + newSize = CGSize(width: floor(newWidth), height: floor(newHeight)) } - if scale == UIScreen.mainScreen().scale { - let newSize = CGSize(width: newSize.width / scale, height: newSize.height / scale) - //println("newSize: \(newSize)") + let newSize = CGSize(width: floor(newSize.width / scale), height: floor(newSize.height / scale)) + //println("A scaleToMinSideLength newSize: \(newSize)") UIGraphicsBeginImageContextWithOptions(newSize, false, scale) - let rect = CGRectMake(0, 0, floor(newSize.width), floor(newSize.height)) + let rect = CGRectMake(0, 0, newSize.width, newSize.height) self.drawInRect(rect) let newImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() @@ -114,9 +113,9 @@ extension UIImage { return self } else { - //println("newSize: \(newSize)") + //println("B scaleToMinSideLength newSize: \(newSize)") UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0) - let rect = CGRectMake(0, 0, floor(newSize.width), floor(newSize.height)) + let rect = CGRectMake(0, 0, newSize.width, newSize.height) self.drawInRect(rect) let newImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() diff --git a/Yep/Services/YepService.swift b/Yep/Services/YepService.swift index a4475989..7b384083 100644 --- a/Yep/Services/YepService.swift +++ b/Yep/Services/YepService.swift @@ -2743,6 +2743,18 @@ struct DiscoveredFeed: Hashable { let attachment: Attachment? + var imageAttachments: [DiscoveredAttachment]? { + if let attachment = attachment, case let .Images(attachments) = attachment { + return attachments + } + + return nil + } + + var imageAttachmentsCount: Int { + return imageAttachments?.count ?? 0 + } + let distance: Double? let skill: Skill? diff --git a/Yep/ViewControllers/Contacts/ContactsViewController.swift b/Yep/ViewControllers/Contacts/ContactsViewController.swift index 13e3e3bf..0f8884b9 100644 --- a/Yep/ViewControllers/Contacts/ContactsViewController.swift +++ b/Yep/ViewControllers/Contacts/ContactsViewController.swift @@ -121,7 +121,7 @@ class ContactsViewController: BaseViewController { } #if DEBUG - view.addSubview(contactsFPSLabel) +// view.addSubview(contactsFPSLabel) #endif } diff --git a/Yep/ViewControllers/Conversation/ConversationViewController.swift b/Yep/ViewControllers/Conversation/ConversationViewController.swift index d5ff8e4b..937d29e1 100644 --- a/Yep/ViewControllers/Conversation/ConversationViewController.swift +++ b/Yep/ViewControllers/Conversation/ConversationViewController.swift @@ -859,7 +859,7 @@ class ConversationViewController: BaseViewController { } #if DEBUG - view.addSubview(conversationFPSLabel) +// view.addSubview(conversationFPSLabel) #endif } diff --git a/Yep/ViewControllers/Conversations/ConversationsViewController.swift b/Yep/ViewControllers/Conversations/ConversationsViewController.swift index cde3d9e7..12476a27 100644 --- a/Yep/ViewControllers/Conversations/ConversationsViewController.swift +++ b/Yep/ViewControllers/Conversations/ConversationsViewController.swift @@ -147,7 +147,7 @@ class ConversationsViewController: UIViewController { } #if DEBUG - view.addSubview(conversationsFPSLabel) +// view.addSubview(conversationsFPSLabel) #endif } diff --git a/Yep/ViewControllers/Discover/DiscoverViewController.swift b/Yep/ViewControllers/Discover/DiscoverViewController.swift index bcbf443d..5750c2a7 100644 --- a/Yep/ViewControllers/Discover/DiscoverViewController.swift +++ b/Yep/ViewControllers/Discover/DiscoverViewController.swift @@ -114,7 +114,7 @@ class DiscoverViewController: BaseViewController { } #if DEBUG - view.addSubview(discoverFPSLabel) +// view.addSubview(discoverFPSLabel) #endif } diff --git a/Yep/ViewControllers/Feeds/FeedsViewController.swift b/Yep/ViewControllers/Feeds/FeedsViewController.swift index 7beab372..f1a765b1 100644 --- a/Yep/ViewControllers/Feeds/FeedsViewController.swift +++ b/Yep/ViewControllers/Feeds/FeedsViewController.swift @@ -14,6 +14,9 @@ import MapKit class FeedsViewController: BaseViewController { var skill: Skill? + var needShowSkill: Bool { + return (skill == nil) ? true : false + } var profileUser: ProfileUser? var preparedFeedsCount = 0 @@ -107,8 +110,14 @@ class FeedsViewController: BaseViewController { #endif private let feedSkillUsersCellID = "FeedSkillUsersCell" - private let feedCellID = "FeedCell" - private let feedSocialWorkCellID = "FeedSocialWorkCell" + private let feedBasicCellID = "FeedBasicCell" + private let feedBiggerImageCellID = "FeedBiggerImageCell" + private let feedNormalImagesCellID = "FeedNormalImagesCell" + private let feedAnyImagesCellID = "FeedAnyImagesCell" + private let feedGithubRepoCellID = "FeedGithubRepoCell" + private let feedDribbbleShotCellID = "FeedDribbbleShotCell" + private let feedVoiceCellID = "FeedVoiceCell" + private let feedLocationCellID = "FeedLocationCell" private let loadMoreTableViewCellID = "LoadMoreTableViewCell" private lazy var noFeedsFooterView: InfoView = InfoView(NSLocalizedString("No Feeds.", comment: "")) @@ -145,7 +154,7 @@ class FeedsViewController: BaseViewController { let indexPath = NSIndexPath(forRow: index, inSection: Section.Feed.rawValue) - if let cell = feedsTableView.cellForRowAtIndexPath(indexPath) as? FeedSocialWorkCell { + if let cell = feedsTableView.cellForRowAtIndexPath(indexPath) as? FeedVoiceCell { cell.audioPlayedDuration = 0 } @@ -176,33 +185,41 @@ class FeedsViewController: BaseViewController { feedsTableView.tableFooterView = feeds.isEmpty ? noFeedsFooterView : UIView() } - private var feedHeightHash = [String: CGFloat]() + private struct LayoutPool { - private func heightOfFeed(feed: DiscoveredFeed) -> CGFloat { + private var feedCellLayoutHash = [String: FeedCellLayout]() - let key = feed.id + private func feedCellLayoutOfFeed(feed: DiscoveredFeed) -> FeedCellLayout? { + let key = feed.id - if let height = feedHeightHash[key] { - return height + return feedCellLayoutHash[key] + } - } else { + private mutating func updateFeedCellLayout(layout: FeedCellLayout, forFeed feed: DiscoveredFeed) { - let height: CGFloat - switch feed.kind { - case .GithubRepo, .DribbbleShot, .Audio, .Location: - height = FeedSocialWorkCell.heightOfFeed(feed) - default: - height = FeedCell.heightOfFeed(feed) - } + let key = feed.id if !key.isEmpty { - feedHeightHash[key] = height + feedCellLayoutHash[key] = layout } - return height + //println("feedCellLayoutHash.count: \(feedCellLayoutHash.count)") + } + + private mutating func heightOfFeed(feed: DiscoveredFeed) -> CGFloat { + + if let layout = feedCellLayoutOfFeed(feed) { + return layout.height + + } else { + let layout = FeedCellLayout(feed: feed) + updateFeedCellLayout(layout, forFeed: feed) + return layout.height + } } } - + private static var layoutPool = LayoutPool() + private var feedSortStyle: FeedSortStyle = .Match { didSet { feeds = [] @@ -279,8 +296,16 @@ class FeedsViewController: BaseViewController { feedsTableView.separatorColor = UIColor.yepCellSeparatorColor() feedsTableView.registerNib(UINib(nibName: feedSkillUsersCellID, bundle: nil), forCellReuseIdentifier: feedSkillUsersCellID) - feedsTableView.registerNib(UINib(nibName: feedCellID, bundle: nil), forCellReuseIdentifier: feedCellID) - feedsTableView.registerNib(UINib(nibName: feedSocialWorkCellID, bundle: nil), forCellReuseIdentifier: feedSocialWorkCellID) + + feedsTableView.registerClass(FeedBasicCell.self, forCellReuseIdentifier: feedBasicCellID) + feedsTableView.registerClass(FeedBiggerImageCell.self, forCellReuseIdentifier: feedBiggerImageCellID) + feedsTableView.registerClass(FeedNormalImagesCell.self, forCellReuseIdentifier: feedNormalImagesCellID) + feedsTableView.registerClass(FeedAnyImagesCell.self, forCellReuseIdentifier: feedAnyImagesCellID) + feedsTableView.registerClass(FeedGithubRepoCell.self, forCellReuseIdentifier: feedGithubRepoCellID) + feedsTableView.registerClass(FeedDribbbleShotCell.self, forCellReuseIdentifier: feedDribbbleShotCellID) + feedsTableView.registerClass(FeedVoiceCell.self, forCellReuseIdentifier: feedVoiceCellID) + feedsTableView.registerClass(FeedLocationCell.self, forCellReuseIdentifier: feedLocationCellID) + feedsTableView.registerNib(UINib(nibName: loadMoreTableViewCellID, bundle: nil), forCellReuseIdentifier: loadMoreTableViewCellID) @@ -542,7 +567,7 @@ class FeedsViewController: BaseViewController { let indexPath = NSIndexPath(forRow: index, inSection: Section.Feed.rawValue) - if let cell = feedsTableView.cellForRowAtIndexPath(indexPath) as? FeedSocialWorkCell { + if let cell = feedsTableView.cellForRowAtIndexPath(indexPath) as? FeedVoiceCell { cell.audioPlayedDuration = currentTime } @@ -793,12 +818,42 @@ extension FeedsViewController: UITableViewDataSource, UITableViewDelegate { switch feed.kind { - case .GithubRepo, .DribbbleShot, .Audio, .Location: - let cell = tableView.dequeueReusableCellWithIdentifier(feedSocialWorkCellID) as! FeedSocialWorkCell + case .Text: + let cell = tableView.dequeueReusableCellWithIdentifier(feedBasicCellID) as! FeedBasicCell + return cell + + case .Image: + if feed.imageAttachmentsCount == 1 { + let cell = tableView.dequeueReusableCellWithIdentifier(feedBiggerImageCellID) as! FeedBiggerImageCell + return cell + + } else if feed.imageAttachmentsCount <= 3 { + let cell = tableView.dequeueReusableCellWithIdentifier(feedNormalImagesCellID) as! FeedNormalImagesCell + return cell + + } else { + let cell = tableView.dequeueReusableCellWithIdentifier(feedAnyImagesCellID) as! FeedAnyImagesCell + return cell + } + + case .GithubRepo: + let cell = tableView.dequeueReusableCellWithIdentifier(feedGithubRepoCellID) as! FeedGithubRepoCell + return cell + + case .DribbbleShot: + let cell = tableView.dequeueReusableCellWithIdentifier(feedDribbbleShotCellID) as! FeedDribbbleShotCell + return cell + + case .Audio: + let cell = tableView.dequeueReusableCellWithIdentifier(feedVoiceCellID) as! FeedVoiceCell + return cell + + case .Location: + let cell = tableView.dequeueReusableCellWithIdentifier(feedLocationCellID) as! FeedLocationCell return cell default: - let cell = tableView.dequeueReusableCellWithIdentifier(feedCellID) as! FeedCell + let cell = tableView.dequeueReusableCellWithIdentifier(feedBasicCellID) as! FeedBasicCell return cell } @@ -867,22 +922,100 @@ extension FeedsViewController: UITableViewDataSource, UITableViewDelegate { tableView.deselectRowAtIndexPath(indexPath, animated: true) } + let layout = FeedsViewController.layoutPool.feedCellLayoutOfFeed(feed) + let update: FeedCellLayout.Update = { newLayout in + FeedsViewController.layoutPool.updateFeedCellLayout(newLayout, forFeed: feed) + } + let layoutCache = (layout: layout, update: update) + switch feed.kind { - case .GithubRepo, .DribbbleShot, .Audio, .Location: + case .Text: - guard let cell = cell as? FeedSocialWorkCell else { + cell.configureWithFeed(feed, layoutCache: layoutCache, needShowSkill: needShowSkill) + + case .Image: + + let tapMediaAction: FeedTapMediaAction = { [weak self] transitionView, image, attachments, index in + + guard image != nil else { + return + } + + let vc = UIStoryboard(name: "MediaPreview", bundle: nil).instantiateViewControllerWithIdentifier("MediaPreviewViewController") as! MediaPreviewViewController + + vc.previewMedias = attachments.map({ PreviewMedia.AttachmentType(attachment: $0) }) + vc.startIndex = index + + let transitionView = transitionView + let frame = transitionView.convertRect(transitionView.bounds, toView: self?.view) + vc.previewImageViewInitalFrame = frame + vc.bottomPreviewImage = image + + vc.transitionView = transitionView + + delay(0) { + transitionView.alpha = 0 // 放到下一个 Runloop 避免太快消失产生闪烁 + } + vc.afterDismissAction = { [weak self] in + transitionView.alpha = 1 + self?.view.window?.makeKeyAndVisible() + } + + mediaPreviewWindow.rootViewController = vc + mediaPreviewWindow.windowLevel = UIWindowLevelAlert - 1 + mediaPreviewWindow.makeKeyAndVisible() + } + + if feed.imageAttachmentsCount == 1 { + guard let cell = cell as? FeedBiggerImageCell else { + break + } + + cell.configureWithFeed(feed, layoutCache: layoutCache, needShowSkill: needShowSkill) + + cell.tapMediaAction = tapMediaAction + + } else if feed.imageAttachmentsCount <= 3 { + + guard let cell = cell as? FeedNormalImagesCell else { + break + } + + cell.configureWithFeed(feed, layoutCache: layoutCache, needShowSkill: needShowSkill) + + cell.tapMediaAction = tapMediaAction + + } else { + guard let cell = cell as? FeedAnyImagesCell else { + break + } + + cell.configureWithFeed(feed, layoutCache: layoutCache, needShowSkill: needShowSkill) + + cell.tapMediaAction = tapMediaAction + } + + case .GithubRepo: + + guard let cell = cell as? FeedGithubRepoCell else { break } - let feed = feeds[indexPath.item] - - cell.configureWithFeed(feed, needShowSkill: (skill == nil) ? true : false) + cell.configureWithFeed(feed, layoutCache: layoutCache, needShowSkill: needShowSkill) cell.tapGithubRepoLinkAction = { [weak self] URL in self?.yep_openURL(URL) } + case .DribbbleShot: + + guard let cell = cell as? FeedDribbbleShotCell else { + break + } + + cell.configureWithFeed(feed, layoutCache: layoutCache, needShowSkill: needShowSkill) + cell.tapDribbbleShotLinkAction = { [weak self] URL in self?.yep_openURL(URL) } @@ -899,7 +1032,7 @@ extension FeedsViewController: UITableViewDataSource, UITableViewDelegate { vc.startIndex = 0 let transitionView = transitionView - let frame = transitionView.convertRect(transitionView.frame, toView: self?.view) + let frame = transitionView.convertRect(transitionView.bounds, toView: self?.view) vc.previewImageViewInitalFrame = frame vc.bottomPreviewImage = image @@ -916,6 +1049,14 @@ extension FeedsViewController: UITableViewDataSource, UITableViewDelegate { mediaPreviewWindow.makeKeyAndVisible() } + case .Audio: + + guard let cell = cell as? FeedVoiceCell else { + break + } + + cell.configureWithFeed(feed, layoutCache: layoutCache, needShowSkill: needShowSkill) + cell.playOrPauseAudioAction = { [weak self] cell in guard let realm = try? Realm(), feedAudio = FeedAudio.feedAudioWithFeedID(feed.id, inRealm: realm) else { @@ -956,7 +1097,7 @@ extension FeedsViewController: UITableViewDataSource, UITableViewDelegate { let indexPath = NSIndexPath(forRow: index, inSection: Section.Feed.rawValue) - if let cell = strongSelf.feedsTableView.cellForRowAtIndexPath(indexPath) as? FeedSocialWorkCell { + if let cell = strongSelf.feedsTableView.cellForRowAtIndexPath(indexPath) as? FeedVoiceCell { cell.audioPlaying = false } @@ -977,6 +1118,14 @@ extension FeedsViewController: UITableViewDataSource, UITableViewDelegate { } } + case .Location: + + guard let cell = cell as? FeedLocationCell else { + break + } + + cell.configureWithFeed(feed, layoutCache: layoutCache, needShowSkill: needShowSkill) + cell.tapLocationAction = { locationName, locationCoordinate in let mapItem = MKMapItem(placemark: MKPlacemark(coordinate: locationCoordinate, addressDictionary: nil)) @@ -986,54 +1135,7 @@ extension FeedsViewController: UITableViewDataSource, UITableViewDelegate { } default: - - guard let cell = cell as? FeedCell else { - break - } - - let feed = feeds[indexPath.item] - - cell.configureWithFeed(feed, needShowSkill: (skill == nil) ? true : false) - - cell.tapMediaAction = { [weak self] transitionView, image, attachments, index in - - guard image != nil else { - return - } - - let vc = UIStoryboard(name: "MediaPreview", bundle: nil).instantiateViewControllerWithIdentifier("MediaPreviewViewController") as! MediaPreviewViewController - - vc.previewMedias = attachments.map({ PreviewMedia.AttachmentType(attachment: $0) }) - vc.startIndex = index - - let transitionView = transitionView - let frame = transitionView.convertRect(transitionView.frame, toView: self?.view) - vc.previewImageViewInitalFrame = frame - vc.bottomPreviewImage = image - - vc.transitionView = transitionView - - delay(0) { - transitionView.alpha = 0 // 放到下一个 Runloop 避免太快消失产生闪烁 - } - vc.afterDismissAction = { [weak self] in - transitionView.alpha = 1 - self?.view.window?.makeKeyAndVisible() - } - - mediaPreviewWindow.rootViewController = vc - mediaPreviewWindow.windowLevel = UIWindowLevelAlert - 1 - mediaPreviewWindow.makeKeyAndVisible() - - /* - let info: [String: AnyObject] = [ - "transitionView": transitionView, - "attachments": Box(value: attachments), - "index": index, - ] - self?.performSegueWithIdentifier("showFeedMedia", sender: info) - */ - } + break } case Section.LoadMore.rawValue: @@ -1066,7 +1168,7 @@ extension FeedsViewController: UITableViewDataSource, UITableViewDelegate { case Section.Feed.rawValue: let feed = feeds[indexPath.row] - return heightOfFeed(feed) + return FeedsViewController.layoutPool.heightOfFeed(feed) case Section.LoadMore.rawValue: return 60 diff --git a/Yep/ViewControllers/NewFeed/NewFeedViewController.swift b/Yep/ViewControllers/NewFeed/NewFeedViewController.swift index 8d7cdb3f..3f276d5d 100644 --- a/Yep/ViewControllers/NewFeed/NewFeedViewController.swift +++ b/Yep/ViewControllers/NewFeed/NewFeedViewController.swift @@ -558,7 +558,7 @@ class NewFeedViewController: UIViewController { uploadState = .Uploading - let message = messageTextView.text + let message = messageTextView.text.trimming(.WhitespaceAndNewline) let coordinate = YepLocationService.sharedManager.currentLocation?.coordinate var kind: FeedKind = .Text var mediaInfo: JSONDictionary? diff --git a/Yep/ViewControllers/Profile/ProfileViewController.swift b/Yep/ViewControllers/Profile/ProfileViewController.swift index 3933e8af..de5689a5 100644 --- a/Yep/ViewControllers/Profile/ProfileViewController.swift +++ b/Yep/ViewControllers/Profile/ProfileViewController.swift @@ -793,7 +793,7 @@ class ProfileViewController: UIViewController { } #if DEBUG - view.addSubview(profileFPSLabel) +// view.addSubview(profileFPSLabel) #endif } diff --git a/Yep/Views/Cells/Feed/FeedAnyImagesCell.swift b/Yep/Views/Cells/Feed/FeedAnyImagesCell.swift new file mode 100644 index 00000000..5f569858 --- /dev/null +++ b/Yep/Views/Cells/Feed/FeedAnyImagesCell.swift @@ -0,0 +1,165 @@ +// +// FeedAnyImagesCell.swift +// Yep +// +// Created by nixzhu on 15/9/30. +// Copyright © 2015年 Catch Inc. All rights reserved. +// + +import UIKit + +let feedAttachmentImageSize = YepConfig.FeedNormalImagesCell.imageSize +let feedAttachmentBiggerImageSize = YepConfig.FeedBiggerImageCell.imageSize + +private let feedMediaCellID = "FeedMediaCell" +private let screenWidth: CGFloat = UIScreen.mainScreen().bounds.width + +typealias FeedTapMediaAction = (transitionView: UIView, image: UIImage?, attachments: [DiscoveredAttachment], index: Int) -> Void + +class FeedAnyImagesCell: FeedBasicCell { + + lazy var mediaCollectionView: UICollectionView = { + + let layout = UICollectionViewFlowLayout() + layout.minimumLineSpacing = 5 + layout.scrollDirection = .Horizontal + + let collectionView = UICollectionView(frame: CGRectZero, collectionViewLayout: layout) + collectionView.scrollsToTop = false + collectionView.contentInset = UIEdgeInsets(top: 0, left: 15 + 40 + 10, bottom: 0, right: 15) + collectionView.showsHorizontalScrollIndicator = false + collectionView.backgroundColor = UIColor.clearColor() + collectionView.registerNib(UINib(nibName: feedMediaCellID, bundle: nil), forCellWithReuseIdentifier: feedMediaCellID) + collectionView.dataSource = self + collectionView.delegate = self + + let backgroundView = TouchClosuresView(frame: collectionView.bounds) + backgroundView.touchesBeganAction = { [weak self] in + if let strongSelf = self { + strongSelf.touchesBeganAction?(strongSelf) + } + } + backgroundView.touchesEndedAction = { [weak self] in + if let strongSelf = self { + if strongSelf.editing { + return + } + strongSelf.touchesEndedAction?(strongSelf) + } + } + backgroundView.touchesCancelledAction = { [weak self] in + if let strongSelf = self { + strongSelf.touchesCancelledAction?(strongSelf) + } + } + collectionView.backgroundView = backgroundView + + return collectionView + }() + + var tapMediaAction: FeedTapMediaAction? + + var attachments = [DiscoveredAttachment]() { + didSet { + + mediaCollectionView.reloadData() + } + } + + override class func heightOfFeed(feed: DiscoveredFeed) -> CGFloat { + + let height = super.heightOfFeed(feed) + feedAttachmentImageSize.height + 15 + + return ceil(height) + } + + override init(style: UITableViewCellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + + contentView.addSubview(mediaCollectionView) + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func prepareForReuse() { + super.prepareForReuse() + + attachments = [] + } + + override func configureWithFeed(feed: DiscoveredFeed, layoutCache: FeedCellLayout.Cache, needShowSkill: Bool) { + + var _newLayout: FeedCellLayout? + super.configureWithFeed(feed, layoutCache: (layout: layoutCache.layout, update: { newLayout in + _newLayout = newLayout + }), needShowSkill: needShowSkill) + + if let anyImagesLayout = layoutCache.layout?.anyImagesLayout { + mediaCollectionView.frame = anyImagesLayout.mediaCollectionViewFrame + + } else { + let y = messageTextView.frame.origin.y + messageTextView.frame.height + 15 + let height = feedAttachmentImageSize.height + mediaCollectionView.frame = CGRect(x: 0, y: y, width: screenWidth, height: height) + } + + if let attachment = feed.attachment, case let .Images(attachments) = attachment { + self.attachments = attachments + } + + if layoutCache.layout == nil { + + let anyImagesLayout = FeedCellLayout.AnyImagesLayout(mediaCollectionViewFrame: mediaCollectionView.frame) + _newLayout?.anyImagesLayout = anyImagesLayout + + if let newLayout = _newLayout { + layoutCache.update(layout: newLayout) + } + } + } +} + +extension FeedAnyImagesCell: UICollectionViewDataSource, UICollectionViewDelegate { + + func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int { + return 1 + } + + func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return attachments.count + } + + func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { + + let cell = collectionView.dequeueReusableCellWithReuseIdentifier(feedMediaCellID, forIndexPath: indexPath) as! FeedMediaCell + + if let attachment = attachments[safe: indexPath.item] { + + //println("attachment imageURL: \(imageURL)") + + cell.configureWithAttachment(attachment, bigger: (attachments.count == 1)) + } + + return cell + } + + func collectionView(collectionView: UICollectionView!, layout collectionViewLayout: UICollectionViewLayout!, sizeForItemAtIndexPath indexPath: NSIndexPath!) -> CGSize { + + return feedAttachmentImageSize + } + + func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAtIndex section: Int) -> UIEdgeInsets { + return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) + } + + func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) { + + let cell = collectionView.cellForItemAtIndexPath(indexPath) as! FeedMediaCell + + let transitionView = cell.imageView + tapMediaAction?(transitionView: transitionView, image: cell.imageView.image, attachments: attachments, index: indexPath.item) + } +} + diff --git a/Yep/Views/Cells/Feed/FeedBasicCell.swift b/Yep/Views/Cells/Feed/FeedBasicCell.swift new file mode 100644 index 00000000..bba2c55b --- /dev/null +++ b/Yep/Views/Cells/Feed/FeedBasicCell.swift @@ -0,0 +1,288 @@ +// +// FeedBasicCell.swift +// Yep +// +// Created by nixzhu on 15/11/20. +// Copyright © 2015年 Catch Inc. All rights reserved. +// + +import UIKit + +private let screenWidth: CGFloat = UIScreen.mainScreen().bounds.width + +class FeedBasicCell: UITableViewCell { + + lazy var avatarImageView: UIImageView = { + let imageView = UIImageView() + + imageView.frame = CGRect(x: 15, y: 10, width: 40, height: 40) + + imageView.contentMode = .ScaleAspectFit + + let tapAvatar = UITapGestureRecognizer(target: self, action: "tapAvatar:") + imageView.userInteractionEnabled = true + imageView.addGestureRecognizer(tapAvatar) + + return imageView + }() + + lazy var nicknameLabel: UILabel = { + let label = UILabel() + label.textColor = UIColor.yepTintColor() + label.font = UIFont.systemFontOfSize(15) + + label.frame = CGRect(x: 65, y: 21, width: 100, height: 18) + label.opaque = true + label.backgroundColor = UIColor.whiteColor() + label.clipsToBounds = true + + return label + }() + + lazy var skillButton: UIButton = { + let button = UIButton() + button.setBackgroundImage(UIImage(named: "skill_bubble_empty"), forState: .Normal) + button.setTitleColor(UIColor.yepTintColor(), forState: .Normal) + button.titleLabel?.font = UIFont.feedSkillFont() + + let cellWidth = self.bounds.width + let width: CGFloat = 60 + button.frame = CGRect(x: cellWidth - width - 15, y: 19, width: width, height: 22) + + button.addTarget(self, action: "tapSkill:", forControlEvents: .TouchUpInside) + + return button + }() + + lazy var messageTextView: FeedTextView = { + let textView = FeedTextView() + textView.textColor = UIColor.yepMessageColor() + textView.font = UIFont.feedMessageFont() + textView.textContainer.lineFragmentPadding = 0 + textView.textContainerInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) + textView.editable = false + textView.scrollEnabled = false + textView.showsHorizontalScrollIndicator = false + textView.showsVerticalScrollIndicator = false + textView.dataDetectorTypes = [.Link, .CalendarEvent] + + textView.frame = CGRect(x: 65, y: 54, width: screenWidth - 65 - 15, height: 26) + textView.opaque = true + textView.backgroundColor = UIColor.whiteColor() + + textView.touchesBeganAction = { [weak self] in + if let strongSelf = self { + strongSelf.touchesBeganAction?(strongSelf) + } + } + textView.touchesEndedAction = { [weak self] in + if let strongSelf = self { + if strongSelf.editing { + return + } + strongSelf.touchesEndedAction?(strongSelf) + } + } + textView.touchesCancelledAction = { [weak self] in + if let strongSelf = self { + strongSelf.touchesCancelledAction?(strongSelf) + } + } + + return textView + }() + + lazy var leftBottomLabel: UILabel = { + let label = UILabel() + label.textColor = UIColor.grayColor() + label.font = UIFont.feedBottomLabelsFont() + + label.frame = CGRect(x: 65, y: 0, width: 200, height: 17) + label.opaque = true + label.backgroundColor = UIColor.whiteColor() + label.clipsToBounds = true + + return label + }() + + lazy var messageCountLabel: UILabel = { + let label = UILabel() + label.textColor = UIColor.yepTintColor() + label.font = UIFont.feedBottomLabelsFont() + + label.frame = CGRect(x: 65, y: 0, width: 200, height: 17) + label.opaque = true + label.backgroundColor = UIColor.whiteColor() + label.clipsToBounds = true + + return label + }() + + lazy var discussionImageView: UIImageView = { + let imageView = UIImageView() + imageView.image = UIImage(named: "icon_discussion") + return imageView + }() + + var feed: DiscoveredFeed? + + var tapAvatarAction: (UITableViewCell -> Void)? + var tapSkillAction: (UITableViewCell -> Void)? + + var touchesBeganAction: (UITableViewCell -> Void)? + var touchesEndedAction: (UITableViewCell -> Void)? + var touchesCancelledAction: (UITableViewCell -> Void)? + + override init(style: UITableViewCellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + + separatorInset = UIEdgeInsets(top: 0, left: 65, bottom: 0, right: 0) + + contentView.addSubview(avatarImageView) + contentView.addSubview(nicknameLabel) + contentView.addSubview(skillButton) + + contentView.addSubview(messageTextView) + + contentView.addSubview(leftBottomLabel) + contentView.addSubview(messageCountLabel) + contentView.addSubview(discussionImageView) + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + static let messageTextViewMaxWidth: CGFloat = { + let maxWidth = UIScreen.mainScreen().bounds.width - (15 + 40 + 10 + 15) + return maxWidth + }() + + class func heightOfFeed(feed: DiscoveredFeed) -> CGFloat { + + let rect = feed.body.boundingRectWithSize(CGSize(width: FeedBasicCell.messageTextViewMaxWidth, height: CGFloat(FLT_MAX)), options: [.UsesLineFragmentOrigin, .UsesFontLeading], attributes: YepConfig.FeedBasicCell.textAttributes, context: nil) + + let height: CGFloat = ceil(rect.height) + 10 + 40 + 4 + 15 + 17 + 15 + + return ceil(height) + } + + override func setSelected(selected: Bool, animated: Bool) { + super.setSelected(selected, animated: animated) + + // Configure the view for the selected state + } + + override func prepareForReuse() { + super.prepareForReuse() + + feed = nil + + messageTextView.text = nil + messageTextView.attributedText = nil + } + + private func calHeightOfMessageTextView() { + + let rect = messageTextView.text.boundingRectWithSize(CGSize(width: FeedBasicCell.messageTextViewMaxWidth, height: CGFloat(FLT_MAX)), options: [.UsesLineFragmentOrigin, .UsesFontLeading], attributes: YepConfig.FeedBasicCell.textAttributes, context: nil) + + messageTextView.frame.size.height = ceil(rect.height) + } + + func configureWithFeed(feed: DiscoveredFeed, layoutCache: FeedCellLayout.Cache, needShowSkill: Bool) { + + self.feed = feed + + let layout = layoutCache.layout + + messageTextView.text = "\u{200B}\(feed.body)" // ref http://stackoverflow.com/a/25994821 + + //println("messageTextView.text: >>>\(messageTextView.text)<<<") + + if let basicLayout = layout?.basicLayout { + messageTextView.frame = basicLayout.messageTextViewFrame + } else { + calHeightOfMessageTextView() + } + + if needShowSkill, let skill = feed.skill { + skillButton.setTitle(skill.localName, forState: .Normal) + skillButton.hidden = false + + if let basicLayout = layout?.basicLayout { + skillButton.frame = basicLayout.skillButtonFrame + nicknameLabel.frame = basicLayout.nicknameLabelFrameWhen(hasLogo: false, hasSkill: true) + + } else { + let rect = skill.localName.boundingRectWithSize(CGSize(width: 320, height: CGFloat(FLT_MAX)), options: [.UsesLineFragmentOrigin, .UsesFontLeading], attributes: YepConfig.FeedBasicCell.skillTextAttributes, context: nil) + + let skillButtonWidth = ceil(rect.width) + 20 + + skillButton.frame = CGRect(x: screenWidth - skillButtonWidth - 15, y: 19, width: skillButtonWidth, height: 22) + + nicknameLabel.frame.size.width = screenWidth - 65 - skillButtonWidth - 20 - 10 - 15 + } + + } else { + skillButton.hidden = true + + if let basicLayout = layout?.basicLayout { + nicknameLabel.frame = basicLayout.nicknameLabelFrameWhen(hasLogo: false, hasSkill: false) + } else { + nicknameLabel.frame.size.width = screenWidth - 65 - 15 + } + } + + let plainAvatar = PlainAvatar(avatarURLString: feed.creator.avatarURLString, avatarStyle: nanoAvatarStyle) + avatarImageView.navi_setAvatar(plainAvatar, withFadeTransitionDuration: avatarFadeTransitionDuration) + + nicknameLabel.text = feed.creator.nickname + + leftBottomLabel.text = feed.timeAndDistanceString + + let messagesCountString = "\(feed.messagesCount)" + messageCountLabel.text = messagesCountString + messageCountLabel.hidden = (feed.messagesCount == 0) + + if let basicLayout = layout?.basicLayout { + leftBottomLabel.frame = basicLayout.leftBottomLabelFrame + messageCountLabel.frame = basicLayout.messageCountLabelFrame + discussionImageView.frame = basicLayout.discussionImageViewFrame + + } else { + leftBottomLabel.frame.origin.y = contentView.bounds.height - leftBottomLabel.frame.height - 10 + + let rect = messagesCountString.boundingRectWithSize(CGSize(width: 320, height: CGFloat(FLT_MAX)), options: [.UsesLineFragmentOrigin, .UsesFontLeading], attributes: YepConfig.FeedBasicCell.bottomLabelsTextAttributes, context: nil) + + let width = ceil(rect.width) + messageCountLabel.frame = CGRect(x: screenWidth - width - 45 - 8, y: leftBottomLabel.frame.origin.y, width: width, height: 19) + + discussionImageView.frame = CGRect(x: screenWidth - 30 - 15, y: leftBottomLabel.frame.origin.y - 1, width: 30, height: 19) + } + + if layoutCache.layout == nil { + + var nicknameLabelFrame = nicknameLabel.frame + nicknameLabelFrame.size.width = screenWidth - 65 - 15 + + let basicLayout = FeedCellLayout.BasicLayout(avatarImageViewFrame: avatarImageView.frame, nicknameLabelFrame: nicknameLabelFrame, skillButtonFrame: skillButton.frame, messageTextViewFrame: messageTextView.frame, leftBottomLabelFrame: leftBottomLabel.frame, messageCountLabelFrame: messageCountLabel.frame, discussionImageViewFrame: discussionImageView.frame) + + let newLayout = FeedCellLayout(height: contentView.bounds.height, basicLayout: basicLayout) + + layoutCache.update(layout: newLayout) + } + } + + // MARK: Actions + + func tapAvatar(sender: UITapGestureRecognizer) { + + tapAvatarAction?(self) + } + + func tapSkill(sender: AnyObject) { + + tapSkillAction?(self) + } +} + diff --git a/Yep/Views/Cells/Feed/FeedBiggerImageCell.swift b/Yep/Views/Cells/Feed/FeedBiggerImageCell.swift new file mode 100644 index 00000000..f0627b24 --- /dev/null +++ b/Yep/Views/Cells/Feed/FeedBiggerImageCell.swift @@ -0,0 +1,96 @@ +// +// FeedBiggerImageCell.swift +// Yep +// +// Created by nixzhu on 15/12/17. +// Copyright © 2015年 Catch Inc. All rights reserved. +// + +import UIKit + +class FeedBiggerImageCell: FeedBasicCell { + + var tapMediaAction: FeedTapMediaAction? + + lazy var biggerImageView: UIImageView = { + let imageView = UIImageView() + imageView.contentMode = .ScaleAspectFill + imageView.clipsToBounds = true + imageView.frame = CGRect(origin: CGPoint(x: 65, y: 0), size: YepConfig.FeedBiggerImageCell.imageSize) + imageView.layer.borderColor = UIColor.yepBorderColor().CGColor + imageView.layer.borderWidth = 1 + + imageView.userInteractionEnabled = true + let tap = UITapGestureRecognizer(target: self, action: "tap:") + imageView.addGestureRecognizer(tap) + + return imageView + }() + + override class func heightOfFeed(feed: DiscoveredFeed) -> CGFloat { + + let height = super.heightOfFeed(feed) + YepConfig.FeedBiggerImageCell.imageSize.height + 15 + + return ceil(height) + } + + override init(style: UITableViewCellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + + contentView.addSubview(biggerImageView) + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func setSelected(selected: Bool, animated: Bool) { + super.setSelected(selected, animated: animated) + + // Configure the view for the selected state + } + + override func prepareForReuse() { + super.prepareForReuse() + + biggerImageView.image = nil + } + + override func configureWithFeed(feed: DiscoveredFeed, layoutCache: FeedCellLayout.Cache, needShowSkill: Bool) { + + var _newLayout: FeedCellLayout? + super.configureWithFeed(feed, layoutCache: (layout: layoutCache.layout, update: { newLayout in + _newLayout = newLayout + }), needShowSkill: needShowSkill) + + if let biggerImageLayout = layoutCache.layout?.biggerImageLayout { + biggerImageView.frame = biggerImageLayout.biggerImageViewFrame + + } else { + biggerImageView.frame.origin.y = messageTextView.frame.origin.y + messageTextView.frame.height + 15 + } + + if let onlyAttachment = feed.imageAttachments?.first { + biggerImageView.yep_showActivityIndicatorWhenLoading = true + biggerImageView.yep_setImageOfAttachment(onlyAttachment, withSize: YepConfig.FeedBiggerImageCell.imageSize) + } + + if layoutCache.layout == nil { + + let biggerImageLayout = FeedCellLayout.BiggerImageLayout(biggerImageViewFrame: biggerImageView.frame) + _newLayout?.biggerImageLayout = biggerImageLayout + + if let newLayout = _newLayout { + layoutCache.update(layout: newLayout) + } + } + } + + @objc private func tap(sender: UITapGestureRecognizer) { + + if let attachments = feed?.imageAttachments { + tapMediaAction?(transitionView: biggerImageView, image: biggerImageView.image, attachments: attachments, index: 0) + } + } +} + diff --git a/Yep/Views/Cells/Feed/FeedCell.swift b/Yep/Views/Cells/Feed/FeedCell.swift deleted file mode 100644 index 8c70c20f..00000000 --- a/Yep/Views/Cells/Feed/FeedCell.swift +++ /dev/null @@ -1,172 +0,0 @@ -// -// FeedCell.swift -// Yep -// -// Created by nixzhu on 15/9/30. -// Copyright © 2015年 Catch Inc. All rights reserved. -// - -import UIKit - -let feedAttachmentImageSize = CGSize(width: 80, height: 80) -let feedAttachmentBiggerImageSize = CGSize(width: 160, height: 160) - -class FeedCell: FeedBasicCell { - - @IBOutlet weak var mediaCollectionView: UICollectionView! - @IBOutlet weak var collectionViewHeight: NSLayoutConstraint! - - @IBOutlet weak var leftBottomLabelTopConstraint: NSLayoutConstraint! - - var tapMediaAction: ((transitionView: UIView, image: UIImage?, attachments: [DiscoveredAttachment], index: Int) -> Void)? - - var attachments = [DiscoveredAttachment]() { - didSet { - - let oldHeight = collectionViewHeight.constant - let newHeight: CGFloat - if attachments.count == 1 { - newHeight = feedAttachmentBiggerImageSize.height - } else { - newHeight = feedAttachmentImageSize.height - } - if newHeight != oldHeight { - collectionViewHeight.constant = newHeight - } - - mediaCollectionView.reloadData() - } - } - - static let messageTextViewMaxWidth: CGFloat = { - let maxWidth = UIScreen.mainScreen().bounds.width - (15 + 40 + 10 + 15) - return maxWidth - }() - - let feedMediaCellID = "FeedMediaCell" - - class func heightOfFeed(feed: DiscoveredFeed) -> CGFloat { - - let rect = feed.body.boundingRectWithSize(CGSize(width: FeedCell.messageTextViewMaxWidth, height: CGFloat(FLT_MAX)), options: [.UsesLineFragmentOrigin, .UsesFontLeading], attributes: YepConfig.FeedBasicCell.textAttributes, context: nil) - - var height: CGFloat = ceil(rect.height) + 10 + 40 + 4 + 15 + 17 + 15 - - if let attachment = feed.attachment { - if case let .Images(attachments) = attachment { - let imageHeight: CGFloat = attachments.count == 1 ? feedAttachmentBiggerImageSize.height : feedAttachmentImageSize.height - height += (imageHeight + 15) - } - } - - return ceil(height) - } - - override func awakeFromNib() { - super.awakeFromNib() - - mediaCollectionView.scrollsToTop = false - mediaCollectionView.contentInset = UIEdgeInsets(top: 0, left: 15 + 40 + 10, bottom: 0, right: 15) - mediaCollectionView.showsHorizontalScrollIndicator = false - mediaCollectionView.backgroundColor = UIColor.clearColor() - mediaCollectionView.registerNib(UINib(nibName: feedMediaCellID, bundle: nil), forCellWithReuseIdentifier: feedMediaCellID) - mediaCollectionView.dataSource = self - mediaCollectionView.delegate = self - - let backgroundView = TouchClosuresView(frame: mediaCollectionView.bounds) - backgroundView.touchesBeganAction = { [weak self] in - if let strongSelf = self { - strongSelf.touchesBeganAction?(strongSelf) - } - } - backgroundView.touchesEndedAction = { [weak self] in - if let strongSelf = self { - if strongSelf.editing { - return - } - strongSelf.touchesEndedAction?(strongSelf) - } - } - backgroundView.touchesCancelledAction = { [weak self] in - if let strongSelf = self { - strongSelf.touchesCancelledAction?(strongSelf) - } - } - mediaCollectionView.backgroundView = backgroundView - } - - override func prepareForReuse() { - super.prepareForReuse() - - attachments = [] - } - - override func configureWithFeed(feed: DiscoveredFeed, needShowSkill: Bool) { - super.configureWithFeed(feed, needShowSkill: needShowSkill) - - var hasMedia = false - - if let attachment = feed.attachment { - if case let .Images(attachments) = attachment { - hasMedia = !attachments.isEmpty - - self.attachments = attachments - } - } - - if attachments.count > 1 { - leftBottomLabelTopConstraint.constant = hasMedia ? (15 + feedAttachmentImageSize.height + 15) : 15 - } else { - leftBottomLabelTopConstraint.constant = hasMedia ? (15 + feedAttachmentBiggerImageSize.height + 15) : 15 - } - - mediaCollectionView.hidden = hasMedia ? false : true - } -} - -extension FeedCell: UICollectionViewDataSource, UICollectionViewDelegate { - - func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int { - return 1 - } - - func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - return attachments.count - } - - func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { - - let cell = collectionView.dequeueReusableCellWithReuseIdentifier(feedMediaCellID, forIndexPath: indexPath) as! FeedMediaCell - - if let attachment = attachments[safe: indexPath.item] { - - //println("attachment imageURL: \(imageURL)") - - cell.configureWithAttachment(attachment, bigger: (attachments.count == 1)) - } - - return cell - } - - func collectionView(collectionView: UICollectionView!, layout collectionViewLayout: UICollectionViewLayout!, sizeForItemAtIndexPath indexPath: NSIndexPath!) -> CGSize { - - if attachments.count > 1 { - return feedAttachmentImageSize - - } else { - return feedAttachmentBiggerImageSize - } - } - - func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAtIndex section: Int) -> UIEdgeInsets { - return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) - } - - func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) { - - let cell = collectionView.cellForItemAtIndexPath(indexPath) as! FeedMediaCell - - let transitionView = cell.imageView - tapMediaAction?(transitionView: transitionView, image: cell.imageView.image, attachments: attachments, index: indexPath.item) - } -} - diff --git a/Yep/Views/Cells/Feed/FeedCell.xib b/Yep/Views/Cells/Feed/FeedCell.xib deleted file mode 100644 index ef065e9d..00000000 --- a/Yep/Views/Cells/Feed/FeedCell.xib +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Yep/Views/Cells/Feed/FeedCellLayout.swift b/Yep/Views/Cells/Feed/FeedCellLayout.swift new file mode 100644 index 00000000..4adce52a --- /dev/null +++ b/Yep/Views/Cells/Feed/FeedCellLayout.swift @@ -0,0 +1,301 @@ +// +// FeedCellLayout.swift +// Yep +// +// Created by nixzhu on 15/12/17. +// Copyright © 2015年 Catch Inc. All rights reserved. +// + +import UIKit + +private let screenWidth: CGFloat = UIScreen.mainScreen().bounds.width + +struct FeedCellLayout { + + typealias Update = (layout: FeedCellLayout) -> Void + typealias Cache = (layout: FeedCellLayout?, update: Update) + + let height: CGFloat + + struct BasicLayout { + + func nicknameLabelFrameWhen(hasLogo hasLogo: Bool, hasSkill: Bool) -> CGRect { + + var frame = nicknameLabelFrame + frame.size.width -= hasLogo ? (18 + 10) : 0 + frame.size.width -= hasSkill ? (skillButtonFrame.width + 10) : 0 + + return frame + } + + let avatarImageViewFrame: CGRect + private let nicknameLabelFrame: CGRect + let skillButtonFrame: CGRect + + let messageTextViewFrame: CGRect + + let leftBottomLabelFrame: CGRect + let messageCountLabelFrame: CGRect + let discussionImageViewFrame: CGRect + + init(avatarImageViewFrame: CGRect, + nicknameLabelFrame: CGRect, + skillButtonFrame: CGRect, + messageTextViewFrame: CGRect, + leftBottomLabelFrame: CGRect, + messageCountLabelFrame: CGRect, + discussionImageViewFrame: CGRect) { + self.avatarImageViewFrame = avatarImageViewFrame + self.nicknameLabelFrame = nicknameLabelFrame + self.skillButtonFrame = skillButtonFrame + + self.messageTextViewFrame = messageTextViewFrame + + self.leftBottomLabelFrame = leftBottomLabelFrame + self.messageCountLabelFrame = messageCountLabelFrame + self.discussionImageViewFrame = discussionImageViewFrame + } + } + let basicLayout: BasicLayout + + struct BiggerImageLayout { + let biggerImageViewFrame: CGRect + + init(biggerImageViewFrame: CGRect) { + self.biggerImageViewFrame = biggerImageViewFrame + } + } + var biggerImageLayout: BiggerImageLayout? + + struct NormalImagesLayout { + + let imageView1Frame: CGRect + let imageView2Frame: CGRect + let imageView3Frame: CGRect + + init(imageView1Frame: CGRect, imageView2Frame: CGRect, imageView3Frame: CGRect) { + self.imageView1Frame = imageView1Frame + self.imageView2Frame = imageView2Frame + self.imageView3Frame = imageView3Frame + } + } + var normalImagesLayout: NormalImagesLayout? + + struct AnyImagesLayout { + + let mediaCollectionViewFrame: CGRect + + init(mediaCollectionViewFrame: CGRect) { + self.mediaCollectionViewFrame = mediaCollectionViewFrame + } + } + var anyImagesLayout: AnyImagesLayout? + + struct GithubRepoLayout { + let githubRepoContainerViewFrame: CGRect + } + var githubRepoLayout: GithubRepoLayout? + + struct DribbbleShotLayout { + let dribbbleShotContainerViewFrame: CGRect + } + var dribbbleShotLayout: DribbbleShotLayout? + + struct AudioLayout { + let voiceContainerViewFrame: CGRect + } + var audioLayout: AudioLayout? + + struct LocationLayout { + let locationContainerViewFrame: CGRect + } + var locationLayout: LocationLayout? + + // MARK: - Init + + init(height: CGFloat, basicLayout: BasicLayout) { + self.height = height + self.basicLayout = basicLayout + } + + init(feed: DiscoveredFeed) { + + let height: CGFloat + + switch feed.kind { + + case .Text: + height = FeedBasicCell.heightOfFeed(feed) + + case .Image: + if feed.imageAttachmentsCount == 1 { + height = FeedBiggerImageCell.heightOfFeed(feed) + + } else if feed.imageAttachmentsCount <= 3 { + height = FeedNormalImagesCell.heightOfFeed(feed) + + } else { + height = FeedAnyImagesCell.heightOfFeed(feed) + } + + case .GithubRepo: + height = FeedGithubRepoCell.heightOfFeed(feed) + + case .DribbbleShot: + height = FeedDribbbleShotCell.heightOfFeed(feed) + + case .Audio: + height = FeedVoiceCell.heightOfFeed(feed) + + case .Location: + height = FeedLocationCell.heightOfFeed(feed) + + default: + height = FeedBasicCell.heightOfFeed(feed) + } + + self.height = height + + let avatarImageViewFrame = CGRect(x: 15, y: 10, width: 40, height: 40) + + let nicknameLabelFrame: CGRect + let skillButtonFrame: CGRect + + if let skill = feed.skill { + + let rect = skill.localName.boundingRectWithSize(CGSize(width: 320, height: CGFloat(FLT_MAX)), options: [.UsesLineFragmentOrigin, .UsesFontLeading], attributes: YepConfig.FeedBasicCell.skillTextAttributes, context: nil) + + let skillButtonWidth = ceil(rect.width) + 20 + + skillButtonFrame = CGRect(x: screenWidth - skillButtonWidth - 15, y: 19, width: skillButtonWidth, height: 22) + + let nicknameLabelWidth = screenWidth - 65 - 15 + nicknameLabelFrame = CGRect(x: 65, y: 21, width: nicknameLabelWidth, height: 18) + + } else { + let nicknameLabelWidth = screenWidth - 65 - 15 + nicknameLabelFrame = CGRect(x: 65, y: 21, width: nicknameLabelWidth, height: 18) + skillButtonFrame = CGRectZero + } + + let _rect1 = feed.body.boundingRectWithSize(CGSize(width: FeedBasicCell.messageTextViewMaxWidth, height: CGFloat(FLT_MAX)), options: [.UsesLineFragmentOrigin, .UsesFontLeading], attributes: YepConfig.FeedBasicCell.textAttributes, context: nil) + + let messageTextViewHeight = ceil(_rect1.height) + let messageTextViewFrame = CGRect(x: 65, y: 54, width: screenWidth - 65 - 15, height: messageTextViewHeight) + + let leftBottomLabelOriginY = height - 17 - 10 + let leftBottomLabelFrame = CGRect(x: 65, y: leftBottomLabelOriginY, width: 200, height: 17) + + let messagesCountString = "\(feed.messagesCount)" + + let _rect2 = messagesCountString.boundingRectWithSize(CGSize(width: 320, height: CGFloat(FLT_MAX)), options: [.UsesLineFragmentOrigin, .UsesFontLeading], attributes: YepConfig.FeedBasicCell.bottomLabelsTextAttributes, context: nil) + + let _width = ceil(_rect2.width) + let messageCountLabelFrame = CGRect(x: screenWidth - _width - 45 - 8, y: leftBottomLabelOriginY, width: _width, height: 19) + + let discussionImageViewFrame = CGRect(x: screenWidth - 30 - 15, y: leftBottomLabelOriginY - 1, width: 30, height: 19) + + let basicLayout = FeedCellLayout.BasicLayout( + avatarImageViewFrame: avatarImageViewFrame, + nicknameLabelFrame: nicknameLabelFrame, + skillButtonFrame: skillButtonFrame, + messageTextViewFrame: messageTextViewFrame, + leftBottomLabelFrame: leftBottomLabelFrame, + messageCountLabelFrame: messageCountLabelFrame, + discussionImageViewFrame: discussionImageViewFrame + ) + + self.basicLayout = basicLayout + + let beginY = messageTextViewFrame.origin.y + messageTextViewFrame.height + 15 + + switch feed.kind { + + case .Text: + break + + case .Image: + + if feed.imageAttachmentsCount == 1 { + + let biggerImageViewFrame = CGRect(origin: CGPoint(x: 65, y: beginY), size: YepConfig.FeedBiggerImageCell.imageSize) + + let biggerImageLayout = FeedCellLayout.BiggerImageLayout(biggerImageViewFrame: biggerImageViewFrame) + + self.biggerImageLayout = biggerImageLayout + + } else if feed.imageAttachmentsCount <= 3 { + + let x1 = 65 + (YepConfig.FeedNormalImagesCell.imageSize.width + 5) * 0 + let imageView1Frame = CGRect(origin: CGPoint(x: x1, y: beginY), size: YepConfig.FeedNormalImagesCell.imageSize) + + let x2 = 65 + (YepConfig.FeedNormalImagesCell.imageSize.width + 5) * 1 + let imageView2Frame = CGRect(origin: CGPoint(x: x2, y: beginY), size: YepConfig.FeedNormalImagesCell.imageSize) + + let x3 = 65 + (YepConfig.FeedNormalImagesCell.imageSize.width + 5) * 2 + let imageView3Frame = CGRect(origin: CGPoint(x: x3, y: beginY), size: YepConfig.FeedNormalImagesCell.imageSize) + + let normalImagesLayout = FeedCellLayout.NormalImagesLayout(imageView1Frame: imageView1Frame, imageView2Frame: imageView2Frame, imageView3Frame: imageView3Frame) + + self.normalImagesLayout = normalImagesLayout + + } else { + let height = feedAttachmentImageSize.height + let mediaCollectionViewFrame = CGRect(x: 0, y: beginY, width: screenWidth, height: height) + + let anyImagesLayout = FeedCellLayout.AnyImagesLayout(mediaCollectionViewFrame: mediaCollectionViewFrame) + + self.anyImagesLayout = anyImagesLayout + } + + case .GithubRepo: + + let height: CGFloat = leftBottomLabelFrame.origin.y - beginY - 15 + let githubRepoContainerViewFrame = CGRect(x: 65, y: beginY, width: screenWidth - 65 - 60, height: height) + + let githubRepoLayout = FeedCellLayout.GithubRepoLayout(githubRepoContainerViewFrame: githubRepoContainerViewFrame) + + self.githubRepoLayout = githubRepoLayout + + case .DribbbleShot: + + let height: CGFloat = leftBottomLabelFrame.origin.y - beginY - 15 + let dribbbleShotContainerViewFrame = CGRect(x: 65, y: beginY, width: screenWidth - 65 - 60, height: height) + + let dribbbleShotLayout = FeedCellLayout.DribbbleShotLayout(dribbbleShotContainerViewFrame: dribbbleShotContainerViewFrame) + + self.dribbbleShotLayout = dribbbleShotLayout + + case .Audio: + + if let attachment = feed.attachment { + if case let .Audio(audioInfo) = attachment { + let timeLengthString = String(format: "%.1f\"", audioInfo.duration) + let rect = timeLengthString.boundingRectWithSize(CGSize(width: 320, height: CGFloat(FLT_MAX)), options: [.UsesLineFragmentOrigin, .UsesFontLeading], attributes: YepConfig.FeedBasicCell.voiceTimeLengthTextAttributes, context: nil) + + let width = 7 + 30 + 5 + CGFloat(audioInfo.sampleValues.count) * 3 + 5 + rect.width + 5 + let y = beginY + 2 + + let voiceContainerViewFrame = CGRect(x: 65, y: y, width: width, height: 40) + + let audioLayout = FeedCellLayout.AudioLayout(voiceContainerViewFrame: voiceContainerViewFrame) + + self.audioLayout = audioLayout + } + } + + case .Location: + + let height: CGFloat = leftBottomLabelFrame.origin.y - beginY - 15 + let locationContainerViewFrame = CGRect(x: 65, y: beginY, width: screenWidth - 65 - 60, height: height) + + let locationLayout = FeedCellLayout.LocationLayout(locationContainerViewFrame: locationContainerViewFrame) + + self.locationLayout = locationLayout + + default: + break + } + } +} + diff --git a/Yep/Views/Cells/Feed/FeedDribbbleShotCell.swift b/Yep/Views/Cells/Feed/FeedDribbbleShotCell.swift new file mode 100644 index 00000000..217ef1a6 --- /dev/null +++ b/Yep/Views/Cells/Feed/FeedDribbbleShotCell.swift @@ -0,0 +1,156 @@ +// +// FeedDribbbleShotCell.swift +// Yep +// +// Created by nixzhu on 15/12/17. +// Copyright © 2015年 Catch Inc. All rights reserved. +// + +import UIKit +import Ruler + +private let screenWidth: CGFloat = UIScreen.mainScreen().bounds.width +private let dribbbleShotHeight: CGFloat = Ruler.iPhoneHorizontal(160, 200, 220).value + +class FeedDribbbleShotCell: FeedBasicCell { + + var tapDribbbleShotLinkAction: (NSURL -> Void)? + var tapDribbbleShotMediaAction: ((transitionView: UIView, image: UIImage?, imageURL: NSURL, linkURL: NSURL) -> Void)? + + lazy var logoImageView: UIImageView = { + let imageView = UIImageView() + imageView.image = UIImage(named: SocialAccount.Dribbble.iconName) + imageView.frame = CGRect(x: 0, y: 0, width: 18, height: 18) + imageView.tintColor = SocialAccount.Dribbble.tintColor + return imageView + }() + + lazy var mediaContainerView: FeedMediaContainerView = { + let view = FeedMediaContainerView() + view.frame = CGRect(x: 0, y: 0, width: 200, height: 200) + return view + }() + + lazy var socialWorkBorderImageView: UIImageView = { + let imageView = UIImageView() + imageView.image = UIImage(named: "social_work_border") + return imageView + }() + + lazy var halfMaskImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "social_media_image_mask")) + return imageView + }() + + override init(style: UITableViewCellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + + contentView.addSubview(logoImageView) + contentView.addSubview(mediaContainerView) + contentView.addSubview(socialWorkBorderImageView) + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func setSelected(selected: Bool, animated: Bool) { + super.setSelected(selected, animated: animated) + + // Configure the view for the selected state + } + + override class func heightOfFeed(feed: DiscoveredFeed) -> CGFloat { + + let height = super.heightOfFeed(feed) + (dribbbleShotHeight + 15) + + return ceil(height) + } + + override func prepareForReuse() { + super.prepareForReuse() + + mediaContainerView.mediaImageView.image = nil + } + + override func configureWithFeed(feed: DiscoveredFeed, layoutCache: FeedCellLayout.Cache, needShowSkill: Bool) { + + var _newLayout: FeedCellLayout? + super.configureWithFeed(feed, layoutCache: (layout: layoutCache.layout, update: { newLayout in + _newLayout = newLayout + }), needShowSkill: needShowSkill) + + if needShowSkill, let _ = feed.skill { + logoImageView.frame.origin.x = skillButton.frame.origin.x - 10 - 18 + logoImageView.frame.origin.y = nicknameLabel.frame.origin.y + + } else { + logoImageView.frame.origin.x = screenWidth - 18 - 15 + logoImageView.frame.origin.y = nicknameLabel.frame.origin.y + } + nicknameLabel.frame.size.width -= logoImageView.bounds.width + 10 + + if let attachment = feed.attachment { + if case let .Dribbble(dribbbleShot) = attachment { + if let URL = NSURL(string: dribbbleShot.imageURLString) { + mediaContainerView.mediaImageView.kf_showIndicatorWhenLoading = true + mediaContainerView.mediaImageView.kf_setImageWithURL(URL, placeholderImage: nil, optionsInfo: MediaOptionsInfos) + } + + mediaContainerView.linkContainerView.textLabel.text = dribbbleShot.title + } + } + + mediaContainerView.tapMediaAction = { [weak self] mediaImageView in + + guard let attachment = feed.attachment else { + return + } + + if case .DribbbleShot = feed.kind { + if case let .Dribbble(shot) = attachment, let imageURL = NSURL(string: shot.imageURLString), let linkURL = NSURL(string: shot.htmlURLString) { + self?.tapDribbbleShotMediaAction?(transitionView: mediaImageView, image: mediaImageView.image, imageURL: imageURL, linkURL: linkURL) + } + } + } + + mediaContainerView.linkContainerView.tapAction = { [weak self] in + + guard let attachment = feed.attachment else { + return + } + + if case .DribbbleShot = feed.kind { + if case let .Dribbble(shot) = attachment, let URL = NSURL(string: shot.htmlURLString) { + self?.tapDribbbleShotLinkAction?(URL) + } + } + } + + if let dribbbleShotLayout = layoutCache.layout?.dribbbleShotLayout { + mediaContainerView.frame = dribbbleShotLayout.dribbbleShotContainerViewFrame + socialWorkBorderImageView.frame = mediaContainerView.frame + + } else { + let y = messageTextView.frame.origin.y + messageTextView.frame.height + 15 + let height: CGFloat = leftBottomLabel.frame.origin.y - y - 15 + mediaContainerView.frame = CGRect(x: 65, y: y, width: screenWidth - 65 - 60, height: height) + socialWorkBorderImageView.frame = mediaContainerView.frame + } + mediaContainerView.layoutIfNeeded() + + halfMaskImageView.frame = mediaContainerView.mediaImageView.bounds + mediaContainerView.mediaImageView.maskView = halfMaskImageView + + if layoutCache.layout == nil { + + let dribbbleShotLayout = FeedCellLayout.DribbbleShotLayout(dribbbleShotContainerViewFrame: mediaContainerView.frame) + _newLayout?.dribbbleShotLayout = dribbbleShotLayout + + if let newLayout = _newLayout { + layoutCache.update(layout: newLayout) + } + } + } +} + diff --git a/Yep/Views/Cells/Feed/FeedGithubRepoCell.swift b/Yep/Views/Cells/Feed/FeedGithubRepoCell.swift new file mode 100644 index 00000000..f9a477ba --- /dev/null +++ b/Yep/Views/Cells/Feed/FeedGithubRepoCell.swift @@ -0,0 +1,121 @@ +// +// FeedGithubRepoCell.swift +// Yep +// +// Created by nixzhu on 15/12/17. +// Copyright © 2015年 Catch Inc. All rights reserved. +// + +import UIKit + +private let screenWidth: CGFloat = UIScreen.mainScreen().bounds.width + +class FeedGithubRepoCell: FeedBasicCell { + + var tapGithubRepoLinkAction: (NSURL -> Void)? + + lazy var logoImageView: UIImageView = { + let imageView = UIImageView() + imageView.image = UIImage(named: SocialAccount.Github.iconName) + imageView.frame = CGRect(x: 0, y: 0, width: 18, height: 18) + imageView.tintColor = SocialAccount.Github.tintColor + return imageView + }() + + lazy var githubRepoContainerView: FeedGithubRepoContainerView = { + let view = FeedGithubRepoContainerView() + view.frame = CGRect(x: 0, y: 0, width: 200, height: 200) + return view + }() + + lazy var socialWorkBorderImageView: UIImageView = { + let imageView = UIImageView() + imageView.image = UIImage(named: "social_work_border") + return imageView + }() + + override init(style: UITableViewCellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + + contentView.addSubview(logoImageView) + contentView.addSubview(githubRepoContainerView) + contentView.addSubview(socialWorkBorderImageView) + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func setSelected(selected: Bool, animated: Bool) { + super.setSelected(selected, animated: animated) + + // Configure the view for the selected state + } + + override class func heightOfFeed(feed: DiscoveredFeed) -> CGFloat { + + let height = super.heightOfFeed(feed) + (80 + 15) + + return ceil(height) + } + + override func configureWithFeed(feed: DiscoveredFeed, layoutCache: FeedCellLayout.Cache, needShowSkill: Bool) { + + var _newLayout: FeedCellLayout? + super.configureWithFeed(feed, layoutCache: (layout: layoutCache.layout, update: { newLayout in + _newLayout = newLayout + }), needShowSkill: needShowSkill) + + if needShowSkill, let _ = feed.skill { + logoImageView.frame.origin.x = skillButton.frame.origin.x - 10 - 18 + logoImageView.frame.origin.y = nicknameLabel.frame.origin.y + + } else { + logoImageView.frame.origin.x = screenWidth - 18 - 15 + logoImageView.frame.origin.y = nicknameLabel.frame.origin.y + } + nicknameLabel.frame.size.width -= logoImageView.bounds.width + 10 + + if let attachment = feed.attachment { + if case let .Github(githubRepo) = attachment { + githubRepoContainerView.nameLabel.text = githubRepo.name + githubRepoContainerView.descriptionLabel.text = githubRepo.description + } + } + + githubRepoContainerView.tapAction = { [weak self] in + guard let attachment = feed.attachment else { + return + } + + if case .GithubRepo = feed.kind { + if case let .Github(repo) = attachment, let URL = NSURL(string: repo.URLString) { + self?.tapGithubRepoLinkAction?(URL) + } + } + } + + if let githubRepoLayout = layoutCache.layout?.githubRepoLayout { + githubRepoContainerView.frame = githubRepoLayout.githubRepoContainerViewFrame + socialWorkBorderImageView.frame = githubRepoContainerView.frame + + } else { + let y = messageTextView.frame.origin.y + messageTextView.frame.height + 15 + let height: CGFloat = leftBottomLabel.frame.origin.y - y - 15 + githubRepoContainerView.frame = CGRect(x: 65, y: y, width: screenWidth - 65 - 60, height: height) + + socialWorkBorderImageView.frame = githubRepoContainerView.frame + } + + if layoutCache.layout == nil { + + let githubRepoLayout = FeedCellLayout.GithubRepoLayout(githubRepoContainerViewFrame: githubRepoContainerView.frame) + _newLayout?.githubRepoLayout = githubRepoLayout + + if let newLayout = _newLayout { + layoutCache.update(layout: newLayout) + } + } + } +} + diff --git a/Yep/Views/Cells/Feed/FeedLocationCell.swift b/Yep/Views/Cells/Feed/FeedLocationCell.swift new file mode 100644 index 00000000..af71b36f --- /dev/null +++ b/Yep/Views/Cells/Feed/FeedLocationCell.swift @@ -0,0 +1,129 @@ +// +// FeedLocationCell.swift +// Yep +// +// Created by nixzhu on 15/12/17. +// Copyright © 2015年 Catch Inc. All rights reserved. +// + +import UIKit +import MapKit + +private let screenWidth: CGFloat = UIScreen.mainScreen().bounds.width + +class FeedLocationCell: FeedBasicCell { + + var tapLocationAction: ((locationName: String, locationCoordinate: CLLocationCoordinate2D) -> Void)? + + lazy var locationContainerView: FeedLocationContainerView = { + let view = FeedLocationContainerView() + view.frame = CGRect(x: 0, y: 0, width: 200, height: 200) + return view + }() + + lazy var socialWorkBorderImageView: UIImageView = { + let imageView = UIImageView() + imageView.image = UIImage(named: "social_work_border") + return imageView + }() + + lazy var halfMaskImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "social_media_image_mask")) + return imageView + }() + + override func prepareForReuse() { + super.prepareForReuse() + + locationContainerView.mapImageView.image = nil + } + + override init(style: UITableViewCellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + + contentView.addSubview(locationContainerView) + contentView.addSubview(socialWorkBorderImageView) + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func setSelected(selected: Bool, animated: Bool) { + super.setSelected(selected, animated: animated) + + // Configure the view for the selected state + } + + override class func heightOfFeed(feed: DiscoveredFeed) -> CGFloat { + + let height = super.heightOfFeed(feed) + (110 + 15) + + return ceil(height) + } + + override func configureWithFeed(feed: DiscoveredFeed, layoutCache: FeedCellLayout.Cache, needShowSkill: Bool) { + + var _newLayout: FeedCellLayout? + super.configureWithFeed(feed, layoutCache: (layout: layoutCache.layout, update: { newLayout in + _newLayout = newLayout + }), needShowSkill: needShowSkill) + + + if let attachment = feed.attachment { + if case let .Location(locationInfo) = attachment { + + let location = CLLocation(latitude: locationInfo.latitude, longitude: locationInfo.longitude) + let size = CGSize(width: UIScreen.mainScreen().bounds.width - 65 - 60, height: 110 - locationContainerView.nameLabel.bounds.height) + locationContainerView.mapImageView.yep_showActivityIndicatorWhenLoading = true + locationContainerView.mapImageView.yep_setImageOfLocation(location, withSize: size) + + if locationInfo.name.isEmpty { + locationContainerView.nameLabel.text = NSLocalizedString("Unknown location", comment: "") + + } else { + locationContainerView.nameLabel.text = locationInfo.name + } + } + } + + locationContainerView.mapImageView.maskView = halfMaskImageView + + locationContainerView.tapAction = { [weak self] in + guard let attachment = feed.attachment else { + return + } + + if case .Location = feed.kind { + if case let .Location(locationInfo) = attachment { + self?.tapLocationAction?(locationName: locationInfo.name, locationCoordinate: locationInfo.coordinate) + } + } + } + + if let locationLayout = layoutCache.layout?.locationLayout { + locationContainerView.frame = locationLayout.locationContainerViewFrame + socialWorkBorderImageView.frame = locationContainerView.frame + + } else { + let y = messageTextView.frame.origin.y + messageTextView.frame.height + 15 + let height: CGFloat = leftBottomLabel.frame.origin.y - y - 15 + locationContainerView.frame = CGRect(x: 65, y: y, width: screenWidth - 65 - 60, height: height) + socialWorkBorderImageView.frame = locationContainerView.frame + } + locationContainerView.layoutIfNeeded() + + halfMaskImageView.frame = locationContainerView.mapImageView.bounds + locationContainerView.mapImageView.maskView = halfMaskImageView + + if layoutCache.layout == nil { + + let locationLayout = FeedCellLayout.LocationLayout(locationContainerViewFrame: locationContainerView.frame) + _newLayout?.locationLayout = locationLayout + + if let newLayout = _newLayout { + layoutCache.update(layout: newLayout) + } + } + } +} diff --git a/Yep/Views/Cells/Feed/FeedNormalImagesCell.swift b/Yep/Views/Cells/Feed/FeedNormalImagesCell.swift new file mode 100644 index 00000000..29eec605 --- /dev/null +++ b/Yep/Views/Cells/Feed/FeedNormalImagesCell.swift @@ -0,0 +1,147 @@ +// +// FeedNormalImagesCell.swift +// Yep +// +// Created by nixzhu on 15/12/17. +// Copyright © 2015年 Catch Inc. All rights reserved. +// + +import UIKit + +class FeedNormalImagesCell: FeedBasicCell { + + var tapMediaAction: FeedTapMediaAction? + + private func createImageViewWithFrame(frame: CGRect) -> UIImageView { + let imageView = UIImageView() + imageView.contentMode = .ScaleAspectFill + imageView.clipsToBounds = true + imageView.frame = CGRect(origin: CGPoint(x: 65, y: 0), size: YepConfig.FeedNormalImagesCell.imageSize) + imageView.frame = frame + imageView.layer.borderColor = UIColor.yepBorderColor().CGColor + imageView.layer.borderWidth = 1 + + imageView.userInteractionEnabled = true + let tap = UITapGestureRecognizer(target: self, action: "tap:") + imageView.addGestureRecognizer(tap) + + return imageView + } + + lazy var imageView1: UIImageView = { + let x = 65 + let frame = CGRect(origin: CGPoint(x: x, y: 0), size: YepConfig.FeedNormalImagesCell.imageSize) + let imageView = self.createImageViewWithFrame(frame) + + return imageView + }() + + lazy var imageView2: UIImageView = { + let x = 65 + (YepConfig.FeedNormalImagesCell.imageSize.width + 5) + let frame = CGRect(origin: CGPoint(x: x, y: 0), size: YepConfig.FeedNormalImagesCell.imageSize) + let imageView = self.createImageViewWithFrame(frame) + + return imageView + }() + + lazy var imageView3: UIImageView = { + let x = 65 + (YepConfig.FeedNormalImagesCell.imageSize.width + 5) * 2 + let frame = CGRect(origin: CGPoint(x: x, y: 0), size: YepConfig.FeedNormalImagesCell.imageSize) + let imageView = self.createImageViewWithFrame(frame) + + return imageView + }() + + override class func heightOfFeed(feed: DiscoveredFeed) -> CGFloat { + + let height = super.heightOfFeed(feed) + YepConfig.FeedNormalImagesCell.imageSize.height + 15 + + return ceil(height) + } + + var imageViews: [UIImageView] = [] + + override init(style: UITableViewCellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + + contentView.addSubview(imageView1) + contentView.addSubview(imageView2) + contentView.addSubview(imageView3) + + imageViews = [imageView1, imageView2, imageView3] + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func setSelected(selected: Bool, animated: Bool) { + super.setSelected(selected, animated: animated) + + // Configure the view for the selected state + } + + override func prepareForReuse() { + super.prepareForReuse() + + imageView1.image = nil + imageView2.image = nil + imageView3.image = nil + } + + override func configureWithFeed(feed: DiscoveredFeed, layoutCache: FeedCellLayout.Cache, needShowSkill: Bool) { + + var _newLayout: FeedCellLayout? + super.configureWithFeed(feed, layoutCache: (layout: layoutCache.layout, update: { newLayout in + _newLayout = newLayout + }), needShowSkill: needShowSkill) + + if let normalImagesLayout = layoutCache.layout?.normalImagesLayout { + imageView1.frame = normalImagesLayout.imageView1Frame + imageView2.frame = normalImagesLayout.imageView2Frame + imageView3.frame = normalImagesLayout.imageView3Frame + + } else { + imageViews.forEach({ + $0.frame.origin.y = messageTextView.frame.origin.y + messageTextView.frame.height + 15 + }) + } + + if let attachments = feed.imageAttachments { + + for i in 0.. Void)? + var audioPlayedDuration: NSTimeInterval = 0 { + willSet { + updateVoiceContainerView() + } + } + private func updateVoiceContainerView() { + + guard let feed = feed, realm = try? Realm(), feedAudio = FeedAudio.feedAudioWithFeedID(feed.id, inRealm: realm) else { + return + } + + if let (audioDuration, audioSamples) = feedAudio.audioMetaInfo { + + voiceContainerView.voiceSampleView.samples = audioSamples + voiceContainerView.voiceSampleView.progress = CGFloat(audioPlayedDuration / audioDuration) + } + + if let playingFeedAudio = YepAudioService.sharedManager.playingFeedAudio where playingFeedAudio.feedID == feedAudio.feedID, let audioPlayer = YepAudioService.sharedManager.audioPlayer where audioPlayer.playing { + audioPlaying = true + } else { + audioPlaying = false + } + } + + override init(style: UITableViewCellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + + contentView.addSubview(voiceContainerView) + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func setSelected(selected: Bool, animated: Bool) { + super.setSelected(selected, animated: animated) + + // Configure the view for the selected state + } + + override class func heightOfFeed(feed: DiscoveredFeed) -> CGFloat { + + let height = super.heightOfFeed(feed) + (44 + 15) + + return ceil(height) + } + + override func configureWithFeed(feed: DiscoveredFeed, layoutCache: FeedCellLayout.Cache, needShowSkill: Bool) { + + var _newLayout: FeedCellLayout? + super.configureWithFeed(feed, layoutCache: (layout: layoutCache.layout, update: { newLayout in + _newLayout = newLayout + }), needShowSkill: needShowSkill) + + if let attachment = feed.attachment { + if case let .Audio(audioInfo) = attachment { + + voiceContainerView.voiceSampleView.sampleColor = UIColor.leftWaveColor() + voiceContainerView.voiceSampleView.samples = audioInfo.sampleValues + + let timeLengthString = String(format: "%.1f\"", audioInfo.duration) + voiceContainerView.timeLengthLabel.text = timeLengthString + + if let audioLayout = layoutCache.layout?.audioLayout { + voiceContainerView.frame = audioLayout.voiceContainerViewFrame + + } else { + let rect = timeLengthString.boundingRectWithSize(CGSize(width: 320, height: CGFloat(FLT_MAX)), options: [.UsesLineFragmentOrigin, .UsesFontLeading], attributes: YepConfig.FeedBasicCell.voiceTimeLengthTextAttributes, context: nil) + + let width = 7 + 30 + 5 + CGFloat(audioInfo.sampleValues.count) * 3 + 5 + rect.width + 5 + let y = messageTextView.frame.origin.y + messageTextView.frame.height + 15 + 2 + voiceContainerView.frame = CGRect(x: 65, y: y, width: width, height: 40) + } + + if let realm = try? Realm() { + + let feedAudio = FeedAudio.feedAudioWithFeedID(audioInfo.feedID, inRealm: realm) + + if let feedAudio = feedAudio, playingFeedAudio = YepAudioService.sharedManager.playingFeedAudio, audioPlayer = YepAudioService.sharedManager.audioPlayer { + audioPlaying = (feedAudio.feedID == playingFeedAudio.feedID) && audioPlayer.playing + } else { + audioPlaying = false + } + + let needDownload = (feedAudio == nil) || (feedAudio?.fileName ?? "").isEmpty + + if needDownload { + if let URL = NSURL(string: audioInfo.URLString) { + YepDownloader.downloadDataFromURL(URL, reportProgress: { progress in + println("audio progress: \(progress)") + + }, finishedAction: { data in + println("audio finish: \(data.length)") + + dispatch_async(dispatch_get_main_queue()) { + if let realm = try? Realm() { + + var feedAudio = FeedAudio.feedAudioWithFeedID(audioInfo.feedID, inRealm: realm) + + if feedAudio == nil { + let newFeedAudio = FeedAudio() + newFeedAudio.feedID = audioInfo.feedID + newFeedAudio.URLString = audioInfo.URLString + newFeedAudio.metadata = audioInfo.metaData + + let _ = try? realm.write { + realm.add(newFeedAudio) + } + + feedAudio = newFeedAudio + } + + if let feedAudio = feedAudio where feedAudio.fileName.isEmpty { + + let fileName = NSUUID().UUIDString + if let _ = NSFileManager.saveMessageAudioData(data, withName: fileName) { + let _ = try? realm.write { + feedAudio.fileName = fileName + } + } + } + } + } + }) + } + } + + voiceContainerView.playOrPauseAudioAction = { [weak self] in + if let strongSelf = self { + strongSelf.playOrPauseAudioAction?(strongSelf) + } + } + } + } + } + + if layoutCache.layout == nil { + + let audioLayout = FeedCellLayout.AudioLayout(voiceContainerViewFrame: voiceContainerView.frame) + _newLayout?.audioLayout = audioLayout + + if let newLayout = _newLayout { + layoutCache.update(layout: newLayout) + } + } + } +} + diff --git a/Yep/Views/Cells/FeedBasic/FeedBasicCell.swift b/Yep/Views/Cells/FeedBasic/FeedBasicCell.swift deleted file mode 100644 index 294bce55..00000000 --- a/Yep/Views/Cells/FeedBasic/FeedBasicCell.swift +++ /dev/null @@ -1,130 +0,0 @@ -// -// FeedBasicCell.swift -// Yep -// -// Created by nixzhu on 15/11/20. -// Copyright © 2015年 Catch Inc. All rights reserved. -// - -import UIKit - -class FeedBasicCell: UITableViewCell { - - @IBOutlet weak var avatarImageView: UIImageView! - @IBOutlet weak var nicknameLabel: UILabel! - - @IBOutlet weak var skillButton: UIButton! - - @IBOutlet weak var messageTextView: FeedTextView! - @IBOutlet weak var messageTextViewHeightConstraint: NSLayoutConstraint! - - @IBOutlet weak var leftBottomLabel: UILabel! - - @IBOutlet weak var messageCountLabel: UILabel! - - var tapAvatarAction: (UITableViewCell -> Void)? - var tapSkillAction: (UITableViewCell -> Void)? - - var touchesBeganAction: (UITableViewCell -> Void)? - var touchesEndedAction: (UITableViewCell -> Void)? - var touchesCancelledAction: (UITableViewCell -> Void)? - - override func prepareForReuse() { - super.prepareForReuse() - - messageTextView.text = nil - messageTextView.attributedText = nil - } - - override func awakeFromNib() { - super.awakeFromNib() - - nicknameLabel.textColor = UIColor.yepTintColor() - messageTextView.textColor = UIColor.yepMessageColor() - leftBottomLabel.textColor = UIColor.grayColor() - messageCountLabel.textColor = UIColor.yepTintColor() - skillButton.setTitleColor(UIColor.yepTintColor(), forState: .Normal) - - messageTextView.font = UIFont.feedMessageFont() - messageTextView.textContainer.lineFragmentPadding = 0 - messageTextView.textContainerInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) - messageTextView.dataDetectorTypes = .Link - - skillButton.titleLabel?.font = UIFont.feedSkillFont() - - let tapAvatar = UITapGestureRecognizer(target: self, action: "tapAvatar:") - avatarImageView.userInteractionEnabled = true - avatarImageView.addGestureRecognizer(tapAvatar) - - skillButton.addTarget(self, action: "tapSkill:", forControlEvents: .TouchUpInside) - - messageTextView.touchesBeganAction = { [weak self] in - if let strongSelf = self { - strongSelf.touchesBeganAction?(strongSelf) - } - } - messageTextView.touchesEndedAction = { [weak self] in - if let strongSelf = self { - if strongSelf.editing { - return - } - strongSelf.touchesEndedAction?(strongSelf) - } - } - messageTextView.touchesCancelledAction = { [weak self] in - if let strongSelf = self { - strongSelf.touchesCancelledAction?(strongSelf) - } - } - } - - override func setSelected(selected: Bool, animated: Bool) { - super.setSelected(selected, animated: animated) - - // Configure the view for the selected state - } - - private func calHeightOfMessageTextView() { - - let rect = messageTextView.text.boundingRectWithSize(CGSize(width: FeedCell.messageTextViewMaxWidth, height: CGFloat(FLT_MAX)), options: [.UsesLineFragmentOrigin, .UsesFontLeading], attributes: YepConfig.FeedBasicCell.textAttributes, context: nil) - messageTextViewHeightConstraint.constant = ceil(rect.height) - } - - func configureWithFeed(feed: DiscoveredFeed, needShowSkill: Bool) { - - messageTextView.text = "\u{200B}\(feed.body)" // ref http://stackoverflow.com/a/25994821 - - calHeightOfMessageTextView() - - if needShowSkill, let skill = feed.skill { - skillButton.setTitle(skill.localName, forState: .Normal) - skillButton.hidden = false - - } else { - skillButton.hidden = true - } - - let plainAvatar = PlainAvatar(avatarURLString: feed.creator.avatarURLString, avatarStyle: nanoAvatarStyle) - avatarImageView.navi_setAvatar(plainAvatar, withFadeTransitionDuration: avatarFadeTransitionDuration) - - nicknameLabel.text = feed.creator.nickname - - leftBottomLabel.text = feed.timeAndDistanceString - - messageCountLabel.text = "\(feed.messagesCount)" - messageCountLabel.hidden = (feed.messagesCount == 0) - } - - // MARK: Actions - - func tapAvatar(sender: UITapGestureRecognizer) { - - tapAvatarAction?(self) - } - - func tapSkill(sender: AnyObject) { - - tapSkillAction?(self) - } -} - diff --git a/Yep/Views/Cells/FeedMedia/FeedMediaCell.swift b/Yep/Views/Cells/FeedMedia/FeedMediaCell.swift index cb14b846..47b54425 100644 --- a/Yep/Views/Cells/FeedMedia/FeedMediaCell.swift +++ b/Yep/Views/Cells/FeedMedia/FeedMediaCell.swift @@ -19,7 +19,7 @@ class FeedMediaCell: UICollectionViewCell { super.awakeFromNib() imageView.backgroundColor = YepConfig.FeedMedia.backgroundColor - imageView.layer.minificationFilter = kCAFilterLinear +// imageView.layer.minificationFilter = kCAFilterLinear contentView.backgroundColor = UIColor.clearColor() } diff --git a/Yep/Views/Cells/FeedSocialWork/FeedSocialWorkCell.swift b/Yep/Views/Cells/FeedSocialWork/FeedSocialWorkCell.swift deleted file mode 100644 index 574abfcb..00000000 --- a/Yep/Views/Cells/FeedSocialWork/FeedSocialWorkCell.swift +++ /dev/null @@ -1,374 +0,0 @@ -// -// FeedSocialWorkCell.swift -// Yep -// -// Created by nixzhu on 15/11/19. -// Copyright © 2015年 Catch Inc. All rights reserved. -// - -import UIKit -import Kingfisher -import Ruler -import RealmSwift -import MapKit - -private let dribbbleShotHeight: CGFloat = Ruler.iPhoneHorizontal(160, 200, 220).value - -class FeedSocialWorkCell: FeedBasicCell { - - @IBOutlet weak var logoImageView: UIImageView! - @IBOutlet weak var logoImageViewTrailingConstraint: NSLayoutConstraint! - - @IBOutlet weak var socialWorkContainerView: UIView! - - @IBOutlet weak var mediaContainerView: FeedMediaContainerView! - - @IBOutlet weak var githubRepoContainerView: FeedGithubRepoContainerView! - - @IBOutlet weak var voiceContainerView: FeedVoiceContainerView! - @IBOutlet weak var voiceContainerViewWidthConstraint: NSLayoutConstraint! - - @IBOutlet weak var locationContainerView: FeedLocationContainerView! - - @IBOutlet weak var socialWorkBorderImageView: UIImageView! - @IBOutlet weak var socialWorkContainerViewHeightConstraint: NSLayoutConstraint! - - lazy var halfMaskImageView: UIImageView = { - let imageView = UIImageView(image: UIImage(named: "social_media_image_mask")) - return imageView - }() - - var feed: DiscoveredFeed? - - var tapGithubRepoLinkAction: (NSURL -> Void)? - var tapDribbbleShotLinkAction: (NSURL -> Void)? - var tapDribbbleShotMediaAction: ((transitionView: UIView, image: UIImage?, imageURL: NSURL, linkURL: NSURL) -> Void)? - var tapLocationAction: ((locationName: String, locationCoordinate: CLLocationCoordinate2D) -> Void)? - - var audioPlaying: Bool = false { - willSet { - voiceContainerView.audioPlaying = newValue - } - } - var playOrPauseAudioAction: (FeedSocialWorkCell -> Void)? - var audioPlayedDuration: NSTimeInterval = 0 { - willSet { - updateVoiceContainerView() - } - } - private func updateVoiceContainerView() { - - guard let feed = feed, realm = try? Realm(), feedAudio = FeedAudio.feedAudioWithFeedID(feed.id, inRealm: realm) else { - return - } - - if let (audioDuration, audioSamples) = feedAudio.audioMetaInfo { - - voiceContainerView.voiceSampleView.samples = audioSamples - voiceContainerView.voiceSampleView.progress = CGFloat(audioPlayedDuration / audioDuration) - } - - if let playingFeedAudio = YepAudioService.sharedManager.playingFeedAudio where playingFeedAudio.feedID == feedAudio.feedID, let audioPlayer = YepAudioService.sharedManager.audioPlayer where audioPlayer.playing { - audioPlaying = true - } else { - audioPlaying = false - } - } - - static let messageTextViewMaxWidth: CGFloat = { - let maxWidth = UIScreen.mainScreen().bounds.width - (15 + 40 + 10 + 15) - return maxWidth - }() - - override func layoutSubviews() { - super.layoutSubviews() - - if feed?.hasSocialImage ?? false { - halfMaskImageView.frame = mediaContainerView.mediaImageView.bounds - } - - if feed?.hasMapImage ?? false { - halfMaskImageView.frame = locationContainerView.mapImageView.bounds - } - } - - override func prepareForReuse() { - super.prepareForReuse() - - mediaContainerView.mediaImageView.image = nil - locationContainerView.mapImageView.image = nil - } - - override func awakeFromNib() { - super.awakeFromNib() - - } - - override func setSelected(selected: Bool, animated: Bool) { - super.setSelected(selected, animated: animated) - - // Configure the view for the selected state - } - - class func heightOfFeed(feed: DiscoveredFeed) -> CGFloat { - - let rect = feed.body.boundingRectWithSize(CGSize(width: FeedSocialWorkCell.messageTextViewMaxWidth, height: CGFloat(FLT_MAX)), options: [.UsesLineFragmentOrigin, .UsesFontLeading], attributes: YepConfig.FeedBasicCell.textAttributes, context: nil) - - var height: CGFloat = ceil(rect.height) + 10 + 40 + 4 + 15 + 17 + 15 - switch feed.kind { - case .GithubRepo: - height += (80 + 15) - case .DribbbleShot: - height += (dribbbleShotHeight + 15) - case .Audio: - height += (44 + 15) - case .Location: - height += (110 + 15) - default: - break - } - - return ceil(height) - } - - override func configureWithFeed(feed: DiscoveredFeed, needShowSkill: Bool) { - super.configureWithFeed(feed, needShowSkill: needShowSkill) - - self.feed = feed - - if needShowSkill, let skill = feed.skill { - let rect = skill.localName.boundingRectWithSize(CGSize(width: 320, height: CGFloat(FLT_MAX)), options: [.UsesLineFragmentOrigin, .UsesFontLeading], attributes: YepConfig.FeedBasicCell.skillTextAttributes, context: nil) - logoImageViewTrailingConstraint.constant = 8 + (10 + rect.width + 10) + 15 - } else { - logoImageViewTrailingConstraint.constant = 15 - } - - if let - accountName = feed.kind.accountName, - socialAccount = SocialAccount(rawValue: accountName) { - logoImageView.image = UIImage(named: socialAccount.iconName) - logoImageView.tintColor = socialAccount.tintColor - logoImageView.hidden = false - - } else { - logoImageView.hidden = true - } - - var socialWorkImageURL: NSURL? - - switch feed.kind { - - case .GithubRepo: - - mediaContainerView.hidden = true - githubRepoContainerView.hidden = false - voiceContainerView.hidden = true - locationContainerView.hidden = true - socialWorkBorderImageView.hidden = false - - if let attachment = feed.attachment { - if case let .Github(githubRepo) = attachment { - githubRepoContainerView.nameLabel.text = githubRepo.name - githubRepoContainerView.descriptionLabel.text = githubRepo.description - } - } - - githubRepoContainerView.tapAction = { [weak self] in - guard let attachment = feed.attachment else { - return - } - - if case .GithubRepo = feed.kind { - if case let .Github(repo) = attachment, let URL = NSURL(string: repo.URLString) { - self?.tapGithubRepoLinkAction?(URL) - } - } - } - - socialWorkContainerViewHeightConstraint.constant = 80 - - case .DribbbleShot: - - mediaContainerView.hidden = false - githubRepoContainerView.hidden = true - voiceContainerView.hidden = true - locationContainerView.hidden = true - socialWorkBorderImageView.hidden = false - - if let attachment = feed.attachment { - if case let .Dribbble(dribbbleShot) = attachment { - socialWorkImageURL = NSURL(string: dribbbleShot.imageURLString) - mediaContainerView.linkContainerView.textLabel.text = dribbbleShot.title - } - } - - mediaContainerView.tapMediaAction = { [weak self] mediaImageView in - - guard let attachment = feed.attachment else { - return - } - - if case .DribbbleShot = feed.kind { - if case let .Dribbble(shot) = attachment, let imageURL = NSURL(string: shot.imageURLString), let linkURL = NSURL(string: shot.htmlURLString) { - self?.tapDribbbleShotMediaAction?(transitionView: mediaImageView, image: mediaImageView.image, imageURL: imageURL, linkURL: linkURL) - } - } - } - - mediaContainerView.linkContainerView.tapAction = { [weak self] in - - guard let attachment = feed.attachment else { - return - } - - if case .DribbbleShot = feed.kind { - if case let .Dribbble(shot) = attachment, let URL = NSURL(string: shot.htmlURLString) { - self?.tapDribbbleShotLinkAction?(URL) - } - } - } - - mediaContainerView.mediaImageView.maskView = halfMaskImageView - - socialWorkContainerViewHeightConstraint.constant = dribbbleShotHeight - contentView.layoutIfNeeded() - - case .Audio: - - mediaContainerView.hidden = true - githubRepoContainerView.hidden = true - voiceContainerView.hidden = false - locationContainerView.hidden = true - socialWorkBorderImageView.hidden = true - - if let attachment = feed.attachment { - if case let .Audio(audioInfo) = attachment { - - voiceContainerView.voiceSampleView.sampleColor = UIColor.leftWaveColor() - voiceContainerView.voiceSampleView.samples = audioInfo.sampleValues - - let timeLengthString = String(format: "%.1f\"", audioInfo.duration) - voiceContainerView.timeLengthLabel.text = timeLengthString - - let rect = timeLengthString.boundingRectWithSize(CGSize(width: 320, height: CGFloat(FLT_MAX)), options: [.UsesLineFragmentOrigin, .UsesFontLeading], attributes: YepConfig.FeedBasicCell.voiceTimeLengthTextAttributes, context: nil) - - voiceContainerViewWidthConstraint.constant = 7 + 30 + 5 + CGFloat(audioInfo.sampleValues.count) * 3 + 5 + rect.width + 5 - - if let realm = try? Realm() { - - let feedAudio = FeedAudio.feedAudioWithFeedID(audioInfo.feedID, inRealm: realm) - - if let feedAudio = feedAudio, playingFeedAudio = YepAudioService.sharedManager.playingFeedAudio, audioPlayer = YepAudioService.sharedManager.audioPlayer { - audioPlaying = (feedAudio.feedID == playingFeedAudio.feedID) && audioPlayer.playing - } else { - audioPlaying = false - } - - let needDownload = (feedAudio == nil) || (feedAudio?.fileName ?? "").isEmpty - - if needDownload { - if let URL = NSURL(string: audioInfo.URLString) { - YepDownloader.downloadDataFromURL(URL, reportProgress: { progress in - println("audio progress: \(progress)") - - }, finishedAction: { data in - println("audio finish: \(data.length)") - - dispatch_async(dispatch_get_main_queue()) { - if let realm = try? Realm() { - - var feedAudio = FeedAudio.feedAudioWithFeedID(audioInfo.feedID, inRealm: realm) - - if feedAudio == nil { - let newFeedAudio = FeedAudio() - newFeedAudio.feedID = audioInfo.feedID - newFeedAudio.URLString = audioInfo.URLString - newFeedAudio.metadata = audioInfo.metaData - - let _ = try? realm.write { - realm.add(newFeedAudio) - } - - feedAudio = newFeedAudio - } - - if let feedAudio = feedAudio where feedAudio.fileName.isEmpty { - - let fileName = NSUUID().UUIDString - if let _ = NSFileManager.saveMessageAudioData(data, withName: fileName) { - let _ = try? realm.write { - feedAudio.fileName = fileName - } - } - } - } - } - }) - } - } - - voiceContainerView.playOrPauseAudioAction = { [weak self] in - if let strongSelf = self { - strongSelf.playOrPauseAudioAction?(strongSelf) - } - } - } - } - } - - socialWorkContainerViewHeightConstraint.constant = 44 - - case .Location: - - mediaContainerView.hidden = true - githubRepoContainerView.hidden = true - voiceContainerView.hidden = true - locationContainerView.hidden = false - socialWorkBorderImageView.hidden = false - - if let attachment = feed.attachment { - if case let .Location(locationInfo) = attachment { - - let location = CLLocation(latitude: locationInfo.latitude, longitude: locationInfo.longitude) - let size = CGSize(width: UIScreen.mainScreen().bounds.width - 65 - 60, height: 110 - locationContainerView.nameLabel.bounds.height) - locationContainerView.mapImageView.yep_showActivityIndicatorWhenLoading = true - locationContainerView.mapImageView.yep_setImageOfLocation(location, withSize: size) - - if locationInfo.name.isEmpty { - locationContainerView.nameLabel.text = NSLocalizedString("Unknown location", comment: "") - - } else { - locationContainerView.nameLabel.text = locationInfo.name - } - } - } - - locationContainerView.mapImageView.maskView = halfMaskImageView - - locationContainerView.tapAction = { [weak self] in - guard let attachment = feed.attachment else { - return - } - - if case .Location = feed.kind { - if case let .Location(locationInfo) = attachment { - self?.tapLocationAction?(locationName: locationInfo.name, locationCoordinate: locationInfo.coordinate) - } - } - } - - socialWorkContainerViewHeightConstraint.constant = 110 - contentView.layoutIfNeeded() - - default: - break - } - - if let URL = socialWorkImageURL { - // ref https://github.com/onevcat/Kingfisher/pull/171 - mediaContainerView.mediaImageView.kf_showIndicatorWhenLoading = true - mediaContainerView.mediaImageView.kf_setImageWithURL(URL, placeholderImage: nil, optionsInfo: MediaOptionsInfos) - } - } -} - diff --git a/Yep/Views/Cells/FeedSocialWork/FeedSocialWorkCell.xib b/Yep/Views/Cells/FeedSocialWork/FeedSocialWorkCell.xib deleted file mode 100644 index a1bf4aa0..00000000 --- a/Yep/Views/Cells/FeedSocialWork/FeedSocialWorkCell.xib +++ /dev/null @@ -1,178 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Yep/Views/ContainerViews/FeedGithubRepoContainerView.swift b/Yep/Views/ContainerViews/FeedGithubRepoContainerView.swift index 5013a7a6..8e0e68f8 100644 --- a/Yep/Views/ContainerViews/FeedGithubRepoContainerView.swift +++ b/Yep/Views/ContainerViews/FeedGithubRepoContainerView.swift @@ -68,11 +68,14 @@ class FeedGithubRepoContainerView: UIView { "accessoryImageView": accessoryImageView, ] - let constraintsH = NSLayoutConstraint.constraintsWithVisualFormat("H:|-10-[iconImageView]-10-[nameLabel]-5-[accessoryImageView]-10-|", options: [], metrics: nil, views: views) + let constraintsH = NSLayoutConstraint.constraintsWithVisualFormat("H:|-10-[iconImageView(16)]-10-[nameLabel]-5-[accessoryImageView(8)]-10-|", options: [], metrics: nil, views: views) iconImageView.setContentHuggingPriority(UILayoutPriorityDefaultHigh, forAxis: .Horizontal) accessoryImageView.setContentHuggingPriority(UILayoutPriorityDefaultHigh, forAxis: .Horizontal) + iconImageView.setContentCompressionResistancePriority(UILayoutPriorityDefaultHigh, forAxis: .Horizontal) + accessoryImageView.setContentCompressionResistancePriority(UILayoutPriorityDefaultHigh, forAxis: .Horizontal) + let constraintsV = NSLayoutConstraint.constraintsWithVisualFormat("V:[nameLabel]-3-[descriptionLabel]", options: [.AlignAllLeading, .AlignAllTrailing], metrics: nil, views: views) let iconImageViewCenterY = NSLayoutConstraint(item: iconImageView, attribute: .CenterY, relatedBy: .Equal, toItem: nameLabel, attribute: .CenterY, multiplier: 1.0, constant: 0) diff --git a/Yep/Views/ContainerViews/LinkContainerView.swift b/Yep/Views/ContainerViews/LinkContainerView.swift index e84419aa..ce0cfe09 100644 --- a/Yep/Views/ContainerViews/LinkContainerView.swift +++ b/Yep/Views/ContainerViews/LinkContainerView.swift @@ -58,10 +58,7 @@ class LinkContainerView: UIView { "accessoryImageView": accessoryImageView, ] - let constraintsH = NSLayoutConstraint.constraintsWithVisualFormat("H:|-10-[iconImageView]-10-[textLabel]-5-[accessoryImageView]-10-|", options: [.AlignAllCenterY], metrics: nil, views: views) - - iconImageView.setContentHuggingPriority(UILayoutPriorityDefaultHigh, forAxis: .Horizontal) - accessoryImageView.setContentHuggingPriority(UILayoutPriorityDefaultHigh, forAxis: .Horizontal) + let constraintsH = NSLayoutConstraint.constraintsWithVisualFormat("H:|-10-[iconImageView(20)]-10-[textLabel]-5-[accessoryImageView(8)]-10-|", options: [.AlignAllCenterY], metrics: nil, views: views) let iconImageViewCenterY = NSLayoutConstraint(item: iconImageView, attribute: .CenterY, relatedBy: .Equal, toItem: self, attribute: .CenterY, multiplier: 1.0, constant: 0)