Merge branch 'feature/feeds' into develop

This commit is contained in:
nixzhu
2015-10-02 12:57:57 +08:00
41 changed files with 2123 additions and 120 deletions
+109 -1
View File
@@ -193,10 +193,14 @@
50A9D44E1ACD382B000B2599 /* NSDateTimeAgo.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 50A9D44C1ACD382B000B2599 /* NSDateTimeAgo.bundle */; };
50A9D4511ACD383E000B2599 /* NSDate+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A9D4501ACD383E000B2599 /* NSDate+Extension.swift */; };
50A9D4571ACD393A000B2599 /* YepWaverView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A9D4541ACD393A000B2599 /* YepWaverView.swift */; };
50AACEC81BB54128004DD54B /* FeedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50AACEC71BB54128004DD54B /* FeedView.swift */; settings = {ASSET_TAGS = (); }; };
50AACECD1BB5419A004DD54B /* FeedView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 50AACECC1BB5419A004DD54B /* FeedView.xib */; settings = {ASSET_TAGS = (); }; };
50AACECF1BB93336004DD54B /* FeedMediaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50AACECE1BB93336004DD54B /* FeedMediaView.swift */; settings = {ASSET_TAGS = (); }; };
50AFBF461BB25F40007D19DA /* ActivityIndicatorTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50AFBF451BB25F40007D19DA /* ActivityIndicatorTitleView.swift */; settings = {ASSET_TAGS = (); }; };
50AFBF491BB2830E007D19DA /* EdgeBorderButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50AFBF481BB2830E007D19DA /* EdgeBorderButton.swift */; settings = {ASSET_TAGS = (); }; };
50AFBF4B1BB2A44C007D19DA /* InfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50AFBF4A1BB2A44C007D19DA /* InfoView.swift */; settings = {ASSET_TAGS = (); }; };
50AFBF4E1BB3A594007D19DA /* bub3.caf in Resources */ = {isa = PBXBuildFile; fileRef = 50AFBF4D1BB3A594007D19DA /* bub3.caf */; settings = {ASSET_TAGS = (); }; };
50AFBF5F1BB4F332007D19DA /* FeedsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50AFBF5E1BB4F332007D19DA /* FeedsViewController.swift */; settings = {ASSET_TAGS = (); }; };
50B38FB71B69CD960064C132 /* Array+Yep.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B38FB61B69CD960064C132 /* Array+Yep.swift */; };
50B38FCC1B6B37F00064C132 /* FeedbackViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B38FCB1B6B37F00064C132 /* FeedbackViewController.swift */; };
50B8E6161B40F9E700C17EA1 /* YepDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B8E6151B40F9E700C17EA1 /* YepDownloader.swift */; };
@@ -231,6 +235,15 @@
50EB8CCE1AEA1B38001AC1EE /* MessageMediaViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50EB8CCD1AEA1B38001AC1EE /* MessageMediaViewController.swift */; };
50F2B9781B2EDF3D00F840CC /* BorderTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50F2B9771B2EDF3D00F840CC /* BorderTextField.swift */; };
50F2B97B1B2EEAF800F840CC /* NavigationTitleLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50F2B97A1B2EEAF800F840CC /* NavigationTitleLabel.swift */; };
50F5AF831BBA67060033C9BC /* NewFeedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50F5AF821BBA67060033C9BC /* NewFeedViewController.swift */; settings = {ASSET_TAGS = (); }; };
50F5AF861BBA676D0033C9BC /* NewFeed.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 50F5AF851BBA676D0033C9BC /* NewFeed.storyboard */; settings = {ASSET_TAGS = (); }; };
50F5AF881BBBB2AE0033C9BC /* HorizontalLineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50F5AF871BBBB2AE0033C9BC /* HorizontalLineView.swift */; settings = {ASSET_TAGS = (); }; };
50F5AF8C1BBBB64C0033C9BC /* FeedCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50F5AF8A1BBBB64C0033C9BC /* FeedCell.swift */; settings = {ASSET_TAGS = (); }; };
50F5AF8D1BBBB64C0033C9BC /* FeedCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 50F5AF8B1BBBB64C0033C9BC /* FeedCell.xib */; settings = {ASSET_TAGS = (); }; };
50F5AF951BBBCDA30033C9BC /* FeedMediaAddCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50F5AF931BBBCDA30033C9BC /* FeedMediaAddCell.swift */; settings = {ASSET_TAGS = (); }; };
50F5AF961BBBCDA30033C9BC /* FeedMediaAddCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 50F5AF941BBBCDA30033C9BC /* FeedMediaAddCell.xib */; settings = {ASSET_TAGS = (); }; };
50F5AF991BBBD02A0033C9BC /* FeedMediaCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50F5AF971BBBD02A0033C9BC /* FeedMediaCell.swift */; settings = {ASSET_TAGS = (); }; };
50F5AF9A1BBBD02A0033C9BC /* FeedMediaCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 50F5AF981BBBD02A0033C9BC /* FeedMediaCell.xib */; settings = {ASSET_TAGS = (); }; };
50FA9A441B661698009BF2A0 /* UIViewController+Yep.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50FA9A431B661698009BF2A0 /* UIViewController+Yep.swift */; };
6A649A9B1BB259CB00EE4FB5 /* YepSoundEffect.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A649A9A1BB259CB00EE4FB5 /* YepSoundEffect.swift */; settings = {ASSET_TAGS = (); }; };
6ADDB4501B848C600012877A /* 1Password.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6ADDB44F1B848C600012877A /* 1Password.xcassets */; };
@@ -455,10 +468,14 @@
50A9D44C1ACD382B000B2599 /* NSDateTimeAgo.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; name = NSDateTimeAgo.bundle; path = Extensions/NSDateTimeAgo.bundle; sourceTree = "<group>"; };
50A9D4501ACD383E000B2599 /* NSDate+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "NSDate+Extension.swift"; path = "Extensions/NSDate+Extension.swift"; sourceTree = "<group>"; };
50A9D4541ACD393A000B2599 /* YepWaverView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = YepWaverView.swift; path = Views/AudioWaves/YepWaverView.swift; sourceTree = "<group>"; };
50AACEC71BB54128004DD54B /* FeedView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedView.swift; path = Views/Feed/FeedView.swift; sourceTree = "<group>"; };
50AACECC1BB5419A004DD54B /* FeedView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = FeedView.xib; path = Views/Feed/FeedView.xib; sourceTree = "<group>"; };
50AACECE1BB93336004DD54B /* FeedMediaView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedMediaView.swift; path = Views/Feed/FeedMediaView.swift; sourceTree = "<group>"; };
50AFBF451BB25F40007D19DA /* ActivityIndicatorTitleView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ActivityIndicatorTitleView.swift; path = Views/TitleViews/ActivityIndicatorTitleView.swift; sourceTree = "<group>"; };
50AFBF481BB2830E007D19DA /* EdgeBorderButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = EdgeBorderButton.swift; path = Views/Buttons/EdgeBorderButton.swift; sourceTree = "<group>"; };
50AFBF4A1BB2A44C007D19DA /* InfoView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = InfoView.swift; path = Views/InfoView/InfoView.swift; sourceTree = "<group>"; };
50AFBF4D1BB3A594007D19DA /* bub3.caf */ = {isa = PBXFileReference; lastKnownFileType = file; name = bub3.caf; path = Sounds/bub3.caf; sourceTree = "<group>"; };
50AFBF5E1BB4F332007D19DA /* FeedsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedsViewController.swift; path = ViewControllers/Feeds/FeedsViewController.swift; sourceTree = "<group>"; };
50B38FB61B69CD960064C132 /* Array+Yep.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Array+Yep.swift"; path = "Extensions/Array+Yep.swift"; sourceTree = "<group>"; };
50B38FCB1B6B37F00064C132 /* FeedbackViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedbackViewController.swift; path = ViewControllers/Feedback/FeedbackViewController.swift; sourceTree = "<group>"; };
50B8E6151B40F9E700C17EA1 /* YepDownloader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = YepDownloader.swift; path = Services/YepDownloader.swift; sourceTree = "<group>"; };
@@ -493,6 +510,15 @@
50EB8CCD1AEA1B38001AC1EE /* MessageMediaViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MessageMediaViewController.swift; path = ViewControllers/MessageMedia/MessageMediaViewController.swift; sourceTree = "<group>"; };
50F2B9771B2EDF3D00F840CC /* BorderTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BorderTextField.swift; path = Views/TextFields/BorderTextField.swift; sourceTree = "<group>"; };
50F2B97A1B2EEAF800F840CC /* NavigationTitleLabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NavigationTitleLabel.swift; path = Views/Labels/NavigationTitleLabel.swift; sourceTree = "<group>"; };
50F5AF821BBA67060033C9BC /* NewFeedViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NewFeedViewController.swift; path = ViewControllers/NewFeed/NewFeedViewController.swift; sourceTree = "<group>"; };
50F5AF851BBA676D0033C9BC /* NewFeed.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = NewFeed.storyboard; path = ViewControllers/NewFeed/NewFeed.storyboard; sourceTree = "<group>"; };
50F5AF871BBBB2AE0033C9BC /* HorizontalLineView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = HorizontalLineView.swift; path = Views/HorizontalLine/HorizontalLineView.swift; sourceTree = "<group>"; };
50F5AF8A1BBBB64C0033C9BC /* FeedCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedCell.swift; path = Views/Cells/Feed/FeedCell.swift; sourceTree = "<group>"; };
50F5AF8B1BBBB64C0033C9BC /* FeedCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = FeedCell.xib; path = Views/Cells/Feed/FeedCell.xib; sourceTree = "<group>"; };
50F5AF931BBBCDA30033C9BC /* FeedMediaAddCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedMediaAddCell.swift; path = Views/Cells/FeedMediaAdd/FeedMediaAddCell.swift; sourceTree = "<group>"; };
50F5AF941BBBCDA30033C9BC /* FeedMediaAddCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = FeedMediaAddCell.xib; path = Views/Cells/FeedMediaAdd/FeedMediaAddCell.xib; sourceTree = "<group>"; };
50F5AF971BBBD02A0033C9BC /* FeedMediaCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedMediaCell.swift; path = Views/Cells/FeedMedia/FeedMediaCell.swift; sourceTree = "<group>"; };
50F5AF981BBBD02A0033C9BC /* FeedMediaCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = FeedMediaCell.xib; path = Views/Cells/FeedMedia/FeedMediaCell.xib; sourceTree = "<group>"; };
50FA9A431B661698009BF2A0 /* UIViewController+Yep.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIViewController+Yep.swift"; path = "Extensions/UIViewController+Yep.swift"; sourceTree = "<group>"; };
6A649A9A1BB259CB00EE4FB5 /* YepSoundEffect.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = YepSoundEffect.swift; path = Helpers/YepSoundEffect.swift; sourceTree = "<group>"; };
6ADDB44F1B848C600012877A /* 1Password.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = 1Password.xcassets; sourceTree = "<group>"; };
@@ -789,6 +815,8 @@
0A943A7C1B0F8EB10022DD67 /* Base */,
0AB8F79B1B15D1DF00F4AF09 /* Nav */,
848806B71B7B4F58000C5424 /* Show */,
50AFBF601BB4F33A007D19DA /* Feeds */,
50F5AF841BBA67140033C9BC /* NewFeed */,
);
name = ViewControllers;
sourceTree = "<group>";
@@ -877,6 +905,8 @@
8409137E1B7DB178001C89D6 /* FriendRequest */,
50AFBF471BB25F81007D19DA /* TitleViews */,
50AFBF4C1BB2A453007D19DA /* InfoView */,
50AACEC91BB54132004DD54B /* Feed */,
50F5AF891BBBB2B30033C9BC /* HorizontalLine */,
);
name = Views;
sourceTree = "<group>";
@@ -923,6 +953,9 @@
5064F6561B0B0E1B0089FAD4 /* AddFriendSearch */,
5064F65B1B0B14470089FAD4 /* AddFriendMore */,
50E348941B9063AD00A8E699 /* About */,
50AFBF651BB4FC37007D19DA /* Feed */,
50F5AF921BBBCC960033C9BC /* FeedMediaAdd */,
50F5AF9B1BBBD0310033C9BC /* FeedMedia */,
);
name = Cells;
sourceTree = "<group>";
@@ -1499,6 +1532,16 @@
name = AudioWaves;
sourceTree = "<group>";
};
50AACEC91BB54132004DD54B /* Feed */ = {
isa = PBXGroup;
children = (
50AACEC71BB54128004DD54B /* FeedView.swift */,
50AACECC1BB5419A004DD54B /* FeedView.xib */,
50AACECE1BB93336004DD54B /* FeedMediaView.swift */,
);
name = Feed;
sourceTree = "<group>";
};
50AFBF471BB25F81007D19DA /* TitleViews */ = {
isa = PBXGroup;
children = (
@@ -1523,6 +1566,23 @@
name = Sounds;
sourceTree = "<group>";
};
50AFBF601BB4F33A007D19DA /* Feeds */ = {
isa = PBXGroup;
children = (
50AFBF5E1BB4F332007D19DA /* FeedsViewController.swift */,
);
name = Feeds;
sourceTree = "<group>";
};
50AFBF651BB4FC37007D19DA /* Feed */ = {
isa = PBXGroup;
children = (
50F5AF8A1BBBB64C0033C9BC /* FeedCell.swift */,
50F5AF8B1BBBB64C0033C9BC /* FeedCell.xib */,
);
name = Feed;
sourceTree = "<group>";
};
50B38FCD1B6B37F90064C132 /* Feedback */ = {
isa = PBXGroup;
children = (
@@ -1660,6 +1720,41 @@
name = Labels;
sourceTree = "<group>";
};
50F5AF841BBA67140033C9BC /* NewFeed */ = {
isa = PBXGroup;
children = (
50F5AF821BBA67060033C9BC /* NewFeedViewController.swift */,
50F5AF851BBA676D0033C9BC /* NewFeed.storyboard */,
);
name = NewFeed;
sourceTree = "<group>";
};
50F5AF891BBBB2B30033C9BC /* HorizontalLine */ = {
isa = PBXGroup;
children = (
50F5AF871BBBB2AE0033C9BC /* HorizontalLineView.swift */,
);
name = HorizontalLine;
sourceTree = "<group>";
};
50F5AF921BBBCC960033C9BC /* FeedMediaAdd */ = {
isa = PBXGroup;
children = (
50F5AF931BBBCDA30033C9BC /* FeedMediaAddCell.swift */,
50F5AF941BBBCDA30033C9BC /* FeedMediaAddCell.xib */,
);
name = FeedMediaAdd;
sourceTree = "<group>";
};
50F5AF9B1BBBD0310033C9BC /* FeedMedia */ = {
isa = PBXGroup;
children = (
50F5AF971BBBD02A0033C9BC /* FeedMediaCell.swift */,
50F5AF981BBBD02A0033C9BC /* FeedMediaCell.xib */,
);
name = FeedMedia;
sourceTree = "<group>";
};
59CC1DE9D59AC08247B97B4F /* Pods */ = {
isa = PBXGroup;
children = (
@@ -1835,11 +1930,14 @@
files = (
5023DE381AB6979600B3EE96 /* ConversationCell.xib in Resources */,
505C338B1BAA92CA00E044E2 /* InfoPlist.strings in Resources */,
50AACECD1BB5419A004DD54B /* FeedView.xib in Resources */,
50F5AF861BBA676D0033C9BC /* NewFeed.storyboard in Resources */,
50C090A11ADE391600CC6389 /* AddSkillsReusableView.xib in Resources */,
504F415B1AD5028200FBB19A /* SkillCell.xib in Resources */,
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 */,
@@ -1872,6 +1970,7 @@
848806B11B789A2D000C5424 /* EditSkillCell.xib in Resources */,
50E114FD1ABC549300F13000 /* ContactsCell.xib in Resources */,
502AE54F1AB93175005BD199 /* ProfileHeaderCell.xib in Resources */,
50F5AF9A1BBBD02A0033C9BC /* FeedMediaCell.xib in Resources */,
50A640EA1B6F45FA0050E4A3 /* DoNotDisturbPeriodCell.xib in Resources */,
0A8F54BB1AB67D11004AD60E /* LaunchScreen.xib in Resources */,
5053AD511AF82A1700B3CBFA /* PickLocationCell.xib in Resources */,
@@ -1880,6 +1979,7 @@
50EB8CC51AE89F12001AC1EE /* ChatRightVideoCell.xib in Resources */,
502AE5191AB70D72005BD199 /* Intro.storyboard in Resources */,
504BDC151B4528A500DBD35D /* Badges.xcassets in Resources */,
50F5AF961BBBCDA30033C9BC /* FeedMediaAddCell.xib in Resources */,
50894D081AEDFDA2000981AF /* EditProfileMoreInfoCell.xib in Resources */,
50A640E61B6F41E00050E4A3 /* DoNotDisturbSwitchCell.xib in Resources */,
508D174D1ACCF49B0092D666 /* ChatRightAudioCell.xib in Resources */,
@@ -1990,6 +2090,7 @@
50553F931ADBA50600F80B59 /* NSDate+Yep.swift in Sources */,
50AFBF461BB25F40007D19DA /* ActivityIndicatorTitleView.swift in Sources */,
50E61CEB1AF1F69D00908A9D /* ConversationTitleView.swift in Sources */,
50F5AF991BBBD02A0033C9BC /* FeedMediaCell.swift in Sources */,
5023DE331AB688F600B3EE96 /* UIColor+Yep.swift in Sources */,
503315971AC1017E0008A209 /* ChatLeftTextCell.swift in Sources */,
0A8F54B11AB67D11004AD60E /* AppDelegate.swift in Sources */,
@@ -2000,7 +2101,9 @@
6A649A9B1BB259CB00EE4FB5 /* YepSoundEffect.swift in Sources */,
508D174C1ACCF49B0092D666 /* ChatRightAudioCell.swift in Sources */,
502AE53D1AB87C0D005BD199 /* YepUserDefaults.swift in Sources */,
50F5AF881BBBB2AE0033C9BC /* HorizontalLineView.swift in Sources */,
50EB8CC41AE89F12001AC1EE /* ChatRightVideoCell.swift in Sources */,
50AACECF1BB93336004DD54B /* FeedMediaView.swift in Sources */,
50D4D0DB1AFA825B005AEB6F /* MediaControlView.swift in Sources */,
0A9018821B0120A500AE4B7F /* OAuthViewController.swift in Sources */,
508D17471ACCF4710092D666 /* ChatLeftAudioCell.swift in Sources */,
@@ -2037,6 +2140,7 @@
505EC81C1ADFBE1B001D27E0 /* SkillSelectionCell.swift in Sources */,
5023DE591AB6DBC100B3EE96 /* UIImage+Yep.swift in Sources */,
508F8FD41B01E3D100461B0B /* SocialWorkDribbbleViewController.swift in Sources */,
50AFBF5F1BB4F332007D19DA /* FeedsViewController.swift in Sources */,
50EB8CBF1AE89ED8001AC1EE /* ChatLeftVideoCell.swift in Sources */,
502AE5331AB86020005BD199 /* RegisterVerifyMobileViewController.swift in Sources */,
5064F6541B0B0E160089FAD4 /* AddFriendSearchCell.swift in Sources */,
@@ -2047,6 +2151,7 @@
84385C4A1B85BE200071130D /* ShowStepMeetViewController.swift in Sources */,
5023DE2D1AB685ED00B3EE96 /* DiscoverViewController.swift in Sources */,
0A1CAC701AFA481600826B45 /* SkillHomeHeaderView.swift in Sources */,
50AACEC81BB54128004DD54B /* FeedView.swift in Sources */,
0AB8F79A1B15D1DC00F4AF09 /* YepNavigationController.swift in Sources */,
502AE53A1AB871C1005BD199 /* YepAlert.swift in Sources */,
5039594E1B05E6E000797A3E /* ProfileSocialAccountGithubCell.swift in Sources */,
@@ -2070,6 +2175,7 @@
0A0A11821AE389380008772C /* RegisterSkillsLayout.swift in Sources */,
50C0909B1ADE234F00CC6389 /* RegisterPickSkillsViewController.swift in Sources */,
5076FC5A1AF9DF6B00D7381A /* ConversationMessagePreviewTransitionManager.swift in Sources */,
50F5AF951BBBCDA30033C9BC /* FeedMediaAddCell.swift in Sources */,
508D16BD1ADCAF0400270E3F /* PullToRefreshView.swift in Sources */,
508D17441ACCDC9F0092D666 /* VoiceRecordButton.swift in Sources */,
502AE5271AB72D06005BD199 /* YepNetworking.swift in Sources */,
@@ -2089,6 +2195,7 @@
50E114F91ABC137A00F13000 /* YepServiceSync.swift in Sources */,
506E241C1AE6254F00A33C1A /* MessageTypeButton.swift in Sources */,
0A3E60671ACCAECE003DC853 /* YepLocationService.swift in Sources */,
50F5AF8C1BBBB64C0033C9BC /* FeedCell.swift in Sources */,
50894CF11AEA4726000981AF /* SettingsViewController.swift in Sources */,
5023DE2A1AB685BA00B3EE96 /* ContactsViewController.swift in Sources */,
50CDBE491ACBAD3200459CE0 /* ChatLeftImageCell.swift in Sources */,
@@ -2123,6 +2230,7 @@
505EC8171ADF8151001D27E0 /* SkillCategoryCell.swift in Sources */,
50E18BB71B180F7D0076C3C6 /* BottomButtonView.swift in Sources */,
0A90187E1B01152800AE4B7F /* ProfileSocialAccountCell.swift in Sources */,
50F5AF831BBA67060033C9BC /* NewFeedViewController.swift in Sources */,
50894CF51AEA4A01000981AF /* SettingsUserCell.swift in Sources */,
84385C441B8592120071130D /* ShowStepGeniusViewController.swift in Sources */,
5053AD5A1AF83B4200B3CBFA /* ChatLeftLocationCell.swift in Sources */,
@@ -2245,7 +2353,7 @@
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
OTHER_SWIFT_FLAGS = "-D DEBUG -D STAGING_";
OTHER_SWIFT_FLAGS = "-D DEBUG -D STAGING";
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
+83 -8
View File
@@ -291,13 +291,13 @@
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="k4j-Ub-3bn" customClass="MediaView" customModule="Yep" customModuleProvider="target">
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="k4j-Ub-3bn" customClass="MediaView" customModule="Yep" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="600" height="551"/>
<animations/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<gestureRecognizers/>
</view>
<view contentMode="scaleToFill" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="2Cb-zQ-dUZ" customClass="MediaControlView" customModule="Yep" customModuleProvider="target">
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="2Cb-zQ-dUZ" customClass="MediaControlView" customModule="Yep" customModuleProvider="target">
<rect key="frame" x="0.0" y="471" width="600" height="80"/>
<animations/>
<color key="backgroundColor" red="0.40000000600000002" green="0.80000001190000003" blue="1" alpha="1" colorSpace="calibratedRGB"/>
@@ -1021,7 +1021,7 @@
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="IrX-gK-Zkg">
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="IrX-gK-Zkg">
<rect key="frame" x="0.0" y="144" width="600" height="44"/>
<animations/>
<constraints>
@@ -1034,7 +1034,7 @@
<action selector="activeFrom" destination="GqQ-V0-aYk" eventType="touchUpInside" id="yIG-DH-kZc"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="flj-e3-cbm">
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="flj-e3-cbm">
<rect key="frame" x="0.0" y="188" width="600" height="44"/>
<animations/>
<constraints>
@@ -1047,7 +1047,7 @@
<action selector="activeTo" destination="GqQ-V0-aYk" eventType="touchUpInside" id="90e-4a-0ZA"/>
</connections>
</button>
<pickerView contentMode="scaleToFill" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="hap-y9-JVX">
<pickerView contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="hap-y9-JVX">
<rect key="frame" x="0.0" y="335" width="600" height="216"/>
<animations/>
<connections>
@@ -1248,14 +1248,14 @@
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" text="We read every feedback" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="pdt-Vd-sD9">
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="We read every feedback" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="pdt-Vd-sD9">
<rect key="frame" x="208" y="104" width="185" height="20.5"/>
<animations/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" misplaced="YES" keyboardDismissMode="onDrag" translatesAutoresizingMaskIntoConstraints="NO" id="fVo-Zl-gHQ" customClass="FeedbackTextView" customModule="Yep" customModuleProvider="target">
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" keyboardDismissMode="onDrag" translatesAutoresizingMaskIntoConstraints="NO" id="fVo-Zl-gHQ" customClass="FeedbackTextView" customModule="Yep" customModuleProvider="target">
<rect key="frame" x="0.0" y="164.5" width="600" height="121.5"/>
<animations/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
@@ -1554,6 +1554,7 @@
<connections>
<segue destination="hOb-vY-dSl" kind="relationship" relationship="viewControllers" id="7mr-Lj-HcA"/>
<segue destination="HI7-HE-lfi" kind="relationship" relationship="viewControllers" id="u9t-ac-tqW"/>
<segue destination="uRR-B1-khD" kind="relationship" relationship="viewControllers" id="hp1-fR-jRe"/>
<segue destination="HFE-uY-fys" kind="relationship" relationship="viewControllers" id="DQ6-Rk-9zC"/>
<segue destination="dwP-GL-7hj" kind="relationship" relationship="viewControllers" id="I8B-nP-KAs"/>
</connections>
@@ -1562,6 +1563,59 @@
</objects>
<point key="canvasLocation" x="621" y="-791"/>
</scene>
<!--Feeds View Controller-->
<scene sceneID="Dcb-bn-5M7">
<objects>
<viewController storyboardIdentifier="FeedsViewController" id="W3w-jm-aNI" customClass="FeedsViewController" customModule="Yep" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="1v6-QD-r3B"/>
<viewControllerLayoutGuide type="bottom" id="Vr5-XL-uqL"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="aTs-V4-FkM">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="D86-dp-qKQ">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<connections>
<outlet property="dataSource" destination="W3w-jm-aNI" id="Ld3-tu-vGC"/>
<outlet property="delegate" destination="W3w-jm-aNI" id="Zmv-Qw-N8o"/>
</connections>
</tableView>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="D86-dp-qKQ" secondAttribute="trailing" id="0cJ-BC-Zme"/>
<constraint firstItem="D86-dp-qKQ" firstAttribute="leading" secondItem="aTs-V4-FkM" secondAttribute="leading" id="7rL-66-51f"/>
<constraint firstAttribute="bottom" secondItem="D86-dp-qKQ" secondAttribute="bottom" id="IGV-gd-9fq"/>
<constraint firstItem="D86-dp-qKQ" firstAttribute="top" secondItem="aTs-V4-FkM" secondAttribute="top" id="WZ4-Nx-qhp"/>
</constraints>
</view>
<navigationItem key="navigationItem" id="LaU-sr-um0">
<barButtonItem key="rightBarButtonItem" systemItem="add" id="Goz-ft-7ec">
<connections>
<segue destination="4oS-hQ-vpS" kind="show" identifier="showNewFeed" id="zix-bq-iJe"/>
</connections>
</barButtonItem>
</navigationItem>
<connections>
<outlet property="feedsTableView" destination="D86-dp-qKQ" id="rh9-wh-IqK"/>
<segue destination="la3-6z-2aH" kind="show" identifier="showConversation" id="Sdt-ox-uOm"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="ETW-Em-Kw5" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="643" y="-1667"/>
</scene>
<!--NewFeedViewController-->
<scene sceneID="LDV-cy-JhT">
<objects>
<viewControllerPlaceholder storyboardName="NewFeed" referencedIdentifier="NewFeedViewController" id="4oS-hQ-vpS" sceneMemberID="viewController"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="HCX-id-Kbz" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1144.5" y="-1667"/>
</scene>
<!--Chats-->
<scene sceneID="ySr-fW-tdP">
<objects>
@@ -1957,6 +2011,25 @@
</objects>
<point key="canvasLocation" x="4277" y="1999"/>
</scene>
<!--Feeds-->
<scene sceneID="lMZ-Mf-PVd">
<objects>
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="uRR-B1-khD" customClass="YepNavigationController" customModule="Yep" customModuleProvider="target" sceneMemberID="viewController">
<tabBarItem key="tabBarItem" title="Feeds" image="icon_feeds" selectedImage="icon_feeds_active" id="1aS-xr-y1X"/>
<toolbarItems/>
<navigationBar key="navigationBar" contentMode="scaleToFill" id="6uo-JX-xvr">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<nil name="viewControllers"/>
<connections>
<segue destination="W3w-jm-aNI" kind="relationship" relationship="rootViewController" id="AMg-D0-eU6"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="vh6-Xb-Pbc" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-97" y="-1659"/>
</scene>
</scenes>
<resources>
<image name="default_avatar" width="100" height="100"/>
@@ -1967,6 +2040,8 @@
<image name="icon_contact_active" width="30" height="30"/>
<image name="icon_explore" width="30" height="30"/>
<image name="icon_explore_active" width="30" height="30"/>
<image name="icon_feeds" width="30" height="30"/>
<image name="icon_feeds_active" width="30" height="30"/>
<image name="icon_me" width="30" height="30"/>
<image name="icon_me_active" width="30" height="30"/>
<image name="icon_more_add_location" width="70" height="50"/>
@@ -1982,7 +2057,7 @@
</resources>
<inferredMetricsTieBreakers>
<segue reference="eTq-BP-vMS"/>
<segue reference="o3b-2D-AMq"/>
<segue reference="Sdt-ox-uOm"/>
<segue reference="6No-Lc-7X4"/>
</inferredMetricsTieBreakers>
</document>
+6
View File
@@ -171,6 +171,12 @@ class YepConfig {
// ]
}
struct FeedCell {
static let textAttributes:[String: NSObject] = [
NSFontAttributeName: UIFont.feedMessageFont(),
]
}
struct MetaData {
static let audioDuration = "audio_duration"
static let audioSamples = "audio_samples"
+2 -1
View File
@@ -30,7 +30,8 @@ extension UIColor {
}
class func yepBorderColor() -> UIColor {
return UIColor(red: 0.898, green: 0.898, blue: 0.898, alpha: 1)
//return UIColor(red: 0.898, green: 0.898, blue: 0.898, alpha: 1)
return UIColor(red: 0.7, green: 0.7, blue: 0.7, alpha: 1)
}
class func avatarBackgroundColor() -> UIColor {
+4
View File
@@ -42,4 +42,8 @@ extension UIFont {
//return UIFont(name: "Helvetica-Bold", size: 15)!
return UIFont.boldSystemFontOfSize(15)
}
class func feedMessageFont() -> UIFont {
return UIFont.systemFontOfSize(17)
}
}
@@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "feed_media_add.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Binary file not shown.
@@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "icon_discussion.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Binary file not shown.
+15
View File
@@ -0,0 +1,15 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "icon_feeds.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "original"
}
}
Binary file not shown.
@@ -0,0 +1,15 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "icon_feeds_active.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "original"
}
}
@@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "icon_media_delete.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
+62 -1
View File
@@ -362,6 +362,7 @@ enum ConversationType: Int {
class Conversation: Object {
var fakeID: String? {
switch type {
case ConversationType.OneToOne.rawValue:
if let withFriend = withFriend {
@@ -372,7 +373,34 @@ class Conversation: Object {
return "group" + withGroup.groupID
}
default:
break
return nil
}
return nil
}
var recipientID: String? {
switch type {
case ConversationType.OneToOne.rawValue:
if let withFriend = withFriend {
return withFriend.userID
}
case ConversationType.Group.rawValue:
if let withGroup = withGroup {
return withGroup.groupID
}
default:
return nil
}
return nil
}
var recipient: Recipient? {
if let recipientType = ConversationType(rawValue: type), recipientID = recipientID {
return Recipient(type: recipientType, ID: recipientID)
}
return nil
@@ -391,7 +419,40 @@ class Conversation: Object {
}
}
// MARK: Feed
enum AttachmentKind: String {
case Image = "image"
case Thumbnail = "thumbnail"
case Audio = "audio"
case Video = "video"
}
/*
class Attachment: Object {
dynamic var kind: String = ""
dynamic var metadata: String = ""
dynamic var URLString: String = ""
}
class Feed: Object {
dynamic var feedID: String = ""
dynamic var allowComment: Bool = true
dynamic var createdUnixTime: NSTimeInterval = NSDate().timeIntervalSince1970
dynamic var updatedUnixTime: NSTimeInterval = NSDate().timeIntervalSince1970
dynamic var creator: User?
dynamic var body: String = ""
var attachments = List<Attachment>()
dynamic var skill: UserSkill?
dynamic var group: Group?
}
*/
// MARK: Helpers
+3
View File
@@ -30,11 +30,14 @@ class YepLocationService: NSObject, CLLocationManagerDelegate {
return locationManager
}()
var currentLocation: CLLocation?
var address: String?
let geocoder = CLGeocoder()
func locationManager(manager: CLLocationManager, didUpdateToLocation newLocation: CLLocation, fromLocation oldLocation: CLLocation) {
currentLocation = newLocation
//
let distance = newLocation.distanceFromLocation(oldLocation)
+2 -2
View File
@@ -102,9 +102,9 @@ var yepNetworkActivityCount = 0 {
#if STAGING
class SessionDelegate: NSObject, NSURLSessionDelegate {
func URLSession(session: NSURLSession, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential!) -> Void) {
func URLSession(session: NSURLSession, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) {
completionHandler(.UseCredential, NSURLCredential(forTrust: challenge.protectionSpace.serverTrust))
completionHandler(.UseCredential, NSURLCredential(forTrust: challenge.protectionSpace.serverTrust!))
}
}
+255 -19
View File
@@ -1504,25 +1504,24 @@ func sentButUnreadMessages(failureHandler failureHandler: ((Reason, String?) ->
func unreadMessages(completion completion: [JSONDictionary] -> Void) {
headUnreadMessages { result in
var messages = [JSONDictionary]()
if let page1Messages = result["messages"] as? [JSONDictionary] {
messages += page1Messages
}
if
let count = result["count"] as? Int,
let currentPage = result["current_page"] as? Int,
let perPage = result["per_page"] as? Int {
if count <= currentPage * perPage {
if let messages = result["messages"] as? [JSONDictionary] {
completion(messages)
} else {
completion([])
}
completion(messages)
} else {
var messages = [JSONDictionary]()
if let page1Messages = result["messages"] as? [JSONDictionary] {
messages += page1Messages
}
// We have more messages
let downloadGroup = dispatch_group_create()
@@ -1532,11 +1531,13 @@ func unreadMessages(completion completion: [JSONDictionary] -> Void) {
moreUnreadMessages(inPage: page, withPerPage: perPage, failureHandler: { (reason, errorMessage) in
dispatch_group_leave(downloadGroup)
}, completion: { result in
if let currentPageMessages = result["messages"] as? [JSONDictionary] {
messages += currentPageMessages
}
dispatch_group_leave(downloadGroup)
}, completion: { result in
if let currentPageMessages = result["messages"] as? [JSONDictionary] {
messages += currentPageMessages
}
dispatch_group_leave(downloadGroup)
})
}
@@ -1544,10 +1545,83 @@ func unreadMessages(completion completion: [JSONDictionary] -> Void) {
completion(messages)
}
}
} else {
//
completion(messages)
}
}
}
struct Recipient {
let type: ConversationType
let ID: String
}
enum TimeDirection {
case Future(minMessageID: String)
case Past(maxMessageID: String)
case None
}
func messagesFromRecipient(recipient: Recipient, withTimeDirection timeDirection: TimeDirection, failureHandler: ((Reason, String?) -> Void)?, completion: Bool -> Void) {
var requestParameters = [
"recipient_type": recipient.type.nameForServer,
"recipient_id": recipient.ID,
]
switch timeDirection {
case .Future(let minMessageID):
requestParameters["min_id"] = minMessageID
case .Past(let maxMessageID):
requestParameters["max_id"] = maxMessageID
case .None:
break
}
let parse: JSONDictionary -> Bool? = { data in
guard let unreadMessagesData = data["messages"] as? [JSONDictionary] else {
return false
}
println("messagesFromRecipient: \(recipient), \(unreadMessagesData.count)")
dispatch_async(dispatch_get_main_queue()) {
guard let realm = try? Realm() else {
return
}
var messageIDs = [String]()
for messageInfo in unreadMessagesData {
syncMessageWithMessageInfo(messageInfo, inRealm: realm) { _messageIDs in
messageIDs += _messageIDs
}
}
if !messageIDs.isEmpty {
let object = ["messageIDs": messageIDs]
NSNotificationCenter.defaultCenter().postNotificationName(YepNewMessagesReceivedNotification, object: object)
}
}
return true
}
let resource = authJsonResource(path: "/api/v1/\(recipient.type.nameForServer)/\(recipient.ID)/messages/unread", method: .GET, requestParameters: requestParameters, parse: parse )
if let failureHandler = failureHandler {
apiRequest({_ in}, baseURL: baseURL, resource: resource, failure: failureHandler, completion: completion)
} else {
apiRequest({_ in}, baseURL: baseURL, resource: resource, failure: defaultFailureHandler, completion: completion)
}
}
func createMessageWithMessageInfo(messageInfo: JSONDictionary, failureHandler: ((Reason, String?) -> Void)?, completion: (messageID: String) -> Void) {
println("Message info \(messageInfo)")
@@ -1561,7 +1635,13 @@ func createMessageWithMessageInfo(messageInfo: JSONDictionary, failureHandler: (
return nil
}
let resource = authJsonResource(path: "/api/v1/messages", method: .POST, requestParameters: messageInfo, parse: parse)
guard let
recipientType = messageInfo["recipient_type"] as? String,
recipientID = messageInfo["recipient_id"] as? String else {
return
}
let resource = authJsonResource(path: "/api/v1/\(recipientType)/\(recipientID)/messages", method: .POST, requestParameters: messageInfo, parse: parse)
if let failureHandler = failureHandler {
apiRequest({_ in}, baseURL: baseURL, resource: resource, failure: failureHandler, completion: completion)
@@ -1846,7 +1926,7 @@ func sendMessage(message: Message, inFilePath filePath: String?, orFileData file
default:
s3PrivateUploadFile(inFilePath: filePath, orFileData: fileData, mimeType: mediaType.mineType, failureHandler: failureHandler, completion: { s3UploadParams in
s3UploadFileOfKind(.Message, inFilePath: filePath, orFileData: fileData, mimeType: mediaType.mineType, failureHandler: failureHandler, completion: { s3UploadParams in
switch mediaType {
@@ -1901,7 +1981,7 @@ func sendMessage(message: Message, inFilePath filePath: String?, orFileData file
thumbnailData = UIImageJPEGRepresentation(image, YepConfig.messageImageCompressionQuality())
}
s3PrivateUploadFile(inFilePath: nil, orFileData: thumbnailData, mimeType: MessageMediaType.Image.mineType, failureHandler: failureHandler, completion: { thumbnailS3UploadParams in
s3UploadFileOfKind(.Message, inFilePath: nil, orFileData: thumbnailData, mimeType: MessageMediaType.Image.mineType, failureHandler: failureHandler, completion: { thumbnailS3UploadParams in
if let metaData = metaData {
let attachments = [
@@ -2062,6 +2142,162 @@ func markAsReadMessage(message: Message ,failureHandler: ((Reason, String?) -> V
}
}
// MARK: - Feeds
enum FeedSortStyle: String {
case Distance = "distance"
case Time = "time"
}
struct DiscoveredAttachment {
let kind: AttachmentKind
let metadata: String
let URLString: String
static func fromJSONDictionary(json: JSONDictionary) -> DiscoveredAttachment? {
guard let
kindString = json["kind"] as? String,
kind = AttachmentKind(rawValue: kindString),
metadata = json["metadata"] as? String,
fileInfo = json["file"] as? JSONDictionary,
URLString = fileInfo["url"] as? String else {
return nil
}
return DiscoveredAttachment(kind: kind, metadata: metadata, URLString: URLString)
}
}
struct DiscoveredFeed {
let id: String
let allowComment: Bool
let createdUnixTime: NSTimeInterval
let updatedUnixTime: NSTimeInterval
let creator: DiscoveredUser
let body: String
let attachments: [DiscoveredAttachment]
let distance: Double?
let skill: Skill?
let groupID: String
let messageCount: Int
static func fromJSONDictionary(json: JSONDictionary) -> DiscoveredFeed? {
guard let
id = json["id"] as? String,
allowComment = json["allow_comment"] as? Bool,
createdUnixTime = json["created_at"] as? NSTimeInterval,
updatedUnixTime = json["updated_at"] as? NSTimeInterval,
creatorInfo = json["user"] as? JSONDictionary,
body = json["body"] as? String,
attachmentsData = json["attachments"] as? [JSONDictionary],
//skill // TODO: skill
groupInfo = json["circle"] as? JSONDictionary,
messageCount = json["message_count"] as? Int else {
return nil
}
guard let creator = parseDiscoveredUser(creatorInfo), groupID = groupInfo["id"] as? String else {
return nil
}
let distance = json["distance"] as? Double
let attachments = attachmentsData.map({ DiscoveredAttachment.fromJSONDictionary($0) }).flatMap({ $0 })
return DiscoveredFeed(id: id, allowComment: allowComment, createdUnixTime: createdUnixTime, updatedUnixTime: updatedUnixTime, creator: creator, body: body, attachments: attachments, distance: distance, skill: nil, groupID: groupID, messageCount: messageCount)
}
}
let parseFeeds: JSONDictionary -> [DiscoveredFeed]? = { data in
println("feedsData: \(data)")
if let feedsData = data["topics"] as? [JSONDictionary] {
return feedsData.map({ DiscoveredFeed.fromJSONDictionary($0) }).flatMap({ $0 })
}
return []
}
func discoverFeedsWithSortStyle(sortStyle: FeedSortStyle, pageIndex: Int, perPage: Int, failureHandler: ((Reason, String?) -> Void)?,completion: [DiscoveredFeed] -> Void) {
let requestParameters: JSONDictionary = [
"sort": sortStyle.rawValue,
"page": pageIndex,
"per_page": perPage,
]
let parse = parseFeeds
let resource = authJsonResource(path: "/api/v1/topics/discover", method: .GET, requestParameters: requestParameters, parse: parse)
if let failureHandler = failureHandler {
apiRequest({_ in}, baseURL: baseURL, resource: resource, failure: failureHandler, completion: completion)
} else {
apiRequest({_ in}, baseURL: baseURL, resource: resource, failure: defaultFailureHandler, completion: completion)
}
}
func myFeedsAtPageIndex(pageIndex: Int, perPage: Int, failureHandler: ((Reason, String?) -> Void)?,completion: [DiscoveredFeed] -> Void) {
let requestParameters: JSONDictionary = [
"page": pageIndex,
"per_page": perPage,
]
let parse = parseFeeds
let resource = authJsonResource(path: "/api/v1/topics", method: .GET, requestParameters: requestParameters, parse: parse)
if let failureHandler = failureHandler {
apiRequest({_ in}, baseURL: baseURL, resource: resource, failure: failureHandler, completion: completion)
} else {
apiRequest({_ in}, baseURL: baseURL, resource: resource, failure: defaultFailureHandler, completion: completion)
}
}
func createFeedWithMessage(message: String, attachments: JSONDictionary?, coordinate: CLLocationCoordinate2D?, skill: Skill?, allowComment: Bool, failureHandler: ((Reason, String?) -> Void)?, completion: JSONDictionary -> Void) {
var requestParameters: JSONDictionary = [
"body": message,
"latitude": 0,
"longitude": 0,
"allow_comment": allowComment,
]
if let coordinate = coordinate {
requestParameters["latitude"] = coordinate.latitude
requestParameters["longitude"] = coordinate.longitude
}
if let skill = skill {
requestParameters["skill_id"] = skill.id
}
if let attachments = attachments {
requestParameters["attachments"] = attachments
}
let parse: JSONDictionary -> JSONDictionary? = { data in
return data
}
let resource = authJsonResource(path: "/api/v1/topics", method: .POST, requestParameters: requestParameters, parse: parse)
if let failureHandler = failureHandler {
apiRequest({_ in}, baseURL: baseURL, resource: resource, failure: failureHandler, completion: completion)
} else {
apiRequest({_ in}, baseURL: baseURL, resource: resource, failure: defaultFailureHandler, completion: completion)
}
}
// MARK: - Social Work
func authURLRequestWithURL(url: NSURL) -> NSURLRequest {
+25 -77
View File
@@ -10,11 +10,12 @@ import Foundation
import MobileCoreServices.UTType
import AFNetworking
/**
Struct of S3 UploadParams
*/
struct S3UploadParams {
let url: String
let key: String
let acl: String
@@ -23,6 +24,13 @@ struct S3UploadParams {
let date: String
let credential: String
let encodedPolicy: String
enum Kind: String {
case Message = "message"
case Avatar = "avatar"
case Feed = "topic"
}
}
/**
@@ -90,72 +98,6 @@ private func uploadFileToS3(inFilePath filePath: String?, orFileData fileData: N
uploadTask.resume()
}
// MARK: Upload
//extension NSMutableData {
//
// /// Append string to NSMutableData
// ///
// /// Rather than littering my code with calls to `dataUsingEncoding` to convert strings to NSData, and then add that data to the NSMutableData, this wraps it in a nice convenient little extension to NSMutableData. This converts using UTF-8.
// ///
// /// :param: string The string to be added to the `NSMutableData`.
//
// func appendString(string: String) {
// let data = string.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
// appendData(data!)
// }
//}
/// Create boundary string for multipart/form-data request
///
/// - returns: The boundary string that consists of "Boundary-" followed by a UUID string.
//func generateBoundaryString() -> String {
// return "Boundary-\(NSUUID().UUIDString)"
//}
/// Get S3 Private Message upload params
///
/// You can use this in Message Attachment
///
/// :S3UploadParams: The Upload Params
private func s3PrivateUploadParams(failureHandler failureHandler: ((Reason, String?) -> ())?, completion: (S3UploadParams) -> Void) {
s3UploadParams("/api/v1/attachments/s3_upload_form_fields", failureHandler: { (reason, error) in
if let failureHandler = failureHandler {
failureHandler(reason, error)
} else {
defaultFailureHandler(reason, errorMessage: error)
}
}, completion: { S3PrivateUploadParams in
completion(S3PrivateUploadParams)
})
}
/// Get S3 public upload params
///
/// You can use this in Avatar
///
/// :S3UploadParams: The Upload Params
private func s3PublicUploadParams(failureHandler failureHandler: ((Reason, String?) -> ())?, completion: (S3UploadParams) -> Void) {
s3UploadParams("/api/v1/attachments/s3_upload_public_form_fields", failureHandler: { (reason, error) in
if let failureHandler = failureHandler {
failureHandler(reason, error)
} else {
defaultFailureHandler(reason, errorMessage: error)
}
}, completion: { S3PublicUploadParams in
completion(S3PublicUploadParams)
})
}
/// Get S3 upload params
///
///
@@ -215,22 +157,28 @@ private func s3UploadParams(url: String, failureHandler: ((Reason, String?) -> (
}
}
private func s3UploadParamsOfKind(kind: S3UploadParams.Kind, failureHandler: ((Reason, String?) -> ())?, completion: (S3UploadParams) -> Void) {
s3UploadParams("/api/v1/attachments/\(kind.rawValue)/s3_upload_form_fields", failureHandler: { (reason, error) in
if let failureHandler = failureHandler {
failureHandler(reason, error)
} else {
defaultFailureHandler(reason, errorMessage: error)
}
}, completion: { S3PrivateUploadParams in
completion(S3PrivateUploadParams)
})
}
// API
func s3PublicUploadFile(inFilePath filePath: String?, orFileData fileData: NSData?, mimeType: String, failureHandler: ((Reason, String?) -> ())?, completion: S3UploadParams -> ()) {
func s3UploadFileOfKind(kind: S3UploadParams.Kind, inFilePath filePath: String?, orFileData fileData: NSData?, mimeType: String, failureHandler: ((Reason, String?) -> ())?, completion: S3UploadParams -> ()) {
s3PublicUploadParams(failureHandler: failureHandler) { s3UploadParams in
s3UploadParamsOfKind(kind, failureHandler: failureHandler) { s3UploadParams in
uploadFileToS3(inFilePath: filePath, orFileData: fileData, mimeType: mimeType, s3UploadParams: s3UploadParams, failureHandler: failureHandler) {
completion(s3UploadParams)
}
}
}
func s3PrivateUploadFile(inFilePath filePath: String?, orFileData fileData: NSData?, mimeType: String, failureHandler: ((Reason, String?) -> ())?, completion: S3UploadParams -> ()) {
s3PrivateUploadParams(failureHandler: failureHandler) { s3UploadParams in
uploadFileToS3(inFilePath: filePath, orFileData: fileData, mimeType: mimeType, s3UploadParams: s3UploadParams, failureHandler: failureHandler) {
completion(s3UploadParams)
}
}
}
@@ -20,6 +20,8 @@ struct MessageNotification {
class ConversationViewController: BaseViewController {
var feed: DiscoveredFeed?
var conversation: Conversation!
var realm: Realm!
@@ -120,7 +122,10 @@ class ConversationViewController: BaseViewController {
}()
var samplesCount = 0
let samplingInterval = 6
var feedView: FeedView?
var dragBeginLocation: CGPoint?
@IBOutlet weak var conversationCollectionView: UICollectionView!
let conversationCollectionViewContentInsetYOffset: CGFloat = 10
@@ -324,6 +329,22 @@ class ConversationViewController: BaseViewController {
}
}
}
// sync unread messages
if let recipient = conversation.recipient {
let timeDirection: TimeDirection
if let minMessageID = messages.last?.messageID {
timeDirection = .Future(minMessageID: minMessageID)
} else {
timeDirection = .None
}
messagesFromRecipient(recipient, withTimeDirection: timeDirection, failureHandler: nil, completion: { success in
println("messagesFromRecipient: \(success)")
})
}
}
@@ -337,6 +358,11 @@ class ConversationViewController: BaseViewController {
if isFirstAppear {
// test
if let feed = feed {
makeFeedViewWithFeed(feed)
}
// 稿
messageToolbar.conversation = conversation
@@ -393,6 +419,13 @@ class ConversationViewController: BaseViewController {
}
}
}
switch currentState {
case .BeginTextInput, .MoreMessages:
self?.tryFoldFeedView()
default:
break
}
}
}
@@ -1024,6 +1057,44 @@ class ConversationViewController: BaseViewController {
}
}
private func makeFeedViewWithFeed(feed: DiscoveredFeed) {
let feedView = FeedView.instanceFromNib()
feedView.feed = feed
feedView.foldAction = { [weak self] in
self?.conversationCollectionView.contentInset.top = 64 + FeedView.foldHeight + conversationCollectionViewContentInsetYOffset
}
feedView.unfoldAction = { [weak self] feedView in
self?.conversationCollectionView.contentInset.top = 64 + feedView.normalHeight + conversationCollectionViewContentInsetYOffset
}
//feedView.backgroundColor = UIColor.orangeColor()
feedView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(feedView)
let views = [
"feedView": feedView
]
let constraintsH = NSLayoutConstraint.constraintsWithVisualFormat("H:|[feedView]|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views)
//let constraintsV = NSLayoutConstraint.constraintsWithVisualFormat("V:|-64-[feedView(==height)]", options: NSLayoutFormatOptions(rawValue: 0), metrics: ["height": FeedView.normalHeight], views: views)
let top = NSLayoutConstraint(item: feedView, attribute: .Top, relatedBy: .Equal, toItem: view, attribute: .Top, multiplier: 1.0, constant: 64)
let height = NSLayoutConstraint(item: feedView, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: feedView.normalHeight)
NSLayoutConstraint.activateConstraints(constraintsH)
//NSLayoutConstraint.activateConstraints(constraintsV)
NSLayoutConstraint.activateConstraints([top, height])
feedView.heightConstraint = height
self.feedView = feedView
}
// MARK: Private
private func setConversaitonCollectionViewContentInsetBottom(bottom: CGFloat) {
@@ -1034,7 +1105,8 @@ class ConversationViewController: BaseViewController {
private func setConversaitonCollectionViewOriginalContentInset() {
conversationCollectionView.contentInset.top = 64 + conversationCollectionViewContentInsetYOffset
let feedViewHeight: CGFloat = (feedView == nil) ? 0 : feedView!.normalHeight
conversationCollectionView.contentInset.top = 64 + feedViewHeight + conversationCollectionViewContentInsetYOffset
setConversaitonCollectionViewContentInsetBottom(CGRectGetHeight(messageToolbar.bounds) + sectionInsetBottom)
}
@@ -1214,6 +1286,18 @@ class ConversationViewController: BaseViewController {
}
}
private func tryFoldFeedView() {
guard let feedView = feedView else {
return
}
if feedView.foldProgress != 1.0 {
feedView.foldProgress = 1.0
}
}
// MARK: Actions
func tapToCollapseMessageToolBar(sender: UITapGestureRecognizer) {
@@ -2463,9 +2547,24 @@ extension ConversationViewController: UICollectionViewDataSource, UICollectionVi
// MARK: UIScrollViewDelegate
func scrollViewWillBeginDragging(scrollView: UIScrollView) {
let location = scrollView.panGestureRecognizer.locationInView(view)
dragBeginLocation = location
}
func scrollViewDidScroll(scrollView: UIScrollView) {
pullToRefreshView.scrollViewDidScroll(scrollView)
if let dragBeginLocation = dragBeginLocation {
let location = scrollView.panGestureRecognizer.locationInView(view)
let deltaY = location.y - dragBeginLocation.y
if deltaY < -30 {
tryFoldFeedView()
}
}
}
func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
@@ -50,7 +50,9 @@ class ConversationsViewController: UIViewController {
}
lazy var conversations: Results<Conversation> = {
return self.realm.objects(Conversation).sorted("updatedUnixTime", ascending: false)
//return self.realm.objects(Conversation).sorted("updatedUnixTime", ascending: false)
let predicate = NSPredicate(format: "type = %d", ConversationType.OneToOne.rawValue)
return self.realm.objects(Conversation).filter(predicate).sorted("updatedUnixTime", ascending: false)
}()
struct Listener {
@@ -431,7 +431,7 @@ extension EditProfileViewController: UIImagePickerControllerDelegate, UINavigati
let image = image.largestCenteredSquareImage().resizeToTargetSize(YepConfig.avatarMaxSize())
let imageData = UIImageJPEGRepresentation(image, YepConfig.avatarCompressionQuality())
s3PublicUploadFile(inFilePath: nil, orFileData: imageData, mimeType: MessageMediaType.Image.mineType, failureHandler: { (reason, errorMessage) in
s3UploadFileOfKind(.Avatar, inFilePath: nil, orFileData: imageData, mimeType: MessageMediaType.Image.mineType, failureHandler: { (reason, errorMessage) in
defaultFailureHandler(reason, errorMessage: errorMessage)
@@ -0,0 +1,182 @@
//
// FeedsViewController.swift
// Yep
//
// Created by nixzhu on 15/9/25.
// Copyright © 2015 Catch Inc. All rights reserved.
//
import UIKit
import RealmSwift
class FeedsViewController: UIViewController {
@IBOutlet weak var feedsTableView: UITableView!
let feedCellID = "FeedCell"
lazy var noFeedsFooterView: InfoView = InfoView(NSLocalizedString("No Feeds.", comment: ""))
var feeds = [DiscoveredFeed]() {
didSet {
dispatch_async(dispatch_get_main_queue()) { [weak self] in
self?.feedsTableView.reloadData()
if let strongSelf = self {
strongSelf.feedsTableView.tableFooterView = strongSelf.feeds.isEmpty ? strongSelf.noFeedsFooterView : UIView()
}
}
}
}
private func heightOfFeed(feed: DiscoveredFeed) -> CGFloat {
let rect = feed.body.boundingRectWithSize(CGSize(width: FeedCell.messageLabelMaxWidth, height: CGFloat(FLT_MAX)), options: [.UsesLineFragmentOrigin, .UsesFontLeading], attributes: YepConfig.ChatCell.textAttributes, context: nil)
let height: CGFloat
if feed.attachments.isEmpty {
height = ceil(rect.height) + 10 + 40 + 4 + 10 + 17 + 10
} else {
height = ceil(rect.height) + 10 + 40 + 4 + 10 + 80 + 10 + 17 + 10
}
return ceil(height)
}
override func viewDidLoad() {
super.viewDidLoad()
title = NSLocalizedString("Feeds", comment: "")
feedsTableView.backgroundColor = UIColor.whiteColor()
feedsTableView.registerNib(UINib(nibName: feedCellID, bundle: nil), forCellReuseIdentifier: feedCellID)
feedsTableView.tableFooterView = UIView()
updateFeeds()
}
// MARK: - Actions
func updateFeeds() {
discoverFeedsWithSortStyle(.Time, pageIndex: 1, perPage: 100, failureHandler: { reason, errorMessage in
defaultFailureHandler(reason, errorMessage: errorMessage)
}, completion: { [weak self] feeds in
self?.feeds = feeds
println("discoverFeeds.count: \(feeds.count)")
})
}
// MARK: - Navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showNewFeed" {
let vc = segue.destinationViewController as! NewFeedViewController
vc.afterCreatedFeedAction = { [weak self] in
self?.updateFeeds()
}
}
if segue.identifier == "showConversation" {
guard let
index = sender as? Int,
feed = feeds[safe: index],
realm = try? Realm() else {
return
}
let vc = segue.destinationViewController as! ConversationViewController
let groupID = feed.groupID
var group = groupWithGroupID(groupID, inRealm: realm)
if group == nil {
let newGroup = Group()
newGroup.groupID = groupID
// TOOD: newGroup of Feed
/*
if let groupInfo = messageInfo["circle"] as? JSONDictionary {
if let groupName = groupInfo["name"] as? String {
newGroup.groupName = groupName
}
}
*/
realm.write {
realm.add(newGroup)
}
group = newGroup
}
guard let feedGroup = group else {
return
}
if feedGroup.conversation == nil {
let newConversation = Conversation()
newConversation.type = ConversationType.Group.rawValue
newConversation.withGroup = feedGroup
realm.write {
realm.add(newConversation)
}
}
guard let feedConversation = feedGroup.conversation else {
return
}
vc.conversation = feedConversation
vc.feed = feed
}
}
}
extension FeedsViewController: UITableViewDataSource, UITableViewDelegate {
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return feeds.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(feedCellID) as! FeedCell
let feed = feeds[indexPath.item]
cell.configureWithFeed(feed)
return cell
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
let feed = feeds[indexPath.item]
return heightOfFeed(feed)
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
performSegueWithIdentifier("showConversation", sender: indexPath.item)
}
}
@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="8191" systemVersion="14F27" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="8154"/>
</dependencies>
<scenes>
<!--New Feed View Controller-->
<scene sceneID="to0-yX-42w">
<objects>
<viewController storyboardIdentifier="NewFeedViewController" automaticallyAdjustsScrollViewInsets="NO" hidesBottomBarWhenPushed="YES" id="qLB-nJ-bBk" customClass="NewFeedViewController" customModule="Yep" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="P6L-c6-EtM"/>
<viewControllerLayoutGuide type="bottom" id="ntq-Pl-HIt"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="XTF-TD-X10">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="kWS-hy-hsK">
<rect key="frame" x="20" y="40" width="335" height="60"/>
<constraints>
<constraint firstAttribute="height" constant="60" id="I5A-Wd-eMC"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
</textView>
<collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="nqc-gK-NZL">
<rect key="frame" x="0.0" y="108" width="375" height="80"/>
<constraints>
<constraint firstAttribute="height" constant="80" id="rBg-4C-s5H"/>
</constraints>
<collectionViewFlowLayout key="collectionViewLayout" scrollDirection="horizontal" minimumLineSpacing="10" minimumInteritemSpacing="10" id="EJB-VM-0EX">
<size key="itemSize" width="50" height="50"/>
<size key="headerReferenceSize" width="0.0" height="0.0"/>
<size key="footerReferenceSize" width="0.0" height="0.0"/>
<inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
</collectionViewFlowLayout>
<cells/>
</collectionView>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="nqc-gK-NZL" secondAttribute="trailing" id="9aV-hx-aoj"/>
<constraint firstItem="nqc-gK-NZL" firstAttribute="leading" secondItem="XTF-TD-X10" secondAttribute="leading" id="be1-Wt-Njn"/>
<constraint firstItem="kWS-hy-hsK" firstAttribute="top" secondItem="P6L-c6-EtM" secondAttribute="bottom" constant="20" id="dKw-0O-6Mx"/>
<constraint firstItem="nqc-gK-NZL" firstAttribute="top" secondItem="kWS-hy-hsK" secondAttribute="bottom" constant="8" id="lP3-le-51b"/>
<constraint firstItem="kWS-hy-hsK" firstAttribute="leading" secondItem="XTF-TD-X10" secondAttribute="leading" constant="20" id="mNE-5D-ygq"/>
<constraint firstAttribute="trailing" secondItem="kWS-hy-hsK" secondAttribute="trailing" constant="20" id="mgZ-OL-1ch"/>
</constraints>
</view>
<simulatedScreenMetrics key="simulatedDestinationMetrics" type="retina47"/>
<connections>
<outlet property="mediaCollectionView" destination="nqc-gK-NZL" id="vUb-KN-uL1"/>
<outlet property="messageTextView" destination="kWS-hy-hsK" id="r5G-r3-3P5"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="JFr-yV-jlt" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="238.5" y="404.5"/>
</scene>
</scenes>
</document>
@@ -0,0 +1,280 @@
//
// NewFeedViewController.swift
// Yep
//
// Created by nixzhu on 15/9/29.
// Copyright © 2015 Catch Inc. All rights reserved.
//
import UIKit
import Proposer
import CoreLocation
import MobileCoreServices
class NewFeedViewController: UIViewController {
var afterCreatedFeedAction: (() -> Void)?
@IBOutlet weak var messageTextView: UITextView!
@IBOutlet weak var mediaCollectionView: UICollectionView!
lazy var imagePicker: UIImagePickerController = {
let imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.mediaTypes = [kUTTypeImage as String]
imagePicker.videoQuality = .TypeMedium
imagePicker.allowsEditing = false
return imagePicker
}()
var mediaImages = [UIImage]() {
didSet {
dispatch_async(dispatch_get_main_queue()) { [weak self] in
self?.mediaCollectionView.reloadData()
}
}
}
let feedMediaAddCellID = "FeedMediaAddCell"
let feedMediaCellID = "FeedMediaCell"
override func viewDidLoad() {
super.viewDidLoad()
title = NSLocalizedString("New Feed", comment: "")
let postButton = UIBarButtonItem(title: NSLocalizedString("Post", comment: ""), style: .Plain, target: self, action: "post:")
navigationItem.rightBarButtonItem = postButton
messageTextView.text = "What's up?"
messageTextView.backgroundColor = UIColor.lightGrayColor()
mediaCollectionView.backgroundColor = UIColor.clearColor()
mediaCollectionView.registerNib(UINib(nibName: feedMediaAddCellID, bundle: nil), forCellWithReuseIdentifier: feedMediaAddCellID)
mediaCollectionView.registerNib(UINib(nibName: feedMediaCellID, bundle: nil), forCellWithReuseIdentifier: feedMediaCellID)
mediaCollectionView.contentInset.left = 20
mediaCollectionView.dataSource = self
mediaCollectionView.delegate = self
// try turn on location
let locationResource = PrivateResource.Location(.WhenInUse)
if locationResource.isNotDeterminedAuthorization {
proposeToAccess(.Location(.WhenInUse), agreed: {
YepLocationService.turnOn()
}, rejected: {
self.alertCanNotAccessLocation()
})
} else {
proposeToAccess(.Location(.WhenInUse), agreed: {
YepLocationService.turnOn()
}, rejected: {
})
}
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
// touch to create (if need) for faster appear
delay(0.2) { [weak self] in
self?.imagePicker.hidesBarsOnTap = false
}
}
// MARK: Actions
func post(sender: UIBarButtonItem) {
YepHUD.showActivityIndicator()
let message = messageTextView.text
let coordinate = YepLocationService.sharedManager.currentLocation?.coordinate
let uploadMediaImagesGroup = dispatch_group_create()
var allS3UploadParams = [S3UploadParams]()
mediaImages.forEach({ image in
if let imageData = UIImageJPEGRepresentation(image, 0.7) {
dispatch_group_enter(uploadMediaImagesGroup)
s3UploadFileOfKind(.Feed, inFilePath: nil, orFileData: imageData, mimeType: MessageMediaType.Image.mineType, failureHandler: { (reason, errorMessage) in
defaultFailureHandler(reason, errorMessage: errorMessage)
dispatch_async(dispatch_get_main_queue()) {
dispatch_group_leave(uploadMediaImagesGroup)
}
}, completion: { s3UploadParams in
dispatch_async(dispatch_get_main_queue()) {
allS3UploadParams.append(s3UploadParams)
dispatch_group_leave(uploadMediaImagesGroup)
}
})
}
})
dispatch_group_notify(uploadMediaImagesGroup, dispatch_get_main_queue()) {
let imageInfosData = allS3UploadParams.map({
[
"file": $0.key,
"metadata": "", // TODO: metadata, maybe not need
]
})
let mediaInfo = [
"image": imageInfosData,
]
createFeedWithMessage(message, attachments: mediaInfo, coordinate: coordinate, skill: nil, allowComment: true, failureHandler: { [weak self] reason, errorMessage in
defaultFailureHandler(reason, errorMessage: errorMessage)
YepAlert.alertSorry(message: errorMessage ?? NSLocalizedString("Create feed failed!", comment: ""), inViewController: self)
YepHUD.hideActivityIndicator()
}, completion: { data in
println(data)
YepHUD.hideActivityIndicator()
dispatch_async(dispatch_get_main_queue()) { [weak self] in
self?.afterCreatedFeedAction?()
self?.navigationController?.popViewControllerAnimated(true)
}
})
}
}
}
extension NewFeedViewController: UICollectionViewDataSource, UICollectionViewDelegate {
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 2
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
switch section {
case 0:
return 1
case 1:
return mediaImages.count
default:
return 0
}
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
switch indexPath.section {
case 0:
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(feedMediaAddCellID, forIndexPath: indexPath) as! FeedMediaAddCell
return cell
case 1:
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(feedMediaCellID, forIndexPath: indexPath) as! FeedMediaCell
let image = mediaImages[indexPath.item]
cell.configureWithImage(image)
return cell
default:
return UICollectionViewCell()
}
}
func collectionView(collectionView: UICollectionView!, layout collectionViewLayout: UICollectionViewLayout!, sizeForItemAtIndexPath indexPath: NSIndexPath!) -> CGSize {
return CGSize(width: 80, height: 80)
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAtIndex section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 10)
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
switch indexPath.section {
case 0:
let openCameraRoll: ProposerAction = { [weak self] in
if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.PhotoLibrary) {
if let strongSelf = self {
strongSelf.imagePicker.sourceType = .SavedPhotosAlbum
strongSelf.presentViewController(strongSelf.imagePicker, animated: true, completion: nil)
}
}
}
proposeToAccess(.Photos, agreed: openCameraRoll, rejected: { [weak self] in
self?.alertCanNotAccessCameraRoll()
})
default:
break
}
}
}
extension NewFeedViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
if let mediaType = info[UIImagePickerControllerMediaType] as? String {
if mediaType == (kUTTypeImage as String) {
if let image = info[UIImagePickerControllerOriginalImage] as? UIImage {
let imageWidth = image.size.width
let imageHeight = image.size.height
let fixedImageWidth: CGFloat
let fixedImageHeight: CGFloat
if imageWidth > imageHeight {
fixedImageWidth = min(imageWidth, YepConfig.Media.imageWidth)
fixedImageHeight = imageHeight * (fixedImageWidth / imageWidth)
} else {
fixedImageHeight = min(imageHeight, YepConfig.Media.imageHeight)
fixedImageWidth = imageWidth * (fixedImageHeight / imageHeight)
}
let fixedSize = CGSize(width: fixedImageWidth, height: fixedImageHeight)
// resize to smaller, not need fixRotation
if let fixedImage = image.resizeToSize(fixedSize, withInterpolationQuality: CGInterpolationQuality.Medium) {
mediaImages.append(fixedImage)
}
}
}
}
dismissViewControllerAnimated(true, completion: nil)
}
}
@@ -228,7 +228,7 @@ class RegisterPickAvatarViewController: UIViewController {
avatar = self.avatar.largestCenteredSquareImage().resizeToTargetSize(YepConfig.avatarMaxSize())
let imageData = UIImageJPEGRepresentation(avatar, YepConfig.avatarCompressionQuality())
s3PublicUploadFile(inFilePath: nil, orFileData: imageData, mimeType: MessageMediaType.Image.mineType, failureHandler: { (reason, errorMessage) in
s3UploadFileOfKind(.Avatar, inFilePath: nil, orFileData: imageData, mimeType: MessageMediaType.Image.mineType, failureHandler: { (reason, errorMessage) in
defaultFailureHandler(reason, errorMessage: errorMessage)
@@ -487,7 +487,7 @@ extension SkillHomeViewController: UIImagePickerControllerDelegate, UINavigation
YepHUD.showActivityIndicator()
s3PublicUploadFile(inFilePath: nil, orFileData: data, mimeType: MessageMediaType.Image.mineType, failureHandler: { [weak self] reason, errorMessage in
s3UploadFileOfKind(.Avatar, inFilePath: nil, orFileData: data, mimeType: MessageMediaType.Image.mineType, failureHandler: { [weak self] reason, errorMessage in
YepHUD.hideActivityIndicator()
@@ -42,6 +42,7 @@ class YepTabBarController: UITabBarController {
let titles = [
NSLocalizedString("Chats", comment: ""),
NSLocalizedString("Contacts", comment: ""),
NSLocalizedString("Feeds", comment: ""),
NSLocalizedString("Discover", comment: ""),
NSLocalizedString("Profile", comment: ""),
]
+122
View File
@@ -0,0 +1,122 @@
//
// FeedCell.swift
// Yep
//
// Created by nixzhu on 15/9/30.
// Copyright © 2015 Catch Inc. All rights reserved.
//
import UIKit
class FeedCell: UITableViewCell {
@IBOutlet weak var avatarImageView: UIImageView!
@IBOutlet weak var nicknameLabel: UILabel!
@IBOutlet weak var distanceLabel: UILabel!
@IBOutlet weak var messageLabel: UILabel!
@IBOutlet weak var mediaCollectionView: UICollectionView!
@IBOutlet weak var timeLabel: UILabel!
@IBOutlet weak var timeLabelTopConstraint: NSLayoutConstraint!
@IBOutlet weak var messageCountLabel: UILabel!
var attachmentURLs = [NSURL]() {
didSet {
mediaCollectionView.reloadData()
}
}
static let messageLabelMaxWidth: CGFloat = {
let maxWidth = UIScreen.mainScreen().bounds.width - (60 + 10)
return maxWidth
}()
let feedMediaCellID = "FeedMediaCell"
override func awakeFromNib() {
super.awakeFromNib()
nicknameLabel.textColor = UIColor.yepTintColor()
messageLabel.textColor = UIColor.darkGrayColor()
distanceLabel.textColor = UIColor.grayColor()
timeLabel.textColor = UIColor.grayColor()
messageCountLabel.textColor = UIColor.yepTintColor()
messageLabel.font = UIFont.feedMessageFont()
mediaCollectionView.backgroundColor = UIColor.clearColor()
mediaCollectionView.registerNib(UINib(nibName: feedMediaCellID, bundle: nil), forCellWithReuseIdentifier: feedMediaCellID)
mediaCollectionView.dataSource = self
mediaCollectionView.delegate = self
mediaCollectionView.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: "cell")
}
func configureWithFeed(feed: DiscoveredFeed) {
messageLabel.text = feed.body
let hasMedia = !feed.attachments.isEmpty
timeLabelTopConstraint.constant = hasMedia ? 100 : 10
mediaCollectionView.hidden = hasMedia ? false : true
attachmentURLs = feed.attachments.map({ NSURL(string: $0.URLString) }).flatMap({ $0 })
let avatarURLString = feed.creator.avatarURLString
let radius = min(CGRectGetWidth(avatarImageView.bounds), CGRectGetHeight(avatarImageView.bounds)) * 0.5
AvatarCache.sharedInstance.roundAvatarWithAvatarURLString(avatarURLString, withRadius: radius) { [weak self] roundImage in
dispatch_async(dispatch_get_main_queue()) {
//if let _ = tableView.cellForRowAtIndexPath(indexPath) {
self?.avatarImageView.image = roundImage
//}
}
}
nicknameLabel.text = feed.creator.nickname
if let distance = feed.distance?.format(".1") {
distanceLabel.text = "\(distance) km"
}
timeLabel.text = "\(NSDate(timeIntervalSince1970: feed.createdUnixTime).timeAgo)"
messageCountLabel.text = "\(feed.messageCount)"
}
}
extension FeedCell: UICollectionViewDataSource, UICollectionViewDelegate {
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return attachmentURLs.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(feedMediaCellID, forIndexPath: indexPath) as! FeedMediaCell
let imageURL = attachmentURLs[indexPath.item]
println("attachment imageURL: \(imageURL)")
cell.configureWithImageURL(imageURL)
return cell
}
func collectionView(collectionView: UICollectionView!, layout collectionViewLayout: UICollectionViewLayout!, sizeForItemAtIndexPath indexPath: NSIndexPath!) -> CGSize {
return CGSize(width: 80, height: 80)
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAtIndex section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
}
}
+110
View File
@@ -0,0 +1,110 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="8191" systemVersion="14F27" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="8154"/>
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="FeedCell" rowHeight="200" id="KGk-i7-Jjw" customClass="FeedCell" customModule="Yep" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="381" height="200"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
<rect key="frame" x="0.0" y="0.0" width="381" height="199.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="2 hours ago" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="yFm-kf-Ghp" userLabel="Time Label">
<rect key="frame" x="60" y="172" width="76" height="17"/>
<fontDescription key="fontDescription" type="system" weight="light" pointSize="14"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="How do you do?" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="WEl-kb-LcL">
<rect key="frame" x="60" y="54" width="109" height="18"/>
<fontDescription key="fontDescription" type="system" weight="light" pointSize="15"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="none" translatesAutoresizingMaskIntoConstraints="NO" id="COt-7F-KVK">
<rect key="frame" x="60" y="82" width="321" height="80"/>
<constraints>
<constraint firstAttribute="height" constant="80" id="6PE-QR-i1C"/>
</constraints>
<collectionViewFlowLayout key="collectionViewLayout" scrollDirection="horizontal" minimumLineSpacing="5" minimumInteritemSpacing="0.0" id="sxC-Ge-mF8">
<size key="itemSize" width="50" height="50"/>
<size key="headerReferenceSize" width="0.0" height="0.0"/>
<size key="footerReferenceSize" width="0.0" height="0.0"/>
<inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
</collectionViewFlowLayout>
</collectionView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="NIX" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="d97-mK-bJE" userLabel="Nickname Label">
<rect key="frame" x="60" y="21" width="25" height="18"/>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="icon_discussion" translatesAutoresizingMaskIntoConstraints="NO" id="B9B-Nc-h8k">
<rect key="frame" x="333.5" y="171" width="30" height="19"/>
</imageView>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="GRs-kP-Ugk" userLabel="Avatar Image View">
<rect key="frame" x="10" y="10" width="40" height="40"/>
<constraints>
<constraint firstAttribute="width" constant="40" id="iWz-Lj-rIE"/>
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="1.2 km" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="uir-A9-aaJ" userLabel="Distance Label">
<rect key="frame" x="322.5" y="22" width="40.5" height="17"/>
<fontDescription key="fontDescription" type="system" weight="light" pointSize="14"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="42" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="6k9-TF-Ftd" userLabel="Message Count Label">
<rect key="frame" x="306.5" y="172" width="17" height="17"/>
<fontDescription key="fontDescription" type="system" weight="light" pointSize="14"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="WEl-kb-LcL" firstAttribute="top" secondItem="GRs-kP-Ugk" secondAttribute="bottom" constant="4" id="1dk-I4-Thh"/>
<constraint firstItem="WEl-kb-LcL" firstAttribute="trailing" relation="lessThanOrEqual" secondItem="uir-A9-aaJ" secondAttribute="trailing" id="440-LJ-l3f"/>
<constraint firstItem="WEl-kb-LcL" firstAttribute="leading" secondItem="d97-mK-bJE" secondAttribute="leading" id="4i1-eZ-94H"/>
<constraint firstAttribute="trailingMargin" secondItem="uir-A9-aaJ" secondAttribute="trailing" constant="10" id="8NU-fY-2JM"/>
<constraint firstAttribute="trailing" secondItem="COt-7F-KVK" secondAttribute="trailing" id="AfO-Hm-djL"/>
<constraint firstItem="B9B-Nc-h8k" firstAttribute="leading" secondItem="6k9-TF-Ftd" secondAttribute="trailing" constant="10" id="JkX-QS-ZmS"/>
<constraint firstItem="yFm-kf-Ghp" firstAttribute="top" secondItem="WEl-kb-LcL" secondAttribute="bottom" constant="100" id="K9W-zF-0zZ"/>
<constraint firstItem="d97-mK-bJE" firstAttribute="leading" secondItem="GRs-kP-Ugk" secondAttribute="trailing" constant="10" id="QXo-33-0ak"/>
<constraint firstItem="B9B-Nc-h8k" firstAttribute="centerY" secondItem="yFm-kf-Ghp" secondAttribute="centerY" id="RQK-md-Cni"/>
<constraint firstItem="6k9-TF-Ftd" firstAttribute="centerY" secondItem="B9B-Nc-h8k" secondAttribute="centerY" id="Sk6-jV-ssD"/>
<constraint firstItem="B9B-Nc-h8k" firstAttribute="trailing" secondItem="uir-A9-aaJ" secondAttribute="trailing" id="VNI-Ix-3aU"/>
<constraint firstItem="d97-mK-bJE" firstAttribute="centerY" secondItem="GRs-kP-Ugk" secondAttribute="centerY" id="XVe-o1-zRc"/>
<constraint firstItem="yFm-kf-Ghp" firstAttribute="leading" secondItem="COt-7F-KVK" secondAttribute="leading" id="bzY-iG-ieh"/>
<constraint firstItem="GRs-kP-Ugk" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" constant="10" id="cna-DW-PPM"/>
<constraint firstItem="GRs-kP-Ugk" firstAttribute="width" secondItem="GRs-kP-Ugk" secondAttribute="height" multiplier="1:1" id="eSL-Jv-LjB"/>
<constraint firstItem="COt-7F-KVK" firstAttribute="leading" secondItem="WEl-kb-LcL" secondAttribute="leading" id="ioj-al-K07"/>
<constraint firstItem="COt-7F-KVK" firstAttribute="top" secondItem="WEl-kb-LcL" secondAttribute="bottom" constant="10" id="iqg-Wr-S7c"/>
<constraint firstItem="GRs-kP-Ugk" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="10" id="tIk-7d-Ux7"/>
<constraint firstItem="uir-A9-aaJ" firstAttribute="centerY" secondItem="d97-mK-bJE" secondAttribute="centerY" id="vTi-1C-OoG"/>
</constraints>
</tableViewCellContentView>
<inset key="separatorInset" minX="60" minY="0.0" maxX="0.0" maxY="0.0"/>
<connections>
<outlet property="avatarImageView" destination="GRs-kP-Ugk" id="Rch-2A-tIe"/>
<outlet property="distanceLabel" destination="uir-A9-aaJ" id="asK-JQ-ew5"/>
<outlet property="mediaCollectionView" destination="COt-7F-KVK" id="ldA-nK-8V3"/>
<outlet property="messageCountLabel" destination="6k9-TF-Ftd" id="GNB-Qx-e2z"/>
<outlet property="messageLabel" destination="WEl-kb-LcL" id="EDB-JJ-Eb4"/>
<outlet property="nicknameLabel" destination="d97-mK-bJE" id="xcQ-hs-iRs"/>
<outlet property="timeLabel" destination="yFm-kf-Ghp" id="Uph-sR-xxS"/>
<outlet property="timeLabelTopConstraint" destination="K9W-zF-0zZ" id="CV7-PW-k7e"/>
</connections>
<point key="canvasLocation" x="322.5" y="375"/>
</tableViewCell>
</objects>
<resources>
<image name="icon_discussion" width="30" height="19"/>
</resources>
</document>
@@ -0,0 +1,36 @@
//
// FeedMediaCell.swift
// Yep
//
// Created by nixzhu on 15/9/30.
// Copyright © 2015 Catch Inc. All rights reserved.
//
import UIKit
import Kingfisher
class FeedMediaCell: UICollectionViewCell {
@IBOutlet weak var imageView: UIImageView!
@IBOutlet weak var deleteImageView: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
contentView.backgroundColor = UIColor.clearColor()
}
func configureWithImage(image: UIImage) {
imageView.image = image
deleteImageView.hidden = false
}
func configureWithImageURL(imageURL: NSURL) {
imageView.kf_setImageWithURL(imageURL)
deleteImageView.hidden = true
}
}
@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="8191" systemVersion="14F27" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="8154"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="FeedMediaCell" id="gTV-IL-0wX" customClass="FeedMediaCell" customModule="Yep" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="80" height="80"/>
<autoresizingMask key="autoresizingMask"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
<rect key="frame" x="0.0" y="0.0" width="80" height="80"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Aee-O7-EqM">
<rect key="frame" x="0.0" y="0.0" width="80" height="80"/>
</imageView>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="icon_media_delete" translatesAutoresizingMaskIntoConstraints="NO" id="hUo-wW-DEo">
<rect key="frame" x="56" y="8" width="16" height="16"/>
</imageView>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</view>
<constraints>
<constraint firstAttribute="trailing" secondItem="hUo-wW-DEo" secondAttribute="trailing" constant="8" id="cve-33-J4n"/>
<constraint firstAttribute="trailing" secondItem="Aee-O7-EqM" secondAttribute="trailing" id="d2z-Xc-HRU"/>
<constraint firstItem="Aee-O7-EqM" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leading" id="dhp-RU-Pr8"/>
<constraint firstAttribute="bottom" secondItem="Aee-O7-EqM" secondAttribute="bottom" id="eRp-F5-4wX"/>
<constraint firstItem="Aee-O7-EqM" firstAttribute="top" secondItem="gTV-IL-0wX" secondAttribute="top" id="o9g-qX-Yef"/>
<constraint firstItem="hUo-wW-DEo" firstAttribute="top" secondItem="gTV-IL-0wX" secondAttribute="top" constant="8" id="pIZ-QQ-Aek"/>
</constraints>
<size key="customSize" width="120" height="116"/>
<connections>
<outlet property="deleteImageView" destination="hUo-wW-DEo" id="DrB-Br-qyb"/>
<outlet property="imageView" destination="Aee-O7-EqM" id="X5m-t5-bCA"/>
</connections>
<point key="canvasLocation" x="306" y="341"/>
</collectionViewCell>
</objects>
<resources>
<image name="icon_media_delete" width="16" height="16"/>
</resources>
</document>
@@ -0,0 +1,18 @@
//
// FeedMediaAddCell.swift
// Yep
//
// Created by nixzhu on 15/9/30.
// Copyright © 2015 Catch Inc. All rights reserved.
//
import UIKit
class FeedMediaAddCell: UICollectionViewCell {
override func awakeFromNib() {
super.awakeFromNib()
contentView.backgroundColor = UIColor.lightGrayColor().colorWithAlphaComponent(0.5)
}
}
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="8191" systemVersion="14F27" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="8154"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="FeedMediaAddCell" id="gTV-IL-0wX" customClass="FeedMediaAddCell" customModule="Yep" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="80" height="80"/>
<autoresizingMask key="autoresizingMask"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
<rect key="frame" x="0.0" y="0.0" width="80" height="80"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="feed_media_add" translatesAutoresizingMaskIntoConstraints="NO" id="wLD-ko-O7n">
<rect key="frame" x="25" y="25" width="30" height="30"/>
</imageView>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</view>
<constraints>
<constraint firstItem="wLD-ko-O7n" firstAttribute="centerY" secondItem="gTV-IL-0wX" secondAttribute="centerY" id="L6z-rD-aF6"/>
<constraint firstItem="wLD-ko-O7n" firstAttribute="centerX" secondItem="gTV-IL-0wX" secondAttribute="centerX" id="ibY-N2-mvO"/>
</constraints>
<size key="customSize" width="100" height="93"/>
<point key="canvasLocation" x="184" y="241.5"/>
</collectionViewCell>
</objects>
<resources>
<image name="feed_media_add" width="30" height="30"/>
</resources>
</document>
+111
View File
@@ -0,0 +1,111 @@
//
// FeedMediaView.swift
// Yep
//
// Created by nixzhu on 15/9/28.
// Copyright © 2015 Catch Inc. All rights reserved.
//
import UIKit
import Kingfisher
class FeedMediaView: UIView {
lazy var imageView1: UIImageView = {
let view = UIImageView()
view.contentMode = .ScaleAspectFill
view.clipsToBounds = true
return view
}()
lazy var imageView2: UIImageView = {
let view = UIImageView()
view.contentMode = .ScaleAspectFill
view.clipsToBounds = true
return view
}()
lazy var imageView3: UIImageView = {
let view = UIImageView()
view.contentMode = .ScaleAspectFill
view.clipsToBounds = true
return view
}()
lazy var imageView4: UIImageView = {
let view = UIImageView()
view.contentMode = .ScaleAspectFill
view.clipsToBounds = true
return view
}()
func setImagesWithURLs(URLs: [NSURL]) {
let fullRect = bounds
let halfRect = CGRect(x: 0, y: 0, width: fullRect.width * 0.5, height: fullRect.height)
let quarterRect = CGRect(x: 0, y: 0, width: fullRect.width * 0.5, height: fullRect.height * 0.5)
switch URLs.count {
case 1:
imageView1.frame = fullRect
imageView1.kf_setImageWithURL(URLs[0])
addSubview(imageView1)
case 2:
imageView1.frame = halfRect
imageView1.center = CGPoint(x: halfRect.width * 0.5, y: imageView1.center.y)
imageView1.kf_setImageWithURL(URLs[0])
imageView2.frame = halfRect
imageView2.center = CGPoint(x: halfRect.width * 1.5, y: imageView2.center.y)
imageView2.kf_setImageWithURL(URLs[1])
addSubview(imageView1)
addSubview(imageView2)
case 3:
imageView1.frame = quarterRect
imageView1.kf_setImageWithURL(URLs[0])
imageView2.frame = quarterRect
imageView2.center = CGPoint(x: imageView2.center.x, y: quarterRect.height * 1.5)
imageView2.kf_setImageWithURL(URLs[1])
imageView3.frame = halfRect
imageView3.center = CGPoint(x: halfRect.width * 1.5, y: imageView3.center.y)
imageView3.kf_setImageWithURL(URLs[2])
addSubview(imageView1)
addSubview(imageView2)
addSubview(imageView3)
case 4..<Int.max:
imageView1.frame = quarterRect
imageView1.kf_setImageWithURL(URLs[0])
imageView2.frame = quarterRect
imageView2.center = CGPoint(x: imageView2.center.x, y: quarterRect.height * 1.5)
imageView2.kf_setImageWithURL(URLs[1])
imageView3.frame = quarterRect
imageView3.center = CGPoint(x: quarterRect.width * 1.5, y: imageView3.center.y)
imageView3.kf_setImageWithURL(URLs[2])
imageView4.frame = quarterRect
imageView4.center = CGPoint(x: quarterRect.width * 1.5, y: quarterRect.height * 1.5)
imageView4.kf_setImageWithURL(URLs[3])
addSubview(imageView1)
addSubview(imageView2)
addSubview(imageView3)
addSubview(imageView4)
default:
break
}
}
}
+209
View File
@@ -0,0 +1,209 @@
//
// FeedView.swift
// Yep
//
// Created by nixzhu on 15/9/25.
// Copyright © 2015 Catch Inc. All rights reserved.
//
import UIKit
class FeedView: UIView {
var feed: DiscoveredFeed? {
willSet {
if let feed = newValue {
configureWithFeed(feed)
}
}
}
static let foldHeight: CGFloat = 60
weak var heightConstraint: NSLayoutConstraint?
class func instanceFromNib() -> FeedView {
return UINib(nibName: "FeedView", bundle: nil).instantiateWithOwner(nil, options: nil)[0] as! FeedView
}
var foldProgress: CGFloat = 0 {
willSet {
if newValue >= 0 && newValue <= 1 {
let normalHeight = self.normalHeight
UIView.animateWithDuration(0.3, delay: 0, usingSpringWithDamping: 0.75, initialSpringVelocity: 0.0, options: UIViewAnimationOptions(rawValue: 0), animations: { [weak self] in
self?.nicknameLabelCenterYConstraint.constant = -10 * newValue
self?.messageLabelTopConstraint.constant = -25 * newValue + 4
if newValue == 1.0 {
self?.messageLabelTrailingConstraint.constant = -(40 + 10)
self?.messageLabel.numberOfLines = 1
}
if newValue == 0.0 {
self?.messageLabelTrailingConstraint.constant = 0
self?.messageLabel.numberOfLines = 0
}
self?.heightConstraint?.constant = FeedView.foldHeight + (normalHeight - FeedView.foldHeight) * (1 - newValue)
self?.layoutIfNeeded()
let foldingAlpha = (1 - newValue)
self?.distanceLabel.alpha = foldingAlpha
self?.mediaCollectionView.alpha = foldingAlpha
self?.timeLabel.alpha = foldingAlpha
self?.mediaView.alpha = newValue
}, completion: nil)
if newValue == 1.0 {
foldAction?()
}
if newValue == 0.0 {
unfoldAction?(self)
}
}
}
}
var foldAction: (() -> Void)?
var unfoldAction: (FeedView -> Void)?
@IBOutlet weak var avatarImageView: UIImageView!
@IBOutlet weak var nicknameLabel: UILabel!
@IBOutlet weak var nicknameLabelCenterYConstraint: NSLayoutConstraint!
@IBOutlet weak var distanceLabel: UILabel!
@IBOutlet weak var mediaView: FeedMediaView!
@IBOutlet weak var messageLabel: UILabel!
@IBOutlet weak var messageLabelTopConstraint: NSLayoutConstraint!
@IBOutlet weak var messageLabelTrailingConstraint: NSLayoutConstraint!
@IBOutlet weak var mediaCollectionView: UICollectionView!
@IBOutlet weak var timeLabel: UILabel!
@IBOutlet weak var timeLabelTopConstraint: NSLayoutConstraint!
var attachmentURLs = [NSURL]() {
didSet {
mediaCollectionView.reloadData()
mediaView.setImagesWithURLs(attachmentURLs)
}
}
let feedMediaCellID = "FeedMediaCell"
override func awakeFromNib() {
super.awakeFromNib()
clipsToBounds = true
nicknameLabel.textColor = UIColor.yepTintColor()
messageLabel.textColor = UIColor.darkGrayColor()
distanceLabel.textColor = UIColor.grayColor()
timeLabel.textColor = UIColor.grayColor()
messageLabel.font = UIFont.feedMessageFont()
mediaView.alpha = 0
mediaCollectionView.backgroundColor = UIColor.clearColor()
mediaCollectionView.registerNib(UINib(nibName: feedMediaCellID, bundle: nil), forCellWithReuseIdentifier: feedMediaCellID)
mediaCollectionView.dataSource = self
mediaCollectionView.delegate = self
//mediaCollectionView.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: "cell")
let tap = UITapGestureRecognizer(target: self, action: "unfold:")
mediaView.addGestureRecognizer(tap)
}
func unfold(sender: UITapGestureRecognizer) {
foldProgress = 0
}
var normalHeight: CGFloat {
guard let feed = feed else {
return 220
}
let rect = feed.body.boundingRectWithSize(CGSize(width: FeedCell.messageLabelMaxWidth, height: CGFloat(FLT_MAX)), options: [.UsesLineFragmentOrigin, .UsesFontLeading], attributes: YepConfig.ChatCell.textAttributes, context: nil)
let height: CGFloat
if feed.attachments.isEmpty {
height = ceil(rect.height) + 10 + 40 + 4 + 10 + 17 + 10
} else {
height = ceil(rect.height) + 10 + 40 + 4 + 10 + 80 + 10 + 17 + 10
}
return ceil(height)
}
private func configureWithFeed(feed: DiscoveredFeed) {
messageLabel.text = feed.body
let hasMedia = !feed.attachments.isEmpty
timeLabelTopConstraint.constant = hasMedia ? 100 : 10
mediaCollectionView.hidden = hasMedia ? false : true
attachmentURLs = feed.attachments.map({ NSURL(string: $0.URLString) }).flatMap({ $0 })
let avatarURLString = feed.creator.avatarURLString
let radius = min(CGRectGetWidth(avatarImageView.bounds), CGRectGetHeight(avatarImageView.bounds)) * 0.5
AvatarCache.sharedInstance.roundAvatarWithAvatarURLString(avatarURLString, withRadius: radius) { [weak self] roundImage in
dispatch_async(dispatch_get_main_queue()) {
self?.avatarImageView.image = roundImage
}
}
nicknameLabel.text = feed.creator.nickname
if let distance = feed.distance?.format(".1") {
distanceLabel.text = "\(distance) km"
}
timeLabel.text = "\(NSDate(timeIntervalSince1970: feed.createdUnixTime).timeAgo)"
}
}
extension FeedView: UICollectionViewDataSource, UICollectionViewDelegate {
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return attachmentURLs.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(feedMediaCellID, forIndexPath: indexPath) as! FeedMediaCell
let imageURL = attachmentURLs[indexPath.item]
println("attachment imageURL: \(imageURL)")
cell.configureWithImageURL(imageURL)
return cell
}
func collectionView(collectionView: UICollectionView!, layout collectionViewLayout: UICollectionViewLayout!, sizeForItemAtIndexPath indexPath: NSIndexPath!) -> CGSize {
return CGSize(width: 80, height: 80)
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAtIndex section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
}
}
+119
View File
@@ -0,0 +1,119 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="8191" systemVersion="14F27" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="8154"/>
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="iN0-l3-epB" customClass="FeedView" customModule="Yep" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="419" height="225"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="0Hb-fw-n9b" userLabel="Avatar Image View">
<rect key="frame" x="10" y="10" width="40" height="40"/>
<constraints>
<constraint firstAttribute="width" constant="40" id="ybP-zf-3Yo"/>
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="NIX" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="C2E-Yo-twp" userLabel="Nickname Label">
<rect key="frame" x="60" y="21" width="25" height="18"/>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="1.2 km" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ffh-Mx-17Q" userLabel="Distance Label">
<rect key="frame" x="368" y="22" width="40.5" height="17"/>
<fontDescription key="fontDescription" type="system" weight="light" pointSize="14"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="How do you do? Do not go gentle into that good night." textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="6i2-Pl-6hN">
<rect key="frame" x="60" y="54" width="328" height="36"/>
<fontDescription key="fontDescription" type="system" weight="light" pointSize="15"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="none" translatesAutoresizingMaskIntoConstraints="NO" id="umP-Za-loD">
<rect key="frame" x="60" y="100" width="359" height="80"/>
<constraints>
<constraint firstAttribute="height" constant="80" id="BNj-Sa-n9K"/>
</constraints>
<collectionViewFlowLayout key="collectionViewLayout" scrollDirection="horizontal" minimumLineSpacing="5" minimumInteritemSpacing="0.0" id="88J-Kb-huC">
<size key="itemSize" width="50" height="50"/>
<size key="headerReferenceSize" width="0.0" height="0.0"/>
<size key="footerReferenceSize" width="0.0" height="0.0"/>
<inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
</collectionViewFlowLayout>
</collectionView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="2 hours ago" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Ix5-dG-THW" userLabel="Time Label">
<rect key="frame" x="60" y="190" width="76" height="17"/>
<fontDescription key="fontDescription" type="system" weight="light" pointSize="14"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="r5V-GF-1ON" customClass="FeedMediaView" customModule="Yep" customModuleProvider="target">
<rect key="frame" x="369" y="10" width="40" height="40"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="width" constant="40" id="8TR-dN-ztJ"/>
<constraint firstAttribute="height" constant="40" id="TDm-8m-Yrn"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="wH6-T7-mOv" customClass="HorizontalLineView" customModule="Yep" customModuleProvider="target">
<rect key="frame" x="0.0" y="224" width="419" height="1"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="height" constant="1" id="1ms-yL-uLa"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="0Hb-fw-n9b" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="10" id="IUd-8S-7JE"/>
<constraint firstAttribute="trailing" secondItem="ffh-Mx-17Q" secondAttribute="trailing" constant="10" id="Irr-i3-cMb"/>
<constraint firstItem="6i2-Pl-6hN" firstAttribute="top" secondItem="0Hb-fw-n9b" secondAttribute="bottom" constant="4" id="Jch-Vu-xwA"/>
<constraint firstAttribute="trailing" secondItem="umP-Za-loD" secondAttribute="trailing" id="SSw-dY-XgC"/>
<constraint firstItem="Ix5-dG-THW" firstAttribute="leading" secondItem="umP-Za-loD" secondAttribute="leading" id="Vq0-HZ-3E7"/>
<constraint firstItem="0Hb-fw-n9b" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="10" id="aje-GN-Nlw"/>
<constraint firstItem="C2E-Yo-twp" firstAttribute="leading" secondItem="0Hb-fw-n9b" secondAttribute="trailing" constant="10" id="cGH-ZO-Eiv"/>
<constraint firstItem="r5V-GF-1ON" firstAttribute="trailing" secondItem="ffh-Mx-17Q" secondAttribute="trailing" id="cRo-WX-ifz"/>
<constraint firstAttribute="bottom" secondItem="wH6-T7-mOv" secondAttribute="bottom" id="eKo-DO-p72"/>
<constraint firstAttribute="trailing" secondItem="wH6-T7-mOv" secondAttribute="trailing" id="gaQ-Ac-b0T"/>
<constraint firstItem="6i2-Pl-6hN" firstAttribute="trailing" relation="lessThanOrEqual" secondItem="ffh-Mx-17Q" secondAttribute="trailing" id="i9m-7j-Roh"/>
<constraint firstItem="r5V-GF-1ON" firstAttribute="top" secondItem="0Hb-fw-n9b" secondAttribute="top" id="iV6-9c-4SJ"/>
<constraint firstItem="umP-Za-loD" firstAttribute="top" secondItem="6i2-Pl-6hN" secondAttribute="bottom" constant="10" id="ibW-Ne-b33"/>
<constraint firstItem="Ix5-dG-THW" firstAttribute="top" secondItem="wH6-T7-mOv" secondAttribute="bottom" id="jYt-zy-Fkb"/>
<constraint firstItem="ffh-Mx-17Q" firstAttribute="centerY" secondItem="C2E-Yo-twp" secondAttribute="centerY" id="lAd-63-gax"/>
<constraint firstItem="C2E-Yo-twp" firstAttribute="centerY" secondItem="0Hb-fw-n9b" secondAttribute="centerY" id="lSc-cs-Lug"/>
<constraint firstItem="Ix5-dG-THW" firstAttribute="top" secondItem="6i2-Pl-6hN" secondAttribute="bottom" constant="100" id="mCj-xS-Tud"/>
<constraint firstItem="umP-Za-loD" firstAttribute="leading" secondItem="6i2-Pl-6hN" secondAttribute="leading" id="mIy-L4-SXl"/>
<constraint firstItem="wH6-T7-mOv" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="niy-IC-PEM"/>
<constraint firstItem="6i2-Pl-6hN" firstAttribute="leading" secondItem="C2E-Yo-twp" secondAttribute="leading" id="rh1-wd-ajF"/>
<constraint firstItem="0Hb-fw-n9b" firstAttribute="width" secondItem="0Hb-fw-n9b" secondAttribute="height" multiplier="1:1" id="sFg-ds-P8V"/>
</constraints>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<variation key="default">
<mask key="constraints">
<exclude reference="jYt-zy-Fkb"/>
</mask>
</variation>
<connections>
<outlet property="avatarImageView" destination="0Hb-fw-n9b" id="AAN-W3-uXW"/>
<outlet property="distanceLabel" destination="ffh-Mx-17Q" id="rwL-f4-mRt"/>
<outlet property="mediaCollectionView" destination="umP-Za-loD" id="4wa-a9-jxB"/>
<outlet property="mediaView" destination="r5V-GF-1ON" id="tPB-3w-zKI"/>
<outlet property="messageLabel" destination="6i2-Pl-6hN" id="8hF-9H-5JD"/>
<outlet property="messageLabelTopConstraint" destination="Jch-Vu-xwA" id="nno-Xq-YwL"/>
<outlet property="messageLabelTrailingConstraint" destination="i9m-7j-Roh" id="0TW-qO-Gzq"/>
<outlet property="nicknameLabel" destination="C2E-Yo-twp" id="gHO-Mm-Ycn"/>
<outlet property="nicknameLabelCenterYConstraint" destination="lSc-cs-Lug" id="10y-pq-G3V"/>
<outlet property="timeLabel" destination="Ix5-dG-THW" id="sGx-zI-RsF"/>
<outlet property="timeLabelTopConstraint" destination="mCj-xS-Tud" id="occ-se-7hx"/>
</connections>
<point key="canvasLocation" x="-182.5" y="-735.5"/>
</view>
</objects>
</document>
@@ -0,0 +1,60 @@
//
// HorizontalLineView.swift
// Yep
//
// Created by nixzhu on 15/9/30.
// Copyright © 2015 Catch Inc. All rights reserved.
//
import UIKit
@IBDesignable
class HorizontalLineView: UIView {
@IBInspectable
var lineColor: UIColor = UIColor.yepBorderColor() {
didSet {
setNeedsDisplay()
}
}
@IBInspectable
var lineWidth: CGFloat = 1.0 / UIScreen.mainScreen().scale {
didSet {
setNeedsDisplay()
}
}
@IBInspectable
var leftMargin: CGFloat = 0 {
didSet {
setNeedsDisplay()
}
}
@IBInspectable
var rightMargin: CGFloat = 0 {
didSet {
setNeedsDisplay()
}
}
// MARK: Draw
override func drawRect(rect: CGRect) {
super.drawRect(rect)
lineColor.setStroke()
let context = UIGraphicsGetCurrentContext()
CGContextSetLineWidth(context, lineWidth)
let y = ceil(CGRectGetHeight(rect) * 0.5)
CGContextMoveToPoint(context, leftMargin, y)
CGContextAddLineToPoint(context, CGRectGetWidth(rect) - rightMargin, y)
CGContextStrokePath(context)
}
}
+3
View File
@@ -629,3 +629,6 @@
"No discovered users." = "No discovered users.";
"No friends yet.\nTry discover or add some." = "No friends yet.\nTry discover or add some.";
"Feeds" = "Feeds";
+8 -5
View File
@@ -150,19 +150,19 @@
"Face to Face" = "面对面";
/* No comment provided by engineer. */
"Failed to resend audio!\nPlease make sure your iPhone is connected to the Internet." = "重发音频失败!\n确保您的iPhone已连接到互联网。";
"Failed to resend audio!\nPlease make sure your iPhone is connected to the Internet." = "重发音频失败!\n确保您的 iPhone 已连接到互联网。";
/* No comment provided by engineer. */
"Failed to resend image!\nPlease make sure your iPhone is connected to the Internet." = "重发图片失败!\n确保您的iPhone已连接到互联网。";
"Failed to resend image!\nPlease make sure your iPhone is connected to the Internet." = "重发图片失败!\n确保您的 iPhone 已连接到互联网。";
/* No comment provided by engineer. */
"Failed to resend location!\nPlease make sure your iPhone is connected to the Internet." = "重发位置失败!\n确保您的iPhone已连接到互联网。";
"Failed to resend location!\nPlease make sure your iPhone is connected to the Internet." = "重发位置失败!\n确保您的 iPhone 已连接到互联网。";
/* No comment provided by engineer. */
"Failed to resend text!\nPlease make sure your iPhone is connected to the Internet." = "重发文字失败!\n确保您的iPhone已连接到互联网。";
"Failed to resend text!\nPlease make sure your iPhone is connected to the Internet." = "重发文字失败!\n确保您的 iPhone 已连接到互联网。";
/* No comment provided by engineer. */
"Failed to resend video!\nPlease make sure your iPhone is connected to the Internet." = "重发视频失败!\n确保您的iPhone已连接到互联网。";
"Failed to resend video!\nPlease make sure your iPhone is connected to the Internet." = "重发视频失败!\n确保您的 iPhone 已连接到互联网。";
/* No comment provided by engineer. */
"Failed to send audio!\nTry tap on message to resend." = "发送音频失败!\n轻按消息气泡可以重发。";
@@ -631,3 +631,6 @@
"No discovered users." = "没有发现其他用户";
"No friends yet.\nTry discover or add some." = "暂无好友,去发现或添加吧";
"Feeds" = "话题";