ld"> 1324 1298
 				05130F6A21C95373004EF1BE /* PaiaiDataKit.framework */,
1325 1299
 				05130F8E21C9E5E8004EF1BE /* Paiai_iOS.framework */,
1326
-				0521144321EF38B60047C55A /* PaiaiDataKitTests.xctest */,
1327 1300
 			);
1328 1301
 			name = Products;
1329 1302
 			sourceTree = "<group>";
@@ -1478,26 +1451,6 @@
1478 1451
 			productReference = 05130F8E21C9E5E8004EF1BE /* Paiai_iOS.framework */;
1479 1452
 			productType = "com.apple.product-type.framework";
1480 1453
 		};
1481
-		0521144221EF38B60047C55A /* PaiaiDataKitTests */ = {
1482
-			isa = PBXNativeTarget;
1483
-			buildConfigurationList = 0521144B21EF38B60047C55A /* Build configuration list for PBXNativeTarget "PaiaiDataKitTests" */;
1484
-			buildPhases = (
1485
-				0521143F21EF38B60047C55A /* Sources */,
1486
-				0521144021EF38B60047C55A /* Frameworks */,
1487
-				0521144121EF38B60047C55A /* Resources */,
1488
-				0521145021EF3C090047C55A /* ShellScript */,
1489
-			);
1490
-			buildRules = (
1491
-			);
1492
-			dependencies = (
1493
-				0521144A21EF38B60047C55A /* PBXTargetDependency */,
1494
-				0521144F21EF3A440047C55A /* PBXTargetDependency */,
1495
-			);
1496
-			name = PaiaiDataKitTests;
1497
-			productName = PaiaiDataKitTests;
1498
-			productReference = 0521144321EF38B60047C55A /* PaiaiDataKitTests.xctest */;
1499
-			productType = "com.apple.product-type.bundle.unit-test";
1500
-		};
1501 1454
 		6CEBD0FE1CA8D680004DBDE0 /* Paiai */ = {
1502 1455
 			isa = PBXNativeTarget;
1503 1456
 			buildConfigurationList = 6CEBD1111CA8D680004DBDE0 /* Build configuration list for PBXNativeTarget "Paiai" */;
@@ -1546,12 +1499,6 @@
1546 1499
 						DevelopmentTeam = Q38447SL4M;
1547 1500
 						ProvisioningStyle = Automatic;
1548 1501
 					};
1549
-					0521144221EF38B60047C55A = {
1550
-						CreatedOnToolsVersion = 10.1;
1551
-						DevelopmentTeam = Q38447SL4M;
1552
-						ProvisioningStyle = Automatic;
1553
-						TestTargetID = 6CEBD0FE1CA8D680004DBDE0;
1554
-					};
1555 1502
 					6CEBD0FE1CA8D680004DBDE0 = {
1556 1503
 						CreatedOnToolsVersion = 7.3;
1557 1504
 						DevelopmentTeam = Q38447SL4M;
@@ -1589,7 +1536,6 @@
1589 1536
 				05130F8D21C9E5E8004EF1BE /* Paiai_iOS */,
1590 1537
 				05130F2D21C94B32004EF1BE /* PaiaiUIKit */,
1591 1538
 				05130F6921C95373004EF1BE /* PaiaiDataKit */,
1592
-				0521144221EF38B60047C55A /* PaiaiDataKitTests */,
1593 1539
 			);
1594 1540
 		};
1595 1541
 /* End PBXProject section */
@@ -1619,20 +1565,13 @@
1619 1565
 				0572B2C921E30D8000EAD2A2 /* PhotoCell.xib in Resources */,
1620 1566
 				0513105B21CA1D50004EF1BE /* Mine.storyboard in Resources */,
1621 1567
 				0513105821CA1D50004EF1BE /* Main.storyboard in Resources */,
1622
-				0513105C21CA1D50004EF1BE /* Detail.storyboard in Resources */,
1568
+				0513105C21CA1D50004EF1BE /* PhotoDetail.storyboard in Resources */,
1623 1569
 				0500C25E21E72E45009A7013 /* Assets.xcassets in Resources */,
1624 1570
 				0513105A21CA1D50004EF1BE /* Message.storyboard in Resources */,
1625 1571
 				0513105921CA1D50004EF1BE /* GroupDetail.storyboard in Resources */,
1626 1572
 			);
1627 1573
 			runOnlyForDeploymentPostprocessing = 0;
1628 1574
 		};
1629
-		0521144121EF38B60047C55A /* Resources */ = {
1630
-			isa = PBXResourcesBuildPhase;
1631
-			buildActionMask = 2147483647;
1632
-			files = (
1633
-			);
1634
-			runOnlyForDeploymentPostprocessing = 0;
1635
-		};
1636 1575
 		6CEBD0FD1CA8D680004DBDE0 /* Resources */ = {
1637 1576
 			isa = PBXResourcesBuildPhase;
1638 1577
 			buildActionMask = 2147483647;
@@ -1646,31 +1585,13 @@
1646 1585
 /* End PBXResourcesBuildPhase section */
1647 1586
 
1648 1587
 /* Begin PBXShellScriptBuildPhase section */
1649
-		0521145021EF3C090047C55A /* ShellScript */ = {
1650
-			isa = PBXShellScriptBuildPhase;
1651
-			buildActionMask = 2147483647;
1652
-			files = (
1653
-			);
1654
-			inputFileListPaths = (
1655
-			);
1656
-			inputPaths = (
1657
-			);
1658
-			outputFileListPaths = (
1659
-			);
1660
-			outputPaths = (
1661
-			);
1662
-			runOnlyForDeploymentPostprocessing = 0;
1663
-			shellPath = /bin/sh;
1664
-			shellScript = "/usr/local/bin/carthage copy-frameworks\n\n";
1665
-		};
1666 1588
 		05D5F0C22015E55000BC890B /* Run Script */ = {
1667 1589
 			isa = PBXShellScriptBuildPhase;
1668
-			buildActionMask = 2147483647;
1590
+			buildActionMask = 12;
1669 1591
 			files = (
1670 1592
 			);
1671 1593
 			inputPaths = (
1672 1594
 				"$(SRCROOT)/Carthage/Build/iOS/SQLite.framework",
1673
-				"$(SRCROOT)/Carthage/Build/iOS/ESPullToRefresh.framework",
1674 1595
 				"$(SRCROOT)/Carthage/Build/iOS/RxCocoa.framework",
1675 1596
 				"$(SRCROOT)/Carthage/Build/iOS/RxSwift.framework",
1676 1597
 				"$(SRCROOT)/Carthage/Build/iOS/RxDataSources.framework",
@@ -1682,7 +1603,6 @@
1682 1603
 			name = "Run Script";
1683 1604
 			outputPaths = (
1684 1605
 				"$(DERIVED_FILE_DIR)/$(FRAMEWORKS_FOLDER_PATH)/SQLite.framework",
1685
-				"$(DERIVED_FILE_DIR)/$(FRAMEWORKS_FOLDER_PATH)/ESPullToRefresh.framework",
1686 1606
 				"$(DERIVED_FILE_DIR)/$(FRAMEWORKS_FOLDER_PATH)/RxCocoa.framework",
1687 1607
 				"$(DERIVED_FILE_DIR)/$(FRAMEWORKS_FOLDER_PATH)/RxSwift.framework",
1688 1608
 				"$(DERIVED_FILE_DIR)/$(FRAMEWORKS_FOLDER_PATH)/RxDataSources.framework",
@@ -1805,37 +1725,34 @@
1805 1725
 				0513102421CA1B67004EF1BE /* Interfaces.swift in Sources */,
1806 1726
 				0513102521CA1B67004EF1BE /* NetworkApi.swift in Sources */,
1807 1727
 				0513102621CA1B67004EF1BE /* Resource.swift in Sources */,
1728
+				05594C012240BF9C002D4910 /* PhotoPurchaseViewModel.swift in Sources */,
1808 1729
 				0513102821CA1B67004EF1BE /* NetWorkCache.swift in Sources */,
1809 1730
 				0535D6DB21D32A9E008D9403 /* GuestUserInfoRemoteAPI.swift in Sources */,
1810 1731
 				05B2C63121D75A1B008063B3 /* ContentResource.swift in Sources */,
1811 1732
 				057CA9CA21DCA70B00FB7D03 /* GroupRepository.swift in Sources */,
1812 1733
 				0513102921CA1B67004EF1BE /* Result.swift in Sources */,
1813 1734
 				05C0D9A421D28591000B7B2A /* UserInfoRemoteAPI.swift in Sources */,
1814
-				0513102B21CA1B67004EF1BE /* HomePhotoRepository.swift in Sources */,
1735
+				0513102B21CA1B67004EF1BE /* HomeRepository.swift in Sources */,
1815 1736
 				0535D6D921D32A89008D9403 /* WXUserInfoRemoteAPI.swift in Sources */,
1816 1737
 				0513102C21CA1B67004EF1BE /* OrderRepository.swift in Sources */,
1817 1738
 				057CA9BA21DC836B00FB7D03 /* GroupPhotoRemoteAPI.swift in Sources */,
1818 1739
 				0513102D21CA1B67004EF1BE /* GroupPhotoRepository.swift in Sources */,
1819
-				05C0D98E21D22119000B7B2A /* ThumbupUserItem.swift in Sources */,
1740
+				05C0D98E21D22119000B7B2A /* PhotoThumbupUserItem.swift in Sources */,
1820 1741
 				05C71C0821DDBD55003E7CEE /* JSONCode.swift in Sources */,
1821 1742
 				0513102E21CA1B67004EF1BE /* PhotoGroupRepository.swift in Sources */,
1822 1743
 				051310B821CB6958004EF1BE /* UserInfoStore.swift in Sources */,
1823
-				05B2C5EF21D48133008063B3 /* HomePhotoRemoteAPI.swift in Sources */,
1824
-				057CA9D021DDB7DD00FB7D03 /* NetWorkData.swift in Sources */,
1825
-				0513102F21CA1B67004EF1BE /* GroupDetailModel.swift in Sources */,
1744
+				05B2C5EF21D48133008063B3 /* HomeRemoteAPI.swift in Sources */,
1745
+				057CA9D021DDB7DD00FB7D03 /* NetworkArrayData.swift in Sources */,
1826 1746
 				057CA9CE21DDAE8100FB7D03 /* GroupDetailRepository.swift in Sources */,
1827
-				0513103021CA1B67004EF1BE /* MessageListRepository.swift in Sources */,
1828 1747
 				0543E80B21D1DF4000A42807 /* GroupMemberItem.swift in Sources */,
1829 1748
 				0513103121CA1B67004EF1BE /* MessageRepository.swift in Sources */,
1830 1749
 				0543E80721D0CDFA00A42807 /* MineFeedbackViewModel.swift in Sources */,
1831
-				0513103221CA1B67004EF1BE /* DetailModel.swift in Sources */,
1832 1750
 				0513103321CA1B67004EF1BE /* UserInfoRepository.swift in Sources */,
1833
-				0513103421CA1B67004EF1BE /* PhotoLocalStorage.swift in Sources */,
1834 1751
 				0513103521CA1B67004EF1BE /* RecentGroupInfo.swift in Sources */,
1835 1752
 				0513103921CA1B67004EF1BE /* HomeViewModel.swift in Sources */,
1836 1753
 				0543E80F21D1FD1100A42807 /* GroupDetailItem.swift in Sources */,
1837 1754
 				0513103A21CA1B67004EF1BE /* GroupViewModel.swift in Sources */,
1838
-				05C0D99021D2219A000B7B2A /* CommentItem.swift in Sources */,
1755
+				05C0D99021D2219A000B7B2A /* PhotoCommentItem.swift in Sources */,
1839 1756
 				0513103B21CA1B67004EF1BE /* GroupDetailViewModel.swift in Sources */,
1840 1757
 				05B2C62B21D750F1008063B3 /* FeedbackRemoteAPI.swift in Sources */,
1841 1758
 				0513103C21CA1B67004EF1BE /* GroupMemberViewModel.swift in Sources */,
@@ -1843,19 +1760,21 @@
1843 1760
 				0513103E21CA1B67004EF1BE /* MineOrderViewModel.swift in Sources */,
1844 1761
 				05B2C62721D74E92008063B3 /* OrderRemoteAPI.swift in Sources */,
1845 1762
 				05B2C62921D74F27008063B3 /* MessageRemoteAPI.swift in Sources */,
1846
-				0513103F21CA1B67004EF1BE /* DetailPageViewModel.swift in Sources */,
1763
+				0513103F21CA1B67004EF1BE /* PhotoDetailViewModel.swift in Sources */,
1764
+				054B6C45223F884600939FE6 /* PhotoDetailRemoteAPI.swift in Sources */,
1847 1765
 				0513104021CA1B67004EF1BE /* MessageListViewModel.swift in Sources */,
1848 1766
 				053E127521F5A72000A64893 /* DataError.swift in Sources */,
1849 1767
 				05B2C61D21D710C5008063B3 /* GroupRemoteAPI.swift in Sources */,
1850 1768
 				0513104121CA1B67004EF1BE /* MessageViewModel.swift in Sources */,
1851
-				0513104221CA1B67004EF1BE /* CreateGroupConfirmViewModel.swift in Sources */,
1769
+				0513104221CA1B67004EF1BE /* CreateGroupViewModel.swift in Sources */,
1852 1770
 				0513104321CA1B67004EF1BE /* ScanQRViewModel.swift in Sources */,
1853 1771
 				05130FB721C9E80F004EF1BE /* MessageListItem.swift in Sources */,
1854 1772
 				05B2C62121D727AA008063B3 /* StatusResource.swift in Sources */,
1855 1773
 				051310C021CB6EF4004EF1BE /* UserInfo.swift in Sources */,
1774
+				05594BFF2240BEDE002D4910 /* PhotoDetailListViewModel.swift in Sources */,
1856 1775
 				05130FB521C9E7CE004EF1BE /* MessageItem.swift in Sources */,
1857 1776
 				057CA9C621DCA2C900FB7D03 /* PhotoRepository.swift in Sources */,
1858
-				05B2C62321D72EAF008063B3 /* MessageListRemoteAPI.swift in Sources */,
1777
+				0546D9852242460700742939 /* OriginData.swift in Sources */,
1859 1778
 				05B2C62F21D754BD008063B3 /* GroupDetailRemoteAPI.swift in Sources */,
1860 1779
 				05130FB321C9E76A004EF1BE /* GroupItem.swift in Sources */,
1861 1780
 				05130FB021C9E6CD004EF1BE /* OrderItem.swift in Sources */,
@@ -1866,13 +1785,13 @@
1866 1785
 			isa = PBXSourcesBuildPhase;
1867 1786
 			buildActionMask = 2147483647;
1868 1787
 			files = (
1788
+				0546D98E2243782300742939 /* ShareView.swift in Sources */,
1869 1789
 				051310B621CB675A004EF1BE /* UIImageView+Kingfisher.swift in Sources */,
1870 1790
 				05130FDD21CA1B04004EF1BE /* GroupViewController.swift in Sources */,
1871
-				05130FDF21CA1B04004EF1BE /* MemberCell.swift in Sources */,
1791
+				05130FDF21CA1B04004EF1BE /* GroupMemberCell.swift in Sources */,
1872 1792
 				05130FE021CA1B04004EF1BE /* GroupDetailViewController.swift in Sources */,
1873
-				05130FE121CA1B04004EF1BE /* GroupMemberController.swift in Sources */,
1874
-				05130FE221CA1B04004EF1BE /* ChangeGroupNameController.swift in Sources */,
1875
-				05130FE321CA1B04004EF1BE /* ShowGroupQRController.swift in Sources */,
1793
+				05130FE121CA1B04004EF1BE /* GroupMemberViewController.swift in Sources */,
1794
+				05130FE221CA1B04004EF1BE /* GroupNameModificationViewController.swift in Sources */,
1876 1795
 				055EFAD7221A4DB400450AD5 /* GroupQRView.swift in Sources */,
1877 1796
 				05130FE421CA1B04004EF1BE /* MessageCommentAndThumbupCell.swift in Sources */,
1878 1797
 				05D3A3CD22000C3A00A29A20 /* GroupCoordinator.swift in Sources */,
@@ -1880,6 +1799,7 @@
1880 1799
 				05130FE621CA1B04004EF1BE /* MessageCoordinator.swift in Sources */,
1881 1800
 				05130FE721CA1B04004EF1BE /* MessageViewController.swift in Sources */,
1882 1801
 				05130FE821CA1B04004EF1BE /* MessageListViewController.swift in Sources */,
1802
+				0530951B221AB3EC00408D34 /* GroupDetailMemeberView.swift in Sources */,
1883 1803
 				0513106921CA34D6004EF1BE /* GroupDetailCoordinator.swift in Sources */,
1884 1804
 				05130FEA21CA1B04004EF1BE /* GroupCell.swift in Sources */,
1885 1805
 				05130FEB21CA1B04004EF1BE /* OrderCell.swift in Sources */,
@@ -1887,18 +1807,15 @@
1887 1807
 				05130FED21CA1B04004EF1BE /* MineViewController.swift in Sources */,
1888 1808
 				05130FEE21CA1B04004EF1BE /* MineGroupViewController.swift in Sources */,
1889 1809
 				05130FEF21CA1B04004EF1BE /* MineFeedbackViewController.swift in Sources */,
1810
+				05594C032240E94E002D4910 /* PhotoDetailImageCell.swift in Sources */,
1890 1811
 				05130FF021CA1B04004EF1BE /* MineOrderViewController.swift in Sources */,
1891 1812
 				05130FF121CA1B04004EF1BE /* MineAboutViewController.swift in Sources */,
1892
-				05130FF321CA1B04004EF1BE /* DetailPageHeadCell.swift in Sources */,
1893
-				05130FF421CA1B04004EF1BE /* DetailPagePhotoCell.swift in Sources */,
1894
-				05130FF521CA1B04004EF1BE /* DetailPageNameCell.swift in Sources */,
1895
-				05130FF621CA1B04004EF1BE /* DetailZanImagesCell.swift in Sources */,
1896
-				05130FF721CA1B04004EF1BE /* DetailCommentCell.swift in Sources */,
1813
+				05130FF721CA1B04004EF1BE /* PhotoDetailCommentCell.swift in Sources */,
1897 1814
 				0513106B21CA3545004EF1BE /* PhotoDetailCoordinator.swift in Sources */,
1898 1815
 				05130FF821CA1B04004EF1BE /* ImageCell.swift in Sources */,
1899
-				05130FF921CA1B04004EF1BE /* DetailPageController.swift in Sources */,
1816
+				05130FF921CA1B04004EF1BE /* PhotoDetailViewController.swift in Sources */,
1900 1817
 				05130FFB21CA1B04004EF1BE /* ShareController.swift in Sources */,
1901
-				05130FFC21CA1B04004EF1BE /* ShowFullPicController.swift in Sources */,
1818
+				05130FFC21CA1B04004EF1BE /* PhotoPreviewViewController.swift in Sources */,
1902 1819
 				05130FD721CA1ADF004EF1BE /* HomeCoordinator.swift in Sources */,
1903 1820
 				05130FD821CA1AE0004EF1BE /* HomeViewController.swift in Sources */,
1904 1821
 				05130FD921CA1AE0004EF1BE /* CreateGroupConfirmViewController.swift in Sources */,
@@ -1911,14 +1828,6 @@
1911 1828
 			);
1912 1829
 			runOnlyForDeploymentPostprocessing = 0;
1913 1830
 		};
1914
-		0521143F21EF38B60047C55A /* Sources */ = {
1915
-			isa = PBXSourcesBuildPhase;
1916
-			buildActionMask = 2147483647;
1917
-			files = (
1918
-				0521144621EF38B60047C55A /* PaiaiDataKitTests.swift in Sources */,
1919
-			);
1920
-			runOnlyForDeploymentPostprocessing = 0;
1921
-		};
1922 1831
 		6CEBD0FB1CA8D680004DBDE0 /* Sources */ = {
1923 1832
 			isa = PBXSourcesBuildPhase;
1924 1833
 			buildActionMask = 2147483647;
@@ -1955,25 +1864,15 @@
1955 1864
 			target = 05130F6921C95373004EF1BE /* PaiaiDataKit */;
1956 1865
 			targetProxy = 051310A921CA451B004EF1BE /* PBXContainerItemProxy */;
1957 1866
 		};
1958
-		0521144A21EF38B60047C55A /* PBXTargetDependency */ = {
1959
-			isa = PBXTargetDependency;
1960
-			target = 05130F6921C95373004EF1BE /* PaiaiDataKit */;
1961
-			targetProxy = 0521144921EF38B60047C55A /* PBXContainerItemProxy */;
1962
-		};
1963
-		0521144F21EF3A440047C55A /* PBXTargetDependency */ = {
1964
-			isa = PBXTargetDependency;
1965
-			target = 6CEBD0FE1CA8D680004DBDE0 /* Paiai */;
1966
-			targetProxy = 0521144E21EF3A440047C55A /* PBXContainerItemProxy */;
1967
-		};
1968 1867
 /* End PBXTargetDependency section */
1969 1868
 
1970 1869
 /* Begin PBXVariantGroup section */
1971
-		A69FFB881E7018CC0006FEE0 /* Detail.storyboard */ = {
1870
+		A69FFB881E7018CC0006FEE0 /* PhotoDetail.storyboard */ = {
1972 1871
 			isa = PBXVariantGroup;
1973 1872
 			children = (
1974 1873
 				A69FFB891E7018CC0006FEE0 /* Base */,
1975 1874
 			);
1976
-			name = Detail.storyboard;
1875
+			name = PhotoDetail.storyboard;
1977 1876
 			sourceTree = "<group>";
1978 1877
 		};
1979 1878
 		A69FFB8A1E7018CC0006FEE0 /* GroupDetail.storyboard */ = {
@@ -2094,7 +1993,7 @@
2094 1993
 				CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES;
2095 1994
 				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
2096 1995
 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
2097
-				CLANG_ENABLE_MODULES = YES;
1996
+				CLANG_ENABLE_MODULES = NO;
2098 1997
 				CLANG_ENABLE_OBJC_WEAK = YES;
2099 1998
 				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
2100 1999
 				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
@@ -2142,7 +2041,7 @@
2142 2041
 				CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES;
2143 2042
 				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
2144 2043
 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
2145
-				CLANG_ENABLE_MODULES = YES;
2044
+				CLANG_ENABLE_MODULES = NO;
2146 2045
 				CLANG_ENABLE_OBJC_WEAK = YES;
2147 2046
 				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
2148 2047
 				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
@@ -2259,61 +2158,6 @@
2259 2158
 			};
2260 2159
 			name = Release;
2261 2160
 		};
2262
-		0521144C21EF38B60047C55A /* Debug */ = {
2263
-			isa = XCBuildConfiguration;
2264
-			buildSettings = {
2265
-				BUNDLE_LOADER = "$(TEST_HOST)";
2266
-				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
2267
-				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
2268
-				CLANG_ENABLE_OBJC_WEAK = YES;
2269
-				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
2270
-				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
2271
-				CODE_SIGN_IDENTITY = "iPhone Developer";
2272
-				CODE_SIGN_STYLE = Automatic;
2273
-				DEVELOPMENT_TEAM = Q38447SL4M;
2274
-				FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/Carthage/Build/iOS";
2275
-				GCC_C_LANGUAGE_STANDARD = gnu11;
2276
-				INFOPLIST_FILE = PaiaiDataKitTests/Info.plist;
2277
-				IPHONEOS_DEPLOYMENT_TARGET = 12.1;
2278
-				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
2279
-				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
2280
-				MTL_FAST_MATH = YES;
2281
-				PRODUCT_BUNDLE_IDENTIFIER = FFIB.PaiaiDataKitTests;
2282
-				PRODUCT_NAME = "$(TARGET_NAME)";
2283
-				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
2284
-				SWIFT_VERSION = 4.2;
2285
-				TARGETED_DEVICE_FAMILY = "1,2";
2286
-				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Paiai.app/Paiai";
2287
-			};
2288
-			name = Debug;
2289
-		};
2290
-		0521144D21EF38B60047C55A /* Release */ = {
2291
-			isa = XCBuildConfiguration;
2292
-			buildSettings = {
2293
-				BUNDLE_LOADER = "$(TEST_HOST)";
2294
-				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
2295
-				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
2296
-				CLANG_ENABLE_OBJC_WEAK = YES;
2297
-				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
2298
-				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
2299
-				CODE_SIGN_IDENTITY = "iPhone Developer";
2300
-				CODE_SIGN_STYLE = Automatic;
2301
-				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
2302
-				DEVELOPMENT_TEAM = Q38447SL4M;
2303
-				FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/Carthage/Build/iOS";
2304
-				GCC_C_LANGUAGE_STANDARD = gnu11;
2305
-				INFOPLIST_FILE = PaiaiDataKitTests/Info.plist;
2306
-				IPHONEOS_DEPLOYMENT_TARGET = 12.1;
2307
-				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
2308
-				MTL_FAST_MATH = YES;
2309
-				PRODUCT_BUNDLE_IDENTIFIER = FFIB.PaiaiDataKitTests;
2310
-				PRODUCT_NAME = "$(TARGET_NAME)";
2311
-				SWIFT_VERSION = 4.2;
2312
-				TARGETED_DEVICE_FAMILY = "1,2";
2313
-				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Paiai.app/Paiai";
2314
-			};
2315
-			name = Release;
2316
-		};
2317 2161
 		6CEBD10F1CA8D680004DBDE0 /* Debug */ = {
2318 2162
 			isa = XCBuildConfiguration;
2319 2163
 			buildSettings = {
@@ -2323,7 +2167,7 @@
2323 2167
 				CLANG_ANALYZER_NONNULL = YES;
2324 2168
 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
2325 2169
 				CLANG_CXX_LIBRARY = "libc++";
2326
-				CLANG_ENABLE_MODULES = YES;
2170
+				CLANG_ENABLE_MODULES = NO;
2327 2171
 				CLANG_ENABLE_OBJC_ARC = YES;
2328 2172
 				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
2329 2173
 				CLANG_WARN_BOOL_CONVERSION = YES;
@@ -2384,7 +2228,7 @@
2384 2228
 				CLANG_ANALYZER_NONNULL = YES;
2385 2229
 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
2386 2230
 				CLANG_CXX_LIBRARY = "libc++";
2387
-				CLANG_ENABLE_MODULES = YES;
2231
+				CLANG_ENABLE_MODULES = NO;
2388 2232
 				CLANG_ENABLE_OBJC_ARC = YES;
2389 2233
 				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
2390 2234
 				CLANG_WARN_BOOL_CONVERSION = YES;
@@ -2434,6 +2278,7 @@
2434 2278
 			isa = XCBuildConfiguration;
2435 2279
 			buildSettings = {
2436 2280
 				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)";
2281
+				ALWAYS_SEARCH_USER_PATHS = NO;
2437 2282
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
2438 2283
 				CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = NO;
2439 2284
 				CLANG_ENABLE_MODULES = NO;
@@ -2462,6 +2307,7 @@
2462 2307
 					"$(inherited)",
2463 2308
 					"$(PROJECT_DIR)/Paiai/wxSDK",
2464 2309
 				);
2310
+				MACH_O_TYPE = mh_execute;
2465 2311
 				ONLY_ACTIVE_ARCH = YES;
2466 2312
 				OTHER_LDFLAGS = "";
2467 2313
 				OTHER_SWIFT_FLAGS = "-DDEBUG";
@@ -2480,6 +2326,7 @@
2480 2326
 			isa = XCBuildConfiguration;
2481 2327
 			buildSettings = {
2482 2328
 				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)";
2329
+				ALWAYS_SEARCH_USER_PATHS = NO;
2483 2330
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
2484 2331
 				CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = NO;
2485 2332
 				CLANG_ENABLE_MODULES = NO;
@@ -2507,6 +2354,7 @@
2507 2354
 					"$(inherited)",
2508 2355
 					"$(PROJECT_DIR)/Paiai/wxSDK",
2509 2356
 				);
2357
+				MACH_O_TYPE = mh_execute;
2510 2358
 				ONLY_ACTIVE_ARCH = YES;
2511 2359
 				OTHER_LDFLAGS = "";
2512 2360
 				OTHER_SWIFT_FLAGS = "";
@@ -2551,15 +2399,6 @@
2551 2399
 			defaultConfigurationIsVisible = 0;
2552 2400
 			defaultConfigurationName = Release;
2553 2401
 		};
2554
-		0521144B21EF38B60047C55A /* Build configuration list for PBXNativeTarget "PaiaiDataKitTests" */ = {
2555
-			isa = XCConfigurationList;
2556
-			buildConfigurations = (
2557
-				0521144C21EF38B60047C55A /* Debug */,
2558
-				0521144D21EF38B60047C55A /* Release */,
2559
-			);
2560
-			defaultConfigurationIsVisible = 0;
2561
-			defaultConfigurationName = Release;
2562
-		};
2563 2402
 		6CEBD0FA1CA8D680004DBDE0 /* Build configuration list for PBXProject "Paiai" */ = {
2564 2403
 			isa = XCConfigurationList;
2565 2404
 			buildConfigurations = (

BIN
PaiAi/Paiai.xcodeproj/project.xcworkspace/xcuserdata/FFIB.xcuserdatad/UserInterfaceState.xcuserstate


+ 0 - 12
PaiAi/Paiai.xcodeproj/xcuserdata/FFIB.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist

@@ -2,16 +2,4 @@
2 2
 <Bucket
3 3
    type = "1"
4 4
    version = "2.0">
5
-   <Breakpoints>
6
-      <BreakpointProxy
7
-         BreakpointExtensionID = "Xcode.Breakpoint.ExceptionBreakpoint">
8
-         <BreakpointContent
9
-            shouldBeEnabled = "Yes"
10
-            ignoreCount = "0"
11
-            continueAfterRunningActions = "No"
12
-            scope = "0"
13
-            stopOnStyle = "0">
14
-         </BreakpointContent>
15
-      </BreakpointProxy>
16
-   </Breakpoints>
17 5
 </Bucket>

BIN
PaiAi/Paiai/Assets.xcassets/.DS_Store


BIN
PaiAi/Paiai/Assets.xcassets/MainPage/.DS_Store


+ 8 - 14
PaiAi/Paiai/Info.plist

@@ -59,6 +59,8 @@
59 59
 	</array>
60 60
 	<key>CFBundleVersion</key>
61 61
 	<string>1</string>
62
+	<key>LSApplicationCategoryType</key>
63
+	<string></string>
62 64
 	<key>LSApplicationQueriesSchemes</key>
63 65
 	<array>
64 66
 		<string>wechat</string>
@@ -81,34 +83,26 @@
81 83
 		<key>NSAllowsArbitraryLoads</key>
82 84
 		<true/>
83 85
 	</dict>
84
-	<key>NSAppleMusicUsageDescription</key>
85
-	<string>App需要您的同意,才能进行查看相册</string>
86
-	<key>NSBluetoothPeripheralUsageDescription</key>
87
-	<string>App需要您的同意,才能进行访问蓝牙</string>
88
-	<key>NSCalendarsUsageDescription</key>
89
-	<string>App需要您的同意,才能进行查看日历</string>
90 86
 	<key>NSCameraUsageDescription</key>
91 87
 	<string>App需要您的同意,才能进行拍摄</string>
92
-	<key>NSContactsUsageDescription</key>
93
-	<string>App需要您的同意,才能进行查看通讯录</string>
94
-	<key>NSLocationAlwaysUsageDescription</key>
95
-	<string>需要您的位置用于在参加旅行团时,方便导游集合团员。如果不允许,将无法更加快捷的集合</string>
96
-	<key>NSLocationUsageDescription</key>
97
-	<string>需要您的位置用于在参加旅行团时,方便导游集合团员。如果不允许,将无法更加快捷的集合</string>
98
-	<key>NSLocationWhenInUseUsageDescription</key>
99
-	<string>App需要您的同意,才能访问您的位置</string>
100 88
 	<key>NSPhotoLibraryUsageDescription</key>
101 89
 	<string>App需要您的同意,才能进行查看相册</string>
90
+	<key>NSPhotoLibraryAddUsageDescription</key>
91
+	<string>cc</string>
102 92
 	<key>UILaunchStoryboardName</key>
103 93
 	<string>LaunchScreen</string>
104 94
 	<key>UIRequiredDeviceCapabilities</key>
105 95
 	<array>
106 96
 		<string>armv7</string>
107 97
 	</array>
98
+	<key>UIStatusBarStyle</key>
99
+	<string>UIStatusBarStyleLightContent</string>
108 100
 	<key>UISupportedInterfaceOrientations</key>
109 101
 	<array>
110 102
 		<string>UIInterfaceOrientationPortrait</string>
111 103
 	</array>
104
+	<key>UIViewControllerBasedStatusBarAppearance</key>
105
+	<true/>
112 106
 	<key>sina.com.cn</key>
113 107
 	<dict>
114 108
 		<key>NSExceptionMinimumTLSVersion</key>

BIN
PaiAi/PaiaiDataKit/DataLayer/.DS_Store


+ 5 - 11
PaiAi/PaiaiDataKit/DataLayer/Model/GroupDetailItem.swift

@@ -10,18 +10,18 @@ import Foundation
10 10
 import ObjectMapper
11 11
 
12 12
 public struct GroupDetailItem {
13
-    var group = GroupItem(json: [:])
14
-    var users: [GroupMemberItem] = []
15
-    var group_id: String = ""
13
+    public var group = GroupItem(json: [:])
14
+    public var users: [GroupMemberItem] = []
15
+    public var group_id: String = ""
16 16
     
17
-    init(json: [String: AnyObject]) {
17
+    public init (json: [String: AnyObject]) {
18 18
         self.init(map: Map(mappingType: .fromJSON, JSON: json))
19 19
     }
20 20
 }
21 21
 
22 22
 extension GroupDetailItem: Mappable {
23 23
     mutating public func mapping(map: Map) {
24
-        users       <-  map["users"]
24
+        users       <-  map["users.passed"]
25 25
         group_id    <-  map["group_id"]
26 26
         group       <-  map["group"]
27 27
     }
@@ -30,9 +30,3 @@ extension GroupDetailItem: Mappable {
30 30
         mapping(map: map)
31 31
     }
32 32
 }
33
-
34
-extension GroupDetailItem: Equatable {
35
-    public static func == (lhs: GroupDetailItem, rhs: GroupDetailItem) -> Bool {
36
-        return lhs.group_id == rhs.group_id
37
-    }
38
-}

+ 10 - 2
PaiAi/PaiaiDataKit/DataLayer/Model/GroupMemberItem.swift

@@ -8,6 +8,7 @@
8 8
 
9 9
 import Foundation
10 10
 import ObjectMapper
11
+import RxDataSources
11 12
 
12 13
 public struct GroupMemberItem {
13 14
     
@@ -16,7 +17,7 @@ public struct GroupMemberItem {
16 17
     public var nickname: String = ""
17 18
     public var admin: Bool = false
18 19
     
19
-    init(json: [String: AnyObject]) {
20
+    public init(json: [String: AnyObject]) {
20 21
         self.init(map: Map(mappingType: .fromJSON, JSON: json))
21 22
     }
22 23
 }
@@ -34,4 +35,11 @@ extension GroupMemberItem: Mappable {
34 35
     }
35 36
 }
36 37
 
37
-
38
+extension GroupMemberItem: IdentifiableType, Equatable {
39
+    public typealias Identity = String
40
+    public var identity: String { return user_id }
41
+    
42
+    public static func == (lhs: GroupMemberItem, rhs: GroupMemberItem) -> Bool {
43
+        return lhs.user_id == rhs.user_id
44
+    }
45
+}

+ 3 - 2
PaiAi/PaiaiDataKit/DataLayer/Model/MessageItem.swift

@@ -17,9 +17,10 @@ public enum MessageType: String {
17 17
 
18 18
 public struct MessageItem: JSONCode {
19 19
     public var msg_unread_num = 0
20
-    public var msg_type_desc = ""
21 20
     public var msg_type = MessageType.system
22
-    public var msg_avatar = ""
21
+    
22
+    var msg_type_desc = ""
23
+    var msg_avatar = ""
23 24
     
24 25
     init(json: [String: AnyObject]) {
25 26
         self.init(map: Map(mappingType: .fromJSON, JSON: json))

+ 0 - 2
PaiAi/PaiaiDataKit/DataLayer/Model/MessageListItem.swift

@@ -20,7 +20,6 @@ public struct MessageListItem: JSONCode {
20 20
     public var group_photo_info = PhotoItem(json: [:])
21 21
 
22 22
     var read: Bool = false
23
-    public var title: String = ""
24 23
     public var created_at: Date?
25 24
     
26 25
     var pk = 0
@@ -45,7 +44,6 @@ extension MessageListItem: Mappable {
45 44
         content             <-  map["content"]
46 45
         pk                  <-  map["pk"]
47 46
         read                <-  map["read"]
48
-        title               <-  map["title"]
49 47
         from_avatar         <-  map["from_avatar"]
50 48
         from_nickname       <-  map["from_nickname"]
51 49
         from_uid            <-  map["from_uid"]

+ 12 - 1
PaiAi/PaiaiDataKit/DataLayer/Model/PhotoCommentItem.swift

@@ -8,8 +8,9 @@
8 8
 
9 9
 import Foundation
10 10
 import ObjectMapper
11
+import RxDataSources
11 12
 
12
-public struct PhotoCommentItem {
13
+public struct PhotoCommentItem: JSONCode {
13 14
     
14 15
     public var avatar: String = ""
15 16
     public var comment: String = ""
@@ -37,3 +38,13 @@ extension PhotoCommentItem: Mappable {
37 38
         mapping(map: map)
38 39
     }
39 40
 }
41
+
42
+extension PhotoCommentItem: IdentifiableType, Equatable {
43
+    public typealias Identity = String
44
+    public var identity: String { return user_id + comment }
45
+    
46
+    public static func == (lhs: PhotoCommentItem, rhs: PhotoCommentItem) -> Bool {
47
+        return lhs.user_id == rhs.user_id && lhs.comment == rhs.comment
48
+    }
49
+}
50
+

+ 13 - 3
PaiAi/PaiaiDataKit/DataLayer/Model/PhotoThumbupUserItem.swift

@@ -1,5 +1,5 @@
1 1
 //
2
-//  ThumbupUserItem.swift
2
+//  PhotoThumbupUserItem.swift
3 3
 //  PaiaiDataKit
4 4
 //
5 5
 //  Created by ffib on 2018/12/25.
@@ -8,8 +8,9 @@
8 8
 
9 9
 import Foundation
10 10
 import ObjectMapper
11
+import RxDataSources
11 12
 
12
-public struct ThumbupUserItem {
13
+public struct PhotoThumbupUserItem: JSONCode {
13 14
     public var avatar: String = ""
14 15
     public var user_id: String = ""
15 16
     public var nickname: String = ""
@@ -19,7 +20,7 @@ public struct ThumbupUserItem {
19 20
     }
20 21
 }
21 22
 
22
-extension ThumbupUserItem: Mappable {
23
+extension PhotoThumbupUserItem: Mappable {
23 24
     mutating public func mapping(map: Map) {
24 25
         avatar          <-  map["avatar"]
25 26
         user_id         <-  map["user_id"]
@@ -30,3 +31,12 @@ extension ThumbupUserItem: Mappable {
30 31
         mapping(map: map)
31 32
     }
32 33
 }
34
+
35
+extension PhotoThumbupUserItem: IdentifiableType, Equatable {
36
+    public typealias Identity = String
37
+    public var identity: String { return user_id }
38
+    
39
+    public static func == (lhs: PhotoThumbupUserItem, rhs: PhotoThumbupUserItem) -> Bool {
40
+        return lhs.user_id == rhs.user_id
41
+    }
42
+}

+ 18 - 0
PaiAi/PaiaiDataKit/DataLayer/Model/Reusable/OriginData.swift

@@ -7,3 +7,21 @@
7 7
 //
8 8
 
9 9
 import Foundation
10
+
11
+struct OriginData<DataItem: JSONCode> {
12
+    var status = 0
13
+    var data: [DataItem] = []
14
+    
15
+    init() {}
16
+    
17
+    init(json: JSON, dataField: String) {
18
+        guard let status = json["status"] as? Int,
19
+            let result = json[dataField] as? [[String: AnyObject]] else { return }
20
+        self.status = status
21
+        data = result.map { DataItem.init(json: $0) }
22
+    }
23
+    
24
+    static func empty() -> OriginData {
25
+        return OriginData()
26
+    }
27
+}

+ 3 - 3
PaiAi/PaiaiDataKit/DataLayer/Repositories/Implementation/HomeRepository.swift

@@ -1,5 +1,5 @@
1 1
 //
2
-//  HomePhotoRepository.swift
2
+//  HomeRepository.swift
3 3
 //  PaiAi
4 4
 //
5 5
 //  Created by FFIB on 16/3/31.
@@ -10,9 +10,9 @@ import Foundation
10 10
 import RxCocoa
11 11
 import RxSwift
12 12
 
13
-struct HomePhotoRepository: PhotoRepository {
13
+struct HomeRepository: PhotoRepository {
14 14
     
15
-    var homeRemoteAPI = HomePhotoRemoteAPI()
15
+    var homeRemoteAPI = HomeRemoteAPI()
16 16
     
17 17
     func load(page: Int) -> Single<NetworkArrayData<PhotoItem>> {
18 18
         return homeRemoteAPI.loadContent(page: page)

+ 56 - 1
PaiAi/PaiaiDataKit/DataLayer/Repositories/Implementation/MessageRepository.swift

@@ -9,11 +9,66 @@
9 9
 import Foundation
10 10
 import RxSwift
11 11
 
12
+public protocol MessageInteractionModel {
13
+    var path: Interfaces { get }
14
+    var removePath: Interfaces { get }
15
+    var readPath: Interfaces { get }
16
+    var title: String { get }
17
+}
18
+
19
+extension MessageType {
20
+    
21
+    public var model: MessageInteractionModel {
22
+        switch self {
23
+        case .thumbup: return MessagethumbupInteractionModel()
24
+        case .comment: return MessageCommentInteractionModel()
25
+        case .system: return MessageSystemInteractionModel()
26
+        }
27
+    }
28
+    
29
+    fileprivate struct MessagethumbupInteractionModel: MessageInteractionModel {
30
+        var path: Interfaces { return .mesThumbupList}
31
+        var removePath: Interfaces { return .mesThumbupClear }
32
+        var readPath: Interfaces { return .mesThumbupRead }
33
+        var title: String { return "赞" }
34
+    }
35
+    
36
+    fileprivate struct MessageCommentInteractionModel: MessageInteractionModel {
37
+        var path: Interfaces { return .mesCommentList}
38
+        var removePath: Interfaces { return .mesCommentClear }
39
+        var readPath: Interfaces { return .mesCommentRead }
40
+        var title: String { return "评论" }
41
+    }
42
+    
43
+    fileprivate struct MessageSystemInteractionModel: MessageInteractionModel {
44
+        var path: Interfaces { return .mesSystemList}
45
+        var removePath: Interfaces { return .mesSystemClear }
46
+        var readPath: Interfaces { return .mesSystemRead }
47
+        var title: String { return "系统消息" }
48
+    }
49
+}
50
+
12 51
 struct MessageRepository {
13 52
     
14 53
     var messageRemoteAPI = MessageRemoteAPI()
15 54
     
16
-    mutating func load() -> Single<[MessageItem]> {
55
+    func loadContent() -> Single<[MessageItem]> {
17 56
         return messageRemoteAPI.loadContent()
18 57
     }
58
+    
59
+    func loadMessageList(_ type: MessageType, page: Int) -> Single<NetworkArrayData<MessageListItem>> {
60
+        return messageRemoteAPI.loadMessageList(type, page: page)
61
+    }
62
+    
63
+    func remove(_ type: MessageType, pk: Int) -> Completable {
64
+        return messageRemoteAPI.remove(type, pk: pk)
65
+    }
66
+    
67
+    func removeAll(_ type: MessageType) -> Completable {
68
+        return messageRemoteAPI.removeAll(type)
69
+    }
70
+    
71
+    func readed(_ type: MessageType) -> Completable {
72
+        return messageRemoteAPI.readed(type)
73
+    }
19 74
 }

+ 0 - 240
PaiAi/PaiaiDataKit/DataLayer/Repositories/MessageRepositorable.swift

@@ -1,240 +0,0 @@
1
-//
2
-//  MessageModel.swift
3
-//  PaiAi
4
-//
5
-//  Created by zhengjianfei on 16/4/7.
6
-//  Copyright © 2016年 FFIB. All rights reserved.
7
-//
8
-
9
-import Foundation
10
-import ObjectMapper
11
-import RxCocoa
12
-import RxDataSources
13
-
14
-import Foundation
15
-import RxCocoa
16
-import RxSwift
17
-
18
-public protocol MessageRepositorable: Repositorable where Content == [GroupItem] {}
19
-
20
-final public class MessageRepository: Resource {
21
-    fileprivate var items: BehaviorRelay<[GroupItem]>
22
-    fileprivate var hasMore: Bool
23
-    fileprivate var page: Int
24
-    fileprivate var loadingSchedule: PublishSubject<Bool>
25
-    
26
-    var path: Interfaces { return .groupList }
27
-    #warning("admin_id 填充")
28
-    var parameter: Parameter = ["admin_id": ""]
29
-    fileprivate var deleteResource: GroupDeleteResource
30
-    
31
-    
32
-    init() {
33
-        page = 1
34
-        hasMore = true
35
-        loadingSchedule = PublishSubject<Bool>()
36
-        items = BehaviorRelay<[GroupItem]>(value: [])
37
-        deleteResource = GroupDeleteResource()
38
-    }
39
-}
40
-
41
-extension GroupRepository: Parsable {
42
-    typealias Model = [GroupItem]
43
-    
44
-    func parse(_ json: JSON) -> [GroupItem]? {
45
-        guard let data = json["data"] as? [String: AnyObject],
46
-            let messages = data["groups"] as? [[String: AnyObject]],
47
-            let left = data["left"] as? Int else { return nil }
48
-        hasMore = left > 0
49
-        return messages.map { return GroupItem(json: $0) }
50
-    }
51
-}
52
-
53
-extension GroupRepository: Gettable {
54
-    public func loadContent(isRefresh: Bool) {
55
-        guard hasMore else {
56
-            loadingSchedule.onNext(isRefresh)
57
-            return
58
-        }
59
-        
60
-        page = isRefresh ? 1 : page + 1
61
-        parameter["page"] = page
62
-        let _ = NetworkApi.share.post(resource: self) { result in
63
-            
64
-            defer { self.loadingSchedule.onNext(isRefresh) }
65
-            
66
-            guard case let .success(items) = result else {
67
-                return
68
-            }
69
-            
70
-            if isRefresh {
71
-                self.items.accept(items)
72
-            } else {
73
-                self.items.accept(self.items.value + items)
74
-            }
75
-        }
76
-    }
77
-}
78
-
79
-extension GroupRepository: Deletable {
80
-    func remove(of index: Int) {
81
-        let item = items.value[index]
82
-        deleteResource.parameter["group_id"] = item.group_id
83
-        NetworkApi.share.post(resource: deleteResource) { [weak self] result in
84
-            guard case let .success(statusModel) = result else { return }
85
-            guard let `self` = self else { return }
86
-            if statusModel.status == 200 {
87
-                var values = self.items.value
88
-                values.remove(at: index)
89
-                self.items.accept(values)
90
-            } else if statusModel.status == 402010 {
91
-                #warning("无权限")
92
-            } else if statusModel.status == 402099 {
93
-                #warning("无权限")
94
-            }
95
-        }
96
-    }
97
-}
98
-
99
-extension GroupRepository: GroupRepositorable {
100
-    public var content: Observable<[GroupItem]> {
101
-        return items.asObservable()
102
-            .flatMap { currentItems in
103
-                Observable.just(currentItems)
104
-                    .distinctUntilChanged()
105
-            }.share()
106
-    }
107
-    
108
-    public var loadingObserver: Observable<Bool> {
109
-        return loadingSchedule.asObserver()
110
-            .flatMap { current in
111
-                Observable.just(current)
112
-            }.share()
113
-    }
114
-}
115
-
116
-
117
-protocol MessageInteractionModel {
118
-    var path: Interfaces { get }
119
-    var deletePath: Interfaces { get }
120
-    var readPath: Interfaces { get }
121
-    var title: String { get }
122
-}
123
-
124
-
125
-public enum MessageType: String {
126
-    case zan = "zan"
127
-    case comment = "comment"
128
-    case system = "system"
129
-    
130
-    fileprivate var messageInteractionModel: MessageInteractionModel {
131
-        switch self {
132
-        case .zan: return MessageZanInteractionModel()
133
-        case .comment: return MessageCommentInteractionModel()
134
-        case .system: return MessageSystemInteractionModel()
135
-        }
136
-    }
137
-    
138
-    fileprivate struct MessageZanInteractionModel: MessageInteractionModel {
139
-        var path: Interfaces { return .mesThumbupList}
140
-        var deletePath: Interfaces { return .mesThumbupClear }
141
-        var readPath: Interfaces { return .mesThumbupRead }
142
-        var title: String { return "赞" }
143
-    }
144
-    
145
-    fileprivate struct MessageCommentInteractionModel: MessageInteractionModel {
146
-        var path: Interfaces { return .mesCommentList}
147
-        var deletePath: Interfaces { return .mesCommentClear }
148
-        var readPath: Interfaces { return .mesCommentRead }
149
-        var title: String { return "评论" }
150
-    }
151
-    
152
-    fileprivate struct MessageSystemInteractionModel: MessageInteractionModel {
153
-        var path: Interfaces { return .mesSystemList}
154
-        var deletePath: Interfaces { return .mesSystemClear }
155
-        var readPath: Interfaces { return .mesSystemRead }
156
-        var title: String { return "系统消息" }
157
-    }
158
-}
159
-
160
-
161
-
162
-final class MessageModel {
163
-    private(set) var items: BehaviorRelay<[MessageItem]>
164
-    private var hasMore = true
165
-    private var page = 1
166
-    private var contentsResource: Resource<[MessageItem]>?
167
-    private var deleteItemResource: Resource<StatusModel>?
168
-    private var readMessageResource: Resource<StatusModel>?
169
-    
170
-    
171
-    init(type: MessageType) {
172
-        items = BehaviorRelay<[MessageItem]>(value: [])
173
-        
174
-        contentsResource =  Resource(url: type.messageInteractionModel.path,
175
-                                     param: ["user_id": SharedUserInfo.userId as AnyObject],
176
-                                     parse: parse)
177
-        deleteItemResource = Resource(url: type.messageInteractionModel.deletePath,
178
-                                      param: ["user_id": SharedUserInfo.userId as AnyObject],
179
-                                      parse: parseStatus)
180
-        readMessageResource = Resource(url: type.messageInteractionModel.readPath,
181
-                                       param: ["user_id": SharedUserInfo.userId as AnyObject,
182
-                                               "all": "true" as AnyObject],
183
-                                       parse: parseStatus)
184
-    }
185
-    
186
-    private func parse(json: [String: AnyObject]) -> [MessageItem]? {
187
-        guard let data = json["data"],
188
-            let messages = data["messages"] as? [[String: AnyObject]],
189
-            let left = data["left"] as? Int else { return nil }
190
-        hasMore = left > 0
191
-        return messages.map { return MessageItem(json: $0) }
192
-    }
193
-    
194
-    private func parseStatus(json: [String: AnyObject]) -> StatusModel? {
195
-        return StatusModel(json: json)
196
-    }
197
-    
198
-    func loadContents(isRefresh: Bool) {
199
-        guard hasMore else { return }
200
-        page = isRefresh ? 1 : page + 1
201
-        contentsResource!.parameter["page"] = page as AnyObject
202
-        NetworkApi.share.post(resource: contentsResource!) { [weak self] result in
203
-            guard case let .success(messageItems) = result else { return }
204
-            guard let `self` = self else { return }
205
-            if isRefresh {
206
-                self.items.accept(messageItems)
207
-            } else {
208
-                self.items.accept(self.items.value + messageItems)
209
-            }
210
-            
211
-        }
212
-    }
213
-    
214
-    func remove(_ item: MessageItem) {
215
-        guard let index = items.value.index(where: { $0 == item }) else { return }
216
-        deleteItemResource!.parameter["pk"] = items.value[index].pk as AnyObject
217
-        NetworkApi.share.post(resource: deleteItemResource!) { [weak self] result in
218
-            guard case .success(_) = result else { return }
219
-            guard let `self` = self else { return }
220
-
221
-            var values = self.items.value
222
-            values.remove(at: index)
223
-            self.items.accept(values)
224
-        }
225
-    }
226
-    
227
-    func removeAll(_ completion:@escaping () -> ()) {
228
-        deleteItemResource!.parameter["all"] = true as AnyObject
229
-        NetworkApi.share.post(resource: deleteItemResource!) { [weak self] result in
230
-            guard case .success(_) = result else { return }
231
-            guard let `self` = self else { return }
232
-            self.items.accept([])
233
-            completion()
234
-        }
235
-    }
236
-    
237
-    func readMessage() {
238
-        NetworkApi.share.post(resource: readMessageResource!, completion: {_ in })
239
-    }
240
-}

+ 0 - 52
PaiAi/PaiaiDataKit/DataLayer/Repositories/OrderModel.swift

@@ -1,52 +0,0 @@
1
-//
2
-//  OrderModel.swift
3
-//  PaiAi
4
-//
5
-//  Created by zhengjianfei on 16/4/7.
6
-//  Copyright © 2016年 FFIB. All rights reserved.
7
-//
8
-
9
-import Foundation
10
-import RxSwift
11
-import RxCocoa
12
-
13
-final class OrderModel {
14
-    
15
-}
16
-
17
-final class OrderModel {
18
-    private(set) var items: BehaviorRelay<[OrderItem]>
19
-    private var hasMore = true
20
-    private var page = 1
21
-    private var contentsResource: Resource<[OrderItem]>?
22
-    
23
-    init() {
24
-        items = BehaviorRelay<[OrderItem]>(value: [])
25
-        contentsResource =  Resource(url: .orderList,
26
-                                     param: ["user_id": SharedUserInfo.userId as AnyObject],
27
-                                     parse: parse)
28
-    }
29
-    
30
-    private func parse(json: [String: AnyObject]) -> [OrderItem]? {
31
-        guard let data = json["data"],
32
-            let orders = data["orders"] as? [[String: AnyObject]],
33
-            let left = data["left"] as? Int else { return nil }
34
-        hasMore = left > 0
35
-        return orders.map { return OrderItem(json: $0) }
36
-    }
37
-    
38
-    func loadContents(isRefresh: Bool) {
39
-        guard hasMore else { return }
40
-        page = isRefresh ? 1 : page + 1
41
-        contentsResource?.parameter["page"] = page as AnyObject
42
-        NetworkApi.share.post(resource: contentsResource!) { [weak self] result in
43
-            guard case let .success(orderItems) = result else { return }
44
-            guard let `self` = self else { return }
45
-            if isRefresh {
46
-                self.items.accept(orderItems)
47
-            } else {
48
-                self.items.accept(orderItems + self.items.value)
49
-            }
50
-        }
51
-    }
52
-}

+ 0 - 140
PaiAi/PaiaiDataKit/DataLayer/Repositories/OrderModelRepository.swift

@@ -1,140 +0,0 @@
1
-//
2
-//  OrderRepository.swift
3
-//  PaiAi
4
-//
5
-//  Created by zhengjianfei on 16/4/7.
6
-//  Copyright © 2016年 FFIB. All rights reserved.
7
-//
8
-
9
-import Foundation
10
-import RxSwift
11
-import RxCocoa
12
-
13
-public protocol OrderRepositorable: Repositorable where Content == OrderItem {}
14
-
15
-final public class OrderModelRepository {
16
-    fileprivate var items: BehaviorRelay<[OrderItem]>
17
-    fileprivate var hasMore = true
18
-    fileprivate var page = 1
19
-    fileprivate var contentsResource: Resource<[OrderItem]>?
20
-}
21
-
22
-public protocol OrderRepository: Repositorable where Content == [PhotoItem] {}
23
-
24
-final public class HomePhotoRepository: Resource {
25
-    fileprivate var items: BehaviorRelay<[PhotoItem]>
26
-    fileprivate var hasMore: Bool
27
-    fileprivate var page: Int
28
-    fileprivate var loadingSchedule: PublishSubject<Bool>
29
-    
30
-    var path: Interfaces { return .home }
31
-    #warning("user_id 填充")
32
-    var parameter: Parameter = ["user_id": ""]
33
-    
34
-    init() {
35
-        page = 1
36
-        hasMore = true
37
-        loadingSchedule = PublishSubject<Bool>()
38
-        items = BehaviorRelay<[PhotoItem]>(value: [])
39
-    }
40
-}
41
-
42
-extension HomePhotoRepository: Parsable {
43
-    typealias Model = [PhotoItem]
44
-    
45
-    func parse(_ json: JSON) -> [PhotoItem]? {
46
-        guard let data = json["data"] as? [String: AnyObject],
47
-            let left = data["left"] as? Int,
48
-            let photos = data["photos"] as? [[String: AnyObject]] else {
49
-                return nil
50
-        }
51
-        hasMore = left > 0
52
-        return photos.map { return PhotoItem(json: $0) }
53
-    }
54
-}
55
-
56
-extension HomePhotoRepository: Gettable {
57
-    public func loadContent(isRefresh: Bool) {
58
-        guard hasMore else {
59
-            loadingSchedule.onNext(isRefresh)
60
-            return
61
-        }
62
-        
63
-        page = isRefresh ? 1 : page + 1
64
-        parameter["page"] = page
65
-        let _ = NetworkApi.share.post(resource: self) { result in
66
-            
67
-            defer { self.loadingSchedule.onNext(isRefresh) }
68
-            
69
-            guard case let .success(photoItems) = result else {
70
-                return
71
-            }
72
-            
73
-            if isRefresh {
74
-                self.items.accept(photoItems)
75
-            } else {
76
-                self.items.accept(self.items.value + photoItems)
77
-            }
78
-        }
79
-    }
80
-}
81
-
82
-extension HomePhotoRepository: HomePhotoRepositorable {
83
-    public var content: Observable<[PhotoItem]> {
84
-        return items.asObservable()
85
-            .flatMap { currentItems in
86
-                Observable.just(currentItems)
87
-                    .distinctUntilChanged()
88
-            }.share()
89
-    }
90
-    
91
-    public var loadingObserver: Observable<Bool> {
92
-        return loadingSchedule.asObserver()
93
-            .flatMap { current in
94
-                Observable.just(current)
95
-            }.share()
96
-    }
97
-}
98
-
99
-extension HomePhotoRepository: Layoutable {
100
-    public func layoutSizeForIndex(_ index: Int) -> CGSize {
101
-        let h = items.value[index].photo_thumbnail_h
102
-        let w = items.value[index].photo_thumbnail_w
103
-        return CGSize(width: w, height: h)
104
-    }
105
-}
106
-
107
-
108
-final class OrderModel {
109
-    
110
-    
111
-    init() {
112
-        items = BehaviorRelay<[OrderItem]>(value: [])
113
-        contentsResource =  Resource(url: .orderList,
114
-                                     param: ["user_id": SharedUserInfo.userId as AnyObject],
115
-                                     parse: parse)
116
-    }
117
-    
118
-    private func parse(json: [String: AnyObject]) -> [OrderItem]? {
119
-        guard let data = json["data"],
120
-            let orders = data["orders"] as? [[String: AnyObject]],
121
-            let left = data["left"] as? Int else { return nil }
122
-        hasMore = left > 0
123
-        return orders.map { return OrderItem(json: $0) }
124
-    }
125
-    
126
-    func loadContents(isRefresh: Bool) {
127
-        guard hasMore else { return }
128
-        page = isRefresh ? 1 : page + 1
129
-        contentsResource?.parameter["page"] = page as AnyObject
130
-        NetworkApi.share.post(resource: contentsResource!) { [weak self] result in
131
-            guard case let .success(orderItems) = result else { return }
132
-            guard let `self` = self else { return }
133
-            if isRefresh {
134
-                self.items.accept(orderItems)
135
-            } else {
136
-                self.items.accept(orderItems + self.items.value)
137
-            }
138
-        }
139
-    }
140
-}

+ 5 - 4
PaiAi/PaiaiDataKit/DataLayer/Repositories/Persistence/RecentGroupInfo.swift

@@ -28,12 +28,10 @@ extension UserDefaults {
28 28
     }
29 29
 }
30 30
 
31
+public var ShareRecentGroupInfo = RecentGroupInfo()
32
+
31 33
 public struct RecentGroupInfo {
32 34
     
33
-    public static var share: RecentGroupInfo {
34
-        return RecentGroupInfo()
35
-    }
36
-    
37 35
     private var infos: [GroupItem]
38 36
     private var lock: NSLock
39 37
     public var count: Int {
@@ -51,9 +49,12 @@ public struct RecentGroupInfo {
51 49
     
52 50
     mutating public func add(_ item: GroupItem) {
53 51
         lock.lock()
52
+        infos.removeAll(where: { $0.group_id == item.group_id })
53
+        
54 54
         if infos.count == 3 {
55 55
             infos.removeFirst()
56 56
         }
57
+        
57 58
         infos.append(item)
58 59
         lock.unlock()
59 60
     }

+ 8 - 8
PaiAi/PaiaiDataKit/DataLayer/Repositories/Remote/GroupDetailRemoteAPI.swift

@@ -23,40 +23,40 @@ struct GroupDetailRemoteAPI {
23 23
     
24 24
     func loadContent() -> Single<GroupDetailItem> {
25 25
         let resource = ContentResource<GroupDetailItem>(path: .groupDetail,
26
-                                                        parameter: ["user_id": "", "group_id": groupId],
26
+                                                        parameter: ["user_id": ShareUserId, "group_id": groupId],
27 27
                                                         parseJSON: parse)
28 28
         return resource.loadContent()
29 29
     }
30 30
     
31 31
     func quit() -> Completable {
32 32
         let quitResoure = StatusResource(path: .groupQuit,
33
-                                         parameter: ["user_id": "", "group_id": groupId])
33
+                                         parameter: ["user_id": ShareUserId, "group_id": groupId])
34 34
         return quitResoure.getStatus()
35 35
     }
36 36
     
37 37
     func lock() -> Completable {
38 38
         let lockResource = StatusResource(path: .groupLock,
39
-                                          parameter: ["user_id": "", "group_id": groupId])
39
+                                          parameter: ["user_id": ShareUserId, "group_id": groupId])
40 40
         return lockResource.getStatus()
41 41
     }
42 42
     
43 43
     func unlock() -> Completable {
44 44
         let unlockResource = StatusResource(path: .groupUnlock,
45
-                                            parameter: ["user_id": "", "group_id": groupId])
45
+                                            parameter: ["user_id": ShareUserId, "group_id": groupId])
46 46
         return unlockResource.getStatus()
47 47
     }
48 48
     
49 49
     func remove(userId: String) -> Completable {
50
-        let deleteResource = StatusResource(path: .groupUnlock,
51
-                                            parameter: ["admin_id": "",
50
+        let deleteResource = StatusResource(path: .groupRemove,
51
+                                            parameter: ["admin_id": ShareUserId,
52 52
                                                         "group_id": groupId,
53
-                                                        "user_id": ""])
53
+                                                        "user_id": userId])
54 54
         return deleteResource.getStatus()
55 55
     }
56 56
     
57 57
     func update(name: String) -> Completable {
58 58
         let updateResource = StatusResource(path: .groupUpdate,
59
-                                            parameter: ["admin_id": "",
59
+                                            parameter: ["admin_id": ShareUserId,
60 60
                                                         "group_id": groupId,
61 61
                                                         "group_name": name])
62 62
         return updateResource.getStatus()

+ 2 - 1
PaiAi/PaiaiDataKit/DataLayer/Repositories/Remote/GroupRemoteAPI.swift

@@ -39,7 +39,8 @@ struct GroupRemoteAPI {
39 39
     
40 40
     func create(groupName: String, avatar: String) -> Single<GroupItem> {
41 41
         let createResource = ContentResource<GroupItem>(path: .groupCreate,
42
-                                                        parameter: ["group_name": groupName,
42
+                                                        parameter: ["user_id": ShareUserId,
43
+                                                                    "group_name": groupName,
43 44
                                                                     "group_default_avatar": avatar],
44 45
                                                         parseJSON: parseGroup)
45 46
         return createResource.loadContent()

+ 2 - 2
PaiAi/PaiaiDataKit/DataLayer/Repositories/Remote/HomeRemoteAPI.swift

@@ -1,5 +1,5 @@
1 1
 //
2
-//  HomePhotoRemoteAPI.swift
2
+//  HomeRemoteAPI.swift
3 3
 //  PaiaiDataKit
4 4
 //
5 5
 //  Created by ffib on 2018/12/27.
@@ -9,7 +9,7 @@
9 9
 import Foundation
10 10
 import RxSwift
11 11
 
12
-struct HomePhotoRemoteAPI {
12
+struct HomeRemoteAPI {
13 13
     
14 14
     private func parse(_ json: JSON) -> NetworkArrayData<PhotoItem>? {
15 15
         guard let data  = json["data"] as? [String: AnyObject] else { return nil }

+ 30 - 0
PaiAi/PaiaiDataKit/DataLayer/Repositories/Remote/MessageRemoteAPI.swift

@@ -17,10 +17,40 @@ struct MessageRemoteAPI {
17 17
         return  messages.map { MessageItem(json: $0) }
18 18
     }
19 19
     
20
+    private func parseMessageList(_ json: JSON) -> NetworkArrayData<MessageListItem>? {
21
+        guard let data  = json["data"] as? [String: AnyObject] else { return nil }
22
+        return NetworkArrayData<MessageListItem>(json: data, dataField: "messages")
23
+    }
24
+    
20 25
     func loadContent() -> Single<[MessageItem]> {
21 26
         let resource = ContentResource<[MessageItem]>(path: .mesList,
22 27
                                                       parameter: ["user_id": ShareUserId],
23 28
                                                       parseJSON: parse)
24 29
         return resource.loadContent()
25 30
     }
31
+    
32
+    func loadMessageList(_ type: MessageType, page: Int) -> Single<NetworkArrayData<MessageListItem>> {
33
+        let resource = ContentResource<NetworkArrayData<MessageListItem>>(path: type.model.path,
34
+                                                                          parameter: ["user_id": ShareUserId, "page": page],
35
+                                                                          parseJSON: parseMessageList)
36
+        return resource.loadContent()
37
+    }
38
+    
39
+    func remove(_ type: MessageType, pk: Int) -> Completable {
40
+        let removeResource = StatusResource(path: type.model.removePath,
41
+                                            parameter: ["user_id": ShareUserId, "pk": pk])
42
+        return removeResource.getStatus()
43
+    }
44
+    
45
+    func removeAll(_ type: MessageType) -> Completable {
46
+        let removeResource = StatusResource(path: type.model.removePath,
47
+                                            parameter: ["user_id": ShareUserId, "all": true])
48
+        return removeResource.getStatus()
49
+    }
50
+    
51
+    func readed(_ type: MessageType) -> Completable {
52
+        let readedResource = StatusResource(path: type.model.readPath,
53
+                                            parameter: ["user_id": ShareUserId])
54
+        return readedResource.getStatus()
55
+    }
26 56
 }

+ 71 - 0
PaiAi/PaiaiDataKit/DataLayer/Repositories/Remote/PhotoDetailRemoteAPI.swift

@@ -7,3 +7,74 @@
7 7
 //
8 8
 
9 9
 import Foundation
10
+import RxSwift
11
+
12
+struct PhotoDetailRemoteAPI {
13
+    var photoId: String
14
+    var groupId: String
15
+    init(photoId: String, groupId: String) {
16
+        self.photoId = photoId
17
+        self.groupId = groupId
18
+    }
19
+    
20
+    private func parseCommtents(_ json: JSON) -> [PhotoCommentItem] {
21
+        guard let data = json["data"] as? [String: AnyObject],
22
+            let comments = data["comments"] as? [[String: AnyObject]] else { return [] }
23
+        return comments.compactMap { PhotoCommentItem(json: $0) }
24
+    }
25
+    
26
+    private func parseThumbups(_ json: JSON) -> [PhotoThumbupUserItem] {
27
+        guard let data = json["data"] as? [String: AnyObject],
28
+            let thumbups = data["thumbups"] as? [[String: AnyObject]] else { return [] }
29
+        return thumbups.compactMap { PhotoThumbupUserItem(json: $0) }
30
+    }
31
+    
32
+    func loadThumbups() -> Single<[PhotoThumbupUserItem]> {
33
+        let resource = ContentResource<[PhotoThumbupUserItem]>(path: .thumbupList,
34
+                                                           parameter: ["group_id": groupId,
35
+                                                                       "photo_id": photoId,
36
+                                                                       "user_id": ShareUserId],
37
+                                                           parseJSON: parseThumbups)
38
+        
39
+        return resource.loadContent()
40
+    }
41
+    
42
+    func loadComments() -> Single<[PhotoCommentItem]> {
43
+        let resource = ContentResource<[PhotoCommentItem]>(path: .commentList,
44
+                                                                           parameter: ["group_id": groupId,
45
+                                                                                       "photo_id": photoId,
46
+                                                                                       "user_id": ShareUserId],
47
+                                                                           parseJSON: parseCommtents)
48
+        
49
+        return resource.loadContent()
50
+    }
51
+    
52
+    func submitComment(text: String) -> Single<[PhotoCommentItem]> {
53
+        let resource = ContentResource<[PhotoCommentItem]>(path: .commentSubmit,
54
+                                                           parameter: ["group_id": groupId,
55
+                                                                       "photo_id": photoId,
56
+                                                                       "user_id": ShareUserId,
57
+                                                                       "comment": text],
58
+                                                           parseJSON: parseCommtents)
59
+        return resource.loadContent()
60
+        
61
+    }
62
+    
63
+    func submitThumbup() -> Single<[PhotoThumbupUserItem]> {
64
+        let resource = ContentResource<[PhotoThumbupUserItem]>(path: .thumbupSubmit,
65
+                                                           parameter: ["group_id": groupId,
66
+                                                                       "photo_id": photoId,
67
+                                                                       "user_id": ShareUserId],
68
+                                                           parseJSON: parseThumbups)
69
+        return resource.loadContent()
70
+    }
71
+    
72
+    func cancelThumbup() {
73
+        let resource = ContentResource<[PhotoCommentItem]>(path: .thumbupCancel,
74
+                                                           parameter: ["group_id": groupId,
75
+                                                                       "photo_id": photoId,
76
+                                                                       "user_id": ShareUserId],
77
+                                                           parseJSON: parseCommtents)
78
+        resource.loadContent()
79
+    }
80
+}

+ 1 - 1
PaiAi/PaiaiDataKit/DataLayer/Repositories/Remote/Reusable/NetWork/Interfaces.swift

@@ -46,7 +46,7 @@ public enum Interfaces: String {
46 46
     case photoUpload = "/f/upload"
47 47
     case commentSubmit = "/f/comment/submit"
48 48
     case thumbupSubmit = "/f/thumbup/submit"
49
-    case thumbupCancle = "/f/thumbup/cancel"
49
+    case thumbupCancel = "/f/thumbup/cancel"
50 50
     case thumbupList = "/f/thumbup/list"
51 51
     case commentList = "/f/comment/list"
52 52
     case picPrice = "/f/price"

+ 7 - 3
PaiAi/PaiaiDataKit/DataLayer/Repositories/Remote/Reusable/NetWork/NetworkApi.swift

@@ -73,7 +73,6 @@ class NetworkApi {
73 73
     }
74 74
     
75 75
     public func post<A: Resource>(resource: A) -> Single<A.Model> {
76
-        print(resource)
77 76
         return Single<A.Model>.create(subscribe: { observer in
78 77
             let request = self.session.request(resource.host + resource.path.rawValue,
79 78
                                                  method: .post,
@@ -82,11 +81,17 @@ class NetworkApi {
82 81
                     switch res.result {
83 82
                     case .success(let json):
84 83
                         guard let json = json as? [String: AnyObject],
84
+                            let status = json["status"] as? Int,
85 85
                             let data = resource.parse(json) else {
86 86
                                 observer(.error(ParseError()))
87 87
                                 return
88 88
                         }
89
-                        observer(.success(data))
89
+                        if status == 200 {
90
+                            observer(.success(data))
91
+                        } else {
92
+                            observer(.error(InteractionError(id: status, errMessage: "")))
93
+                        }
94
+                        
90 95
                     case .failure(let error):
91 96
                         observer(.error(error))
92 97
                     }
@@ -118,7 +123,6 @@ class NetworkApi {
118 123
     }
119 124
     
120 125
     public func upload<A: Resource>(resource: A) -> Single<A.Model> {
121
-        print(resource.parameter)
122 126
         return Single<A.Model>.create(subscribe: { (observer) in
123 127
             let request = self.session.upload(multipartFormData: { (multiPartData) in
124 128
                 for (key, value) in resource.parameter {

+ 0 - 2
PaiAi/PaiaiDataKit/DataLayer/Repositories/Remote/Reusable/StatusResource.swift

@@ -9,8 +9,6 @@
9 9
 import Foundation
10 10
 import RxSwift
11 11
 
12
-
13
-
14 12
 struct StatusResource: Resource {
15 13
     typealias Model = StatusModel
16 14
     

+ 0 - 28
PaiAi/PaiaiDataKit/DataLayer/Repositories/UserSessionRepository.swift

@@ -1,28 +0,0 @@
1
-//
2
-//  UserSessionRepository.swift
3
-//  PaiAi
4
-//
5
-//  Created by FFIB on 16/4/1.
6
-//  Copyright © 2016年 FFIB. All rights reserved.
7
-//
8
-
9
-import Foundation
10
-
11
-final class UserInfoRepository {
12
-    
13
-    fileprivate var loginRemoteAPI: LoginRemoteAPI
14
-    fileprivate var userSessionStore: UserSessionStore
15
-    init(loginRemoteAPI: LoginRemoteAPI = LoginRemoteAPI(),
16
-         userSessionStore: UserSessionStore = UserSessionStore()) {
17
-        self.loginRemoteAPI = loginRemoteAPI
18
-        self.userSessionStore = userSessionStore
19
-    }
20
-    
21
-    func guestLogin() {
22
-        loginRemoteAPI.guestLogin()
23
-    }
24
-    
25
-    func wxLogin() {
26
-        loginRemoteAPI.wxLogin()
27
-    }
28
-}

+ 76 - 61
PaiAi/PaiaiDataKit/PresentLayer/Group/GroupDetail/GroupDetailViewModel.swift

@@ -10,83 +10,98 @@ import Foundation
10 10
 import RxSwift
11 11
 import RxCocoa
12 12
 
13
-protocol GroupDetailViewModelDelegate {
14
-    
13
+public protocol GroupDetailViewModelDelegate: class {
14
+    func navigationToGroupMember(_ item: GroupDetailItem)
15
+    func navigationToGroupNameModification(_ item: GroupDetailItem)
16
+    func navigationToRootViewController()
15 17
 }
16 18
 
17 19
 public class GroupDetailViewModel {
18 20
     
19
-    private let respository: GroupDetailRepository
20
-    private let item: BehaviorRelay<GroupDetailItem>
21
+    private let repository: GroupDetailRepository
22
+    private var disposeBag = DisposeBag()
23
+    
24
+    public weak var delegate: GroupDetailViewModelDelegate?
25
+    
26
+    public let item: BehaviorRelay<GroupDetailItem>
27
+    public var groupName: Observable<String> {
28
+        return item.asObservable().flatMapLatest({ (item) -> Observable<String> in
29
+            return Observable.just(item.group.group_name)
30
+        })
31
+    }
32
+    
33
+    public var groupMembers: Observable<[GroupMemberItem]> {
34
+        return item.asObservable().flatMapLatest({ (item) -> Observable<[GroupMemberItem]> in
35
+            return Observable.just(item.users)
36
+        })
37
+    }
38
+    
39
+    public var groupMemberCount: Observable<String> {
40
+        return item.asObservable().flatMapLatest({ (item) -> Observable<String> in
41
+            return Observable.just("\(item.users.count)")
42
+        })
43
+    }
44
+    
45
+    public var isAdmin: Observable<Bool> {
46
+        return item.asObservable().flatMapLatest({ (item) -> Observable<Bool> in
47
+            return Observable.just(item.group.admin_id == ShareUserId)
48
+        })
49
+    }
21 50
     
22
-    public init(groupId: String) {
23
-        self.respository = PhotoGroupDetailRepository(groupId: groupId)
24
-        item = BehaviorRelay<GroupDetailItem>(value: GroupDetailItem(json: [:]))
51
+    public var groupLock: Observable<Bool> {
52
+        return item.asObservable().flatMapLatest({ (item) -> Observable<Bool> in
53
+            return Observable.just(item.group.group_lock)
54
+        })
25 55
     }
26 56
     
27
-    public var contents: Observable<GroupDetailItem> {
28
-        return item.asObservable()
57
+    public init(item: GroupItem) {
58
+        self.repository = PhotoGroupDetailRepository(groupId: item.group_id)
59
+        
60
+        var groupDetailItem = GroupDetailItem(json: [:])
61
+        groupDetailItem.group = item
62
+        self.item = BehaviorRelay<GroupDetailItem>(value: groupDetailItem)
29 63
     }
30 64
     
31 65
     public func toggle(isLock: Bool) {
32
-//        return respository.update(data: isLock)
66
+        if isLock {
67
+            repository.lock().subscribe(onCompleted: {
68
+                var v = self.item.value
69
+                v.group.group_lock = true
70
+                self.item.accept(v)
71
+            }) { (error) in
72
+                
73
+            }.disposed(by: disposeBag)
74
+        } else {
75
+            repository.unlock().subscribe(onCompleted: {
76
+                var v = self.item.value
77
+                v.group.group_lock = false
78
+            }, onError: nil).disposed(by: disposeBag)
79
+        }
33 80
     }
34 81
     
35 82
     public func reload() {
36
-        respository.load()
83
+        repository.load().subscribe(onSuccess: {[unowned self] (v) in
84
+            self.item.accept(v)
85
+        }, onError: nil).disposed(by: disposeBag)
37 86
     }
38 87
     
39 88
     public func quit() {
40
-        respository.quit()
89
+        guard item.value.group.admin_id != ShareUserId else {
90
+            return
91
+        }
92
+        repository.quit()
93
+            .subscribe(onCompleted: {[unowned self] in
94
+                self.delegate?.navigationToRootViewController()
95
+            }).disposed(by: disposeBag)
41 96
     }
42 97
 }
43 98
 
44
-//public struct GroupDetailViewModel {
45
-//    public var groupDetailData = Variable<GroupDetailModel>(GroupDetailModel())
46
-//
47
-//    public init() {
48
-//
49
-//    }
50
-//
51
-//    public func fetchGroupDetailData(groupId: String) {
52
-//        let params = ["group_id": groupId,
53
-//                      "user_id": SharedUserInfo.userId] as [String: AnyObject]
54
-//        let request = GroupDetailNetworkQequest(param: params, path: .groupDetail)
55
-//        networkApi.post(request: request) { (res) in
56
-//            self.groupDetailData.value = res
57
-////            FFToastView.hideLoadingToast()
58
-//        }
59
-//    }
60
-//
61
-//    public func quitQroup(success: @escaping () -> Void) {
62
-////        if groupDetailData.value.group?.admin_id == SharedUserInfo.userId {
63
-////            FFToastView.showToast(inView: UIApplication.shared.keyWindow!, withText: "管理员不能退出")
64
-////            return
65
-////        } else {
66
-//            let params = ["group_id": groupDetailData.value.group_id,
67
-//                           "user_id": SharedUserInfo.userId]  as [String: AnyObject]
68
-//            let request = StatusNetworkRequest(param: params, path: .groupQuit)
69
-//            networkApi.post(request: request, handler: { (res) in
70
-//                guard res.status == 200 else {
71
-//                    return
72
-//                }
73
-//                success()
74
-//                PhotoLocalStorage.instance.removeLocalData(group_id: self.groupDetailData.value.group_id)
75
-//            })
76
-////        }
77
-//    }
78
-//
79
-//    public func postLock(isLock: Bool) {
80
-//        let url = isLock ? Interfaces.groupLock : Interfaces.groupUnlock
81
-////        self.groupDetailData.value.group?.group_lock = isLock
82
-//        let params = ["group_id": groupDetailData.value.group_id,
83
-//                                           "user_id": SharedUserInfo.userId]  as [String: AnyObject]
84
-//        let request = StatusNetworkRequest(param: params, path: url)
85
-//        networkApi.post(request: request) { (res) in
86
-//            guard res.status == 200 else {
87
-//                return
88
-//            }
89
-////            FFToastView.showToast(inView: UIApplication.shared.keyWindow!, withText: res.message)
90
-//        }
91
-//    }
92
-//}
99
+public extension GroupDetailViewModel {
100
+    func navigationToGroupMember() {
101
+        delegate?.navigationToGroupMember(item.value)
102
+    }
103
+    
104
+    func navigationToGroupNameModification() {
105
+        delegate?.navigationToGroupNameModification(item.value)
106
+    }
107
+}

+ 29 - 6
PaiAi/PaiaiDataKit/PresentLayer/Group/GroupDetail/GroupMemberViewModel.swift

@@ -7,14 +7,37 @@
7 7
 //
8 8
 
9 9
 import UIKit
10
+import RxSwift
11
+import RxCocoa
12
+import RxDataSources
10 13
 
11 14
 public class GroupMemberViewModel {
12 15
     
13
-    public var isManage: Bool = false
14
-    public init() {}
15
-    public func deleteUser(_ index: Int) {
16
-//        NetworkApi.share.post(resource: self) { (result) in
17
-//            guard case let .success(data) = result else { return }
18
-//        }
16
+    private var repository: PhotoGroupDetailRepository
17
+    private var items: BehaviorRelay<[GroupMemberItem]>
18
+    
19
+    private var disposeBag = DisposeBag()
20
+    
21
+    public var isAdmin: Bool
22
+    
23
+    public var contents: Observable<[AnimatableSectionModel<Int, GroupMemberItem>]> {
24
+        return items.map({ model in
25
+            return [AnimatableSectionModel(model: 0, items: model)]
26
+        })
27
+    }
28
+    
29
+    public init(item: GroupDetailItem) {
30
+        self.repository = PhotoGroupDetailRepository(groupId: item.group_id)
31
+        self.items = BehaviorRelay<[GroupMemberItem]>(value: item.users)
32
+        self.isAdmin = item.group.admin_id == ShareUserId
33
+    }
34
+    
35
+    public func removeMember(_ item: GroupMemberItem) {
36
+        repository.removeMember(userId: item.user_id)
37
+            .subscribe(onCompleted: {[unowned self] in
38
+                var content = self.items.value
39
+                content.removeAll(where: { $0.user_id == item.user_id })
40
+                self.items.accept(content)
41
+        }).disposed(by: disposeBag)
19 42
     }
20 43
 }

+ 25 - 9
PaiAi/PaiaiDataKit/PresentLayer/Group/GroupViewModel.swift

@@ -12,19 +12,33 @@ import RxCocoa
12 12
 import RxDataSources
13 13
 
14 14
 public protocol GroupViewModelDelegate: class {
15
-    func didSelect(_ item: PhotoItem)
16
-    func navigateToGroupDetail()
15
+    func didSelect(_ items: [PhotoItem], currIndex: Int)
16
+    func navigateToGroupDetail(_ item: GroupItem)
17 17
 }
18 18
 
19 19
 public class GroupViewModel {
20 20
     
21 21
     private let disposeBag = DisposeBag()
22 22
     
23
-    private var respository: GroupPhotoRepository
23
+    private var repository: GroupPhotoRepository
24
+    
24 25
     
25 26
     private var _isLoading = PublishSubject<Void>()
26 27
     private let items = BehaviorRelay<[PhotoItem]>(value: [])
27 28
     
29
+    public var groupItem: BehaviorRelay<GroupItem>
30
+    
31
+    public var groupName: Observable<String> {
32
+        return groupItem.asObservable().flatMapLatest({ (item) -> Observable<String> in
33
+            return Observable.just(item.group_name)
34
+        })
35
+    }
36
+    
37
+    public var groupAvatar: Observable<String> {
38
+        return groupItem.asObservable().flatMapLatest({ (item) -> Observable<String> in
39
+            return Observable.just("Group\(item.group_default_avatar)")
40
+        })
41
+    }
28 42
     
29 43
     public var isLoading: Observable<Void> {
30 44
         return _isLoading.asObservable()
@@ -39,11 +53,13 @@ public class GroupViewModel {
39 53
     public weak var delegate: GroupViewModelDelegate?
40 54
     
41 55
     public init(groupItem: GroupItem) {
42
-        self.respository = GroupPhotoRepository(groupId: groupItem.group_id)
56
+        self.repository = GroupPhotoRepository(groupId: groupItem.group_id)
57
+        self.groupItem = BehaviorRelay<GroupItem>(value: groupItem)
58
+        ShareRecentGroupInfo.add(groupItem)
43 59
     }
44 60
     
45 61
     public func reload() {
46
-        respository.load()
62
+        repository.load()
47 63
             .subscribe(onSuccess: {[weak self] (result) in
48 64
                 guard let `self` = self else { return }
49 65
                 self._isLoading.onNext(())
@@ -55,7 +71,7 @@ public class GroupViewModel {
55 71
     }
56 72
 
57 73
     public func submit(data: Data) {
58
-        respository.upload(data: data).subscribe(onSuccess: { [weak self] result in
74
+        repository.upload(data: data).subscribe(onSuccess: { [weak self] result in
59 75
             guard let `self` = self else { return }
60 76
             self.items.accept(result)
61 77
             self._isLoading.onNext(())
@@ -77,11 +93,11 @@ public class GroupViewModel {
77 93
 
78 94
 /// GroupViewModelDelegate
79 95
 public extension GroupViewModel {
80
-    func didSelect(_ item: PhotoItem) {
81
-        delegate?.didSelect(item)
96
+    func didSelect(_ index: Int) {
97
+        delegate?.didSelect(items.value, currIndex: index)
82 98
     }
83 99
     
84 100
     @objc func navigateToGroupDetail() {
85
-        delegate?.navigateToGroupDetail()
101
+        delegate?.navigateToGroupDetail(groupItem.value)
86 102
     }
87 103
 }

+ 25 - 39
PaiAi/PaiaiDataKit/PresentLayer/Home/CreateGroupViewModel.swift

@@ -1,5 +1,5 @@
1 1
 //
2
-//  CreateGroupConfirmViewModel.swift
2
+//  CreateGroupViewModel.swift
3 3
 //  PaiAi
4 4
 //
5 5
 //  Created by ffib on 2018/12/17.
@@ -10,53 +10,39 @@ import Foundation
10 10
 import RxSwift
11 11
 import RxCocoa
12 12
 
13
-fileprivate let DefaultGroupNames = ["苹果", "香蕉", "西兰花",
13
+public protocol CreateGroupViewModelDelegate: class {
14
+    func navigationToGroup(_ item: GroupItem)
15
+}
16
+
17
+public final class CreateGroupViewModel {
18
+    
19
+    private var repository: PhotoGroupRepository
20
+    
21
+    public var avatar: String
22
+    public var name: String
23
+    public var mappingIndex: Int
24
+    public weak var delegate: CreateGroupViewModelDelegate?
25
+    
26
+    private let defaultGroupNames = ["苹果", "香蕉", "西兰花",
14 27
                                      "杨梅", "胡萝卜", "樱桃",
15 28
                                      "玉米", "火龙果", "茄子",
16 29
                                      "无花果", "葡萄", "柠檬",
17 30
                                      "山竹", "橘子", "木瓜",
18 31
                                      "水蜜桃", "菠萝", "草莓",
19 32
                                      "西瓜"]
20
-
21
-public final class CreateGroupConfirmViewModel {
22
-    
23
-    private var item: BehaviorRelay<GroupItem?>
24
-    private var repository: PhotoGroupRepository
25
-    
26
-    public var avatar: Observable<String>
27
-    public var name: Observable<String>
28
-    public var groupModelUntilNotNil: Observable<GroupItem?>
29
-    
33
+    private let disposeBag = DisposeBag()
30 34
     
31
-    public init() {
32
-        repository = PhotoGroupRepository()
33
-        let random = Int(arc4random_uniform(19))
34
-        avatar = Observable.create({ (observer) -> Disposable in
35
-            observer.onNext(DefaultGroupNames[random])
36
-            return Disposables.create()
37
-        })
38
-        
39
-        name = avatar.flatMapLatest({ (currentAvatar) in
40
-//            Observable.just("\(UserInfoViewModel)的\(currentAvatar)群")
41
-            Observable.just("群")
42
-        }).share()
43
-        
44
-        item = BehaviorRelay<GroupItem?>(value: nil)
45
-        
46
-        groupModelUntilNotNil = item.filter { $0 != nil }
47
-        
48
-        name.bind { (groupName) in
49
-            
50
-//            self.parameter = ["group_name": groupName as AnyObject,
51
-//                          "group_default_avatar": random as AnyObject]
52
-        }.dispose()
35
+    public init(userInfoViewModel: UserInfoViewModel) {
36
+        self.mappingIndex = Int(arc4random_uniform(19))
37
+        self.repository = PhotoGroupRepository()
38
+        self.avatar = "Group\(mappingIndex)"
39
+        self.name = userInfoViewModel.shareUserInfo.value.userName + "的" + defaultGroupNames[mappingIndex] + "群"
53 40
     }
54 41
     
55 42
     public func createGroup() {
56
-//        repository.create(groupName: avatar, avatar: <#T##String#>)
57
-//        NetworkApi.share.post(resource: self) { (result) in
58
-//            guard case let .success(item) = result else { return }
59
-//            self.groupModel.accept(item)
60
-//        }
43
+        return repository.create(groupName: name, avatar: "\(mappingIndex)")
44
+            .subscribe(onSuccess: {[unowned self] item in
45
+                self.delegate?.navigationToGroup(item)
46
+            }).disposed(by: disposeBag)
61 47
     }
62 48
 }

+ 7 - 7
PaiAi/PaiaiDataKit/PresentLayer/Home/HomeViewModel.swift

@@ -14,7 +14,7 @@ import RxDataSources
14 14
 public protocol HomeViewModelDelegate: class {
15 15
     func scanQR()
16 16
     func createGroup()
17
-    func didSelect(_ item: PhotoItem)
17
+    func didSelect(_ items: [PhotoItem], currIndex: Int)
18 18
 }
19 19
 
20 20
 public class HomeViewModel {
@@ -22,7 +22,7 @@ public class HomeViewModel {
22 22
     private var page: Int = 1
23 23
     private let disposeBag = DisposeBag()
24 24
     
25
-    private var respository: PhotoRepository
25
+    private var repository: HomeRepository
26 26
     
27 27
     private var _isLoading = PublishSubject<Bool>()
28 28
     private var _hasMoreData = BehaviorRelay<Bool>(value: true)
@@ -49,7 +49,7 @@ public class HomeViewModel {
49 49
     public weak var delegate: HomeViewModelDelegate?
50 50
     
51 51
     public init() {
52
-        self.respository = HomePhotoRepository()
52
+        self.repository = HomeRepository()
53 53
         
54 54
         scanBtnTapped.subscribe({
55 55
             [weak self] (_) in
@@ -66,7 +66,7 @@ public class HomeViewModel {
66 66
 
67 67
     public func reload() {
68 68
         page = 1
69
-        respository.load(page: page)
69
+        repository.load(page: page)
70 70
             .subscribe(onSuccess: {[weak self] (result) in
71 71
                 guard let `self` = self else { return }
72 72
                 self._isLoading.onNext(true)
@@ -81,7 +81,7 @@ public class HomeViewModel {
81 81
     public func preload() {
82 82
         guard _hasMoreData.value else { return }
83 83
         page += 1
84
-        respository.load(page: page)
84
+        repository.load(page: page)
85 85
             .subscribe(onSuccess: {[weak self] (result) in
86 86
                 guard let `self` = self else { return }
87 87
                 self._isLoading.onNext(false)
@@ -114,7 +114,7 @@ extension HomeViewModel {
114 114
         delegate?.createGroup()
115 115
     }
116 116
     
117
-    public func didSelect(_ item: PhotoItem) {
118
-        delegate?.didSelect(item)
117
+    public func didSelect(_ currIndex: Int) {
118
+        delegate?.didSelect(items.value, currIndex: currIndex)
119 119
     }
120 120
 }

+ 4 - 4
PaiAi/PaiaiDataKit/PresentLayer/Home/ScanQRViewModel.swift

@@ -16,10 +16,10 @@ public final class ScanQRViewModel {
16 16
     
17 17
     weak var delegate: ScanQRViewModelDelegate?
18 18
     
19
-    var respository: PhotoGroupRepository
19
+    var repository: PhotoGroupRepository
20 20
     
21 21
     public init() {
22
-        respository = PhotoGroupRepository()
22
+        repository = PhotoGroupRepository()
23 23
     }
24 24
     
25 25
     public func join(code: String) {
@@ -31,10 +31,10 @@ public final class ScanQRViewModel {
31 31
             guard let lensman_id = params.components(separatedBy: "=").last,
32 32
                 let session_id = params.components(separatedBy: "?").first else { return }
33 33
             
34
-            respository.join(type: .session, parameter: ["session_id": session_id, "lensman_id": lensman_id])
34
+            repository.join(type: .session, parameter: ["session_id": session_id, "lensman_id": lensman_id])
35 35
             
36 36
         case "g":
37
-            respository.join(type: .session, parameter: ["group_id": params])
37
+            repository.join(type: .session, parameter: ["group_id": params])
38 38
         default:
39 39
             return
40 40
         }

+ 12 - 6
PaiAi/PaiaiDataKit/PresentLayer/Message/MessageListViewModel.swift

@@ -17,7 +17,8 @@ public protocol MessageListViewModelDelegate: class {
17 17
 
18 18
 public class MessageListViewModel {
19 19
     
20
-    private let repository: MessageListRepository
20
+    private let repository: MessageRepository
21
+    private let type: MessageType
21 22
     private var disposeBag = DisposeBag()
22 23
     private var page = 1
23 24
     
@@ -52,12 +53,13 @@ public class MessageListViewModel {
52 53
     }
53 54
     
54 55
     public init(type: MessageType) {
55
-        self.repository = MessageListRepository(type: type)
56
+        self.repository = MessageRepository()
57
+        self.type = type
56 58
     }
57 59
     
58 60
     public func reload() {
59 61
         page = 1
60
-        repository.load(page: page)
62
+        repository.loadMessageList(type, page: page)
61 63
             .asDriver(onErrorJustReturn: NetworkArrayData<MessageListItem>.empty())
62 64
             .drive(onNext: {[weak self] (model) in
63 65
                 guard let `self` = self else { return }
@@ -74,7 +76,7 @@ public class MessageListViewModel {
74 76
     public func preload() {
75 77
         page += 1
76 78
         
77
-        repository.load(page: page)
79
+        repository.loadMessageList(type, page: page)
78 80
             .asDriver(onErrorJustReturn: NetworkArrayData<MessageListItem>.empty())
79 81
             .drive(onNext: {[weak self] (model) in
80 82
                 guard let `self` = self else { return }
@@ -87,7 +89,7 @@ public class MessageListViewModel {
87 89
     }
88 90
     
89 91
     public func remove(of index: Int) {
90
-        repository.remove(pk: items.value[index].pk)
92
+        repository.remove(type, pk: items.value[index].pk)
91 93
             .subscribe(onCompleted: {[weak self] in
92 94
                 guard let `self` = self else { return }
93 95
                 var _items = self.items.value
@@ -103,7 +105,7 @@ public class MessageListViewModel {
103 105
     }
104 106
     
105 107
     public func removeAll() {
106
-        repository.removeAll()
108
+        repository.removeAll(type)
107 109
             .subscribe(onCompleted: {[weak self] in
108 110
                 guard let `self` = self else { return }
109 111
                 self.items.accept([])
@@ -116,6 +118,10 @@ public class MessageListViewModel {
116 118
             }.disposed(by: disposeBag)
117 119
     }
118 120
     
121
+    public func readed() {
122
+        
123
+    }
124
+    
119 125
     public func didSelect(item: MessageListItem) {
120 126
         delegate?.didSelect(item: item)
121 127
     }

+ 3 - 3
PaiAi/PaiaiDataKit/PresentLayer/Message/MessageViewModel.swift

@@ -16,7 +16,7 @@ public protocol MessageViewModelDelegate: class {
16 16
 
17 17
 public class MessageViewModel {
18 18
     
19
-    private var respository: MessageRepository
19
+    private var repository: MessageRepository
20 20
     private var disposeBag = DisposeBag()
21 21
     
22 22
     public weak var delegate: MessageViewModelDelegate?
@@ -30,7 +30,7 @@ public class MessageViewModel {
30 30
     public var thumbupBtnTapped = PublishSubject<Void>()
31 31
     
32 32
     public init() {
33
-        self.respository = MessageRepository()
33
+        self.repository = MessageRepository()
34 34
         
35 35
         sysBtnTapped.asObservable().subscribe {[weak self] (_) in
36 36
             guard let `self` = self else { return }
@@ -49,7 +49,7 @@ public class MessageViewModel {
49 49
     }
50 50
     
51 51
     public func reload() {
52
-        respository.load().asObservable().subscribe(onNext: {[unowned self] (items) in
52
+        repository.loadContent().asObservable().subscribe(onNext: {[unowned self] (items) in
53 53
             for item in items {
54 54
                 switch item.msg_type {
55 55
                 case .system:

+ 40 - 0
PaiAi/PaiaiDataKit/PresentLayer/PhotoDetail/PhotoDetailListViewModel.swift

@@ -7,3 +7,43 @@
7 7
 //
8 8
 
9 9
 import Foundation
10
+import RxSwift
11
+import RxCocoa
12
+import RxDataSources
13
+
14
+public protocol PhotoDetailListViewModelSynchronization: class {
15
+    func willShow(_ item: PhotoItem)
16
+}
17
+
18
+public protocol PhotoDetailListViewModelDelegate: class {
19
+    func didSelected()
20
+}
21
+
22
+
23
+public final class PhotoDetailListViewModel {
24
+    private var items: BehaviorRelay<[PhotoItem]>
25
+    public var currIndex: Int
26
+    
27
+    public weak var delegate: PhotoDetailListViewModelDelegate?
28
+    public weak var synchronization: PhotoDetailListViewModelSynchronization?
29
+    
30
+    public var content: Observable<[AnimatableSectionModel<Int, PhotoItem>]> {
31
+        return items.map({ model in
32
+            return [AnimatableSectionModel(model: 0, items: model)]
33
+        })
34
+    }
35
+ 
36
+    public init(items: [PhotoItem], currIndex: Int) {
37
+        self.items = BehaviorRelay<[PhotoItem]>(value: items)
38
+        self.currIndex = currIndex
39
+    }
40
+    
41
+    public func willShow(index: Int) {
42
+        currIndex = index
43
+        synchronization?.willShow(items.value[currIndex])
44
+    }
45
+    
46
+    public func didSelected() {
47
+        delegate?.didSelected()
48
+    }
49
+}

+ 110 - 183
PaiAi/PaiaiDataKit/PresentLayer/PhotoDetail/PhotoDetailViewModel.swift

@@ -6,203 +6,130 @@
6 6
 //  Copyright © 2017年 FFIB. All rights reserved.
7 7
 //
8 8
 
9
-import UIKit
10
-import ObjectMapper
11
-
9
+import Foundation
10
+import RxSwift
11
+import RxCocoa
12
+import RxDataSources
12 13
 
14
+public protocol PhotoDetailViewModelDelegate: class {
15
+    func navigateToGroup(_ item: GroupItem)
16
+}
13 17
 
14 18
 public final class PhotoDetailViewModel {
15
-    public lazy var currentPhoto = PhotoItem(json: [:])
16
-    public lazy var thumbups = [thumbupUserModel]()
17
-    public lazy var comments = [CommentItem]()
18
-    public lazy var thumbupsCount = 0
19
-    public lazy var commentsCount = 0
20
-    public lazy var hdPrice: Double = -0.01
21
-    public lazy var watermarkPrice: Double = -0.01
22
-    var detailPageApi = NetworkApi()
23
-//    var tipDelegate : TipProtocol? = nil
24
-
25
-    public init() {}
26
-    
27
-    public final func fetchThumbup(success: @escaping () -> Void) {
28
-//        let params = ["group_id": currentPhoto.group_id, "user_id": SharedUserInfo.userId, "photo_id": currentPhoto.photo_id] as [String: AnyObject]
29
-//        let request = thumbupUserNetworkRequest(parameter: params)
30
-//        detailPageApi.post(request: request) {[weak self] (res) in
31
-//            guard let weakself = self else {
32
-//                return
33
-//            }
34
-//            weakself.thumbups = res
35
-//            weakself.thumbupsCount = res.count
36
-//            success()
37
-//        }
38
-    }
39
-    public final func fetchComment(success: @escaping () -> Void) {
40
-
41
-//        let params = ["group_id": currentPhoto.group_id, "user_id": SharedUserInfo.userId, "photo_id": currentPhoto.photo_id] as [String: AnyObject]
42
-//        let request = CommentNetworkRequest(parameter: params)
43
-//        detailPageApi.post(request: request) {[weak self] (res) in
44
-//            guard let weakself = self else {
45
-//                return
46
-//            }
47
-//            weakself.comments = res
48
-//            weakself.commentsCount = res.count
49
-//            success()
50
-//        }
19
+    
20
+    public var thumbupItems: Observable<[PhotoThumbupUserItem]> {
21
+        return _thumbupItems.asObservable()
51 22
     }
52
-
53
-    public final func sendThumbup(success: @escaping () -> Void) {
54
-//        let params = ["group_id": currentPhoto.group_id, "user_id": SharedUserInfo.userId, "photo_id": currentPhoto.photo_id] as [String: AnyObject]
55
-//        let request = StatusNetworkRequest(param: params, path: .thumbupSubmit)
56
-//
57
-//        detailPageApi.post(request: request) { (res) in
58
-//            if res.message == "Duplicate Thumb Up" {
59
-////                FFToastView.showToast(inView: UIApplication.shared.keyWindow!, withText: "照片已经点过赞了")
60
-//            }
61
-//            guard res.status == 200 else {
62
-////                FFToastView.showToast(inView: UIApplication.shared.keyWindow!, withText: "照片点赞失败")
63
-//                return
64
-//            }
65
-////            FFToastView.showToast(inView: UIApplication.shared.keyWindow!, withText: "照片点赞成功")
66
-//            success()
67
-//        }
23
+    
24
+    public var commentItems: Observable<[AnimatableSectionModel<Int, PhotoCommentItem>]> {
25
+        return _commentItems.map ({ model in
26
+            return [AnimatableSectionModel(model: 0, items: model)]
27
+        })
68 28
     }
69
-
70
-    public final func sendComment(content: String, success: @escaping () -> Void) {
71
-        guard !content.isEmpty else {
72
-            return
73
-        }
74
-
75
-//        let params = ["group_id": currentPhoto.group_id, "user_id": SharedUserInfo.userId, "photo_id": currentPhoto.photo_id, "comment": content] as [String: AnyObject]
76
-//
77
-//        let request = StatusNetworkRequest(param: params, path: .commentSubmit)
78
-//
79
-//        detailPageApi.post(request: request) {(res) in
80
-//            guard res.status == 200 else {
81
-//                FFToastView.showToast(inView: UIApplication.shared.keyWindow!, withText: "评论失败")
82
-//                return
83
-//            }
84
-//            FFToastView.showToast(inView: UIApplication.shared.keyWindow!, withText: "评论成功")
85
-//            success()
86
-//        }
29
+    
30
+    public var groupName: Observable<String> {
31
+        return item.map({ v in
32
+            return v.group_name
33
+        })
87 34
     }
88
-
89
-    // MARK: pay
90
-    public var orderId: String! = ""
91
-
92
-    deinit {
93
-        NotificationCenter.default.removeObserver(self)
35
+    
36
+    public var groupAvatar: Observable<String> {
37
+        return item.map({ v in
38
+            return v.group_avatar
39
+        })
94 40
     }
95
-}
96
-
97
-//wechat pay
98
-extension PhotoDetailViewModel {
99
-    public final func handleResult(errorCode: Int, success: @escaping ((_ item: PhotoItem) -> Void)) {
100
-//        func fetchOrderDetail() {
101
-//            detailPageApi.post(param: ["order_id": orderId, "user_id": SharedUserInfo.userId] as [String: AnyObject], url: .orderDetail) { (result) in
102
-//                guard let status = result["status"] as? Int, let data = result["data"] as? [String: AnyObject], let photoInfo = data["group_photo_info"] as? [String: AnyObject], status == 200 else {
103
-////                    FFToastView.hideLoadingToast()
104
-////                    FFToastView.showToast(inView: UIApplication.shared.keyWindow!, withText: "")
105
-//                    return
106
-//                }
107
-//                let PhotoItem = PhotoItem(map: Map(mappingType: .fromJSON, JSON: photoInfo))
108
-//                self.currentPhoto.murl = PhotoItem.murl
109
-//                self.currentPhoto.rurl = PhotoItem.rurl
110
-//                success(PhotoItem)
111
-////                FFToastView.hideLoadingToast()
112
-////                FFToastView.showToast(inView: UIApplication.shared.keyWindow!, withText: "")
113
-////                PhotoLocalStorage.instance.updateLocalData(PhotoItem: PhotoItem)
114
-//            }
115
-//        }
116
-////        Delay(3) {
117
-//            let orderQequest = StatusNetworkRequest(param: ["order_id": self.orderId as AnyObject], path: .orderQuery)
118
-//            self.detailPageApi.post(request: orderQequest, handler: { (res) in
119
-//                if res.status == 200 {
120
-//                    fetchOrderDetail()
121
-//                } else {
122
-////                    FFToastView.hideLoadingToast()
123
-////                    FFToastView.showToast(inView: UIApplication.shared.keyWindow!, withText: "支付失败")
124
-//                }
125
-//            })
126
-////        }
41
+    
42
+    public var userName: Observable<String> {
43
+        return item.map({ v in
44
+            return v.nickname
45
+        })
46
+    }
47
+    
48
+    public var userAvatar: Observable<String> {
49
+        return item.map({ v in
50
+            return v.avatar
51
+        })
127 52
     }
53
+    
54
+    public var photoTime: Observable<String> {
55
+        return item.map({ v in
56
+            return v.create_at
57
+        })
58
+    }
59
+    
60
+    public var thumbupCount: Observable<String> {
61
+        return _thumbupItems.map({ v in
62
+            return "(\(v.count))"
63
+        })
64
+    }
65
+    
66
+    public var commentCount: Observable<String> {
67
+        return _commentItems.map({ v in
68
+             return "(\(v.count))"
69
+        })
70
+    }
71
+    
72
+    public var canBuy: Observable<Bool> {
73
+        return item.map({ v in
74
+            return v.display_payment_btn > 0
75
+        })
76
+    }
77
+    
78
+    public var isHiddenEnterGroupBtn = BehaviorRelay<Bool>(value: false)
79
+    
80
+    public var viewWillAppear = BehaviorRelay<Void>(value: ())
81
+    public weak var delegate: PhotoDetailViewModelDelegate?
82
+    
83
+    private var item: BehaviorRelay<PhotoItem>
84
+    private var _thumbupItems = BehaviorRelay<[PhotoThumbupUserItem]>(value: [])
85
+    private var _commentItems = BehaviorRelay<[PhotoCommentItem]>(value: [])
128 86
 
129
-    public final func getHD(getPriceSuccess: ((_ isExist: Bool) -> Void)?) {
130
-        if !currentPhoto.rurl.isEmpty {
131
-            getPriceSuccess!(true)
132
-            return
133
-        } else {
134
-            if hdPrice != -0.01 {
135
-                payPrice(photoType: "origin")
136
-            } else {
137
-                getPrice(photoType: "origin", success: getPriceSuccess)
138
-            }
139
-        }
87
+    var repository: PhotoDetailRepository
88
+    
89
+    var disposeBag = DisposeBag()
90
+
91
+    public init(item: PhotoItem) {
92
+        self.item = BehaviorRelay<PhotoItem>(value: item)
93
+        repository = PhotoDetailRepository(photoId: item.photo_id, groupId: item.group_id)
94
+        
95
+        self.item.subscribe(onNext: {[unowned self] (photoItem) in
96
+            self.repository = PhotoDetailRepository(photoId: photoItem.photo_id, groupId: photoItem.group_id)
97
+            self.loadCommentItems()
98
+            self.loadThumbupUserItems()
99
+        }).disposed(by: disposeBag)
100
+    }
101
+    
102
+    private func loadThumbupUserItems() {
103
+        repository.loadThumbups().subscribe(onSuccess: { (items) in
104
+            self._thumbupItems.accept(items)
105
+        }).disposed(by: disposeBag)
106
+    }
107
+    
108
+    private func loadCommentItems() {
109
+        repository.loadComments().subscribe(onSuccess: { (items) in
110
+            self._commentItems.accept(items)
111
+        }).disposed(by: disposeBag)
140 112
     }
141 113
 
142
-    public final func getWatermark(getPriceSuccess: ((_ isExist: Bool) -> Void)?) {
143
-        if !currentPhoto.murl.isEmpty {
144
-            getPriceSuccess!(true)
145
-            return
146
-        } else {
147
-            if watermarkPrice != -0.01 {
148
-                payPrice(photoType: "nomark")
149
-            } else {
150
-                getPrice(photoType: "nomark", success: getPriceSuccess)
151
-            }
152
-        }
114
+    public final func submitThumbup() {
115
+        repository.submitThumbup().subscribe(onSuccess: { items in
116
+            self._thumbupItems.accept(items)
117
+        }).disposed(by: disposeBag)
153 118
     }
154 119
 
155
-    public final func payPrice(photoType: String) {
156
-        //下单
157
-//        let body = photoType == "origin" ? "获取高清照片" : "获取去水印照片"
158
-//        let price = photoType == "origin" ? hdPrice : watermarkPrice
159
-//        let params = ["user_id": SharedUserInfo.userId, "body": body, "total_fee": price, "trade_type": "APP", "group_id": currentPhoto.group_id, "photo_id": currentPhoto.photo_id, "photo_type": photoType] as [String: AnyObject]
160
-//        detailPageApi.post(param: params, url: .wxorderCreat) { (result) in
161
-////            FFToastView.hideLoadingToast()
162
-//            guard let status = result["status"] as? Int, let items = result["data"] as? [String: AnyObject], status == 200 else {
163
-//                return
164
-//            }
165
-//            if let orderid = items["order_id"] as? String, let prepay_id = items["prepay_id"] as? String, let wxpay_params = items["wxpay_params"] as? [String: AnyObject], let appid = wxpay_params["appid"] as? String, let noncestr = wxpay_params["noncestr"] as? String, let package = wxpay_params["package"] as? String, let sign = wxpay_params["sign"] as? String, let partnerid = wxpay_params["partnerid"] as? String, let timestamp = wxpay_params["timestamp"] as? String {
166
-//                //支付
167
-//                self.orderId = orderid
168
-//                #if !((arch(i386) || arch(x86_64)))
169
-//                    guard WXApi.isWXAppInstalled() else {
170
-//                        FFToastView.showToast(inView: UIApplication.shared.keyWindow!, withText: "尚未安装微信")
171
-//                        return
172
-//                    }
173
-//
174
-//                    WXApi.registerApp(appid)
175
-//                    let payRequest = PayReq()
176
-//                    payRequest.openID = appid
177
-//                    payRequest.partnerId = partnerid
178
-//                    payRequest.prepayId = prepay_id
179
-//                    payRequest.package = package
180
-//                    payRequest.nonceStr = noncestr
181
-//                    payRequest.timeStamp = UInt32(timestamp) ?? 0
182
-//                    payRequest.sign = sign
183
-//                    WXApi.send(payRequest)
184
-//                #endif
185
-//            }
186
-//        }
120
+    public final func submitComment(text: String) {
121
+        repository.submitComment(text: text).subscribe(onSuccess: { (items) in
122
+            self._commentItems.accept(items)
123
+        }).disposed(by: disposeBag)
187 124
     }
125
+    
126
+    public func navigateToGroup() {
127
+        delegate?.navigateToGroup(GroupItem(json: item.value.toJSON() as [String : AnyObject]))
128
+    }
129
+}
188 130
 
189
-    public final func getPrice(photoType: String, success: ((_ isExist: Bool) -> Void)?) {
190
-//        let params = ["user_id": SharedUserInfo.userId, "photo_id": currentPhoto.photo_id, "photo_type": photoType] as [String: AnyObject]
191
-//        detailPageApi.post(param: params, url: .picPrice) {[weak self] (result) in
192
-//            guard let status = result["status"] as? Int,
193
-//                let items = result["data"] as? [String: AnyObject],
194
-//                status == 200,
195
-//                let weakself = self else {
196
-//                return
197
-//            }
198
-//            if let price = items["price"] as? Double {
199
-//                if photoType == "origin"{
200
-//                    weakself.hdPrice = price
201
-//                } else {
202
-//                    weakself.watermarkPrice = price
203
-//                }
204
-//                success!(false)
205
-//            }
206
-//        }
131
+extension PhotoDetailViewModel: PhotoDetailListViewModelSynchronization {
132
+    public func willShow(_ item: PhotoItem) {
133
+        self.item.accept(item)
207 134
     }
208 135
 }

+ 113 - 0
PaiAi/PaiaiDataKit/PresentLayer/PhotoDetail/PhotoPurchaseViewModel.swift

@@ -7,3 +7,116 @@
7 7
 //
8 8
 
9 9
 import Foundation
10
+
11
+//wechat pay
12
+extension PhotoDetailViewModel {
13
+    public final func handleResult(errorCode: Int, success: @escaping ((_ item: PhotoItem) -> Void)) {
14
+        //        func fetchOrderDetail() {
15
+        //            detailPageApi.post(param: ["order_id": orderId, "user_id": SharedUserInfo.userId] as [String: AnyObject], url: .orderDetail) { (result) in
16
+        //                guard let status = result["status"] as? Int, let data = result["data"] as? [String: AnyObject], let photoInfo = data["group_photo_info"] as? [String: AnyObject], status == 200 else {
17
+        ////                    FFToastView.hideLoadingToast()
18
+        ////                    FFToastView.showToast(inView: UIApplication.shared.keyWindow!, withText: "")
19
+        //                    return
20
+        //                }
21
+        //                let PhotoItem = PhotoItem(map: Map(mappingType: .fromJSON, JSON: photoInfo))
22
+        //                self.currentPhoto.murl = PhotoItem.murl
23
+        //                self.currentPhoto.rurl = PhotoItem.rurl
24
+        //                success(PhotoItem)
25
+        ////                FFToastView.hideLoadingToast()
26
+        ////                FFToastView.showToast(inView: UIApplication.shared.keyWindow!, withText: "")
27
+        ////                PhotoLocalStorage.instance.updateLocalData(PhotoItem: PhotoItem)
28
+        //            }
29
+        //        }
30
+        ////        Delay(3) {
31
+        //            let orderQequest = StatusNetworkRequest(param: ["order_id": self.orderId as AnyObject], path: .orderQuery)
32
+        //            self.detailPageApi.post(request: orderQequest, handler: { (res) in
33
+        //                if res.status == 200 {
34
+        //                    fetchOrderDetail()
35
+        //                } else {
36
+        ////                    FFToastView.hideLoadingToast()
37
+        ////                    FFToastView.showToast(inView: UIApplication.shared.keyWindow!, withText: "支付失败")
38
+        //                }
39
+        //            })
40
+        ////        }
41
+    }
42
+    
43
+    public final func getHD(getPriceSuccess: ((_ isExist: Bool) -> Void)?) {
44
+        //        if !currentPhoto.rurl.isEmpty {
45
+        //            getPriceSuccess!(true)
46
+        //            return
47
+        //        } else {
48
+        //            if hdPrice != -0.01 {
49
+        //                payPrice(photoType: "origin")
50
+        //            } else {
51
+        //                getPrice(photoType: "origin", success: getPriceSuccess)
52
+        //            }
53
+        //        }
54
+    }
55
+    
56
+    public final func getWatermark(getPriceSuccess: ((_ isExist: Bool) -> Void)?) {
57
+        //        if !currentPhoto.murl.isEmpty {
58
+        //            getPriceSuccess!(true)
59
+        //            return
60
+        //        } else {
61
+        //            if watermarkPrice != -0.01 {
62
+        //                payPrice(photoType: "nomark")
63
+        //            } else {
64
+        //                getPrice(photoType: "nomark", success: getPriceSuccess)
65
+        //            }
66
+        //        }
67
+    }
68
+    
69
+    public final func payPrice(photoType: String) {
70
+        //下单
71
+        //        let body = photoType == "origin" ? "获取高清照片" : "获取去水印照片"
72
+        //        let price = photoType == "origin" ? hdPrice : watermarkPrice
73
+        //        let params = ["user_id": SharedUserInfo.userId, "body": body, "total_fee": price, "trade_type": "APP", "group_id": currentPhoto.group_id, "photo_id": currentPhoto.photo_id, "photo_type": photoType] as [String: AnyObject]
74
+        //        detailPageApi.post(param: params, url: .wxorderCreat) { (result) in
75
+        ////            FFToastView.hideLoadingToast()
76
+        //            guard let status = result["status"] as? Int, let items = result["data"] as? [String: AnyObject], status == 200 else {
77
+        //                return
78
+        //            }
79
+        //            if let orderid = items["order_id"] as? String, let prepay_id = items["prepay_id"] as? String, let wxpay_params = items["wxpay_params"] as? [String: AnyObject], let appid = wxpay_params["appid"] as? String, let noncestr = wxpay_params["noncestr"] as? String, let package = wxpay_params["package"] as? String, let sign = wxpay_params["sign"] as? String, let partnerid = wxpay_params["partnerid"] as? String, let timestamp = wxpay_params["timestamp"] as? String {
80
+        //                //支付
81
+        //                self.orderId = orderid
82
+        //                #if !((arch(i386) || arch(x86_64)))
83
+        //                    guard WXApi.isWXAppInstalled() else {
84
+        //                        FFToastView.showToast(inView: UIApplication.shared.keyWindow!, withText: "尚未安装微信")
85
+        //                        return
86
+        //                    }
87
+        //
88
+        //                    WXApi.registerApp(appid)
89
+        //                    let payRequest = PayReq()
90
+        //                    payRequest.openID = appid
91
+        //                    payRequest.partnerId = partnerid
92
+        //                    payRequest.prepayId = prepay_id
93
+        //                    payRequest.package = package
94
+        //                    payRequest.nonceStr = noncestr
95
+        //                    payRequest.timeStamp = UInt32(timestamp) ?? 0
96
+        //                    payRequest.sign = sign
97
+        //                    WXApi.send(payRequest)
98
+        //                #endif
99
+        //            }
100
+        //        }
101
+    }
102
+    
103
+    public final func getPrice(photoType: String, success: ((_ isExist: Bool) -> Void)?) {
104
+        //        let params = ["user_id": SharedUserInfo.userId, "photo_id": currentPhoto.photo_id, "photo_type": photoType] as [String: AnyObject]
105
+        //        detailPageApi.post(param: params, url: .picPrice) {[weak self] (result) in
106
+        //            guard let status = result["status"] as? Int,
107
+        //                let items = result["data"] as? [String: AnyObject],
108
+        //                status == 200,
109
+        //                let weakself = self else {
110
+        //                return
111
+        //            }
112
+        //            if let price = items["price"] as? Double {
113
+        //                if photoType == "origin"{
114
+        //                    weakself.hdPrice = price
115
+        //                } else {
116
+        //                    weakself.watermarkPrice = price
117
+        //                }
118
+        //                success!(false)
119
+        //            }
120
+        //        }
121
+    }
122
+}

+ 5 - 4
PaiAi/PaiaiDataKit/PresentLayer/UserInfoViewModel.swift

@@ -8,12 +8,13 @@
8 8
 
9 9
 import Foundation
10 10
 import RxSwift
11
+import RxCocoa
11 12
 
12 13
 fileprivate(set) var ShareUserId: String = ""
13 14
 
14 15
 public class UserInfoViewModel {
15 16
     
16
-    public var shareUserInfo = BehaviorSubject<UserInfo>(value: UserInfo())
17
+    public var shareUserInfo = BehaviorRelay<UserInfo>(value: UserInfo())
17 18
     
18 19
     public var isLoggedIn: Observable<Void> {
19 20
         return shareUserInfo.asObservable()
@@ -63,12 +64,12 @@ public class UserInfoViewModel {
63 64
     }
64 65
     
65 66
     fileprivate func load() {
66
-        shareUserInfo.onNext(repository.readUserInfo())
67
+        shareUserInfo.accept(repository.readUserInfo())
67 68
     }
68 69
     
69 70
     public func guestLogin() {
70 71
         repository.guestLogin().subscribe(onSuccess: { (userInfo) in
71
-            self.shareUserInfo.onNext(userInfo)
72
+            self.shareUserInfo.accept(userInfo)
72 73
             self._loginCompleted.onNext(())
73 74
         }) { (error) in
74 75
             #warning("错误处理")
@@ -77,7 +78,7 @@ public class UserInfoViewModel {
77 78
     
78 79
     public func wxLogin() {
79 80
         repository.guestLogin().subscribe(onSuccess: { (userInfo) in
80
-            self.shareUserInfo.onNext(userInfo)
81
+            self.shareUserInfo.accept(userInfo)
81 82
             self._loginCompleted.onNext(())
82 83
         }) { (error) in
83 84
             #warning("错误处理")

+ 2 - 2
PaiAi/PaiaiDataKit/Resuable/Extension/UserDefaultsExt.swift

@@ -104,8 +104,8 @@ extension ArrayUserDefaultable where ArrayDefaultKey.RawValue == String {
104 104
         guard let value = UserDefaults.standard.array(forKey: key) as? [[String: AnyObject]] else {
105 105
             return []
106 106
         }
107
-        let items = value.flatMap { Item.init(value: $0) }
108
-        return items ?? [Item]()
107
+        let items = value.compactMap { Item.init(value: $0) }
108
+        return items 
109 109
     }
110 110
 }
111 111
 

+ 6 - 1
PaiAi/PaiaiUIKit/Reusable/Extension/UIKit/UIImageExt.swift

@@ -46,5 +46,10 @@ public extension UIImage {
46 46
         }
47 47
     }
48 48
     
49
-    
49
+    struct PhotoDetail {
50
+        public static var purchaseBackground: UIImage? {
51
+            return UIImage(named: "purchase-background")?.resizableImage(withCapInsets: UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0),
52
+                                                                           resizingMode: .stretch)
53
+        }
54
+    }
50 55
 }

+ 1 - 11
PaiAi/PaiaiUIKit/Reusable/Extension/UIKit/UITextFieldExt.swift

@@ -10,17 +10,7 @@ import UIKit
10 10
 
11 11
 extension UITextField {
12 12
     
13
-//    public var isEmpty: Bool{
14
-//        return text?.isEmpty ?? false
15
-//    }
16
-    
17
-//    public var isPhoneNumber: Bool {
18
-//        return text?.isPhoneNumber ?? false
19
-//    }
20
-    
21
-//    public var isEmail: Bool {
22
-//        return text?.isEmail ?? false
23
-//    }
13
+    public var isEmpty: Bool { return text?.isEmpty ?? false }
24 14
     
25 15
     public func setPlaceHolderTextColor(_ color: UIColor) {
26 16
         guard let holder = placeholder, !holder.isEmpty else {

+ 1 - 1
PaiAi/PaiaiUIKit/Reusable/Extension/UIKit/UITextViewExt.swift

@@ -11,7 +11,7 @@ import UIKit
11 11
 //
12 12
 extension UITextView {
13 13
     
14
-    public var isEmpty: Bool{
14
+    public var isEmpty: Bool {
15 15
         return text?.isEmpty ?? false
16 16
     }
17 17
     

+ 3 - 3
PaiAi/PaiaiUIKit/Reusable/UIKit/AlertViewController/Default/AlertView.swift

@@ -113,11 +113,11 @@ public final class AlertView: UIView {
113 113
     
114 114
     private func installTarget() {
115 115
         if cancelAction != nil {
116
-            cancelItem.addTarget(self, action: #selector(cancelAction(btn:)), for: .touchDown)
116
+            cancelItem.addTarget(self, action: #selector(cancelAction(btn:)), for: .touchUpInside)
117 117
         }
118 118
         
119 119
         if confirmAction != nil {
120
-            confirmItem.addTarget(self, action: #selector(confirmAction(btn:)), for: .touchDown)
120
+            confirmItem.addTarget(self, action: #selector(confirmAction(btn:)), for: .touchUpInside)
121 121
         }
122 122
     }
123 123
     
@@ -219,7 +219,7 @@ fileprivate extension AlertView {
219 219
             NSLayoutConstraint.activate([
220 220
                 label.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor),
221 221
                 label.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor),
222
-                label.topAnchor.constraint(equalTo: last?.bottomAnchor ?? layoutMarginsGuide.topAnchor, constant: 6),
222
+                label.topAnchor.constraint(equalTo: last?.bottomAnchor ?? layoutMarginsGuide.topAnchor, constant: 12),
223 223
                 ])
224 224
             
225 225
             last = label

+ 0 - 1
PaiAi/PaiaiUIKit/Reusable/UIKit/NavigationBar/NavigationBarInteractivePopDeletegate.swift

@@ -47,7 +47,6 @@ extension UIViewController {
47 47
         
48 48
         guard gesture.state == .ended else { return }
49 49
 
50
-        for vc in navigationController?.viewControllers ?? [] { print(vc) }
51 50
         if (percentage > 0.5 && navigationController == nil) ||
52 51
             (percentage < 0.5 && navigationController?.visibleViewController == self) {
53 52
             fadeOutAnimation()

+ 4 - 2
PaiAi/Paiai_iOS/App/AppCoordinator.swift

@@ -6,7 +6,7 @@
6 6
 //  Copyright © 2018 yb. All rights reserved.
7 7
 //
8 8
 
9
-import Foundation
9
+import UIKit
10 10
 import PaiaiDataKit
11 11
 import PaiaiUIKit
12 12
 
@@ -45,7 +45,9 @@ public final class AppCoordinator {
45 45
                                              PageItem(title: "消息",
46 46
                                                       viewController: messageVC)]
47 47
         
48
-        let homeCoordinator = HomeCoordinator(homeVC, userInfoViewModel: shareUserInfoViewModel)
48
+        let homeCoordinator = HomeCoordinator(homeVC,
49
+                                              navigationController: navigationController,
50
+                                              userInfoViewModel: shareUserInfoViewModel)
49 51
         let messageCoordinator = MessageCoordinator(messageVC,
50 52
                                                     navigationController: navigationController,
51 53
                                                     userInfoViewModel: shareUserInfoViewModel)

+ 1 - 0
PaiAi/Paiai_iOS/App/ContainerViewController.swift

@@ -44,6 +44,7 @@ public final class ContainerViewController: PageViewController {
44 44
     private var navigationContentView: UIView = {
45 45
         let contentView = UIView(frame: CGRect(x: 0, y: 0, width: 50, height: 32))
46 46
         return contentView
47
+        
47 48
     }()
48 49
     
49 50
     /// life circle

+ 22 - 4
PaiAi/Paiai_iOS/App/Group/GroupCoordinator.swift

@@ -14,27 +14,45 @@ class GroupCoordinator: Coordinator {
14 14
     let navigationController: UINavigationController
15 15
     let groupViewController: GroupViewController
16 16
     
17
+    var coordinators: [String: Coordinator] = [:]
18
+    
17 19
     init(_ groupVC: GroupViewController, navigationController: UINavigationController) {
18 20
         self.groupViewController = groupVC
19 21
         self.navigationController = navigationController
20 22
         
21 23
         groupViewController.viewModel.delegate = self
24
+        
22 25
     }
23 26
 }
24 27
 
25 28
 extension GroupCoordinator: GroupViewModelDelegate {
26
-    func navigateToGroupDetail() {
29
+    func navigateToGroupDetail(_ item: GroupItem) {
30
+        let coordinator = GroupDetailCoordinator(makeGroupDetailViewController(item), navigationController: navigationController)
31
+        coordinators["groupDetail"] = coordinator
27 32
         
33
+        navigationController.pushViewController(coordinator.groupDetailViewController)
28 34
     }
29 35
     
30
-    func didSelect(_ item: PhotoItem) {
31
-        
36
+    func didSelect(_ items: [PhotoItem], currIndex: Int) {
37
+        let ctl = UIStoryboard.photoDetail.instantiateController(PhotoDetailViewController.self)
38
+        let viewModel = PhotoDetailViewModel(item: items[currIndex])
39
+        viewModel.isHiddenEnterGroupBtn.accept(true)
40
+        let coordinator = PhotoDetailCoordinator(ctl, nav: navigationController,
41
+                                                 viewModel: viewModel,
42
+                                                 listViewModel: PhotoDetailListViewModel(items: items, currIndex: currIndex))
43
+        coordinators["photoDetail"] = coordinator
44
+        coordinator.start()
45
+        navigationController.pushViewController(coordinator.photoDetailViewController)
32 46
     }
33 47
 }
34 48
 
35 49
 fileprivate extension GroupCoordinator {
36
-    func makeGroupDetailViewController() {
50
+    func makeGroupDetailViewController(_ item: GroupItem) -> GroupDetailViewController
51
+    {
52
+        let vc = UIStoryboard.groupDetail.instantiateController(GroupDetailViewController.self)
53
+        vc.viewModel = GroupDetailViewModel(item: item)
37 54
         
55
+        return vc
38 56
     }
39 57
 }
40 58
 

+ 134 - 197
PaiAi/Paiai_iOS/App/Group/GroupDetail/GroupDetail.storyboard

@@ -1,7 +1,12 @@
1 1
 <?xml version="1.0" encoding="UTF-8"?>
2
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES" initialViewController="s9X-RR-Rat">
2
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useSafeAreas="YES" colorMatched="YES">
3
+    <device id="retina4_7" orientation="portrait">
4
+        <adaptation id="fullscreen"/>
5
+    </device>
3 6
     <dependencies>
7
+        <deployment identifier="iOS"/>
4 8
         <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/>
9
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
5 10
         <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
6 11
     </dependencies>
7 12
     <scenes>
@@ -9,10 +14,6 @@
9 14
         <scene sceneID="XYC-ye-yVT">
10 15
             <objects>
11 16
                 <viewController storyboardIdentifier="ShowGroupQRController" automaticallyAdjustsScrollViewInsets="NO" id="1xf-Gx-gQ6" userLabel="ShowGroupQRController" customClass="ShowGroupQRController" customModule="PaiAi" sceneMemberID="viewController">
12
-                    <layoutGuides>
13
-                        <viewControllerLayoutGuide type="top" id="VlO-HE-vXU"/>
14
-                        <viewControllerLayoutGuide type="bottom" id="rXV-YC-UCZ"/>
15
-                    </layoutGuides>
16 17
                     <view key="view" contentMode="scaleToFill" id="ZBk-gn-qqZ">
17 18
                         <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
18 19
                         <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
@@ -74,9 +75,10 @@
74 75
                         <constraints>
75 76
                             <constraint firstItem="3Yp-sn-LQg" firstAttribute="centerX" secondItem="omt-Jt-Qr4" secondAttribute="centerX" id="5jg-XD-1je"/>
76 77
                             <constraint firstItem="AOa-xP-S2e" firstAttribute="centerY" secondItem="ZBk-gn-qqZ" secondAttribute="centerY" constant="-20" id="gqp-We-1FY"/>
77
-                            <constraint firstItem="AOa-xP-S2e" firstAttribute="centerX" secondItem="ZBk-gn-qqZ" secondAttribute="centerX" id="ou7-87-DCL"/>
78
+                            <constraint firstItem="AOa-xP-S2e" firstAttribute="centerX" secondItem="x9Y-Y7-7ge" secondAttribute="centerX" id="ou7-87-DCL"/>
78 79
                             <constraint firstItem="3Yp-sn-LQg" firstAttribute="top" secondItem="omt-Jt-Qr4" secondAttribute="bottom" constant="12" id="vwi-7r-nUU"/>
79 80
                         </constraints>
81
+                        <viewLayoutGuide key="safeArea" id="x9Y-Y7-7ge"/>
80 82
                         <connections>
81 83
                             <outletCollection property="gestureRecognizers" destination="qe1-cB-TmI" appends="YES" id="9kM-Mf-00B"/>
82 84
                         </connections>
@@ -101,15 +103,11 @@
101 103
         <scene sceneID="Oa4-Yi-HJu">
102 104
             <objects>
103 105
                 <viewController storyboardIdentifier="GroupDetailViewController" id="s9X-RR-Rat" userLabel="GroupDetailViewController" customClass="GroupDetailViewController" customModule="Paiai_iOS" customModuleProvider="target" sceneMemberID="viewController">
104
-                    <layoutGuides>
105
-                        <viewControllerLayoutGuide type="top" id="w38-w0-jLd"/>
106
-                        <viewControllerLayoutGuide type="bottom" id="a7q-m3-cDV"/>
107
-                    </layoutGuides>
108 106
                     <view key="view" contentMode="scaleToFill" id="OXa-NJ-XOO">
109 107
                         <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
110 108
                         <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
111 109
                         <subviews>
112
-                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="fpL-PF-0xo">
110
+                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="fpL-PF-0xo" userLabel="Group Name">
113 111
                                 <rect key="frame" x="0.0" y="25" width="375" height="45"/>
114 112
                                 <subviews>
115 113
                                     <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="群名称" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="NME-fp-tEb">
@@ -119,95 +117,91 @@
119 117
                                         <color key="textColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
120 118
                                         <nil key="highlightedColor"/>
121 119
                                     </label>
122
-                                    <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="列表箭头" translatesAutoresizingMaskIntoConstraints="NO" id="7Hg-xN-Smd">
123
-                                        <rect key="frame" x="339" y="4.5" width="24" height="36"/>
124
-                                    </imageView>
125
-                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="名" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="1X6-D5-hkj">
126
-                                        <rect key="frame" x="312" y="12.5" width="17" height="20"/>
120
+                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="1X6-D5-hkj">
121
+                                        <rect key="frame" x="329" y="22.5" width="0.0" height="0.0"/>
127 122
                                         <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
128 123
                                         <fontDescription key="fontDescription" type="system" pointSize="16"/>
129 124
                                         <color key="textColor" red="0.59999999999999998" green="0.59999999999999998" blue="0.59999999999999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
130 125
                                         <nil key="highlightedColor"/>
131 126
                                     </label>
132
-                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="eaB-II-BjR">
133
-                                        <rect key="frame" x="0.0" y="0.0" width="375" height="45"/>
134
-                                        <connections>
135
-                                            <segue destination="ahh-gm-C2t" kind="modal" id="OpJ-RB-GhS"/>
136
-                                        </connections>
137
-                                    </button>
138
-                                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="NP3-gL-4Lm">
139
-                                        <rect key="frame" x="0.0" y="44.5" width="375" height="0.5"/>
140
-                                        <color key="backgroundColor" red="0.90588235289999997" green="0.90588235289999997" blue="0.90588235289999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
141
-                                        <constraints>
142
-                                            <constraint firstAttribute="height" constant="0.5" id="2p1-dV-Xd4"/>
143
-                                        </constraints>
144
-                                    </view>
127
+                                    <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="list-arrow" translatesAutoresizingMaskIntoConstraints="NO" id="7Hg-xN-Smd">
128
+                                        <rect key="frame" x="339" y="4.5" width="24" height="36"/>
129
+                                    </imageView>
145 130
                                 </subviews>
146 131
                                 <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
132
+                                <gestureRecognizers/>
147 133
                                 <constraints>
148
-                                    <constraint firstItem="eaB-II-BjR" firstAttribute="top" secondItem="fpL-PF-0xo" secondAttribute="top" id="AfI-TX-MBT"/>
149
-                                    <constraint firstItem="NP3-gL-4Lm" firstAttribute="leading" secondItem="fpL-PF-0xo" secondAttribute="leading" id="Mkh-ez-jmF"/>
150 134
                                     <constraint firstItem="7Hg-xN-Smd" firstAttribute="centerY" secondItem="fpL-PF-0xo" secondAttribute="centerY" id="Rjv-h5-Wtq"/>
151 135
                                     <constraint firstAttribute="height" constant="45" id="UPg-bK-pto"/>
152
-                                    <constraint firstAttribute="trailing" secondItem="eaB-II-BjR" secondAttribute="trailing" id="Wd9-LL-kRx"/>
153 136
                                     <constraint firstItem="NME-fp-tEb" firstAttribute="centerY" secondItem="fpL-PF-0xo" secondAttribute="centerY" id="X0F-x5-Iql"/>
154 137
                                     <constraint firstItem="NME-fp-tEb" firstAttribute="leading" secondItem="fpL-PF-0xo" secondAttribute="leading" constant="12" id="Znr-w6-kFQ"/>
155 138
                                     <constraint firstItem="7Hg-xN-Smd" firstAttribute="leading" secondItem="1X6-D5-hkj" secondAttribute="trailing" constant="10" id="qjJ-vN-hzP"/>
156 139
                                     <constraint firstAttribute="trailing" secondItem="7Hg-xN-Smd" secondAttribute="trailing" constant="12" id="tEJ-5a-H97"/>
157
-                                    <constraint firstAttribute="bottom" secondItem="eaB-II-BjR" secondAttribute="bottom" id="tMA-ex-sLV"/>
158
-                                    <constraint firstAttribute="trailing" secondItem="NP3-gL-4Lm" secondAttribute="trailing" id="tRS-n4-IKl"/>
159
-                                    <constraint firstAttribute="bottom" secondItem="NP3-gL-4Lm" secondAttribute="bottom" id="tiZ-Os-N8E"/>
160 140
                                     <constraint firstItem="1X6-D5-hkj" firstAttribute="centerY" secondItem="fpL-PF-0xo" secondAttribute="centerY" id="v6h-VQ-qa8"/>
161
-                                    <constraint firstItem="eaB-II-BjR" firstAttribute="leading" secondItem="fpL-PF-0xo" secondAttribute="leading" id="yWU-Jg-dGJ"/>
162 141
                                 </constraints>
142
+                                <connections>
143
+                                    <outletCollection property="gestureRecognizers" destination="QhO-Be-7C1" appends="YES" id="9lj-Za-c71"/>
144
+                                </connections>
163 145
                             </view>
164
-                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="pMy-uJ-dNy">
165
-                                <rect key="frame" x="0.0" y="70" width="375" height="45"/>
146
+                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="pMy-uJ-dNy" userLabel="Group Member">
147
+                                <rect key="frame" x="0.0" y="70" width="375" height="90"/>
166 148
                                 <subviews>
167
-                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="群成员" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="NxF-Pa-Uwm">
168
-                                        <rect key="frame" x="12" y="12.5" width="49" height="20"/>
169
-                                        <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
170
-                                        <fontDescription key="fontDescription" type="system" pointSize="16"/>
171
-                                        <color key="textColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
172
-                                        <nil key="highlightedColor"/>
173
-                                    </label>
174
-                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="10人" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="AxF-rT-r4a">
175
-                                        <rect key="frame" x="295" y="12.5" width="34" height="20"/>
176
-                                        <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
177
-                                        <fontDescription key="fontDescription" type="system" pointSize="16"/>
178
-                                        <color key="textColor" red="0.59999999999999998" green="0.59999999999999998" blue="0.59999999999999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
179
-                                        <nil key="highlightedColor"/>
180
-                                    </label>
181
-                                    <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="列表箭头" translatesAutoresizingMaskIntoConstraints="NO" id="deA-EV-s1P">
182
-                                        <rect key="frame" x="339" y="4.5" width="24" height="36"/>
183
-                                    </imageView>
149
+                                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="wyR-pX-r7o">
150
+                                        <rect key="frame" x="0.0" y="0.0" width="375" height="45"/>
151
+                                        <subviews>
152
+                                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="群成员" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="NxF-Pa-Uwm">
153
+                                                <rect key="frame" x="12" y="12.5" width="49" height="20"/>
154
+                                                <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
155
+                                                <fontDescription key="fontDescription" type="system" pointSize="16"/>
156
+                                                <color key="textColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
157
+                                                <nil key="highlightedColor"/>
158
+                                            </label>
159
+                                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="10人" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="AxF-rT-r4a">
160
+                                                <rect key="frame" x="299" y="12.5" width="34" height="20"/>
161
+                                                <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
162
+                                                <fontDescription key="fontDescription" type="system" pointSize="16"/>
163
+                                                <color key="textColor" red="0.59999999999999998" green="0.59999999999999998" blue="0.59999999999999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
164
+                                                <nil key="highlightedColor"/>
165
+                                            </label>
166
+                                            <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="list-arrow" translatesAutoresizingMaskIntoConstraints="NO" id="deA-EV-s1P">
167
+                                                <rect key="frame" x="339" y="4.5" width="24" height="36"/>
168
+                                            </imageView>
169
+                                        </subviews>
170
+                                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
171
+                                        <constraints>
172
+                                            <constraint firstAttribute="trailing" secondItem="deA-EV-s1P" secondAttribute="trailing" constant="12" id="99G-fm-NjO"/>
173
+                                            <constraint firstItem="deA-EV-s1P" firstAttribute="leading" secondItem="AxF-rT-r4a" secondAttribute="trailing" constant="6" id="NoO-0x-719"/>
174
+                                            <constraint firstAttribute="height" constant="45" id="OmQ-ue-3uc"/>
175
+                                            <constraint firstItem="NxF-Pa-Uwm" firstAttribute="leading" secondItem="wyR-pX-r7o" secondAttribute="leading" constant="12" id="RxE-Ec-vzI"/>
176
+                                            <constraint firstItem="NxF-Pa-Uwm" firstAttribute="centerY" secondItem="wyR-pX-r7o" secondAttribute="centerY" id="jRi-u6-4du"/>
177
+                                            <constraint firstItem="deA-EV-s1P" firstAttribute="centerY" secondItem="wyR-pX-r7o" secondAttribute="centerY" id="lcj-B2-CSF"/>
178
+                                            <constraint firstItem="AxF-rT-r4a" firstAttribute="centerY" secondItem="wyR-pX-r7o" secondAttribute="centerY" id="s7D-rf-GV6"/>
179
+                                        </constraints>
180
+                                    </view>
181
+                                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="PEf-pV-jyM" customClass="GroupDetailMemeberView" customModule="Paiai_iOS" customModuleProvider="target">
182
+                                        <rect key="frame" x="0.0" y="45" width="375" height="45"/>
183
+                                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
184
+                                        <constraints>
185
+                                            <constraint firstAttribute="height" constant="45" id="zVY-I6-46P"/>
186
+                                        </constraints>
187
+                                    </view>
184 188
                                 </subviews>
185 189
                                 <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
186 190
                                 <constraints>
187
-                                    <constraint firstAttribute="trailing" secondItem="deA-EV-s1P" secondAttribute="trailing" constant="12" id="3bb-27-Gxq"/>
188
-                                    <constraint firstItem="deA-EV-s1P" firstAttribute="leading" secondItem="AxF-rT-r4a" secondAttribute="trailing" constant="10" id="4BA-Q0-0hV"/>
189
-                                    <constraint firstItem="NxF-Pa-Uwm" firstAttribute="centerY" secondItem="pMy-uJ-dNy" secondAttribute="centerY" id="BZS-nD-l69"/>
190
-                                    <constraint firstItem="AxF-rT-r4a" firstAttribute="centerY" secondItem="pMy-uJ-dNy" secondAttribute="centerY" id="MbN-zG-cZO"/>
191
-                                    <constraint firstItem="NxF-Pa-Uwm" firstAttribute="leading" secondItem="pMy-uJ-dNy" secondAttribute="leading" constant="12" id="WQ3-O6-roa"/>
192
-                                    <constraint firstAttribute="height" constant="45" id="ksr-Ie-zrU"/>
193
-                                    <constraint firstItem="deA-EV-s1P" firstAttribute="centerY" secondItem="pMy-uJ-dNy" secondAttribute="centerY" id="sej-I4-LMf"/>
194
-                                </constraints>
195
-                            </view>
196
-                            <scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6Ye-xx-V3F">
197
-                                <rect key="frame" x="0.0" y="115" width="375" height="50"/>
198
-                                <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
199
-                                <constraints>
200
-                                    <constraint firstAttribute="height" constant="50" id="It1-Qr-cpK"/>
191
+                                    <constraint firstAttribute="trailing" secondItem="PEf-pV-jyM" secondAttribute="trailing" id="FQu-9X-fNW"/>
192
+                                    <constraint firstAttribute="trailing" secondItem="wyR-pX-r7o" secondAttribute="trailing" id="Rhr-fu-vjC"/>
193
+                                    <constraint firstItem="PEf-pV-jyM" firstAttribute="top" secondItem="wyR-pX-r7o" secondAttribute="bottom" id="T2J-6h-hMy"/>
194
+                                    <constraint firstAttribute="height" constant="90" id="ksr-Ie-zrU"/>
195
+                                    <constraint firstItem="wyR-pX-r7o" firstAttribute="top" secondItem="pMy-uJ-dNy" secondAttribute="top" id="sND-x0-GOF"/>
196
+                                    <constraint firstItem="wyR-pX-r7o" firstAttribute="leading" secondItem="pMy-uJ-dNy" secondAttribute="leading" id="wJT-Tt-evG"/>
197
+                                    <constraint firstItem="PEf-pV-jyM" firstAttribute="leading" secondItem="pMy-uJ-dNy" secondAttribute="leading" id="xfn-6R-s6e"/>
201 198
                                 </constraints>
202
-                            </scrollView>
203
-                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ttH-tw-fbl">
204
-                                <rect key="frame" x="0.0" y="70" width="375" height="95"/>
205 199
                                 <connections>
206
-                                    <segue destination="bLF-bf-ZTt" kind="modal" id="C0s-8v-GKU"/>
200
+                                    <outletCollection property="gestureRecognizers" destination="PAc-yU-TO1" appends="YES" id="HrQ-Km-0rQ"/>
207 201
                                 </connections>
208
-                            </button>
209
-                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="rPs-2T-yj8">
210
-                                <rect key="frame" x="0.0" y="165" width="375" height="45"/>
202
+                            </view>
203
+                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="rPs-2T-yj8" userLabel="Group QR">
204
+                                <rect key="frame" x="0.0" y="160" width="375" height="45"/>
211 205
                                 <subviews>
212 206
                                     <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="群二维码" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ZfP-6I-3jY">
213 207
                                         <rect key="frame" x="12" y="12.5" width="66" height="20"/>
@@ -216,56 +210,33 @@
216 210
                                         <color key="textColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
217 211
                                         <nil key="highlightedColor"/>
218 212
                                     </label>
219
-                                    <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="列表箭头" translatesAutoresizingMaskIntoConstraints="NO" id="Uws-7i-Qzj">
213
+                                    <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="list-arrow" translatesAutoresizingMaskIntoConstraints="NO" id="Uws-7i-Qzj">
220 214
                                         <rect key="frame" x="339" y="4.5" width="24" height="36"/>
221 215
                                     </imageView>
222
-                                    <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="二维码" translatesAutoresizingMaskIntoConstraints="NO" id="ZNi-wI-pOY">
223
-                                        <rect key="frame" x="249" y="-17.5" width="80" height="80"/>
224
-                                    </imageView>
225
-                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="8lP-Cq-74A">
226
-                                        <rect key="frame" x="0.0" y="0.0" width="375" height="45"/>
227
-                                        <connections>
228
-                                            <action selector="showEWM" destination="s9X-RR-Rat" eventType="touchUpInside" id="GIw-VW-HQt"/>
229
-                                        </connections>
230
-                                    </button>
231
-                                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="48Y-jJ-6H8">
232
-                                        <rect key="frame" x="0.0" y="44.5" width="375" height="0.5"/>
233
-                                        <color key="backgroundColor" red="0.90588235289999997" green="0.90588235289999997" blue="0.90588235289999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
216
+                                    <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="list-QR" translatesAutoresizingMaskIntoConstraints="NO" id="ZNi-wI-pOY">
217
+                                        <rect key="frame" x="305" y="10.5" width="24" height="24"/>
234 218
                                         <constraints>
235
-                                            <constraint firstAttribute="height" constant="0.5" id="JC8-gM-JvE"/>
219
+                                            <constraint firstAttribute="width" constant="24" id="DzA-gm-u2E"/>
220
+                                            <constraint firstAttribute="height" constant="24" id="kJ3-8V-wGS"/>
236 221
                                         </constraints>
237
-                                    </view>
238
-                                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="rZZ-0X-2OD">
239
-                                        <rect key="frame" x="0.0" y="0.0" width="375" height="0.5"/>
240
-                                        <color key="backgroundColor" red="0.90588235289999997" green="0.90588235289999997" blue="0.90588235289999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
241
-                                        <constraints>
242
-                                            <constraint firstAttribute="height" constant="0.5" id="4j1-Xu-jW0"/>
243
-                                        </constraints>
244
-                                    </view>
222
+                                    </imageView>
245 223
                                 </subviews>
246 224
                                 <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
247 225
                                 <constraints>
248
-                                    <constraint firstAttribute="trailing" secondItem="rZZ-0X-2OD" secondAttribute="trailing" id="1dW-hC-FDy"/>
249 226
                                     <constraint firstItem="Uws-7i-Qzj" firstAttribute="centerY" secondItem="rPs-2T-yj8" secondAttribute="centerY" id="2wQ-j8-t3z"/>
250 227
                                     <constraint firstAttribute="trailing" secondItem="Uws-7i-Qzj" secondAttribute="trailing" constant="12" id="7qb-cu-8iE"/>
251 228
                                     <constraint firstItem="ZfP-6I-3jY" firstAttribute="leading" secondItem="rPs-2T-yj8" secondAttribute="leading" constant="12" id="9xA-HF-Mlj"/>
252 229
                                     <constraint firstItem="ZfP-6I-3jY" firstAttribute="centerY" secondItem="rPs-2T-yj8" secondAttribute="centerY" id="GAH-Ls-Ixp"/>
253
-                                    <constraint firstItem="48Y-jJ-6H8" firstAttribute="leading" secondItem="rPs-2T-yj8" secondAttribute="leading" id="JIv-0e-gHj"/>
254 230
                                     <constraint firstItem="ZNi-wI-pOY" firstAttribute="centerY" secondItem="rPs-2T-yj8" secondAttribute="centerY" id="Mst-vH-6Op"/>
255
-                                    <constraint firstItem="8lP-Cq-74A" firstAttribute="leading" secondItem="rPs-2T-yj8" secondAttribute="leading" id="NMw-0N-fZx"/>
256
-                                    <constraint firstAttribute="trailing" secondItem="48Y-jJ-6H8" secondAttribute="trailing" id="VtK-02-UJa"/>
257
-                                    <constraint firstItem="rZZ-0X-2OD" firstAttribute="top" secondItem="rPs-2T-yj8" secondAttribute="top" id="Z6z-RI-FFz"/>
258
-                                    <constraint firstItem="rZZ-0X-2OD" firstAttribute="leading" secondItem="rPs-2T-yj8" secondAttribute="leading" id="d4k-V8-dK8"/>
259 231
                                     <constraint firstItem="Uws-7i-Qzj" firstAttribute="leading" secondItem="ZNi-wI-pOY" secondAttribute="trailing" constant="10" id="eQT-7f-sku"/>
260
-                                    <constraint firstAttribute="bottom" secondItem="48Y-jJ-6H8" secondAttribute="bottom" id="f3e-9q-qtH"/>
261
-                                    <constraint firstAttribute="trailing" secondItem="8lP-Cq-74A" secondAttribute="trailing" id="l98-kH-PHH"/>
262
-                                    <constraint firstItem="8lP-Cq-74A" firstAttribute="top" secondItem="rPs-2T-yj8" secondAttribute="top" id="wCy-B2-xGE"/>
263
-                                    <constraint firstAttribute="bottom" secondItem="8lP-Cq-74A" secondAttribute="bottom" id="wkR-RU-Ksc"/>
264 232
                                     <constraint firstAttribute="height" constant="45" id="ypH-Mg-3iM"/>
265 233
                                 </constraints>
234
+                                <connections>
235
+                                    <outletCollection property="gestureRecognizers" destination="CLv-er-MwX" appends="YES" id="6Fw-lQ-Aqq"/>
236
+                                </connections>
266 237
                             </view>
267
-                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Pb3-YX-nuZ">
268
-                                <rect key="frame" x="0.0" y="210" width="375" height="45"/>
238
+                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Pb3-YX-nuZ" userLabel="Group Lock">
239
+                                <rect key="frame" x="0.0" y="205" width="375" height="45"/>
269 240
                                 <subviews>
270 241
                                     <switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="1000" verticalHuggingPriority="750" horizontalCompressionResistancePriority="1000" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="t9Z-kG-PPs">
271 242
                                         <rect key="frame" x="314" y="7" width="51" height="31"/>
@@ -291,6 +262,7 @@
291 262
                                 </subviews>
292 263
                                 <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
293 264
                                 <constraints>
265
+                                    <constraint firstAttribute="trailing" secondItem="t9Z-kG-PPs" secondAttribute="trailing" constant="12" id="KkU-tC-Egu"/>
294 266
                                     <constraint firstAttribute="height" constant="45" id="SJk-Ib-saK"/>
295 267
                                     <constraint firstItem="t9Z-kG-PPs" firstAttribute="leading" secondItem="niR-rQ-aFl" secondAttribute="trailing" constant="16" id="fz2-LG-fUc"/>
296 268
                                     <constraint firstItem="pGa-DR-SDU" firstAttribute="centerY" secondItem="Pb3-YX-nuZ" secondAttribute="centerY" id="g0e-A6-o5n"/>
@@ -303,56 +275,59 @@
303 275
                         </subviews>
304 276
                         <color key="backgroundColor" red="0.94117647058823528" green="0.94117647058823528" blue="0.94117647058823528" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
305 277
                         <constraints>
306
-                            <constraint firstItem="ttH-tw-fbl" firstAttribute="top" secondItem="fpL-PF-0xo" secondAttribute="bottom" id="6cp-ka-r88"/>
307
-                            <constraint firstItem="6Ye-xx-V3F" firstAttribute="top" secondItem="pMy-uJ-dNy" secondAttribute="bottom" id="C1f-d3-HD9"/>
308
-                            <constraint firstItem="pMy-uJ-dNy" firstAttribute="leading" secondItem="OXa-NJ-XOO" secondAttribute="leading" id="DJT-ec-BYi"/>
309
-                            <constraint firstAttribute="trailing" secondItem="Pb3-YX-nuZ" secondAttribute="trailing" id="GHr-gH-44j"/>
310
-                            <constraint firstAttribute="trailing" secondItem="rPs-2T-yj8" secondAttribute="trailing" id="SBF-Gf-sea"/>
311
-                            <constraint firstItem="6Ye-xx-V3F" firstAttribute="leading" secondItem="OXa-NJ-XOO" secondAttribute="leading" id="Vp9-OK-KTg"/>
312
-                            <constraint firstAttribute="trailing" secondItem="6Ye-xx-V3F" secondAttribute="trailing" id="XOF-8e-oMx"/>
313
-                            <constraint firstAttribute="trailing" secondItem="fpL-PF-0xo" secondAttribute="trailing" id="dKZ-bN-aud"/>
314
-                            <constraint firstItem="fpL-PF-0xo" firstAttribute="leading" secondItem="OXa-NJ-XOO" secondAttribute="leading" id="dvc-Qp-fCK"/>
315
-                            <constraint firstItem="rPs-2T-yj8" firstAttribute="top" secondItem="ttH-tw-fbl" secondAttribute="bottom" id="dzI-1e-y1G"/>
316
-                            <constraint firstItem="rPs-2T-yj8" firstAttribute="leading" secondItem="OXa-NJ-XOO" secondAttribute="leading" id="exJ-yM-58B"/>
317
-                            <constraint firstItem="ttH-tw-fbl" firstAttribute="leading" secondItem="OXa-NJ-XOO" secondAttribute="leading" id="fiH-X6-HUW"/>
278
+                            <constraint firstItem="pMy-uJ-dNy" firstAttribute="leading" secondItem="cIo-kp-fSE" secondAttribute="leading" id="DJT-ec-BYi"/>
279
+                            <constraint firstItem="cIo-kp-fSE" firstAttribute="trailing" secondItem="Pb3-YX-nuZ" secondAttribute="trailing" id="GHr-gH-44j"/>
280
+                            <constraint firstItem="rPs-2T-yj8" firstAttribute="top" secondItem="pMy-uJ-dNy" secondAttribute="bottom" id="PC0-el-XOL"/>
281
+                            <constraint firstItem="cIo-kp-fSE" firstAttribute="trailing" secondItem="rPs-2T-yj8" secondAttribute="trailing" id="SBF-Gf-sea"/>
282
+                            <constraint firstItem="cIo-kp-fSE" firstAttribute="trailing" secondItem="fpL-PF-0xo" secondAttribute="trailing" id="dKZ-bN-aud"/>
283
+                            <constraint firstItem="fpL-PF-0xo" firstAttribute="leading" secondItem="cIo-kp-fSE" secondAttribute="leading" id="dvc-Qp-fCK"/>
284
+                            <constraint firstItem="rPs-2T-yj8" firstAttribute="leading" secondItem="cIo-kp-fSE" secondAttribute="leading" id="exJ-yM-58B"/>
318 285
                             <constraint firstItem="pMy-uJ-dNy" firstAttribute="top" secondItem="fpL-PF-0xo" secondAttribute="bottom" id="iBX-Qu-4Rh"/>
319
-                            <constraint firstItem="t9Z-kG-PPs" firstAttribute="trailing" secondItem="Uws-7i-Qzj" secondAttribute="trailing" id="ir1-U9-9vc"/>
320
-                            <constraint firstItem="Pb3-YX-nuZ" firstAttribute="leading" secondItem="OXa-NJ-XOO" secondAttribute="leading" id="lm3-hT-PcR"/>
321
-                            <constraint firstAttribute="trailing" secondItem="pMy-uJ-dNy" secondAttribute="trailing" id="mOe-DU-Rdn"/>
286
+                            <constraint firstItem="Pb3-YX-nuZ" firstAttribute="leading" secondItem="cIo-kp-fSE" secondAttribute="leading" id="lm3-hT-PcR"/>
287
+                            <constraint firstItem="cIo-kp-fSE" firstAttribute="trailing" secondItem="pMy-uJ-dNy" secondAttribute="trailing" id="mOe-DU-Rdn"/>
322 288
                             <constraint firstItem="Pb3-YX-nuZ" firstAttribute="top" secondItem="rPs-2T-yj8" secondAttribute="bottom" id="ow5-8x-3xA"/>
323
-                            <constraint firstItem="rPs-2T-yj8" firstAttribute="top" secondItem="6Ye-xx-V3F" secondAttribute="bottom" id="vGu-Nc-0B8"/>
324
-                            <constraint firstItem="fpL-PF-0xo" firstAttribute="top" secondItem="w38-w0-jLd" secondAttribute="bottom" constant="5" id="wQP-uP-4dl"/>
325
-                            <constraint firstAttribute="trailing" secondItem="ttH-tw-fbl" secondAttribute="trailing" id="ymt-0n-er5"/>
289
+                            <constraint firstItem="fpL-PF-0xo" firstAttribute="top" secondItem="cIo-kp-fSE" secondAttribute="top" constant="5" id="wQP-uP-4dl"/>
326 290
                         </constraints>
291
+                        <viewLayoutGuide key="safeArea" id="cIo-kp-fSE"/>
327 292
                     </view>
328 293
                     <navigationItem key="navigationItem" id="Cjc-V8-n6M"/>
329 294
                     <connections>
330 295
                         <outlet property="groupLockSwitch" destination="t9Z-kG-PPs" id="mJz-yT-bcT"/>
331 296
                         <outlet property="groupLockTip" destination="niR-rQ-aFl" id="AX6-6k-zux"/>
332
-                        <outlet property="groupNameLabel" destination="1X6-D5-hkj" id="rM6-pN-lg1"/>
333
-                        <outlet property="groupUserCountLabel" destination="AxF-rT-r4a" id="eOC-vz-TTc"/>
334
-                        <outlet property="groupUserHeaderScrollView" destination="6Ye-xx-V3F" id="VvQ-Bd-bZq"/>
335
-                        <outlet property="scrollViewConstraint" destination="It1-Qr-cpK" id="ume-bG-RSv"/>
297
+                        <outlet property="groupMemberCountLabel" destination="AxF-rT-r4a" id="eOC-vz-TTc"/>
298
+                        <outlet property="groupMemeberView" destination="PEf-pV-jyM" id="Cqs-Fq-QzC"/>
299
+                        <outlet property="groupNameLabel" destination="1X6-D5-hkj" id="neB-7y-IOQ"/>
336 300
                     </connections>
337 301
                 </viewController>
338 302
                 <placeholder placeholderIdentifier="IBFirstResponder" id="3Gk-LZ-t7S" sceneMemberID="firstResponder"/>
303
+                <tapGestureRecognizer id="QhO-Be-7C1" userLabel="GroupNameGesture">
304
+                    <connections>
305
+                        <action selector="navigationToGroupNameModification:" destination="s9X-RR-Rat" id="R9K-8v-b14"/>
306
+                    </connections>
307
+                </tapGestureRecognizer>
308
+                <tapGestureRecognizer id="PAc-yU-TO1" userLabel="GroupMemeberGesture">
309
+                    <connections>
310
+                        <action selector="navigationToGroupMember:" destination="s9X-RR-Rat" id="V81-eM-vdl"/>
311
+                    </connections>
312
+                </tapGestureRecognizer>
313
+                <tapGestureRecognizer id="CLv-er-MwX" userLabel="GroupQRGesture">
314
+                    <connections>
315
+                        <action selector="presentGroupQR:" destination="s9X-RR-Rat" id="RHF-fz-Emo"/>
316
+                    </connections>
317
+                </tapGestureRecognizer>
339 318
             </objects>
340 319
             <point key="canvasLocation" x="-983.20000000000005" y="926.08695652173924"/>
341 320
         </scene>
342
-        <!--ChangeGroupNameController-->
321
+        <!--GroupNameModificationViewController-->
343 322
         <scene sceneID="LCo-hF-TXa">
344 323
             <objects>
345
-                <viewController storyboardIdentifier="ChangeGroupNameController" id="xy6-fJ-25c" userLabel="ChangeGroupNameController" customClass="ChangeGroupNameController" customModule="PaiAi" sceneMemberID="viewController">
346
-                    <layoutGuides>
347
-                        <viewControllerLayoutGuide type="top" id="js2-YR-fZ8"/>
348
-                        <viewControllerLayoutGuide type="bottom" id="UR6-vz-0Cc"/>
349
-                    </layoutGuides>
324
+                <viewController storyboardIdentifier="GroupNameModificationViewController" id="xy6-fJ-25c" userLabel="GroupNameModificationViewController" customClass="GroupNameModificationViewController" customModule="Paiai_iOS" customModuleProvider="target" sceneMemberID="viewController">
350 325
                     <view key="view" contentMode="scaleToFill" id="k0W-jb-XQ1">
351 326
                         <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
352 327
                         <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
353 328
                         <subviews>
354 329
                             <textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="群名称" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="8A1-xH-nsg">
355
-                                <rect key="frame" x="0.0" y="64" width="375" height="44"/>
330
+                                <rect key="frame" x="0.0" y="20" width="375" height="44"/>
356 331
                                 <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
357 332
                                 <constraints>
358 333
                                     <constraint firstAttribute="height" constant="44" id="K1B-yr-x1W"/>
@@ -361,12 +336,12 @@
361 336
                                 <textInputTraits key="textInputTraits"/>
362 337
                             </textField>
363 338
                             <button opaque="NO" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="6ys-SB-QLv">
364
-                                <rect key="frame" x="0.0" y="138" width="375" height="48"/>
339
+                                <rect key="frame" x="0.0" y="94" width="375" height="48"/>
365 340
                                 <constraints>
366 341
                                     <constraint firstAttribute="height" constant="48" id="VPL-IJ-M2Y"/>
367 342
                                 </constraints>
368 343
                                 <fontDescription key="fontDescription" type="system" pointSize="18"/>
369
-                                <state key="normal" title="保存" backgroundImage="标题栏 copy 2">
344
+                                <state key="normal" title="保存" backgroundImage="navigation-background">
370 345
                                     <color key="titleColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
371 346
                                 </state>
372 347
                                 <connections>
@@ -378,12 +353,13 @@
378 353
                         <gestureRecognizers/>
379 354
                         <constraints>
380 355
                             <constraint firstItem="6ys-SB-QLv" firstAttribute="top" secondItem="8A1-xH-nsg" secondAttribute="bottom" constant="30" id="4qN-11-vnF"/>
381
-                            <constraint firstItem="6ys-SB-QLv" firstAttribute="leading" secondItem="k0W-jb-XQ1" secondAttribute="leading" id="Fen-r1-swQ"/>
382
-                            <constraint firstAttribute="trailing" secondItem="6ys-SB-QLv" secondAttribute="trailing" id="KeF-22-9xs"/>
383
-                            <constraint firstItem="8A1-xH-nsg" firstAttribute="top" secondItem="js2-YR-fZ8" secondAttribute="bottom" id="O3J-RE-wko"/>
384
-                            <constraint firstItem="8A1-xH-nsg" firstAttribute="leading" secondItem="k0W-jb-XQ1" secondAttribute="leading" id="a8E-Fz-s5Z"/>
385
-                            <constraint firstAttribute="trailing" secondItem="8A1-xH-nsg" secondAttribute="trailing" id="cAq-3V-p2y"/>
356
+                            <constraint firstItem="6ys-SB-QLv" firstAttribute="leading" secondItem="Xk6-cx-bQU" secondAttribute="leading" id="Fen-r1-swQ"/>
357
+                            <constraint firstItem="Xk6-cx-bQU" firstAttribute="trailing" secondItem="6ys-SB-QLv" secondAttribute="trailing" id="KeF-22-9xs"/>
358
+                            <constraint firstItem="8A1-xH-nsg" firstAttribute="top" secondItem="Xk6-cx-bQU" secondAttribute="top" id="O3J-RE-wko"/>
359
+                            <constraint firstItem="8A1-xH-nsg" firstAttribute="leading" secondItem="Xk6-cx-bQU" secondAttribute="leading" id="a8E-Fz-s5Z"/>
360
+                            <constraint firstItem="Xk6-cx-bQU" firstAttribute="trailing" secondItem="8A1-xH-nsg" secondAttribute="trailing" id="cAq-3V-p2y"/>
386 361
                         </constraints>
362
+                        <viewLayoutGuide key="safeArea" id="Xk6-cx-bQU"/>
387 363
                         <connections>
388 364
                             <outletCollection property="gestureRecognizers" destination="TeB-xY-v1V" appends="YES" id="o89-cE-p8S"/>
389 365
                         </connections>
@@ -403,24 +379,20 @@
403 379
             </objects>
404 380
             <point key="canvasLocation" x="665.60000000000002" y="277.06146926536735"/>
405 381
         </scene>
406
-        <!--GroupUserController-->
382
+        <!--GroupMemberViewController-->
407 383
         <scene sceneID="LEa-gB-9eo">
408 384
             <objects>
409
-                <viewController storyboardIdentifier="GroupUserController" id="DUe-lx-JN1" userLabel="GroupUserController" customClass="GroupUserController" customModule="PaiAi" sceneMemberID="viewController">
410
-                    <layoutGuides>
411
-                        <viewControllerLayoutGuide type="top" id="akD-at-rrg"/>
412
-                        <viewControllerLayoutGuide type="bottom" id="Yii-Z2-rvQ"/>
413
-                    </layoutGuides>
385
+                <viewController storyboardIdentifier="GroupMemberViewController" id="DUe-lx-JN1" userLabel="GroupMemberViewController" customClass="GroupMemberViewController" customModule="Paiai_iOS" customModuleProvider="target" sceneMemberID="viewController">
414 386
                     <view key="view" contentMode="scaleToFill" id="wjV-Cy-YkZ">
415 387
                         <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
416 388
                         <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
417 389
                         <subviews>
418 390
                             <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" rowHeight="44" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="cas-Ep-jsP">
419
-                                <rect key="frame" x="0.0" y="6" width="375" height="661"/>
391
+                                <rect key="frame" x="0.0" y="20" width="375" height="647"/>
420 392
                                 <color key="backgroundColor" red="0.94901960780000005" green="0.94901960780000005" blue="0.94901960780000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
421 393
                                 <color key="separatorColor" red="0.94117647058823528" green="0.94117647058823528" blue="0.94117647058823528" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
422 394
                                 <prototypes>
423
-                                    <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" reuseIdentifier="MemberCell" rowHeight="50" id="DJ1-Ig-ZYH" customClass="MemberCell" customModule="Paiai_iOS" customModuleProvider="target">
395
+                                    <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" reuseIdentifier="groupMemberCell" rowHeight="50" id="DJ1-Ig-ZYH" customClass="GroupMemberCell" customModule="Paiai_iOS" customModuleProvider="target">
424 396
                                         <rect key="frame" x="0.0" y="28" width="375" height="50"/>
425 397
                                         <autoresizingMask key="autoresizingMask"/>
426 398
                                         <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="DJ1-Ig-ZYH" id="IJX-qw-roh">
@@ -500,11 +472,12 @@
500 472
                         </subviews>
501 473
                         <color key="backgroundColor" red="0.94117647058823528" green="0.94117647058823528" blue="0.94117647058823528" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
502 474
                         <constraints>
503
-                            <constraint firstItem="Yii-Z2-rvQ" firstAttribute="top" secondItem="cas-Ep-jsP" secondAttribute="bottom" id="A2j-Na-7en"/>
504
-                            <constraint firstItem="cas-Ep-jsP" firstAttribute="leading" secondItem="wjV-Cy-YkZ" secondAttribute="leading" id="NW3-AC-Ax4"/>
505
-                            <constraint firstItem="cas-Ep-jsP" firstAttribute="top" secondItem="akD-at-rrg" secondAttribute="bottom" constant="-64" id="NvE-Ok-Rur"/>
506
-                            <constraint firstAttribute="trailing" secondItem="cas-Ep-jsP" secondAttribute="trailing" id="PPv-Ze-mtL"/>
475
+                            <constraint firstItem="D3F-7B-V4M" firstAttribute="bottom" secondItem="cas-Ep-jsP" secondAttribute="bottom" id="A2j-Na-7en"/>
476
+                            <constraint firstItem="cas-Ep-jsP" firstAttribute="leading" secondItem="D3F-7B-V4M" secondAttribute="leading" id="NW3-AC-Ax4"/>
477
+                            <constraint firstItem="cas-Ep-jsP" firstAttribute="top" secondItem="D3F-7B-V4M" secondAttribute="top" id="NvE-Ok-Rur"/>
478
+                            <constraint firstItem="D3F-7B-V4M" firstAttribute="trailing" secondItem="cas-Ep-jsP" secondAttribute="trailing" id="PPv-Ze-mtL"/>
507 479
                         </constraints>
480
+                        <viewLayoutGuide key="safeArea" id="D3F-7B-V4M"/>
508 481
                     </view>
509 482
                     <navigationItem key="navigationItem" id="7KI-2g-oIA"/>
510 483
                     <connections>
@@ -515,48 +488,12 @@
515 488
             </objects>
516 489
             <point key="canvasLocation" x="665.60000000000002" y="1461.7691154422789"/>
517 490
         </scene>
518
-        <!--Navigation Controller-->
519
-        <scene sceneID="ov2-RO-i1Z">
520
-            <objects>
521
-                <navigationController automaticallyAdjustsScrollViewInsets="NO" id="ahh-gm-C2t" sceneMemberID="viewController">
522
-                    <toolbarItems/>
523
-                    <navigationBar key="navigationBar" contentMode="scaleToFill" id="6Xa-cD-JBm">
524
-                        <rect key="frame" x="0.0" y="20" width="375" height="44"/>
525
-                        <autoresizingMask key="autoresizingMask"/>
526
-                    </navigationBar>
527
-                    <nil name="viewControllers"/>
528
-                    <connections>
529
-                        <segue destination="xy6-fJ-25c" kind="relationship" relationship="rootViewController" id="cOL-rO-K5H"/>
530
-                    </connections>
531
-                </navigationController>
532
-                <placeholder placeholderIdentifier="IBFirstResponder" id="KNI-l3-BnW" userLabel="First Responder" sceneMemberID="firstResponder"/>
533
-            </objects>
534
-            <point key="canvasLocation" x="-228.80000000000001" y="277.06146926536735"/>
535
-        </scene>
536
-        <!--Navigation Controller-->
537
-        <scene sceneID="5Kq-iV-cq7">
538
-            <objects>
539
-                <navigationController automaticallyAdjustsScrollViewInsets="NO" id="bLF-bf-ZTt" sceneMemberID="viewController">
540
-                    <toolbarItems/>
541
-                    <navigationBar key="navigationBar" contentMode="scaleToFill" id="XDR-Gh-5JG">
542
-                        <rect key="frame" x="0.0" y="20" width="375" height="44"/>
543
-                        <autoresizingMask key="autoresizingMask"/>
544
-                    </navigationBar>
545
-                    <nil name="viewControllers"/>
546
-                    <connections>
547
-                        <segue destination="DUe-lx-JN1" kind="relationship" relationship="rootViewController" id="eVW-yF-vp0"/>
548
-                    </connections>
549
-                </navigationController>
550
-                <placeholder placeholderIdentifier="IBFirstResponder" id="ewD-Hk-TCe" userLabel="First Responder" sceneMemberID="firstResponder"/>
551
-            </objects>
552
-            <point key="canvasLocation" x="-230.40000000000001" y="1461.7691154422789"/>
553
-        </scene>
554 491
     </scenes>
555 492
     <resources>
556 493
         <image name="delete" width="66" height="66"/>
557
-        <image name="二维码" width="80" height="80"/>
558
-        <image name="列表箭头" width="24" height="36"/>
559
-        <image name="标题栏 copy 2" width="6" height="66"/>
494
+        <image name="list-QR" width="36" height="36"/>
495
+        <image name="list-arrow" width="24" height="36"/>
496
+        <image name="navigation-background" width="12" height="132"/>
560 497
         <image name="默认头像" width="240" height="240"/>
561 498
     </resources>
562 499
 </document>

+ 31 - 2
PaiAi/Paiai_iOS/App/Group/GroupDetail/GroupDetailCoordinator.swift

@@ -6,7 +6,8 @@
6 6
 //  Copyright © 2018 yb. All rights reserved.
7 7
 //
8 8
 
9
-import Foundation
9
+import UIKit
10
+import PaiaiDataKit
10 11
 
11 12
 class GroupDetailCoordinator: Coordinator {
12 13
     let navigationController: UINavigationController
@@ -16,7 +17,35 @@ class GroupDetailCoordinator: Coordinator {
16 17
         self.groupDetailViewController = groupDetailVC
17 18
         self.navigationController = navigationController
18 19
         
19
-//        groupViewController.viewModel.delegate = self
20
+        groupDetailViewController.viewModel.delegate = self
21
+    }
22
+}
23
+
24
+extension GroupDetailCoordinator: GroupDetailViewModelDelegate {
25
+    func navigationToRootViewController() {
26
+        navigationController.popToRootViewController(animated: true)
27
+    }
28
+    
29
+    func navigationToGroupMember(_ item: GroupDetailItem) {
30
+        navigationController.pushViewController(makeGroupMemberViewController(item))
31
+    }
32
+    
33
+    func navigationToGroupNameModification(_ item: GroupDetailItem) {
34
+        navigationController.pushViewController(makeGroupNameModificationViewController(item))
35
+    }
36
+}
37
+
38
+extension GroupDetailCoordinator {
39
+    func makeGroupMemberViewController(_ item: GroupDetailItem) -> GroupMemberViewController {
40
+        let vc = UIStoryboard.groupDetail.instantiateController(GroupMemberViewController.self)
41
+        vc.viewModel = GroupMemberViewModel(item: item)
42
+        return vc
43
+    }
44
+    
45
+    func makeGroupNameModificationViewController(_ item: GroupDetailItem) -> GroupNameModificationViewController {
46
+        let vc = UIStoryboard.groupDetail.instantiateController(GroupNameModificationViewController.self)
47
+        vc.item = item
48
+        return vc
20 49
     }
21 50
 }
22 51
 

+ 47 - 8
PaiAi/Paiai_iOS/App/Group/GroupDetail/GroupDetailMemeberView.swift

@@ -9,13 +9,52 @@
9 9
 import UIKit
10 10
 
11 11
 class GroupDetailMemeberView: UIView {
12
-
13
-    /*
14
-    // Only override draw() if you perform custom drawing.
15
-    // An empty implementation adversely affects performance during animation.
16
-    override func draw(_ rect: CGRect) {
17
-        // Drawing code
12
+    var avatars = [String]() {
13
+        didSet {
14
+            constructViewHierarchy()
15
+            activateConstraints()
16
+        }
17
+    }
18
+    
19
+    private var imageViews = [UIImageView]()
20
+    private var visiableMembersCount = 0
21
+    private var space: CGFloat = 0
22
+    
23
+    override func didMoveToWindow() {
24
+        visiableMembersCount = Int((width - 6) / 46)
25
+        space = (width - CGFloat(40 * visiableMembersCount)) / CGFloat(visiableMembersCount + 1)
26
+    }
27
+    
28
+    func constructViewHierarchy() {
29
+        imageViews.forEach { $0.removeFromSuperview() }
30
+        imageViews.removeAll()
31
+        
32
+        
33
+        for avatar in avatars {
34
+            let imageView = UIImageView()
35
+            
36
+            imageView.cornerRadius = 5
37
+            imageView.setImage(avatar, placeholder: UIImage.defaultAvatar)
38
+            
39
+            addSubview(imageView)
40
+            imageViews.append(imageView)
41
+        }
42
+    }
43
+    
44
+    func activateConstraints() {
45
+        var last: UIImageView?
46
+        for imageView in imageViews {
47
+            imageView.translatesAutoresizingMaskIntoConstraints = false
48
+            
49
+            NSLayoutConstraint.activate([
50
+                imageView.widthAnchor.constraint(equalToConstant: 40),
51
+                imageView.heightAnchor.constraint(equalToConstant: 40),
52
+                imageView.centerYAnchor.constraint(equalTo: centerYAnchor),
53
+                imageView.leadingAnchor.constraint(equalTo: last?.trailingAnchor ?? leadingAnchor, constant: space)
54
+            ])
55
+            
56
+            last = imageView
57
+        }
18 58
     }
19
-    */
20
-
21 59
 }
60
+

+ 83 - 99
PaiAi/Paiai_iOS/App/Group/GroupDetail/GroupDetailViewController.swift

@@ -14,120 +14,104 @@ import PaiaiDataKit
14 14
 
15 15
 final class GroupDetailViewController: UIViewController {
16 16
 
17
+    @IBOutlet weak var groupNameLabel: UILabel!
17 18
     // MARK: Storyboard property
18
-    @IBOutlet weak var scrollViewConstraint: NSLayoutConstraint!
19
-    @IBOutlet weak var groupLockConstraint: NSLayoutConstraint!
20
-    @IBOutlet var titleLabel: UILabel!
21
-    @IBOutlet var groupNameLabel: UILabel!
22
-    @IBOutlet var groupUserCountLabel: UILabel!
23
-    @IBOutlet var groupUserHeaderScrollView: UIScrollView!
24
-    @IBOutlet var groupLockSwitch: UISwitch!
25
-    @IBOutlet var groupLockTip: UILabel!
19
+    @IBOutlet weak var groupMemberCountLabel: UILabel!
20
+    @IBOutlet weak var groupMemeberView: GroupDetailMemeberView!
21
+    @IBOutlet weak var groupLockSwitch: UISwitch!
22
+    @IBOutlet weak var groupLockTip: UILabel!
26 23
 
27 24
     var viewModel: GroupDetailViewModel!
28
-  // MARK: parameter property
29 25
     let disposeBag = DisposeBag()
30
-    // MARK: view function
26
+
31 27
     override func viewDidLoad() {
32 28
         super.viewDidLoad()
33
-//        rightBarAction = #selector(quit)
34
-        configureInterface()
29
+        binding()
30
+        viewModel.reload()
31
+        title = "群资料"
32
+        setRightBarButtonItems()
35 33
     }
36
-
37
-    override func viewWillAppear(_ animated: Bool) {
38
-        super.viewWillAppear(true)
39
-//        titleWithbackBar = "群资料"
34
+    
35
+    func setRightBarButtonItems() {
36
+        let item = UIBarButtonItem(image: UIImage(named: "navigation-right"), target: self, action: #selector(quit))
37
+        navigationItem.rightBarButtonItem = item
40 38
     }
41
-
42
-    // MARK: init interface
43
-    func configureInterface() {
44
-        
45
-//        groupDetailViewModel.groupDetailData.asObservable().bind {[unowned self] (model) in
46
-//            if let adminID = model.group?.admin_id {
47
-//                self.groupLockSwitch.isHidden = adminID != SharedUserInfo.userId
48
-//            } else {
49
-//                self.groupLockSwitch.isHidden = true
50
-//            }
51
-//
52
-//            self.groupNameLabel.text = model.group?.group_name
53
-//            if let count = model.users?.passed_count {
54
-//                self.groupUserCountLabel.text = "\(count)名"
55
-//            } else {
56
-//                self.groupUserCountLabel.text = "0名"
57
-//            }
58
-//
59
-//            guard let passUsers = model.users?.passed else {
60
-//                return
61
-//            }
62
-//            //锁定
63
-//            if let lock = model.group?.group_lock {
64
-//                self.groupLockSwitch.isOn = lock
65
-//                self.groupLockTip.text = lock ? "群已锁定,不再允许新成员加入" : "群未锁定,可以邀请新成员加入"
66
-//            }
67
-//            let userHead = passUsers.flatMap {[$0.avatar]}
68
-//            self.configureScrollView(userHead: userHead)
69
-//        }.disposed(by: disposeBag)
70
-
39
+    
40
+    @objc func quit() {
41
+        let alert = AlertController(title: "退出群", message: "退出后将不再能看到群内照片")
42
+        alert.addAlertAction(AlertAction(title: "取消", style: .cancel))
43
+        alert.addAlertAction(AlertAction(title: "确定", handler: { _ in
44
+            self.viewModel.quit()
45
+        }))
46
+        presentController(alert)
71 47
     }
48
+}
72 49
 
73
-    func configureScrollView(userHead: [String]) {
74
-        var count = -1
75
-        let headerUrl = userHead.filter { (_) -> Bool in
76
-            count += 1
77
-            return count < 6
78
-        }
79
-        let userSize = (view.width - 70) / 6
80
-        self.scrollViewConstraint.constant = userSize + 12
81
-//        self.groupUserHeaderScrollView.buildImageViews(headerUrl, width: userSize, height: (view.width - 70) / 6, spacing: 10, topSpcing: 0) { (_) in
82
-
83
-//        }
84
-
50
+/// binding UI
51
+fileprivate extension GroupDetailViewController {
52
+    func binding() {
53
+        bindViewModelToGroupName()
54
+        bindViewModelToGroupMemberCount()
55
+        bindViewModelToGroupMemeber()
56
+        bindViewModelToGroupLock()
57
+        bindViewModelToGroupLockSwitch()
58
+        bindGroupLockSwitchToGroupLockTip()
85 59
     }
86
-
87
-    // MARK: Storyboard  button
88
-
89
-    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
90
-//        let navigationController = segue.destination as! UINavigationController
91
-//        if let ctl = navigationController.topViewController as? ChangeGroupNameController {
92
-//            ctl.detailData = groupDetailViewModel.groupDetailData.value
93
-//        }
94
-//        if let ctl = navigationController.topViewController as? GroupUserController {
95
-//            guard let passed = groupDetailViewModel.groupDetailData.value.users?.passed else {
96
-//                return
97
-//            }
98
-//            ctl.meberData = Variable<[UserModel]>(passed)
99
-//            ctl.groupId = groupDetailViewModel.groupDetailData.value.group_id
100
-//        }
60
+    
61
+    func bindViewModelToGroupName() {
62
+        viewModel.groupName.bind(to: groupNameLabel.rx.text).disposed(by: disposeBag)
101 63
     }
102
-
103
-    @IBAction func showEWM() {
104
-        let ctl = UIStoryboard.groupDetail.instantiateController(ShowGroupQRController.self)
105
-//        ctl.data = groupDetailViewModel.groupDetailData.value.group
106
-        presentController(ctl)
64
+    
65
+    func bindViewModelToGroupMemberCount() {
66
+        viewModel.groupMemberCount.bind(to: groupMemberCountLabel.rx.text).disposed(by: disposeBag)
107 67
     }
108
-
109
-    @objc func quit() {
110
-//        let alert = FFAlertController(title: "", message: "", alertStyle: .actionSheet)
111
-//        alert.addAlertAction(alertAction: DestructiveAlertAction(title: "退出该群", handler: { (alertAction) in
112
-//            let subAlert = FFAlertController(title: "退出群", message: "退出后将不再能看到群内照片", alertStyle: .alert)
113
-//            subAlert.addAlertAction(alertAction: CancelAlertAction())
114
-//            subAlert.addAlertAction(alertAction: ConfirmAlertAction(handler: { (alertAction) in
115
-//                self.groupDetailViewModel.quitQroup(success: {[weak self] in
116
-//                    if let weakself = self {
117
-//                        PhotoLocalStorage.instance.removeLocalData(group_id: weakself.groupId)
118
-//                        subAlert.dismissController()
119
-//                        _ = weakself.navigationController?.popToRootViewController(animated: true)
120
-//                        FFToastView.showToast(inView: UIApplication.shared.keyWindow ?? weakself.view, withText: "退出群成功")
121
-//                    }
122
-//                })
123
-//            }))
124
-//            self.presentController(subAlert)
125
-//        }))
126
-//        alert.addAlertAction(alertAction: FFAlertAction(title: "取消", handler: nil))
127
-//        presentController(alert)
68
+    
69
+    func bindViewModelToGroupMemeber() {
70
+        viewModel.groupMembers.subscribe(onNext: { self.setupGroupMember(groupMemebers: $0) }).disposed(by: disposeBag)
71
+    }
72
+    
73
+    func bindViewModelToGroupLock() {
74
+        viewModel.groupLock.bind(to: groupLockSwitch.rx.value).disposed(by: disposeBag)
75
+    }
76
+    
77
+    func bindViewModelToGroupLockSwitch() {
78
+        viewModel.isAdmin.map { !$0 }.bind(to: groupLockSwitch.rx.isHidden).disposed(by: disposeBag)
79
+    }
80
+    
81
+    func bindGroupLockSwitchToGroupLockTip() {
82
+        groupLockSwitch.rx.value.subscribe(onNext: {[weak self] (v) in
83
+            guard let `self` = self else { return }
84
+            self.groupLockTip.text = v ? "群已锁定,不再允许新成员加入" : "群未锁定,可以邀请新成员加入"
85
+        }).disposed(by: disposeBag)
128 86
     }
87
+    
88
+    private func setupGroupMember(groupMemebers: [GroupMemberItem]) {
89
+        groupMemeberView.avatars = groupMemebers.compactMap { $0.avatar }
90
+    }
91
+}
129 92
 
93
+/// storyboard action
94
+extension GroupDetailViewController {
95
+    @IBAction func navigationToGroupMember(_ sender: UITapGestureRecognizer) {
96
+        viewModel.navigationToGroupMember()
97
+    }
98
+    
99
+    @IBAction func navigationToGroupNameModification(_ sender: UITapGestureRecognizer) {
100
+        viewModel.navigationToGroupNameModification()
101
+    }
102
+    
103
+    @IBAction func presentGroupQR(_ sender: UITapGestureRecognizer) {
104
+        let groupItem = viewModel.item.value.group
105
+        let alert = AlertViewController(style: .custom(GroupQRView(groupName: groupItem.group_name,
106
+                                                                   groupAvatar: "Group\(groupItem.group_default_avatar)",
107
+            groupQR: "https:pai.ai/g/\(groupItem.group_id)"),
108
+                                                       AlertAnimator()) )
109
+        presentController(alert)
110
+    }
111
+    
130 112
     @IBAction func changeSwitch() {
131
-//        groupDetailViewModel.postLock(isLock: groupLockSwitch.isOn)
113
+        viewModel.toggle(isLock: groupLockSwitch.isOn)
132 114
     }
133 115
 }
116
+
117
+extension GroupDetailViewController: NavigationBackViewController {}

+ 11 - 22
PaiAi/Paiai_iOS/App/Group/GroupDetail/GroupMemberCell.swift

@@ -1,5 +1,5 @@
1 1
 //
2
-//  MemberCell.swift
2
+//  GroupMemberCell.swift
3 3
 //  PaiAi
4 4
 //
5 5
 //  Created by mac on 16/5/20.
@@ -10,11 +10,11 @@ import UIKit
10 10
 import PaiaiDataKit
11 11
 import PaiaiUIKit
12 12
 
13
-protocol ReturnIndexPathDelegate: class {
14
-    func renturnIndexPath(_ indexpath: IndexPath)
13
+protocol GroupMemberCellDelegate: class {
14
+    func remove(_ item: GroupMemberItem)
15 15
 }
16 16
 
17
-final class MemberCell: UITableViewCell {
17
+final class GroupMemberCell: UITableViewCell {
18 18
 
19 19
     // MARK: Storyboard property
20 20
     @IBOutlet weak var isManager: UILabel!
@@ -24,32 +24,21 @@ final class MemberCell: UITableViewCell {
24 24
     @IBOutlet weak var uerImage: UIImageView!
25 25
 
26 26
     // MARK: delete property
27
-    weak var delegate: ReturnIndexPathDelegate?
28
-
29
-  // MARK: parameter property
30
-    fileprivate var index: IndexPath!
31
-
32
-    // MARK: view function
33
-    override func awakeFromNib() {
34
-        super.awakeFromNib()
35
-    }
36
-
37
-    override func setSelected(_ selected: Bool, animated: Bool) {
38
-        super.setSelected(selected, animated: animated)
39
-    }
27
+    weak var delegate: GroupMemberCellDelegate?
28
+    var item = GroupMemberItem(json: [:])
40 29
 
41 30
     // MARK: init interface
42
-    func setInfo(_ model: GroupMemberItem, isManage: Bool, indexPath: IndexPath) {
43
-        index = indexPath
31
+    func setInfo(_ model: GroupMemberItem, isAdmin: Bool) {
32
+        item = model
44 33
         meberName.text = model.nickname
45
-//        uerImage.setImageWithNullableURL(model.avatar, placeholderImage: defaultAvatar)
34
+        uerImage.setImage(model.avatar, placeholder: UIImage.defaultAvatar)
46 35
         isManager.isHidden = !model.admin
47
-        deleteConstraint.constant = !isManage || model.admin ? -32 : 12
36
+        deleteConstraint.constant = !isAdmin || model.admin ? -32 : 12
48 37
     }
49 38
 
50 39
     // MARK: Storyboard  button
51 40
     @IBAction func deleteMemberAction(_ sender: UIButton) {
52
-        delegate?.renturnIndexPath(index)
41
+        delegate?.remove(item)
53 42
     }
54 43
 
55 44
 }

+ 40 - 44
PaiAi/Paiai_iOS/App/Group/GroupDetail/GroupMemberViewController.swift

@@ -1,5 +1,5 @@
1 1
 //
2
-//  GroupMemberController.swift
2
+//  GroupMemberViewController.swift
3 3
 //  PaiAi
4 4
 //
5 5
 //  Created by zhengjianfei on 16/4/6.
@@ -9,62 +9,58 @@
9 9
 import UIKit
10 10
 import RxCocoa
11 11
 import RxSwift
12
+import RxDataSources
12 13
 import PaiaiUIKit
13 14
 import PaiaiDataKit
14 15
 
15
-final class GroupMemberController: UIViewController {
16
+final class GroupMemberViewController: UIViewController {
16 17
 
17
-    // MARK: Storyboard property
18
-    @IBOutlet var tableView: UITableView!
18
+    @IBOutlet weak var tableView: UITableView!
19 19
 
20
-    // MARK: data property
21
-    lazy var meberData = Variable<[GroupMemberItem]>([])
22
-    var groupMebersViewModel = GroupMemberViewModel()
23
-
24
-  // MARK: parameter property
25
-    lazy var groupId = ""
26
-    lazy var isManage = false
20
+    var viewModel: GroupMemberViewModel!
27 21
     let disposeBag = DisposeBag()
22
+    
28 23
     // MARK: view function
29 24
     override func viewDidLoad() {
30 25
         super.viewDidLoad()
31
-        tableView?.rowHeight = 44.0
32
-//        isManage = meberData.value.filter({ (userModel) -> Bool in
33
-//            return userModel.user_id == SharedUserInfo.userId && userModel.admin
34
-//        }).count > 0
35
-        meberData.asObservable().bind(to: tableView.rx.items(cellIdentifier: "MemberCell", cellType: MemberCell.self)) { (row, model, cell) in
36
-            cell.setInfo(model, isManage: self.isManage, indexPath :IndexPath(row: row, section: 0))
37
-            cell.delegate = self
38
-        }.disposed(by: disposeBag)
26
+        title = "群成员"
27
+        binding()
39 28
     }
29
+}
40 30
 
41
-//    override func backToController() {
42
-//        dismissController()
43
-//    }
44
-
45
-    override func viewWillAppear(_ animated: Bool) {
46
-        super.viewWillAppear(true)
47
-//        titleWithbackBar = "群成员"
31
+fileprivate extension GroupMemberViewController {
32
+    
33
+    var dataSource: RxTableViewSectionedAnimatedDataSource<AnimatableSectionModel<Int, GroupMemberItem>> {
34
+        return RxTableViewSectionedAnimatedDataSource<AnimatableSectionModel<Int, GroupMemberItem>>(
35
+            configureCell: { (dataSource, tableView, indexPath, item) in
36
+                let cell = tableView.dequeueReusableCell(withIdentifier: "groupMemberCell", for: indexPath) as! GroupMemberCell
37
+                cell.setInfo(item, isAdmin: self.viewModel.isAdmin)
38
+                cell.delegate = self
39
+                return cell
40
+        })
41
+    }
42
+    
43
+    func binding() {
44
+        bindViewModelToTableView()
45
+    }
46
+    
47
+    func bindViewModelToTableView() {
48
+        viewModel.contents
49
+            .bind(to: tableView.rx.items(dataSource: dataSource))
50
+            .disposed(by: disposeBag)
48 51
     }
49 52
 }
50 53
 
51
-// MARK: custom delegate
52
-extension GroupMemberController: ReturnIndexPathDelegate {
53
-
54
-    func renturnIndexPath(_ indexpath: IndexPath) {
55
-
56
-//        let alert = FFAlertController(title: "删除群成员", message: "删除后将不再看见群内照片", alertStyle: .alert)
57
-//        alert.addAlertAction(alertAction: CancelAlertAction())
58
-//        alert.addAlertAction(alertAction: ConfirmAlertAction(handler: { (alertAction) in
59
-//            let params: [String: AnyObject] = ["group_id": self.groupId as AnyObject, "admin_id": SharedUserInfo.userId as AnyObject, "user_id": self.meberData.value[indexpath.row].user_id as AnyObject]
60
-//            self.groupMebersViewModel.deleteMeber(params: params, success: {
61
-//                self.meberData.value.remove(at: indexpath.row)
62
-//                self.tableView.beginUpdates()
63
-//                self.tableView.deleteRows(at: [indexpath], with: UITableViewRowAnimation.left)
64
-//                self.tableView.endUpdates()
65
-//            })
66
-//        }))
67
-//        presentController(alert)
54
+extension GroupMemberViewController: GroupMemberCellDelegate {
55
+    func remove(_ item: GroupMemberItem) {
56
+        let alert = AlertController(title: "删除群成员", message: "删除后将不再看见群内照片")
57
+        alert.addAlertAction(AlertAction(title: "取消", style: .cancel))
58
+        alert.addAlertAction(AlertAction(title: "确定", handler: {(_) in
59
+            self.viewModel.removeMember(item)
60
+        }))
61
+        
62
+        presentController(alert)
68 63
     }
69
-
70 64
 }
65
+
66
+extension GroupMemberViewController: NavigationBackViewController {}

+ 54 - 54
PaiAi/Paiai_iOS/App/Group/GroupDetail/GroupNameModificationViewController.swift

@@ -1,5 +1,5 @@
1 1
 //
2
-//  ChangeGroupNameController.swift
2
+//  GroupNameModificationViewController.swift
3 3
 //  PaiAi
4 4
 //
5 5
 //  Created by zhengjianfei on 16/4/6.
@@ -12,79 +12,79 @@ import RxCocoa
12 12
 import PaiaiUIKit
13 13
 import PaiaiDataKit
14 14
 
15
-final class ChangeGroupNameController: UIViewController {
15
+final class GroupNameModificationViewController: UIViewController {
16 16
 
17 17
     // MARK: Storyboard property
18
-    @IBOutlet var textField: UITextField!
18
+    @IBOutlet weak var textField: UITextField!
19 19
     @IBOutlet weak var saveButton: UIButton!
20
-
21
-    // MARK: data property
22
-//    var detailData: GroupDetailModel?
20
+    
21
+    var item = GroupDetailItem(json: [:])
22
+    
23 23
     let disposeBag = DisposeBag()
24 24
     // MARK: view function
25 25
     override func viewDidLoad() {
26 26
         super.viewDidLoad()
27
-        configureTextField()
27
+        title = "群名称"
28
+        setupTextFieldLeftView()
29
+        bindTextFieldToSaveButton()
28 30
     }
29
-
30
-    override func viewWillAppear(_ animated: Bool) {
31
-        super.viewWillAppear(true)
32
-//        titleWithbackBar = "群名称"
31
+    
32
+    func setupTextFieldLeftView() {
33
+        let leftView = UIView(frame: CGRect(x: 0, y: 0, width: 12, height: textField.width))
34
+        textField.leftView = leftView
35
+        textField.leftViewMode = .always
36
+        textField.placeholder = item.group.group_name
33 37
     }
34 38
 
35
-//    override func backToController() {
36
-//        dismissController()
37
-//    }
38
-
39
-    func configureTextField() {
40
-        do {
41
-            let view = UIView(frame: CGRect(x: 0, y: 0, width: 12, height: textField.width))
42
-            textField.leftView = view
43
-            textField.leftViewMode = .always
44
-//            textField.placeholder = detailData?.group?.group_name
45
-        }
46
-
47
-        do {
48
-            textField.rx.text
49
-                        .map {!($0?.isEmpty)!}
50
-                        .bind(to: saveButton.rx.isEnabled)
51
-                        .disposed(by: disposeBag)
52
-        }
39
+    func bindTextFieldToSaveButton() {
40
+        textField.rx.text
41
+                    .map {!($0?.isEmpty)!}
42
+                    .bind(to: saveButton.rx.isEnabled)
43
+                    .disposed(by: disposeBag)
53 44
     }
45
+}
54 46
 
55
-    // MARK: Storyboard  button
47
+extension GroupNameModificationViewController {
48
+    
56 49
     @IBAction func saveName() {
57
-        guard NSString(string: textField.text ?? "").length < 20 else {
58
-            jumpToSaveFailController()
50
+        if (textField.text ?? "").count > 20 {
51
+            saveFailed()
59 52
             return
60 53
         }
61
-//        guard let info = detailData else {
62
-//            return
63
-//        }
64
-//        guard let group = detailData?.group else {
65
-//            return
66
-//        }
67
-//        let params = ["group_id": info.group_id,
68
-//        "admin_id": group.admin_id, "group_name": textField.text ?? ""] as [String: AnyObject]
69
-//        let request = StatusNetworkRequest(param: params, path: .groupUpdate)
70
-//        NetworkApi.share.post(request: request) { (res) in
71
-//            guard res.status == 200 else {
72
-//                return
73
-//            }
74
-//            addGroupInfoToRecent(group)
75
-//            FFToastView.showToast(inView: self.view, withText: "保存成功")
76
-//            self.textField.resignFirstResponder()
77
-//        }
54
+        
55
+        
56
+        
57
+        //        guard let info = detailData else {
58
+        //            return
59
+        //        }
60
+        //        guard let group = detailData?.group else {
61
+        //            return
62
+        //        }
63
+        //        let params = ["group_id": info.group_id,
64
+        //        "admin_id": group.admin_id, "group_name": textField.text ?? ""] as [String: AnyObject]
65
+        //        let request = StatusNetworkRequest(param: params, path: .groupUpdate)
66
+        //        NetworkApi.share.post(request: request) { (res) in
67
+        //            guard res.status == 200 else {
68
+        //                return
69
+        //            }
70
+        //            addGroupInfoToRecent(group)
71
+        //            FFToastView.showToast(inView: self.view, withText: "保存成功")
72
+        //            self.textField.resignFirstResponder()
73
+        //        }
78 74
     }
79
-
80
-    func jumpToSaveFailController() {
75
+    
76
+    func saveFailed() {
81 77
         textField.resignFirstResponder()
82
-//        let alert = FFAlertController(title: "保存失败", message: "群名称不能超过20个字", alertStyle: .alert)
83
-//        alert.addAlertAction(alertAction: ConfirmAlertAction(handler: nil))
84
-//        presentController(alert)
78
+        let alert = AlertController(title: "保存失败", message: "群名称不能超过20个字")
79
+        alert.addAlertAction(AlertAction(title: "确定"))
80
+        presentController(alert)
85 81
     }
86 82
 
83
+    
87 84
     @IBAction func tapView() {
88 85
         textField.resignFirstResponder()
89 86
     }
90 87
 }
88
+
89
+
90
+extension GroupNameModificationViewController: NavigationBackViewController {}

+ 12 - 11
PaiAi/Paiai_iOS/App/Group/GroupViewController.swift

@@ -17,7 +17,7 @@ import PullToRefresh
17 17
 final class GroupViewController: UIViewController {
18 18
 
19 19
     // MARK: Storyboard property
20
-    @IBOutlet var collectionView: UICollectionView!
20
+    @IBOutlet weak var collectionView: UICollectionView!
21 21
     @IBOutlet weak var photographBtn: UIButton!
22 22
 
23 23
     // MARK: custom UI property
@@ -43,7 +43,6 @@ final class GroupViewController: UIViewController {
43 43
     
44 44
     // MARK: data property
45 45
     var viewModel: GroupViewModel!
46
-    var groupItem: GroupItem!
47 46
     
48 47
     fileprivate var navigationViewNotReady = true
49 48
     fileprivate let disposeBag = DisposeBag()
@@ -113,19 +112,23 @@ fileprivate extension GroupViewController {
113 112
     }
114 113
     
115 114
     func bindCollectionViewToViewModel() {
116
-        collectionView.rx.modelSelected(PhotoItem.self)
115
+        collectionView.rx.itemSelected
117 116
             .asDriver()
118
-            .drive(onNext: { [unowned self] in self.viewModel.didSelect($0) })
117
+            .drive(onNext: { [unowned self] in self.viewModel.didSelect($0.item) })
119 118
             .disposed(by: disposeBag)
120 119
     }
121 120
     
122 121
     func bindViewModelToNavigationBarTitle() {
123
-        navigationBarViewTitle.text = groupItem.group_name
122
+        viewModel.groupName.bind(to: navigationBarViewTitle.rx.text).disposed(by: disposeBag)
124 123
     }
125 124
     
126 125
     func bindViewModelToNavigationBarImage() {
127
-        navigationBarViewImage.setImage(groupItem.group_avatar,
128
-                                        placeholder: UIImage(named: "Group\(groupItem.group_default_avatar)"))
126
+        viewModel.groupAvatar
127
+            .subscribe(onNext: {[weak self] (avatar) in
128
+                guard let `self` = self else { return }
129
+                self.navigationBarViewImage.image = UIImage(named: avatar)
130
+        }).disposed(by: disposeBag)
131
+        
129 132
     }
130 133
 }
131 134
 
@@ -160,6 +163,8 @@ extension GroupViewController: NavigationBarInteractiveViewController {
160 163
     }
161 164
     
162 165
     @objc func presentGroupQR() {
166
+        
167
+        let groupItem = viewModel.groupItem.value
163 168
         let alert = AlertViewController(style: .custom(GroupQRView(groupName: groupItem.group_name,
164 169
                                                                    groupAvatar: "Group\(groupItem.group_default_avatar)",
165 170
                                                                    groupQR: "https:pai.ai/g/\(groupItem.group_id)"),
@@ -211,10 +216,6 @@ fileprivate extension GroupViewController {
211 216
     }
212 217
 }
213 218
 
214
-extension GroupViewController {
215
-    
216
-}
217
-
218 219
 extension GroupViewController: UICollectionViewDelegateFlowLayout {
219 220
     func collectionView(_ collectionView: UICollectionView,
220 221
                         layout collectionViewLayout: UICollectionViewLayout,

+ 31 - 46
PaiAi/Paiai_iOS/App/Home/CreateGroupConfirmViewController.swift

@@ -7,67 +7,52 @@
7 7
 //
8 8
 
9 9
 import UIKit
10
-import RxSwift
11 10
 import PaiaiDataKit
12 11
 import PaiaiUIKit
13 12
 
14 13
 
15 14
 final class CreateGroupConfirmViewController: AlertViewController {
15
+    
16
+    override var animationView: UIView? {
17
+        return tipView
18
+    }
19
+    
16 20
     // MARK: Storyboard property
17
-    @IBOutlet var tipView: UIView!
18
-    @IBOutlet var nameLabel: UILabel!
19
-    @IBOutlet var imageView: UIImageView!
21
+    @IBOutlet weak var tipView: UIView!
22
+    @IBOutlet weak var nameLabel: UILabel!
23
+    @IBOutlet weak var imageView: UIImageView!
20 24
     
21
-    var viewModel = CreateGroupConfirmViewModel()
22
-    var disposeBag = DisposeBag()
25
+    var viewModel: CreateGroupViewModel!
23 26
 
24 27
     override func viewDidLoad() {
25 28
         super.viewDidLoad()
26
-        configurationRx()
27
-    }
28
-    
29
-    func configurationRx() {
30
-        viewModel.name.bind(to: nameLabel.rx.text).disposed(by: disposeBag)
29
+        binding()
31 30
     }
32
-    
33
-    // MARK: Storyboard  button function
31
+}
32
+
33
+/// storyboard button action
34
+extension CreateGroupConfirmViewController {
34 35
     @IBAction func confirmAction() {
35
-        self.viewModel.createGroup()
36
+        viewModel.createGroup()
36 37
     }
37 38
     
38
-    override var animationView: UIView? {
39
-        return tipView
40
-    }
41
-
42
-//    func createGroup(_ name: String, avatar: Int) {
43
-//        let params = ["user_id": SharedUserInfo.userId, "group_name": name, "group_default_avatar": avatar] as [String: AnyObject]
44
-//        FFToastView.showLoadingToast(inView: view, blockSuperView: true)
45
-////        let request = CommonNetworkRequest<GroupModel>(param: params, path: .groupCreate)
46
-////        NetworkApi.share.post(request: request) {[weak self] (res) in
47
-////            guard let weakself = self else {
48
-////                return
49
-////            }
50
-////            weakself.group = res.first ?? GroupModel()
51
-////            weakself.confirmAndJump()
52
-////
53
-////        }
54
-//    }
55
-
56
-//    func confirmAndJump() {
57
-//        let ctl = UIStoryboard.main.instantiateController(GroupViewController.self)
58
-//        ctl.groupModel = group
59
-//        ctl.isCreate = true
60
-//        guard let parentController = presentingViewController as? UINavigationController else {
61
-//            return
62
-//        }
63
-//        guard let rootController = parentController.viewControllers.first as? HomeViewController else {
64
-//            return
65
-//        }
66
-//        dismissController()
67
-//        rootController.navigationController?.pushViewController(ctl, animated: true)
68
-//    }
69
-
70 39
     @IBAction func cancelAction() {
71 40
         dismissController()
72 41
     }
73 42
 }
43
+
44
+/// binding UI
45
+extension CreateGroupConfirmViewController {
46
+    func binding() {
47
+        bindGroupName()
48
+        bindGroupAvatar()
49
+    }
50
+    
51
+    func bindGroupName() {
52
+        nameLabel.text = viewModel.name
53
+    }
54
+    
55
+    func bindGroupAvatar() {
56
+        imageView.image = UIImage(named: viewModel.avatar)
57
+    }
58
+}

+ 6 - 6
PaiAi/Paiai_iOS/App/Home/CreateGroupViewController.swift

@@ -35,7 +35,7 @@ final class CreateGroupViewController: AlertViewController {
35 35
     // MARK: view function
36 36
     override func viewDidLoad() {
37 37
         super.viewDidLoad()
38
-        contentHeightConstraint.constant = 48 + 44 * CGFloat(RecentGroupInfo.share.count + 1)
38
+        contentHeightConstraint.constant = 48 + 44 * CGFloat(ShareRecentGroupInfo.count + 1)
39 39
     }
40 40
 
41 41
     // MARK: Storyboard  button function
@@ -50,7 +50,7 @@ extension CreateGroupViewController: UITableViewDataSource, UITableViewDelegate
50 50
     }
51 51
 
52 52
     func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
53
-        return RecentGroupInfo.share.count + 1
53
+        return ShareRecentGroupInfo.count + 1
54 54
     }
55 55
 
56 56
     func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
@@ -59,7 +59,7 @@ extension CreateGroupViewController: UITableViewDataSource, UITableViewDelegate
59 59
         case 0:
60 60
             self.delegate?.navigateToCreateGroupConfirm()
61 61
         default:
62
-            self.delegate?.didSelect(RecentGroupInfo.share[indexPath.row - 1])
62
+            self.delegate?.didSelect(ShareRecentGroupInfo[indexPath.row - 1])
63 63
         }
64 64
     }
65 65
 
@@ -70,9 +70,9 @@ extension CreateGroupViewController: UITableViewDataSource, UITableViewDelegate
70 70
             return cell
71 71
         default:
72 72
             let cell = tableView.dequeueReusableCell(withIdentifier: "RecentCell", for: indexPath)
73
-//            let group =
74
-//            (cell.viewWithTag(90) as? UILabel).flatMap {$0}?.text = group.group_name
75
-//            (cell.viewWithTag(90) as? UIImageView).flatMap {$0}?.setImageWithNullableURL(group.group_avatar, placeholderImage: UIImage(named: "Group\(group.group_default_avatar)"))
73
+            let group = ShareRecentGroupInfo[indexPath.row - 1]
74
+            cell.textLabel?.text = group.group_name
75
+            cell.imageView?.setImage(group.group_avatar, placeholder: UIImage(named: "Group\(group.group_default_avatar)"))
76 76
 
77 77
             return cell
78 78
         }

+ 44 - 7
PaiAi/Paiai_iOS/App/Home/HomeCoordinator.swift

@@ -11,12 +11,24 @@ import PaiaiDataKit
11 11
 import PaiaiUIKit
12 12
 
13 13
 class HomeCoordinator: Coordinator {
14
+    fileprivate enum CoordinatorKey: String {
15
+        case home = "home"
16
+        case message = "message"
17
+        case mine = "mine"
18
+        case photoDetail = "photoDetail"
19
+        case group = "group"
20
+    }
21
+    fileprivate var coordinators = [CoordinatorKey: Coordinator]()
14 22
     
15 23
     var homeViewController: HomeViewController
24
+    var navigationController: UINavigationController
16 25
     var shareUserInfoViewModel: UserInfoViewModel
17 26
     
18
-    init(_ homeVC: HomeViewController, userInfoViewModel: UserInfoViewModel) {
27
+    init(_ homeVC: HomeViewController,
28
+         navigationController: UINavigationController,
29
+         userInfoViewModel: UserInfoViewModel) {
19 30
         homeViewController = homeVC
31
+        self.navigationController = navigationController
20 32
         shareUserInfoViewModel = userInfoViewModel
21 33
         homeViewController.viewModel.delegate = self
22 34
     }
@@ -27,9 +39,14 @@ class HomeCoordinator: Coordinator {
27 39
 }
28 40
 
29 41
 extension HomeCoordinator: HomeViewModelDelegate {
30
-    func didSelect(_ item: PhotoItem) {
31
-        let ctl = UIStoryboard.photoDetail.instantiateController(DetailPageController.self)
32
-        homeViewController.pushController(ctl)
42
+    func didSelect(_ items: [PhotoItem], currIndex: Int) {
43
+        let ctl = UIStoryboard.photoDetail.instantiateController(PhotoDetailViewController.self)
44
+        let coordinator = PhotoDetailCoordinator(ctl, nav: navigationController,
45
+                                                 viewModel: PhotoDetailViewModel(item: items[currIndex]),
46
+                                                 listViewModel: PhotoDetailListViewModel(items: items, currIndex: currIndex))
47
+        coordinators[.photoDetail] = coordinator
48
+        coordinator.start()
49
+        navigationController.pushViewController(coordinator.photoDetailViewController)
33 50
     }
34 51
     
35 52
     func createGroup() {
@@ -46,16 +63,36 @@ extension HomeCoordinator: HomeViewModelDelegate {
46 63
 
47 64
 extension HomeCoordinator: CreateGroupViewControllerDelegate {
48 65
     func didSelect(_ item: GroupItem) {
49
-//        let ctl = UIStoryboard.
66
+        let ctl = UIStoryboard.group.instantiateController(GroupViewController.self)
67
+        ctl.viewModel = GroupViewModel(groupItem: item)
68
+        let coordinator = GroupCoordinator(ctl,
69
+                                           navigationController: navigationController)
70
+        coordinators[.group] = coordinator
71
+        navigationController.pushViewController(ctl)
50 72
     }
51
-    
73
+
52 74
     func navigateToCreateGroupConfirm() {
53 75
         let ctl = UIStoryboard.main.instantiateCreateGroupConfirmViewController()
54
-        
76
+        ctl.viewModel = CreateGroupViewModel(userInfoViewModel: shareUserInfoViewModel)
77
+        ctl.viewModel.delegate = self
55 78
         homeViewController.presentController(ctl)
56 79
     }
57 80
 }
58 81
 
82
+extension HomeCoordinator: CreateGroupViewModelDelegate {
83
+    func navigationToGroup(_ item: GroupItem) {
84
+        guard let vc = homeViewController.presentedViewController, vc.isMember(of: CreateGroupConfirmViewController.self) else { return }
85
+        vc.dismissController()
86
+        
87
+        let ctl = UIStoryboard.group.instantiateController(GroupViewController.self)
88
+        let coordinator = GroupCoordinator(ctl,
89
+                                           navigationController: navigationController)
90
+        coordinators[.group] = coordinator
91
+        ctl.viewModel = GroupViewModel(groupItem: item)
92
+        navigationController.pushViewController(ctl)
93
+    }
94
+}
95
+
59 96
 extension UIStoryboard {
60 97
     fileprivate func instantiateCreateGroupViewController() -> CreateGroupViewController {
61 98
         let createGroupVC = instantiateController(CreateGroupViewController.self)

+ 3 - 3
PaiAi/Paiai_iOS/App/Home/HomeViewController.swift

@@ -120,9 +120,9 @@ fileprivate extension HomeViewController {
120 120
             .drive(onNext: { [unowned self] in self.preload(indexPath: $0.at) })
121 121
             .disposed(by: disposeBag)
122 122
         
123
-        collectionView.rx.modelSelected(PhotoItem.self)
124
-            .asDriver()
125
-            .drive(onNext: { [unowned self] in self.viewModel.didSelect($0) })
123
+        collectionView.rx.itemSelected
124
+            .asDriver(onErrorJustReturn: IndexPath(item: 0, section: 0))
125
+            .drive(onNext: { [unowned self] in self.viewModel.didSelect($0.row) } )
126 126
             .disposed(by: disposeBag)
127 127
     }
128 128
     

+ 2 - 2
PaiAi/Paiai_iOS/App/Home/ScanQRViewController.swift

@@ -14,8 +14,8 @@ import PaiaiUIKit
14 14
 final class ScanQRViewController: UIViewController {
15 15
 
16 16
     // MARK: Storyboard property
17
-    @IBOutlet var scanView: QRCodeScanView!
18
-    @IBOutlet var lightLabel: UILabel!
17
+    @IBOutlet weak var scanView: QRCodeScanView!
18
+    @IBOutlet weak var lightLabel: UILabel!
19 19
 
20 20
    // MARK: parameter property
21 21
     var viewModel = ScanQRViewModel()

+ 4 - 4
PaiAi/Paiai_iOS/App/LoginViewController.swift

@@ -14,10 +14,10 @@ import RxSwift
14 14
 
15 15
 final class LoginViewController: UIViewController {
16 16
 
17
-    @IBOutlet var pageControl: UIPageControl!
18
-    @IBOutlet var scrollView: UIScrollView!
19
-    @IBOutlet var guestLoginBtn: UIButton!
20
-    @IBOutlet var weixinLoginBtn: UIButton!
17
+    @IBOutlet weak var pageControl: UIPageControl!
18
+    @IBOutlet weak var scrollView: UIScrollView!
19
+    @IBOutlet weak var guestLoginBtn: UIButton!
20
+    @IBOutlet weak var weixinLoginBtn: UIButton!
21 21
     
22 22
     public var userInfoViewModel: UserInfoViewModel!
23 23
     

+ 10 - 14
PaiAi/Paiai_iOS/App/Message/Message.storyboard

@@ -1,10 +1,6 @@
1 1
 <?xml version="1.0" encoding="UTF-8"?>
2 2
 <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useSafeAreas="YES" colorMatched="YES">
3
-    <device id="retina4_7" orientation="portrait">
4
-        <adaptation id="fullscreen"/>
5
-    </device>
6 3
     <dependencies>
7
-        <deployment identifier="iOS"/>
8 4
         <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/>
9 5
         <capability name="Safe area layout guides" minToolsVersion="9.0"/>
10 6
         <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
@@ -213,8 +209,8 @@
213 209
                                         <color key="textColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
214 210
                                         <nil key="highlightedColor"/>
215 211
                                     </label>
216
-                                    <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="列表箭头" translatesAutoresizingMaskIntoConstraints="NO" id="mre-Tt-F0S">
217
-                                        <rect key="frame" x="341" y="14" width="24" height="36"/>
212
+                                    <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="list-arrow" translatesAutoresizingMaskIntoConstraints="NO" id="mre-Tt-F0S">
213
+                                        <rect key="frame" x="349" y="24" width="16" height="16"/>
218 214
                                     </imageView>
219 215
                                     <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="T9q-a4-Ugc">
220 216
                                         <rect key="frame" x="0.0" y="64" width="375" height="64"/>
@@ -230,8 +226,8 @@
230 226
                                             <constraint firstAttribute="width" secondItem="NEm-bF-yno" secondAttribute="height" id="snW-w9-7b3"/>
231 227
                                         </constraints>
232 228
                                     </imageView>
233
-                                    <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="列表箭头" translatesAutoresizingMaskIntoConstraints="NO" id="8VZ-dR-b6z">
234
-                                        <rect key="frame" x="341" y="78" width="24" height="36"/>
229
+                                    <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="list-arrow" translatesAutoresizingMaskIntoConstraints="NO" id="8VZ-dR-b6z">
230
+                                        <rect key="frame" x="349" y="88" width="16" height="16"/>
235 231
                                     </imageView>
236 232
                                     <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="赞" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="J3p-B7-gMi">
237 233
                                         <rect key="frame" x="70" y="86" width="17" height="20"/>
@@ -253,8 +249,8 @@
253 249
                                             <constraint firstAttribute="width" constant="48" id="Kmf-oj-nfI"/>
254 250
                                         </constraints>
255 251
                                     </imageView>
256
-                                    <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="列表箭头" translatesAutoresizingMaskIntoConstraints="NO" id="hJe-J2-RpF">
257
-                                        <rect key="frame" x="341" y="142" width="24" height="36"/>
252
+                                    <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="list-arrow" translatesAutoresizingMaskIntoConstraints="NO" id="hJe-J2-RpF">
253
+                                        <rect key="frame" x="349" y="152" width="16" height="16"/>
258 254
                                     </imageView>
259 255
                                     <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="评论" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="6fy-0a-WYd">
260 256
                                         <rect key="frame" x="70" y="150" width="33" height="20"/>
@@ -263,7 +259,7 @@
263 259
                                         <nil key="highlightedColor"/>
264 260
                                     </label>
265 261
                                     <view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="oLf-Xb-QSv">
266
-                                        <rect key="frame" x="325" y="28" width="8" height="8"/>
262
+                                        <rect key="frame" x="333" y="28" width="8" height="8"/>
267 263
                                         <color key="backgroundColor" red="1" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
268 264
                                         <constraints>
269 265
                                             <constraint firstAttribute="height" constant="8" id="8PB-CF-AvI"/>
@@ -276,7 +272,7 @@
276 272
                                         </userDefinedRuntimeAttributes>
277 273
                                     </view>
278 274
                                     <view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="R8U-gK-x5D">
279
-                                        <rect key="frame" x="325" y="92" width="8" height="8"/>
275
+                                        <rect key="frame" x="333" y="92" width="8" height="8"/>
280 276
                                         <color key="backgroundColor" red="1" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
281 277
                                         <constraints>
282 278
                                             <constraint firstAttribute="height" constant="8" id="pvk-DY-af3"/>
@@ -289,7 +285,7 @@
289 285
                                         </userDefinedRuntimeAttributes>
290 286
                                     </view>
291 287
                                     <view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Qi9-c7-GCt">
292
-                                        <rect key="frame" x="325" y="156" width="8" height="8"/>
288
+                                        <rect key="frame" x="333" y="156" width="8" height="8"/>
293 289
                                         <color key="backgroundColor" red="1" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
294 290
                                         <constraints>
295 291
                                             <constraint firstAttribute="height" constant="8" id="d1V-XC-UJk"/>
@@ -390,10 +386,10 @@
390 386
         </scene>
391 387
     </scenes>
392 388
     <resources>
389
+        <image name="list-arrow" width="16" height="16"/>
393 390
         <image name="message-comment" width="144" height="144"/>
394 391
         <image name="message-system" width="144" height="144"/>
395 392
         <image name="message-thumpub" width="144" height="144"/>
396
-        <image name="列表箭头" width="24" height="36"/>
397 393
         <image name="默认头像" width="240" height="240"/>
398 394
     </resources>
399 395
 </document>

+ 5 - 5
PaiAi/Paiai_iOS/App/Message/MessageCommentAndThumbupCell.swift

@@ -13,11 +13,11 @@ final class MessageCommentAndThumbupCell: UITableViewCell {
13 13
 
14 14
     // MARK: Storyboard property
15 15
     @IBOutlet weak var thumbupOrComment: UILabel!
16
-    @IBOutlet var userImage: UIImageView!
17
-    @IBOutlet var username: UILabel!
18
-    @IBOutlet var content: UILabel!
19
-    @IBOutlet var time: UILabel!
20
-    @IBOutlet var myPhoto: UIImageView!
16
+    @IBOutlet weak var userImage: UIImageView!
17
+    @IBOutlet weak var username: UILabel!
18
+    @IBOutlet weak var content: UILabel!
19
+    @IBOutlet weak var time: UILabel!
20
+    @IBOutlet weak var myPhoto: UIImageView!
21 21
 
22 22
     // MARK: view function
23 23
     override func awakeFromNib() {

+ 1 - 1
PaiAi/Paiai_iOS/App/Message/MessageListViewController.swift

@@ -20,7 +20,7 @@ protocol MessageViewControllerDelegate: class {
20 20
 
21 21
 final class MessageListViewController: UIViewController {
22 22
 
23
-    @IBOutlet var tableView: UITableView!
23
+    @IBOutlet weak var tableView: UITableView!
24 24
     
25 25
     var emptyView : UILabel = {
26 26
         let empty = UILabel(frame: CGRect(x: kScreenWidth / 2 - 50, y: 74, width: 50, height: 50))

+ 1 - 1
PaiAi/Paiai_iOS/App/Message/MessageSystemCell.swift

@@ -19,7 +19,7 @@ final class MessageSystemCell: UITableViewCell {
19 19
     
20 20
     func setInfo(_ info: MessageListItem) {
21 21
 //        userImage.setImageWithNullableURL(info.url, placeholderImage: defaultAvatar)
22
-        name.text = info.title
22
+        name.text = info.msg_title
23 23
         content.text = info.content
24 24
     }
25 25
 }

+ 3 - 3
PaiAi/Paiai_iOS/App/Message/MessageViewController.swift

@@ -12,9 +12,9 @@ import PaiaiDataKit
12 12
 
13 13
 class MessageViewController: UIViewController {
14 14
 
15
-    @IBOutlet var sysUnreadTip: UIView!
16
-    @IBOutlet var thumbupUnreadTip: UIView!
17
-    @IBOutlet var commentUnreadTip: UIView!
15
+    @IBOutlet weak var sysUnreadTip: UIView!
16
+    @IBOutlet weak var thumbupUnreadTip: UIView!
17
+    @IBOutlet weak var commentUnreadTip: UIView!
18 18
     
19 19
     @IBOutlet weak var sysBtn: UIButton!
20 20
     @IBOutlet weak var thumbupBtn: UIButton!

+ 3 - 3
PaiAi/Paiai_iOS/App/Mine/GroupCell.swift

@@ -13,9 +13,9 @@ import PaiaiUIKit
13 13
 final class GroupCell: UITableViewCell {
14 14
 
15 15
     // MARK: Storyboard property
16
-    @IBOutlet var groupImageView: UIImageView!
17
-    @IBOutlet var groupNameLabel: UILabel!
18
-    @IBOutlet var createTimeLabel: UILabel!
16
+    @IBOutlet weak var groupImageView: UIImageView!
17
+    @IBOutlet weak var groupNameLabel: UILabel!
18
+    @IBOutlet weak var createTimeLabel: UILabel!
19 19
     @IBOutlet weak var photoNumLabel: UILabel!
20 20
 
21 21
     // MARK: init interface

+ 8 - 15
PaiAi/Paiai_iOS/App/Mine/Mine.storyboard

@@ -42,7 +42,7 @@
42 42
                                                         </userDefinedRuntimeAttribute>
43 43
                                                     </userDefinedRuntimeAttributes>
44 44
                                                 </imageView>
45
-                                                <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="列表箭头" translatesAutoresizingMaskIntoConstraints="NO" id="GAH-5d-ArA">
45
+                                                <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="list-arrow" translatesAutoresizingMaskIntoConstraints="NO" id="GAH-5d-ArA">
46 46
                                                     <rect key="frame" x="339" y="12" width="24" height="36"/>
47 47
                                                 </imageView>
48 48
                                                 <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="群名称" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Bx9-cB-B0U">
@@ -127,23 +127,15 @@
127 127
                                 <fontDescription key="fontDescription" type="system" pointSize="14"/>
128 128
                                 <textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
129 129
                             </textView>
130
-                            <button opaque="NO" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="kyg-hG-JSP">
130
+                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="kyg-hG-JSP">
131 131
                                 <rect key="frame" x="0.0" y="190" width="375" height="45"/>
132 132
                                 <constraints>
133 133
                                     <constraint firstAttribute="height" constant="45" id="x3X-y4-kmZ"/>
134 134
                                 </constraints>
135 135
                                 <fontDescription key="fontDescription" type="system" pointSize="20"/>
136
-                                <state key="normal" title="发送">
136
+                                <state key="normal" title="发送" backgroundImage="BTN-send">
137 137
                                     <color key="titleColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
138 138
                                 </state>
139
-                                <userDefinedRuntimeAttributes>
140
-                                    <userDefinedRuntimeAttribute type="color" keyPath="normalStatusBackgroundColor">
141
-                                        <color key="value" red="0.98431372549999996" green="0.31372549020000001" blue="0.31372549020000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
142
-                                    </userDefinedRuntimeAttribute>
143
-                                    <userDefinedRuntimeAttribute type="color" keyPath="pressedStatusBackgroundColor">
144
-                                        <color key="value" red="0.98431372549999996" green="0.31372549020000001" blue="0.31372549020000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
145
-                                    </userDefinedRuntimeAttribute>
146
-                                </userDefinedRuntimeAttributes>
147 139
                                 <connections>
148 140
                                     <action selector="sendFeedBack" destination="iwu-HG-AWF" eventType="touchUpInside" id="hQt-ku-2nH"/>
149 141
                                 </connections>
@@ -228,7 +220,7 @@
228 220
                                                 <color key="textColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
229 221
                                                 <nil key="highlightedColor"/>
230 222
                                             </label>
231
-                                            <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="列表箭头" translatesAutoresizingMaskIntoConstraints="NO" id="MgR-ta-3jg">
223
+                                            <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="list-arrow" translatesAutoresizingMaskIntoConstraints="NO" id="MgR-ta-3jg">
232 224
                                                 <rect key="frame" x="349" y="6" width="24" height="36"/>
233 225
                                             </imageView>
234 226
                                             <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="L1g-sb-a5F">
@@ -263,7 +255,7 @@
263 255
                                                 <color key="textColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
264 256
                                                 <nil key="highlightedColor"/>
265 257
                                             </label>
266
-                                            <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="列表箭头" translatesAutoresizingMaskIntoConstraints="NO" id="vLw-Zd-Mis">
258
+                                            <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="list-arrow" translatesAutoresizingMaskIntoConstraints="NO" id="vLw-Zd-Mis">
267 259
                                                 <rect key="frame" x="349" y="6" width="24" height="36"/>
268 260
                                             </imageView>
269 261
                                             <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="FIE-jb-ZGu">
@@ -298,7 +290,7 @@
298 290
                                                 <color key="textColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
299 291
                                                 <nil key="highlightedColor"/>
300 292
                                             </label>
301
-                                            <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="列表箭头" translatesAutoresizingMaskIntoConstraints="NO" id="mhJ-Uj-tdj">
293
+                                            <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="list-arrow" translatesAutoresizingMaskIntoConstraints="NO" id="mhJ-Uj-tdj">
302 294
                                                 <rect key="frame" x="349" y="6" width="24" height="36"/>
303 295
                                             </imageView>
304 296
                                             <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="VGv-oO-leT">
@@ -623,12 +615,13 @@
623 615
         </scene>
624 616
     </scenes>
625 617
     <resources>
618
+        <image name="BTN-send" width="608" height="304"/>
626 619
         <image name="Logo" width="140" height="140"/>
627 620
         <image name="about-contactUs" width="32" height="32"/>
628 621
         <image name="about-score" width="32" height="32"/>
629 622
         <image name="about-userAgreement" width="32" height="32"/>
623
+        <image name="list-arrow" width="24" height="36"/>
630 624
         <image name="navigation-background" width="12" height="132"/>
631
-        <image name="列表箭头" width="24" height="36"/>
632 625
         <image name="注销切图" width="32" height="32"/>
633 626
         <image name="默认头像" width="240" height="240"/>
634 627
     </resources>

+ 1 - 1
PaiAi/Paiai_iOS/App/Mine/MineAboutViewController.swift

@@ -17,7 +17,7 @@ final class MineAboutViewController: UIViewController {
17 17
     @IBOutlet weak var contactUsBtn: UIButton!
18 18
     @IBOutlet weak var scoreBtn: UIButton!
19 19
     @IBOutlet weak var userAgreementBtn: UIButton!
20
-    @IBOutlet var versionLabel: UILabel!
20
+    @IBOutlet weak var versionLabel: UILabel!
21 21
     
22 22
     private var disposeBag = DisposeBag()
23 23
     

+ 0 - 1
PaiAi/Paiai_iOS/App/Mine/MineCoordinator.swift

@@ -100,7 +100,6 @@ fileprivate extension MineCoordinator {
100 100
     func makeGroupViewController(item: GroupItem) -> GroupViewController {
101 101
         let vc = GroupViewController.instantiate()
102 102
         vc.viewModel = GroupViewModel(groupItem: item)
103
-        vc.groupItem = item
104 103
         return vc
105 104
     }
106 105
 }

+ 2 - 2
PaiAi/Paiai_iOS/App/Mine/MineFeedbackViewController.swift

@@ -15,8 +15,8 @@ import PaiaiUIKit
15 15
 final class MineFeedbackViewController: UIViewController {
16 16
 
17 17
     // MARK: Storyboard property
18
-    @IBOutlet var textView: UITextView!
19
-    @IBOutlet var sendBtn: UIButton!
18
+    @IBOutlet weak var textView: UITextView!
19
+    @IBOutlet weak var sendBtn: UIButton!
20 20
     fileprivate let disposeBag = DisposeBag()
21 21
     
22 22
     var feedbackAPI: FeedbackRemoteAPI!

+ 1 - 1
PaiAi/Paiai_iOS/App/Mine/MineGroupViewController.swift

@@ -17,7 +17,7 @@ import PullToRefresh
17 17
 final class MineGroupViewController: UIViewController {
18 18
 
19 19
     // MARK: Storyboard property
20
-    @IBOutlet var tableView: UITableView!
20
+    @IBOutlet weak var tableView: UITableView!
21 21
 
22 22
     // MARK: data property
23 23
     fileprivate let disposeBag = DisposeBag()

+ 1 - 1
PaiAi/Paiai_iOS/App/Mine/MineOrderViewController.swift

@@ -17,7 +17,7 @@ import PullToRefresh
17 17
 final class MineOrderViewController: UIViewController {
18 18
 
19 19
     // MARK: Storyboard property
20
-    @IBOutlet var tableView: UITableView!
20
+    @IBOutlet weak var tableView: UITableView!
21 21
     
22 22
     // MARK: data property
23 23
     fileprivate let disposeBag = DisposeBag()

+ 4 - 4
PaiAi/Paiai_iOS/App/Mine/OrderCell.swift

@@ -13,10 +13,10 @@ import PaiaiUIKit
13 13
 final class OrderCell: UITableViewCell {
14 14
 
15 15
     // MARK: Storyboard property
16
-    @IBOutlet var photo: UIImageView!
17
-    @IBOutlet var time: UILabel!
18
-    @IBOutlet var price: UILabel!
19
-    @IBOutlet var orderNumber: UILabel!
16
+    @IBOutlet weak var photo: UIImageView!
17
+    @IBOutlet weak var time: UILabel!
18
+    @IBOutlet weak var price: UILabel!
19
+    @IBOutlet weak var orderNumber: UILabel!
20 20
 
21 21
     // MARK: init interface
22 22
     func setInfo(_ info: OrderItem) {

+ 4 - 11
PaiAi/Paiai_iOS/App/PhotoDetail/ImageCell.swift

@@ -11,26 +11,20 @@ import PaiaiDataKit
11 11
 import PaiaiUIKit
12 12
 
13 13
 final class ImageCell: UICollectionViewCell, UIScrollViewDelegate {
14
-    @IBOutlet var scrollView: UIScrollView!
14
+    @IBOutlet weak var scrollView: UIScrollView!
15 15
     var photoImage = UIImageView()
16 16
 
17 17
     func setModel(url: String) {
18
-        photoImage.frame = CGRect.init(x: 0, y: 0, width: width, height: height)
18
+        photoImage.frame = CGRect(x: 0, y: 0, width: width, height: height)
19 19
         photoImage.contentMode = .scaleAspectFit
20 20
         scrollView.contentSize = size
21 21
         scrollView.addSubview(photoImage)
22
-//        photoImage.image = UIImage.imageWithColor(UIColor.black)
23
-        if !url.isEmpty {
24
-//            photoImage.setImageWithNullableURL(url, placeholderImage: UIImage(named: "详情页占位图"))
25
-        } else {
26
-            photoImage.image =  UIImage(named: url)
27
-        }
22
+        
23
+        photoImage.setImage(url, placeholder: UIImage.photoPlaceholder)
28 24
 
29 25
         let tapGr = UITapGestureRecognizer(target: self, action: #selector(ImageCell.doubleTap(_:)))
30 26
         tapGr.numberOfTapsRequired = 2
31 27
         scrollView.addGestureRecognizer(tapGr)
32
-
33
-//        printLog(scrollView)
34 28
     }
35 29
 
36 30
     func viewForZooming(in scrollView: UIScrollView) -> UIView? {
@@ -43,7 +37,6 @@ final class ImageCell: UICollectionViewCell, UIScrollViewDelegate {
43 37
         } else {
44 38
             //Zoom to rect
45 39
             let tapPt = gr.location(in: scrollView)
46
-//            printLog(tapPt)
47 40
             var zoomRect = CGRect.zero
48 41
             zoomRect.size.width = frame.width / scrollView.maximumZoomScale
49 42
             zoomRect.size.height = frame.height / scrollView.maximumZoomScale

+ 349 - 494
PaiAi/Paiai_iOS/App/PhotoDetail/PhotoDetail.storyboard

@@ -1,445 +1,300 @@
1 1
 <?xml version="1.0" encoding="UTF-8"?>
2
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES" initialViewController="qsT-Pc-Bhh">
2
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useSafeAreas="YES" colorMatched="YES">
3
+    <device id="retina4_7" orientation="portrait">
4
+        <adaptation id="fullscreen"/>
5
+    </device>
3 6
     <dependencies>
7
+        <deployment identifier="iOS"/>
4 8
         <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/>
9
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
5 10
         <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
6 11
     </dependencies>
7 12
     <scenes>
8
-        <!--ReportController-->
9
-        <scene sceneID="wUV-hi-YAV">
10
-            <objects>
11
-                <viewController storyboardIdentifier="ReportController" automaticallyAdjustsScrollViewInsets="NO" id="DkF-mX-lIk" userLabel="ReportController" customClass="ReportController" customModule="Paiai_iOS" customModuleProvider="target" sceneMemberID="viewController">
12
-                    <layoutGuides>
13
-                        <viewControllerLayoutGuide type="top" id="4jJ-Gj-1F2"/>
14
-                        <viewControllerLayoutGuide type="bottom" id="ZhS-9D-uxH"/>
15
-                    </layoutGuides>
16
-                    <view key="view" contentMode="scaleToFill" id="VKJ-Rl-cDh">
17
-                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
18
-                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
19
-                        <subviews>
20
-                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="dKK-5n-7NI">
21
-                                <rect key="frame" x="0.0" y="467" width="375" height="200"/>
22
-                                <subviews>
23
-                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="r4F-Uk-4U9">
24
-                                        <rect key="frame" x="0.0" y="152" width="375" height="48"/>
25
-                                        <constraints>
26
-                                            <constraint firstAttribute="height" constant="48" id="PNZ-IF-PSp"/>
27
-                                        </constraints>
28
-                                        <fontDescription key="fontDescription" type="system" pointSize="20"/>
29
-                                        <state key="normal" title="取消">
30
-                                            <color key="titleColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
31
-                                        </state>
32
-                                        <connections>
33
-                                            <action selector="cancelAction:" destination="DkF-mX-lIk" eventType="touchUpInside" id="bXf-2r-PWF"/>
34
-                                        </connections>
35
-                                    </button>
36
-                                    <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="hrt-tA-trR">
37
-                                        <rect key="frame" x="0.0" y="0.0" width="375" height="152"/>
38
-                                        <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
39
-                                        <prototypes>
40
-                                            <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="reportCell" id="mem-R8-etc">
41
-                                                <rect key="frame" x="0.0" y="28" width="375" height="44"/>
42
-                                                <autoresizingMask key="autoresizingMask"/>
43
-                                                <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="mem-R8-etc" id="tpv-xM-lZp">
44
-                                                    <rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
45
-                                                    <autoresizingMask key="autoresizingMask"/>
46
-                                                </tableViewCellContentView>
47
-                                            </tableViewCell>
48
-                                        </prototypes>
49
-                                        <connections>
50
-                                            <outlet property="dataSource" destination="DkF-mX-lIk" id="NNr-XE-WFH"/>
51
-                                            <outlet property="delegate" destination="DkF-mX-lIk" id="Ar9-vT-UJr"/>
52
-                                        </connections>
53
-                                    </tableView>
54
-                                </subviews>
55
-                                <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
56
-                                <constraints>
57
-                                    <constraint firstAttribute="trailing" secondItem="r4F-Uk-4U9" secondAttribute="trailing" id="QiQ-0q-XK9"/>
58
-                                    <constraint firstAttribute="bottom" secondItem="r4F-Uk-4U9" secondAttribute="bottom" id="TWR-hL-NWZ"/>
59
-                                    <constraint firstItem="r4F-Uk-4U9" firstAttribute="leading" secondItem="dKK-5n-7NI" secondAttribute="leading" id="XkS-7p-opA"/>
60
-                                    <constraint firstItem="r4F-Uk-4U9" firstAttribute="top" secondItem="hrt-tA-trR" secondAttribute="bottom" id="haU-qN-JV2"/>
61
-                                    <constraint firstItem="hrt-tA-trR" firstAttribute="leading" secondItem="dKK-5n-7NI" secondAttribute="leading" id="loV-jI-jzd"/>
62
-                                    <constraint firstAttribute="height" constant="200" id="m2O-Lg-r1R"/>
63
-                                    <constraint firstItem="hrt-tA-trR" firstAttribute="top" secondItem="dKK-5n-7NI" secondAttribute="top" id="rtt-WL-c3Z"/>
64
-                                    <constraint firstAttribute="trailing" secondItem="hrt-tA-trR" secondAttribute="trailing" id="wVv-Cc-7Ox"/>
65
-                                </constraints>
66
-                            </view>
67
-                        </subviews>
68
-                        <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
69
-                        <constraints>
70
-                            <constraint firstItem="ZhS-9D-uxH" firstAttribute="top" secondItem="dKK-5n-7NI" secondAttribute="bottom" id="8px-JV-5Rs"/>
71
-                            <constraint firstItem="dKK-5n-7NI" firstAttribute="leading" secondItem="VKJ-Rl-cDh" secondAttribute="leading" id="Vsd-a3-Kp9"/>
72
-                            <constraint firstAttribute="trailing" secondItem="dKK-5n-7NI" secondAttribute="trailing" id="pCe-x1-6IP"/>
73
-                        </constraints>
74
-                    </view>
75
-                    <connections>
76
-                        <outlet property="contentView" destination="dKK-5n-7NI" id="I15-oP-aR3"/>
77
-                        <outlet property="reportTableView" destination="hrt-tA-trR" id="gkP-cc-Emj"/>
78
-                    </connections>
79
-                </viewController>
80
-                <placeholder placeholderIdentifier="IBFirstResponder" id="FMr-bp-t1r" userLabel="First Responder" sceneMemberID="firstResponder"/>
81
-            </objects>
82
-            <point key="canvasLocation" x="-953.60000000000002" y="1390.704647676162"/>
83
-        </scene>
84 13
         <!--PhotoDetailViewController-->
85 14
         <scene sceneID="OIh-Ut-mfb">
86 15
             <objects>
87
-                <viewController storyboardIdentifier="PhotoDetailViewController" automaticallyAdjustsScrollViewInsets="NO" id="qsT-Pc-Bhh" userLabel="PhotoDetailViewController" customClass="PhotoDetailViewController" customModule="PaiAi" sceneMemberID="viewController">
88
-                    <layoutGuides>
89
-                        <viewControllerLayoutGuide type="top" id="aUY-hC-XIK"/>
90
-                        <viewControllerLayoutGuide type="bottom" id="9RM-c8-CL6"/>
91
-                    </layoutGuides>
16
+                <viewController storyboardIdentifier="PhotoDetailViewController" automaticallyAdjustsScrollViewInsets="NO" id="qsT-Pc-Bhh" userLabel="PhotoDetailViewController" customClass="PhotoDetailViewController" customModule="Paiai_iOS" customModuleProvider="target" sceneMemberID="viewController">
92 17
                     <view key="view" contentMode="scaleToFill" id="DXj-L8-o9b">
93
-                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
18
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="1000"/>
94 19
                         <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
95 20
                         <subviews>
96
-                            <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" dataMode="prototypes" style="plain" allowsSelection="NO" rowHeight="409" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="BJb-h6-o9s">
97
-                                <rect key="frame" x="0.0" y="20" width="375" height="647"/>
98
-                                <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
99
-                                <color key="separatorColor" red="0.94117647058823528" green="0.94117647058823528" blue="0.94117647058823528" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
100
-                                <prototypes>
101
-                                    <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="headCell" rowHeight="48" id="i3U-Ls-3d4" customClass="DetailPageHeadCell" customModule="Paiai_iOS" customModuleProvider="target">
102
-                                        <rect key="frame" x="0.0" y="28" width="375" height="48"/>
103
-                                        <autoresizingMask key="autoresizingMask"/>
104
-                                        <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="i3U-Ls-3d4" id="wrX-0i-fve">
105
-                                            <rect key="frame" x="0.0" y="0.0" width="375" height="47.5"/>
106
-                                            <autoresizingMask key="autoresizingMask"/>
21
+                            <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="fD2-Ow-gtt">
22
+                                <rect key="frame" x="0.0" y="20" width="375" height="980"/>
23
+                                <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
24
+                                <view key="tableHeaderView" contentMode="scaleToFill" id="kJj-s4-SK1" userLabel="header view">
25
+                                    <rect key="frame" x="0.0" y="0.0" width="375" height="533"/>
26
+                                    <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
27
+                                    <subviews>
28
+                                        <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="rL2-VZ-0O7" userLabel="Group Info View">
29
+                                            <rect key="frame" x="0.0" y="0.0" width="375" height="48"/>
107 30
                                             <subviews>
108
-                                                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="mcz-eC-zz1">
109
-                                                    <rect key="frame" x="0.0" y="0.0" width="375" height="47.5"/>
31
+                                                <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="nng-M9-7cj">
32
+                                                    <rect key="frame" x="10" y="10" width="28" height="28"/>
33
+                                                    <constraints>
34
+                                                        <constraint firstAttribute="width" secondItem="nng-M9-7cj" secondAttribute="height" id="69G-dm-HcL"/>
35
+                                                        <constraint firstAttribute="width" constant="28" id="Pq9-h7-Ls2"/>
36
+                                                    </constraints>
37
+                                                    <userDefinedRuntimeAttributes>
38
+                                                        <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
39
+                                                            <real key="value" value="14"/>
40
+                                                        </userDefinedRuntimeAttribute>
41
+                                                        <userDefinedRuntimeAttribute type="number" keyPath="borderWidth">
42
+                                                            <real key="value" value="0.5"/>
43
+                                                        </userDefinedRuntimeAttribute>
44
+                                                        <userDefinedRuntimeAttribute type="color" keyPath="borderColor">
45
+                                                            <color key="value" white="0.66666666669999997" alpha="1" colorSpace="calibratedWhite"/>
46
+                                                        </userDefinedRuntimeAttribute>
47
+                                                    </userDefinedRuntimeAttributes>
48
+                                                </imageView>
49
+                                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="群名称" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="XM7-FX-tOk">
50
+                                                    <rect key="frame" x="48" y="15.5" width="43" height="17"/>
51
+                                                    <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
52
+                                                    <fontDescription key="fontDescription" type="system" pointSize="14"/>
53
+                                                    <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
54
+                                                    <nil key="highlightedColor"/>
55
+                                                </label>
56
+                                                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="gSr-Cm-y1W">
57
+                                                    <rect key="frame" x="300" y="0.0" width="75" height="48"/>
110 58
                                                     <subviews>
111
-                                                        <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Qun-83-nZU">
112
-                                                            <rect key="frame" x="10" y="10" width="28" height="28"/>
113
-                                                            <constraints>
114
-                                                                <constraint firstAttribute="width" secondItem="Qun-83-nZU" secondAttribute="height" id="faj-ic-igd"/>
115
-                                                                <constraint firstAttribute="width" constant="28" id="lVF-LX-TCT"/>
116
-                                                            </constraints>
117
-                                                            <userDefinedRuntimeAttributes>
118
-                                                                <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
119
-                                                                    <real key="value" value="14"/>
120
-                                                                </userDefinedRuntimeAttribute>
121
-                                                                <userDefinedRuntimeAttribute type="number" keyPath="borderWidth">
122
-                                                                    <real key="value" value="0.5"/>
123
-                                                                </userDefinedRuntimeAttribute>
124
-                                                                <userDefinedRuntimeAttribute type="color" keyPath="borderColor">
125
-                                                                    <color key="value" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
126
-                                                                </userDefinedRuntimeAttribute>
127
-                                                            </userDefinedRuntimeAttributes>
128
-                                                        </imageView>
129
-                                                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="群名称" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="PYj-cS-z4N">
130
-                                                            <rect key="frame" x="48" y="15.5" width="43" height="17"/>
59
+                                                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="进入群" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="VBE-46-KXK">
60
+                                                            <rect key="frame" x="19" y="18" width="31" height="12"/>
131 61
                                                             <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
132
-                                                            <fontDescription key="fontDescription" type="system" pointSize="14"/>
133
-                                                            <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
62
+                                                            <fontDescription key="fontDescription" type="system" pointSize="10"/>
63
+                                                            <color key="textColor" red="0.98431372549999996" green="0.31372549020000001" blue="0.31372549020000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
134 64
                                                             <nil key="highlightedColor"/>
135 65
                                                         </label>
136
-                                                        <button opaque="NO" tag="40001" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="1wY-9u-hwn">
137
-                                                            <rect key="frame" x="243" y="11" width="60" height="25"/>
138
-                                                            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
139
-                                                            <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
140
-                                                            <state key="normal" title="举报"/>
141
-                                                        </button>
142
-                                                        <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="vNs-t5-E73">
143
-                                                            <rect key="frame" x="300" y="0.0" width="75" height="47.5"/>
144
-                                                            <subviews>
145
-                                                                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="E7A-3b-Oup">
146
-                                                                    <rect key="frame" x="0.0" y="0.0" width="75" height="47.5"/>
147
-                                                                    <connections>
148
-                                                                        <action selector="enterGroup" destination="i3U-Ls-3d4" eventType="touchUpInside" id="u7N-vm-SPZ"/>
149
-                                                                    </connections>
150
-                                                                </button>
151
-                                                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="进入群" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="KGW-c8-vqa">
152
-                                                                    <rect key="frame" x="19" y="18" width="31" height="12"/>
153
-                                                                    <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
154
-                                                                    <fontDescription key="fontDescription" type="system" pointSize="10"/>
155
-                                                                    <color key="textColor" red="0.98431372549999996" green="0.31372549020000001" blue="0.31372549020000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
156
-                                                                    <nil key="highlightedColor"/>
157
-                                                                </label>
158
-                                                                <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="进入群" translatesAutoresizingMaskIntoConstraints="NO" id="dhi-7u-NYu">
159
-                                                                    <rect key="frame" x="9" y="11.5" width="60" height="25"/>
160
-                                                                    <constraints>
161
-                                                                        <constraint firstAttribute="height" constant="25" id="dTO-tb-zE5"/>
162
-                                                                        <constraint firstAttribute="width" constant="60" id="mIF-dz-pKG"/>
163
-                                                                    </constraints>
164
-                                                                </imageView>
165
-                                                            </subviews>
166
-                                                            <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
66
+                                                        <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="进入群" translatesAutoresizingMaskIntoConstraints="NO" id="70o-zv-I0l">
67
+                                                            <rect key="frame" x="9" y="11.5" width="60" height="25"/>
167 68
                                                             <constraints>
168
-                                                                <constraint firstAttribute="trailing" secondItem="KGW-c8-vqa" secondAttribute="trailing" constant="25" id="0dn-xR-eOs"/>
169
-                                                                <constraint firstAttribute="trailing" secondItem="dhi-7u-NYu" secondAttribute="trailing" constant="6" id="36c-1S-xFf"/>
170
-                                                                <constraint firstItem="E7A-3b-Oup" firstAttribute="top" secondItem="vNs-t5-E73" secondAttribute="top" id="GK3-PV-8bY"/>
171
-                                                                <constraint firstAttribute="trailing" secondItem="E7A-3b-Oup" secondAttribute="trailing" id="KSp-sT-kJX"/>
172
-                                                                <constraint firstAttribute="width" constant="75" id="TRt-Gp-4pZ"/>
173
-                                                                <constraint firstAttribute="bottom" secondItem="E7A-3b-Oup" secondAttribute="bottom" id="XJv-kC-HNg"/>
174
-                                                                <constraint firstItem="KGW-c8-vqa" firstAttribute="centerY" secondItem="vNs-t5-E73" secondAttribute="centerY" id="gzf-lb-UQR"/>
175
-                                                                <constraint firstItem="dhi-7u-NYu" firstAttribute="centerY" secondItem="vNs-t5-E73" secondAttribute="centerY" id="oVV-L7-SwG"/>
176
-                                                                <constraint firstItem="E7A-3b-Oup" firstAttribute="leading" secondItem="vNs-t5-E73" secondAttribute="leading" id="z56-tc-xs8"/>
69
+                                                                <constraint firstAttribute="height" constant="25" id="X8l-P5-3Rd"/>
70
+                                                                <constraint firstAttribute="width" constant="60" id="hNf-F0-tvW"/>
177 71
                                                             </constraints>
178
-                                                        </view>
72
+                                                        </imageView>
179 73
                                                     </subviews>
180
-                                                    <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
74
+                                                    <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
181 75
                                                     <constraints>
182
-                                                        <constraint firstItem="Qun-83-nZU" firstAttribute="leading" secondItem="mcz-eC-zz1" secondAttribute="leading" constant="10" id="4Uk-DP-TXV"/>
183
-                                                        <constraint firstItem="PYj-cS-z4N" firstAttribute="leading" secondItem="Qun-83-nZU" secondAttribute="trailing" constant="10" id="4iY-yd-PZA"/>
184
-                                                        <constraint firstAttribute="trailing" secondItem="vNs-t5-E73" secondAttribute="trailing" id="VSQ-UO-9pM"/>
185
-                                                        <constraint firstItem="vNs-t5-E73" firstAttribute="top" secondItem="mcz-eC-zz1" secondAttribute="top" id="lDC-67-VeA"/>
186
-                                                        <constraint firstAttribute="bottom" secondItem="vNs-t5-E73" secondAttribute="bottom" id="m3i-3L-tTn"/>
187
-                                                        <constraint firstItem="Qun-83-nZU" firstAttribute="centerY" secondItem="mcz-eC-zz1" secondAttribute="centerY" id="vUx-nv-jbb"/>
188
-                                                        <constraint firstItem="PYj-cS-z4N" firstAttribute="centerY" secondItem="Qun-83-nZU" secondAttribute="centerY" id="vco-EW-1A0"/>
76
+                                                        <constraint firstAttribute="width" constant="75" id="BPB-Pz-Ugv"/>
77
+                                                        <constraint firstItem="70o-zv-I0l" firstAttribute="centerY" secondItem="gSr-Cm-y1W" secondAttribute="centerY" id="ByJ-1c-nyQ"/>
78
+                                                        <constraint firstItem="VBE-46-KXK" firstAttribute="centerY" secondItem="gSr-Cm-y1W" secondAttribute="centerY" id="bzl-Og-LB3"/>
79
+                                                        <constraint firstAttribute="trailing" secondItem="70o-zv-I0l" secondAttribute="trailing" constant="6" id="cio-Sc-fn0"/>
80
+                                                        <constraint firstAttribute="trailing" secondItem="VBE-46-KXK" secondAttribute="trailing" constant="25" id="weN-fs-GNd"/>
189 81
                                                     </constraints>
82
+                                                    <connections>
83
+                                                        <outletCollection property="gestureRecognizers" destination="EHE-XX-kIE" appends="YES" id="80J-8y-IJs"/>
84
+                                                    </connections>
190 85
                                                 </view>
191 86
                                             </subviews>
87
+                                            <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
192 88
                                             <constraints>
193
-                                                <constraint firstItem="mcz-eC-zz1" firstAttribute="leading" secondItem="wrX-0i-fve" secondAttribute="leading" id="6eI-bf-ijz"/>
194
-                                                <constraint firstAttribute="trailing" secondItem="mcz-eC-zz1" secondAttribute="trailing" id="FHX-FY-vOl"/>
195
-                                                <constraint firstAttribute="bottom" secondItem="mcz-eC-zz1" secondAttribute="bottom" id="kkV-lT-uZs"/>
196
-                                                <constraint firstItem="mcz-eC-zz1" firstAttribute="top" secondItem="wrX-0i-fve" secondAttribute="top" id="tWa-Ve-Mgf"/>
89
+                                                <constraint firstAttribute="trailing" secondItem="gSr-Cm-y1W" secondAttribute="trailing" id="5tR-Qu-wtQ"/>
90
+                                                <constraint firstAttribute="bottom" secondItem="gSr-Cm-y1W" secondAttribute="bottom" id="NCO-KD-pVQ"/>
91
+                                                <constraint firstItem="XM7-FX-tOk" firstAttribute="leading" secondItem="nng-M9-7cj" secondAttribute="trailing" constant="10" id="Wig-8j-STm"/>
92
+                                                <constraint firstItem="nng-M9-7cj" firstAttribute="leading" secondItem="rL2-VZ-0O7" secondAttribute="leading" constant="10" id="ZHc-Mw-2tJ"/>
93
+                                                <constraint firstItem="gSr-Cm-y1W" firstAttribute="top" secondItem="rL2-VZ-0O7" secondAttribute="top" id="b1T-Xy-yZs"/>
94
+                                                <constraint firstItem="XM7-FX-tOk" firstAttribute="centerY" secondItem="nng-M9-7cj" secondAttribute="centerY" id="dVs-1n-mqL"/>
95
+                                                <constraint firstAttribute="height" constant="48" id="v42-oR-6qs"/>
96
+                                                <constraint firstItem="nng-M9-7cj" firstAttribute="centerY" secondItem="rL2-VZ-0O7" secondAttribute="centerY" id="zGd-66-ngX"/>
197 97
                                             </constraints>
198
-                                        </tableViewCellContentView>
199
-                                        <connections>
200
-                                            <outlet property="enterView" destination="vNs-t5-E73" id="wuj-yg-uj8"/>
201
-                                            <outlet property="groupImage" destination="Qun-83-nZU" id="vVq-cH-ZgN"/>
202
-                                            <outlet property="groupName" destination="PYj-cS-z4N" id="RdJ-r6-Jqw"/>
203
-                                        </connections>
204
-                                    </tableViewCell>
205
-                                    <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="detailPagePhotoCell" rowHeight="360" id="5X4-7Q-chL" customClass="DetailPagePhotoCell" customModule="Paiai_iOS" customModuleProvider="target">
206
-                                        <rect key="frame" x="0.0" y="76" width="375" height="360"/>
207
-                                        <autoresizingMask key="autoresizingMask"/>
208
-                                        <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="5X4-7Q-chL" id="yVY-TK-vg7">
209
-                                            <rect key="frame" x="0.0" y="0.0" width="375" height="359.5"/>
210
-                                            <autoresizingMask key="autoresizingMask"/>
211
-                                            <subviews>
212
-                                                <collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="lAD-oZ-Zgp">
213
-                                                    <rect key="frame" x="0.0" y="0.0" width="375" height="359.5"/>
214
-                                                    <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
215
-                                                    <collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="10" minimumInteritemSpacing="10" id="A4Q-fW-D0V">
216
-                                                        <size key="itemSize" width="375" height="319"/>
217
-                                                        <size key="headerReferenceSize" width="0.0" height="0.0"/>
218
-                                                        <size key="footerReferenceSize" width="0.0" height="0.0"/>
219
-                                                        <inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
220
-                                                    </collectionViewFlowLayout>
221
-                                                    <cells>
222
-                                                        <collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="photoDetailCell" id="UJ2-Hm-obI">
223
-                                                            <rect key="frame" x="0.0" y="0.0" width="375" height="319"/>
224
-                                                            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
225
-                                                            <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO">
98
+                                        </view>
99
+                                        <collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" bounces="NO" pagingEnabled="YES" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" bouncesZoom="NO" dataMode="prototypes" prefetchingEnabled="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dtf-M8-otl">
100
+                                            <rect key="frame" x="0.0" y="48" width="375" height="360"/>
101
+                                            <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
102
+                                            <constraints>
103
+                                                <constraint firstAttribute="height" constant="360" id="4L8-84-zDa"/>
104
+                                            </constraints>
105
+                                            <collectionViewFlowLayout key="collectionViewLayout" scrollDirection="horizontal" minimumLineSpacing="10" minimumInteritemSpacing="10" id="xaD-fO-dLy">
106
+                                                <size key="itemSize" width="375" height="319"/>
107
+                                                <size key="headerReferenceSize" width="0.0" height="0.0"/>
108
+                                                <size key="footerReferenceSize" width="0.0" height="0.0"/>
109
+                                                <inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
110
+                                            </collectionViewFlowLayout>
111
+                                            <cells>
112
+                                                <collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="photoDetailImageCell" id="odI-PO-hV5" customClass="PhotoDetailImageCell" customModule="Paiai_iOS" customModuleProvider="target">
113
+                                                    <rect key="frame" x="0.0" y="20.5" width="375" height="319"/>
114
+                                                    <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
115
+                                                    <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO">
116
+                                                        <rect key="frame" x="0.0" y="0.0" width="375" height="319"/>
117
+                                                        <autoresizingMask key="autoresizingMask"/>
118
+                                                        <subviews>
119
+                                                            <imageView userInteractionEnabled="NO" tag="1010" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="9v7-Ky-ukt">
226 120
                                                                 <rect key="frame" x="0.0" y="0.0" width="375" height="319"/>
227
-                                                                <autoresizingMask key="autoresizingMask"/>
228
-                                                                <subviews>
229
-                                                                    <imageView userInteractionEnabled="NO" tag="1010" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="ld5-dn-2RB">
230
-                                                                        <rect key="frame" x="0.0" y="0.0" width="375" height="319"/>
231
-                                                                    </imageView>
232
-                                                                </subviews>
233
-                                                            </view>
234
-                                                            <constraints>
235
-                                                                <constraint firstAttribute="trailing" secondItem="ld5-dn-2RB" secondAttribute="trailing" id="GLQ-xO-RUy"/>
236
-                                                                <constraint firstAttribute="bottom" secondItem="ld5-dn-2RB" secondAttribute="bottom" id="Ttz-Zt-E6n"/>
237
-                                                                <constraint firstItem="ld5-dn-2RB" firstAttribute="top" secondItem="UJ2-Hm-obI" secondAttribute="top" id="cSU-ct-Cep"/>
238
-                                                                <constraint firstItem="ld5-dn-2RB" firstAttribute="leading" secondItem="UJ2-Hm-obI" secondAttribute="leading" id="veK-zA-dfx"/>
239
-                                                            </constraints>
240
-                                                        </collectionViewCell>
241
-                                                    </cells>
121
+                                                            </imageView>
122
+                                                        </subviews>
123
+                                                    </view>
124
+                                                    <constraints>
125
+                                                        <constraint firstItem="9v7-Ky-ukt" firstAttribute="top" secondItem="odI-PO-hV5" secondAttribute="top" id="PCk-i0-Rwg"/>
126
+                                                        <constraint firstAttribute="bottom" secondItem="9v7-Ky-ukt" secondAttribute="bottom" id="R3n-1a-d5C"/>
127
+                                                        <constraint firstItem="9v7-Ky-ukt" firstAttribute="leading" secondItem="odI-PO-hV5" secondAttribute="leading" id="YBl-MG-BWY"/>
128
+                                                        <constraint firstAttribute="trailing" secondItem="9v7-Ky-ukt" secondAttribute="trailing" id="xxx-f9-RZf"/>
129
+                                                    </constraints>
242 130
                                                     <connections>
243
-                                                        <outlet property="dataSource" destination="5X4-7Q-chL" id="gmm-ci-u2l"/>
244
-                                                        <outlet property="delegate" destination="5X4-7Q-chL" id="RHM-y7-e7G"/>
131
+                                                        <outlet property="imageView" destination="9v7-Ky-ukt" id="wbu-di-9Ls"/>
245 132
                                                     </connections>
246
-                                                </collectionView>
247
-                                            </subviews>
248
-                                            <constraints>
249
-                                                <constraint firstAttribute="trailing" secondItem="lAD-oZ-Zgp" secondAttribute="trailing" id="0fy-qh-Y4r"/>
250
-                                                <constraint firstItem="lAD-oZ-Zgp" firstAttribute="leading" secondItem="yVY-TK-vg7" secondAttribute="leading" id="Fcg-rJ-o3p"/>
251
-                                                <constraint firstAttribute="bottom" secondItem="lAD-oZ-Zgp" secondAttribute="bottom" id="JiM-Rf-C9H"/>
252
-                                                <constraint firstItem="lAD-oZ-Zgp" firstAttribute="top" secondItem="yVY-TK-vg7" secondAttribute="top" id="Vdg-DU-USU"/>
253
-                                            </constraints>
254
-                                        </tableViewCellContentView>
255
-                                        <connections>
256
-                                            <outlet property="collectionView" destination="lAD-oZ-Zgp" id="DhH-TY-4el"/>
257
-                                        </connections>
258
-                                    </tableViewCell>
259
-                                    <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" reuseIdentifier="nameCell" rowHeight="47" id="4vg-z0-coW" customClass="DetailPageNameCell" customModule="Paiai_iOS" customModuleProvider="target">
260
-                                        <rect key="frame" x="0.0" y="436" width="375" height="47"/>
261
-                                        <autoresizingMask key="autoresizingMask"/>
262
-                                        <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="4vg-z0-coW" id="cco-VM-Dnu">
263
-                                            <rect key="frame" x="0.0" y="0.0" width="375" height="46.5"/>
264
-                                            <autoresizingMask key="autoresizingMask"/>
133
+                                                </collectionViewCell>
134
+                                            </cells>
135
+                                        </collectionView>
136
+                                        <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="rr6-c2-Nmb" userLabel="Photo Info View">
137
+                                            <rect key="frame" x="0.0" y="408" width="375" height="36"/>
265 138
                                             <subviews>
266
-                                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="名字" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="waZ-4s-ioU">
267
-                                                    <rect key="frame" x="41" y="17.5" width="21" height="12"/>
139
+                                                <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="defaultAvatar" translatesAutoresizingMaskIntoConstraints="NO" id="Zj6-ve-uzJ">
140
+                                                    <rect key="frame" x="15" y="8" width="20" height="20"/>
141
+                                                    <constraints>
142
+                                                        <constraint firstAttribute="width" constant="20" id="dui-uv-HdI"/>
143
+                                                        <constraint firstAttribute="width" secondItem="Zj6-ve-uzJ" secondAttribute="height" multiplier="1:1" id="mya-LW-5NJ"/>
144
+                                                    </constraints>
145
+                                                    <userDefinedRuntimeAttributes>
146
+                                                        <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
147
+                                                            <real key="value" value="4"/>
148
+                                                        </userDefinedRuntimeAttribute>
149
+                                                    </userDefinedRuntimeAttributes>
150
+                                                </imageView>
151
+                                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="名字" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="jmc-9F-Uzr">
152
+                                                    <rect key="frame" x="41" y="12" width="21" height="12"/>
268 153
                                                     <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
269 154
                                                     <fontDescription key="fontDescription" type="system" pointSize="10"/>
270 155
                                                     <color key="textColor" red="0.59999999999999998" green="0.59999999999999998" blue="0.59999999999999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
271 156
                                                     <nil key="highlightedColor"/>
272 157
                                                 </label>
273
-                                                <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="icon-记录" translatesAutoresizingMaskIntoConstraints="NO" id="jOJ-N0-rYP">
274
-                                                    <rect key="frame" x="286" y="5.5" width="36" height="36"/>
158
+                                                <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="icon-time" translatesAutoresizingMaskIntoConstraints="NO" id="xXQ-Hj-wzP">
159
+                                                    <rect key="frame" x="286" y="0.0" width="36" height="36"/>
275 160
                                                 </imageView>
276
-                                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="5分钟前" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="d9Q-3T-h1t">
277
-                                                    <rect key="frame" x="328" y="17.5" width="37" height="12"/>
161
+                                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="5分钟前" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="QpI-Mp-URP">
162
+                                                    <rect key="frame" x="332" y="12" width="37" height="12"/>
278 163
                                                     <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
279 164
                                                     <fontDescription key="fontDescription" type="system" pointSize="10"/>
280 165
                                                     <color key="textColor" red="0.59999999999999998" green="0.59999999999999998" blue="0.59999999999999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
281 166
                                                     <nil key="highlightedColor"/>
282 167
                                                 </label>
283
-                                                <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="默认头像" translatesAutoresizingMaskIntoConstraints="NO" id="ngb-KR-cA6">
284
-                                                    <rect key="frame" x="15" y="13.5" width="20" height="20"/>
285
-                                                    <constraints>
286
-                                                        <constraint firstAttribute="width" constant="20" id="cSl-og-Uae"/>
287
-                                                        <constraint firstAttribute="width" secondItem="ngb-KR-cA6" secondAttribute="height" id="fbg-yA-QyN"/>
288
-                                                    </constraints>
289
-                                                    <userDefinedRuntimeAttributes>
290
-                                                        <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
291
-                                                            <real key="value" value="4"/>
292
-                                                        </userDefinedRuntimeAttribute>
293
-                                                    </userDefinedRuntimeAttributes>
294
-                                                </imageView>
295 168
                                             </subviews>
169
+                                            <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
296 170
                                             <constraints>
297
-                                                <constraint firstItem="d9Q-3T-h1t" firstAttribute="leading" secondItem="jOJ-N0-rYP" secondAttribute="trailing" constant="6" id="DFx-VY-iOc"/>
298
-                                                <constraint firstItem="waZ-4s-ioU" firstAttribute="leading" secondItem="ngb-KR-cA6" secondAttribute="trailing" constant="6" id="Yai-Qc-Kxw"/>
299
-                                                <constraint firstItem="waZ-4s-ioU" firstAttribute="centerY" secondItem="cco-VM-Dnu" secondAttribute="centerY" id="aUY-C4-Hbb"/>
300
-                                                <constraint firstItem="ngb-KR-cA6" firstAttribute="width" secondItem="ngb-KR-cA6" secondAttribute="height" id="ery-Ch-bGL"/>
301
-                                                <constraint firstItem="jOJ-N0-rYP" firstAttribute="centerY" secondItem="waZ-4s-ioU" secondAttribute="centerY" id="m0E-Mf-ee0"/>
302
-                                                <constraint firstItem="waZ-4s-ioU" firstAttribute="centerY" secondItem="ngb-KR-cA6" secondAttribute="centerY" id="nUJ-pU-dXK"/>
303
-                                                <constraint firstItem="ngb-KR-cA6" firstAttribute="leading" secondItem="cco-VM-Dnu" secondAttribute="leading" constant="15" id="vfD-Pc-Dfp"/>
304
-                                                <constraint firstAttribute="trailing" secondItem="d9Q-3T-h1t" secondAttribute="trailing" constant="10" id="w69-ZT-64v"/>
305
-                                                <constraint firstItem="d9Q-3T-h1t" firstAttribute="centerY" secondItem="jOJ-N0-rYP" secondAttribute="centerY" id="zau-ES-xhb"/>
171
+                                                <constraint firstAttribute="trailing" secondItem="QpI-Mp-URP" secondAttribute="trailing" constant="6" id="GDW-SR-ql9"/>
172
+                                                <constraint firstItem="QpI-Mp-URP" firstAttribute="leading" secondItem="xXQ-Hj-wzP" secondAttribute="trailing" constant="10" id="NNQ-Rx-3ip"/>
173
+                                                <constraint firstItem="Zj6-ve-uzJ" firstAttribute="leading" secondItem="rr6-c2-Nmb" secondAttribute="leading" constant="15" id="NWK-xB-He1"/>
174
+                                                <constraint firstItem="jmc-9F-Uzr" firstAttribute="centerY" secondItem="rr6-c2-Nmb" secondAttribute="centerY" id="PsY-nq-8ao"/>
175
+                                                <constraint firstAttribute="height" constant="36" id="SUQ-5X-XLy"/>
176
+                                                <constraint firstItem="jmc-9F-Uzr" firstAttribute="leading" secondItem="Zj6-ve-uzJ" secondAttribute="trailing" constant="6" id="cr3-c0-hhq"/>
177
+                                                <constraint firstItem="QpI-Mp-URP" firstAttribute="centerY" secondItem="rr6-c2-Nmb" secondAttribute="centerY" id="eBQ-qI-A8r"/>
178
+                                                <constraint firstItem="Zj6-ve-uzJ" firstAttribute="centerY" secondItem="rr6-c2-Nmb" secondAttribute="centerY" id="t6b-zO-3rf"/>
179
+                                                <constraint firstItem="xXQ-Hj-wzP" firstAttribute="centerY" secondItem="rr6-c2-Nmb" secondAttribute="centerY" id="x1T-b0-thB"/>
306 180
                                             </constraints>
307
-                                        </tableViewCellContentView>
308
-                                        <connections>
309
-                                            <outlet property="personImage" destination="ngb-KR-cA6" id="UKC-bx-df1"/>
310
-                                            <outlet property="personName" destination="waZ-4s-ioU" id="7kC-Jy-joK"/>
311
-                                            <outlet property="time" destination="d9Q-3T-h1t" id="8ku-p3-pIy"/>
312
-                                        </connections>
313
-                                    </tableViewCell>
314
-                                    <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" reuseIdentifier="thumbupHeadCell" rowHeight="45" id="drb-Ur-Tep">
315
-                                        <rect key="frame" x="0.0" y="483" width="375" height="45"/>
316
-                                        <autoresizingMask key="autoresizingMask"/>
317
-                                        <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="drb-Ur-Tep" id="BWN-JQ-YDl">
318
-                                            <rect key="frame" x="0.0" y="0.0" width="375" height="44.5"/>
319
-                                            <autoresizingMask key="autoresizingMask"/>
181
+                                        </view>
182
+                                        <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="q4A-0r-b7Z" userLabel="Thumbup Head View">
183
+                                            <rect key="frame" x="0.0" y="444" width="375" height="44"/>
320 184
                                             <subviews>
321
-                                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="赞" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="G5n-Xf-7Hy">
322
-                                                    <rect key="frame" x="57" y="14" width="15" height="17"/>
185
+                                                <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="icon-thumbup" translatesAutoresizingMaskIntoConstraints="NO" id="haH-1L-wfF">
186
+                                                    <rect key="frame" x="15" y="4" width="36" height="36"/>
187
+                                                </imageView>
188
+                                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="赞" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vrx-vV-ymg">
189
+                                                    <rect key="frame" x="57" y="13.5" width="14.5" height="17"/>
323 190
                                                     <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
324 191
                                                     <fontDescription key="fontDescription" type="system" pointSize="14"/>
325 192
                                                     <color key="textColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
326 193
                                                     <nil key="highlightedColor"/>
327 194
                                                 </label>
328
-                                                <imageView userInteractionEnabled="NO" tag="1008" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="list-arrow" translatesAutoresizingMaskIntoConstraints="NO" id="pC6-7A-WSm">
329
-                                                    <rect key="frame" x="349" y="14.5" width="16" height="16"/>
330
-                                                </imageView>
331
-                                                <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="icon-赞" translatesAutoresizingMaskIntoConstraints="NO" id="jXa-8T-THD">
332
-                                                    <rect key="frame" x="15" y="4.5" width="36" height="36"/>
333
-                                                </imageView>
334
-                                                <label opaque="NO" userInteractionEnabled="NO" tag="1001" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="(0)" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="60x-jR-ECW">
335
-                                                    <rect key="frame" x="72" y="15" width="17" height="15"/>
195
+                                                <label opaque="NO" userInteractionEnabled="NO" tag="1002" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="(0)" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="h88-PP-cvG">
196
+                                                    <rect key="frame" x="77.5" y="14.5" width="17" height="15"/>
336 197
                                                     <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
337 198
                                                     <fontDescription key="fontDescription" type="system" pointSize="12"/>
338 199
                                                     <color key="textColor" red="0.59999999999999998" green="0.59999999999999998" blue="0.59999999999999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
339 200
                                                     <nil key="highlightedColor"/>
340 201
                                                 </label>
341
-                                                <button opaque="NO" tag="1011" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="LBo-30-wNB">
342
-                                                    <rect key="frame" x="0.0" y="0.0" width="375" height="44.5"/>
343
-                                                </button>
202
+                                                <imageView userInteractionEnabled="NO" tag="1008" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="list-arrow" translatesAutoresizingMaskIntoConstraints="NO" id="sg5-Nx-u2C">
203
+                                                    <rect key="frame" x="341" y="4" width="24" height="36"/>
204
+                                                </imageView>
344 205
                                             </subviews>
345
-                                            <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
206
+                                            <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
346 207
                                             <constraints>
347
-                                                <constraint firstItem="60x-jR-ECW" firstAttribute="leading" secondItem="G5n-Xf-7Hy" secondAttribute="trailing" id="9q1-CU-lc7"/>
348
-                                                <constraint firstItem="pC6-7A-WSm" firstAttribute="centerY" secondItem="BWN-JQ-YDl" secondAttribute="centerY" id="GBf-JN-0V2"/>
349
-                                                <constraint firstItem="jXa-8T-THD" firstAttribute="leading" secondItem="BWN-JQ-YDl" secondAttribute="leading" constant="15" id="GLF-x3-OKn"/>
350
-                                                <constraint firstItem="LBo-30-wNB" firstAttribute="leading" secondItem="BWN-JQ-YDl" secondAttribute="leading" id="JoW-OJ-4RR"/>
351
-                                                <constraint firstItem="60x-jR-ECW" firstAttribute="centerY" secondItem="G5n-Xf-7Hy" secondAttribute="centerY" id="KDe-lJ-k9I"/>
352
-                                                <constraint firstAttribute="trailing" secondItem="pC6-7A-WSm" secondAttribute="trailing" constant="10" id="TIX-Ap-qwt"/>
353
-                                                <constraint firstItem="G5n-Xf-7Hy" firstAttribute="centerY" secondItem="jXa-8T-THD" secondAttribute="centerY" id="aY4-8A-f3G"/>
354
-                                                <constraint firstAttribute="trailing" secondItem="LBo-30-wNB" secondAttribute="trailing" id="mO4-S9-lSy"/>
355
-                                                <constraint firstItem="jXa-8T-THD" firstAttribute="centerY" secondItem="BWN-JQ-YDl" secondAttribute="centerY" id="nGe-eB-vLt"/>
356
-                                                <constraint firstAttribute="bottom" secondItem="LBo-30-wNB" secondAttribute="bottom" id="rzW-nS-eo8"/>
357
-                                                <constraint firstItem="G5n-Xf-7Hy" firstAttribute="leading" secondItem="jXa-8T-THD" secondAttribute="trailing" constant="6" id="vMx-nk-gUB"/>
358
-                                                <constraint firstItem="LBo-30-wNB" firstAttribute="top" secondItem="BWN-JQ-YDl" secondAttribute="top" id="xOJ-uY-wdZ"/>
208
+                                                <constraint firstItem="vrx-vV-ymg" firstAttribute="centerY" secondItem="q4A-0r-b7Z" secondAttribute="centerY" id="2aB-mH-SX5"/>
209
+                                                <constraint firstAttribute="trailing" secondItem="sg5-Nx-u2C" secondAttribute="trailing" constant="10" id="Xcf-rE-5in"/>
210
+                                                <constraint firstItem="haH-1L-wfF" firstAttribute="leading" secondItem="q4A-0r-b7Z" secondAttribute="leading" constant="15" id="coB-Ko-wkQ"/>
211
+                                                <constraint firstAttribute="height" constant="44" id="iuu-F3-RtX"/>
212
+                                                <constraint firstItem="haH-1L-wfF" firstAttribute="centerY" secondItem="q4A-0r-b7Z" secondAttribute="centerY" id="j94-Un-Fr7"/>
213
+                                                <constraint firstItem="h88-PP-cvG" firstAttribute="centerY" secondItem="q4A-0r-b7Z" secondAttribute="centerY" id="miO-hD-cUC"/>
214
+                                                <constraint firstItem="h88-PP-cvG" firstAttribute="leading" secondItem="vrx-vV-ymg" secondAttribute="trailing" constant="6" id="okS-hS-zFj"/>
215
+                                                <constraint firstItem="vrx-vV-ymg" firstAttribute="leading" secondItem="haH-1L-wfF" secondAttribute="trailing" constant="6" id="rQM-ic-NS0"/>
216
+                                                <constraint firstItem="sg5-Nx-u2C" firstAttribute="centerY" secondItem="q4A-0r-b7Z" secondAttribute="centerY" id="yjO-bc-Du8"/>
359 217
                                             </constraints>
360
-                                        </tableViewCellContentView>
361
-                                    </tableViewCell>
362
-                                    <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" reuseIdentifier="thumbupCell" rowHeight="40" id="53z-YY-Hrb" customClass="DetailthumbupImagesCell" customModule="Paiai_iOS" customModuleProvider="target">
363
-                                        <rect key="frame" x="0.0" y="528" width="375" height="40"/>
364
-                                        <autoresizingMask key="autoresizingMask"/>
365
-                                        <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="53z-YY-Hrb" id="L28-C6-ile">
366
-                                            <rect key="frame" x="0.0" y="0.0" width="375" height="39.5"/>
367
-                                            <autoresizingMask key="autoresizingMask"/>
368
-                                            <subviews>
369
-                                                <scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="MCX-77-Qga">
370
-                                                    <rect key="frame" x="0.0" y="0.0" width="375" height="39.5"/>
371
-                                                </scrollView>
372
-                                            </subviews>
218
+                                        </view>
219
+                                        <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Zde-8U-5Bl" userLabel="Thumbup View">
220
+                                            <rect key="frame" x="0.0" y="488" width="375" height="1"/>
221
+                                            <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
373 222
                                             <constraints>
374
-                                                <constraint firstAttribute="trailing" secondItem="MCX-77-Qga" secondAttribute="trailing" id="2pY-hr-G4z"/>
375
-                                                <constraint firstItem="MCX-77-Qga" firstAttribute="leading" secondItem="L28-C6-ile" secondAttribute="leading" id="YJo-K5-cWs"/>
376
-                                                <constraint firstAttribute="bottom" secondItem="MCX-77-Qga" secondAttribute="bottom" id="nby-bv-mI5"/>
377
-                                                <constraint firstItem="MCX-77-Qga" firstAttribute="top" secondItem="L28-C6-ile" secondAttribute="top" id="wbM-of-emg"/>
223
+                                                <constraint firstAttribute="height" constant="1" id="fc8-6l-lEC"/>
378 224
                                             </constraints>
379
-                                        </tableViewCellContentView>
380
-                                        <connections>
381
-                                            <outlet property="imagesScrollView" destination="MCX-77-Qga" id="qdA-ux-1Op"/>
382
-                                        </connections>
383
-                                    </tableViewCell>
384
-                                    <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" reuseIdentifier="comentHeadCell" rowHeight="45" id="avB-IM-8sZ">
385
-                                        <rect key="frame" x="0.0" y="568" width="375" height="45"/>
386
-                                        <autoresizingMask key="autoresizingMask"/>
387
-                                        <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="avB-IM-8sZ" id="B2U-lF-5Lm">
388
-                                            <rect key="frame" x="0.0" y="0.0" width="375" height="44.5"/>
389
-                                            <autoresizingMask key="autoresizingMask"/>
225
+                                        </view>
226
+                                        <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="IOw-Xw-2vP" userLabel="Comment Head View">
227
+                                            <rect key="frame" x="0.0" y="489" width="375" height="44"/>
390 228
                                             <subviews>
391
-                                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="评论" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="sbI-Yk-yw3">
392
-                                                    <rect key="frame" x="57" y="14" width="29" height="17"/>
229
+                                                <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="icon-comment" translatesAutoresizingMaskIntoConstraints="NO" id="zTQ-T2-IMt">
230
+                                                    <rect key="frame" x="15" y="4" width="36" height="36"/>
231
+                                                </imageView>
232
+                                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="评论" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vLK-B7-FQc">
233
+                                                    <rect key="frame" x="57" y="13.5" width="29" height="17"/>
393 234
                                                     <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
394 235
                                                     <fontDescription key="fontDescription" type="system" pointSize="14"/>
395 236
                                                     <color key="textColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
396 237
                                                     <nil key="highlightedColor"/>
397 238
                                                 </label>
398
-                                                <imageView userInteractionEnabled="NO" tag="1009" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="list-arrow" translatesAutoresizingMaskIntoConstraints="NO" id="GOS-Xt-kBa">
399
-                                                    <rect key="frame" x="349" y="14.5" width="16" height="16"/>
400
-                                                </imageView>
401
-                                                <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="icon-评论" translatesAutoresizingMaskIntoConstraints="NO" id="7bL-8L-k4P">
402
-                                                    <rect key="frame" x="15" y="4.5" width="36" height="36"/>
403
-                                                </imageView>
404
-                                                <label opaque="NO" userInteractionEnabled="NO" tag="1002" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="(0)" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vvS-oJ-TqJ">
405
-                                                    <rect key="frame" x="86" y="15" width="17" height="15"/>
239
+                                                <label opaque="NO" userInteractionEnabled="NO" tag="1002" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="(0)" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="cuT-s1-NnA">
240
+                                                    <rect key="frame" x="92" y="14.5" width="17" height="15"/>
406 241
                                                     <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
407 242
                                                     <fontDescription key="fontDescription" type="system" pointSize="12"/>
408 243
                                                     <color key="textColor" red="0.59999999999999998" green="0.59999999999999998" blue="0.59999999999999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
409 244
                                                     <nil key="highlightedColor"/>
410 245
                                                 </label>
411
-                                                <button opaque="NO" tag="1012" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="qe6-le-PxN">
412
-                                                    <rect key="frame" x="0.0" y="0.0" width="375" height="44.5"/>
413
-                                                </button>
246
+                                                <imageView userInteractionEnabled="NO" tag="1009" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="list-arrow" translatesAutoresizingMaskIntoConstraints="NO" id="ns5-B1-ilP">
247
+                                                    <rect key="frame" x="341" y="4" width="24" height="36"/>
248
+                                                </imageView>
414 249
                                             </subviews>
415
-                                            <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
250
+                                            <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
416 251
                                             <constraints>
417
-                                                <constraint firstAttribute="trailing" secondItem="GOS-Xt-kBa" secondAttribute="trailing" constant="10" id="44I-Im-laR"/>
418
-                                                <constraint firstItem="7bL-8L-k4P" firstAttribute="centerY" secondItem="B2U-lF-5Lm" secondAttribute="centerY" id="DvT-0Q-xhb"/>
419
-                                                <constraint firstItem="sbI-Yk-yw3" firstAttribute="centerY" secondItem="7bL-8L-k4P" secondAttribute="centerY" id="Ee0-la-oHg"/>
420
-                                                <constraint firstAttribute="bottom" secondItem="qe6-le-PxN" secondAttribute="bottom" id="HHp-89-beY"/>
421
-                                                <constraint firstItem="7bL-8L-k4P" firstAttribute="leading" secondItem="B2U-lF-5Lm" secondAttribute="leading" constant="15" id="LAr-Q4-Zfc"/>
422
-                                                <constraint firstAttribute="trailing" secondItem="qe6-le-PxN" secondAttribute="trailing" id="MzB-GI-zPj"/>
423
-                                                <constraint firstItem="qe6-le-PxN" firstAttribute="top" secondItem="B2U-lF-5Lm" secondAttribute="top" id="OK4-7y-Ty6"/>
424
-                                                <constraint firstItem="sbI-Yk-yw3" firstAttribute="leading" secondItem="7bL-8L-k4P" secondAttribute="trailing" constant="6" id="VUL-1O-eDc"/>
425
-                                                <constraint firstItem="vvS-oJ-TqJ" firstAttribute="leading" secondItem="sbI-Yk-yw3" secondAttribute="trailing" id="ekQ-rb-Mq7"/>
426
-                                                <constraint firstItem="vvS-oJ-TqJ" firstAttribute="centerY" secondItem="sbI-Yk-yw3" secondAttribute="centerY" id="fed-Gs-JKg"/>
427
-                                                <constraint firstItem="GOS-Xt-kBa" firstAttribute="centerY" secondItem="B2U-lF-5Lm" secondAttribute="centerY" id="gap-OK-zCS"/>
428
-                                                <constraint firstItem="qe6-le-PxN" firstAttribute="leading" secondItem="B2U-lF-5Lm" secondAttribute="leading" id="iNa-2Q-Opq"/>
252
+                                                <constraint firstAttribute="height" constant="44" id="3Tu-zh-sgQ"/>
253
+                                                <constraint firstItem="vLK-B7-FQc" firstAttribute="leading" secondItem="zTQ-T2-IMt" secondAttribute="trailing" constant="6" id="DSt-2x-Aqs"/>
254
+                                                <constraint firstItem="zTQ-T2-IMt" firstAttribute="centerY" secondItem="IOw-Xw-2vP" secondAttribute="centerY" id="MFO-T3-4Bi"/>
255
+                                                <constraint firstItem="cuT-s1-NnA" firstAttribute="centerY" secondItem="IOw-Xw-2vP" secondAttribute="centerY" id="N92-Fg-wmf"/>
256
+                                                <constraint firstAttribute="trailing" secondItem="ns5-B1-ilP" secondAttribute="trailing" constant="10" id="Q1P-ZJ-bnb"/>
257
+                                                <constraint firstItem="vLK-B7-FQc" firstAttribute="centerY" secondItem="IOw-Xw-2vP" secondAttribute="centerY" id="fcK-Ns-e87"/>
258
+                                                <constraint firstItem="cuT-s1-NnA" firstAttribute="leading" secondItem="vLK-B7-FQc" secondAttribute="trailing" constant="6" id="kub-1K-GZD"/>
259
+                                                <constraint firstItem="zTQ-T2-IMt" firstAttribute="leading" secondItem="IOw-Xw-2vP" secondAttribute="leading" constant="15" id="qjz-mA-ueh"/>
260
+                                                <constraint firstItem="ns5-B1-ilP" firstAttribute="centerY" secondItem="IOw-Xw-2vP" secondAttribute="centerY" id="zaJ-pH-gPl"/>
429 261
                                             </constraints>
430
-                                        </tableViewCellContentView>
431
-                                    </tableViewCell>
432
-                                    <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" reuseIdentifier="comentCell" rowHeight="95" id="DCX-Km-bjG" customClass="DetailCommentCell" customModule="Paiai_iOS" customModuleProvider="target">
433
-                                        <rect key="frame" x="0.0" y="613" width="375" height="95"/>
262
+                                        </view>
263
+                                    </subviews>
264
+                                    <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
265
+                                    <constraints>
266
+                                        <constraint firstItem="rr6-c2-Nmb" firstAttribute="leading" secondItem="kJj-s4-SK1" secondAttribute="leading" id="0SQ-nj-VPb"/>
267
+                                        <constraint firstItem="dtf-M8-otl" firstAttribute="top" secondItem="rL2-VZ-0O7" secondAttribute="bottom" id="5yA-SJ-n2O"/>
268
+                                        <constraint firstAttribute="trailing" secondItem="rL2-VZ-0O7" secondAttribute="trailing" id="9Fd-m1-g5N"/>
269
+                                        <constraint firstAttribute="trailing" secondItem="IOw-Xw-2vP" secondAttribute="trailing" id="HO5-eq-e4w"/>
270
+                                        <constraint firstItem="dtf-M8-otl" firstAttribute="leading" secondItem="kJj-s4-SK1" secondAttribute="leading" id="HaR-NE-PdQ"/>
271
+                                        <constraint firstItem="q4A-0r-b7Z" firstAttribute="leading" secondItem="kJj-s4-SK1" secondAttribute="leading" id="LbP-SU-Mc1"/>
272
+                                        <constraint firstItem="IOw-Xw-2vP" firstAttribute="top" secondItem="Zde-8U-5Bl" secondAttribute="bottom" id="QU5-ve-FU8"/>
273
+                                        <constraint firstAttribute="trailing" secondItem="Zde-8U-5Bl" secondAttribute="trailing" id="QiZ-Na-0ZD"/>
274
+                                        <constraint firstItem="q4A-0r-b7Z" firstAttribute="top" secondItem="rr6-c2-Nmb" secondAttribute="bottom" id="Rdi-IX-jRd"/>
275
+                                        <constraint firstItem="rL2-VZ-0O7" firstAttribute="top" secondItem="kJj-s4-SK1" secondAttribute="top" id="ePm-7e-WNF"/>
276
+                                        <constraint firstAttribute="trailing" secondItem="dtf-M8-otl" secondAttribute="trailing" id="eRL-0a-k9R"/>
277
+                                        <constraint firstItem="IOw-Xw-2vP" firstAttribute="leading" secondItem="kJj-s4-SK1" secondAttribute="leading" id="fXq-hn-nah"/>
278
+                                        <constraint firstAttribute="bottom" secondItem="IOw-Xw-2vP" secondAttribute="bottom" id="gyd-SZ-DKX"/>
279
+                                        <constraint firstAttribute="trailing" secondItem="rr6-c2-Nmb" secondAttribute="trailing" id="jux-w6-IPv"/>
280
+                                        <constraint firstItem="rr6-c2-Nmb" firstAttribute="top" secondItem="dtf-M8-otl" secondAttribute="bottom" id="oGA-xB-bcp"/>
281
+                                        <constraint firstItem="Zde-8U-5Bl" firstAttribute="top" secondItem="q4A-0r-b7Z" secondAttribute="bottom" id="sGh-IF-x0B"/>
282
+                                        <constraint firstItem="rL2-VZ-0O7" firstAttribute="leading" secondItem="kJj-s4-SK1" secondAttribute="leading" id="w0b-Fi-mSe"/>
283
+                                        <constraint firstItem="Zde-8U-5Bl" firstAttribute="leading" secondItem="kJj-s4-SK1" secondAttribute="leading" id="wXy-sO-XoE"/>
284
+                                        <constraint firstAttribute="trailing" secondItem="q4A-0r-b7Z" secondAttribute="trailing" id="wuV-6N-4YD"/>
285
+                                    </constraints>
286
+                                </view>
287
+                                <prototypes>
288
+                                    <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" reuseIdentifier="photoDetailCommentCell" rowHeight="95" id="DCX-Km-bjG" customClass="PhotoDetailCommentCell" customModule="Paiai_iOS" customModuleProvider="target">
289
+                                        <rect key="frame" x="0.0" y="561" width="375" height="95"/>
434 290
                                         <autoresizingMask key="autoresizingMask"/>
435 291
                                         <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="DCX-Km-bjG" id="ncE-AG-MIP">
436 292
                                             <rect key="frame" x="0.0" y="0.0" width="375" height="94.5"/>
437 293
                                             <autoresizingMask key="autoresizingMask"/>
438 294
                                             <subviews>
439
-                                                <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="默认头像" translatesAutoresizingMaskIntoConstraints="NO" id="str-tM-QWN">
295
+                                                <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="defaultAvatar" translatesAutoresizingMaskIntoConstraints="NO" id="str-tM-QWN">
440 296
                                                     <rect key="frame" x="15" y="10" width="28" height="28"/>
441 297
                                                     <constraints>
442
-                                                        <constraint firstAttribute="width" secondItem="str-tM-QWN" secondAttribute="height" id="PB6-88-f0G"/>
443 298
                                                         <constraint firstAttribute="width" constant="28" id="VSg-1T-4Hy"/>
444 299
                                                     </constraints>
445 300
                                                     <userDefinedRuntimeAttributes>
@@ -455,13 +310,6 @@
455 310
                                                     <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
456 311
                                                     <nil key="highlightedColor"/>
457 312
                                                 </label>
458
-                                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="iwc-4j-TRS">
459
-                                                    <rect key="frame" x="53" y="34" width="312" height="29"/>
460
-                                                    <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
461
-                                                    <fontDescription key="fontDescription" type="system" pointSize="12"/>
462
-                                                    <color key="textColor" red="0.40000000000000002" green="0.40000000000000002" blue="0.40000000000000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
463
-                                                    <nil key="highlightedColor"/>
464
-                                                </label>
465 313
                                                 <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="0分钟前" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="MsN-4R-lt2">
466 314
                                                     <rect key="frame" x="327" y="12" width="38" height="12"/>
467 315
                                                     <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
@@ -469,17 +317,24 @@
469 317
                                                     <color key="textColor" red="0.59999999999999998" green="0.59999999999999998" blue="0.59999999999999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
470 318
                                                     <nil key="highlightedColor"/>
471 319
                                                 </label>
320
+                                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="iwc-4j-TRS">
321
+                                                    <rect key="frame" x="15" y="44" width="350" height="50.5"/>
322
+                                                    <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
323
+                                                    <fontDescription key="fontDescription" type="system" pointSize="12"/>
324
+                                                    <color key="textColor" red="0.40000000000000002" green="0.40000000000000002" blue="0.40000000000000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
325
+                                                    <nil key="highlightedColor"/>
326
+                                                </label>
472 327
                                             </subviews>
473 328
                                             <constraints>
474
-                                                <constraint firstItem="iwc-4j-TRS" firstAttribute="top" secondItem="u6l-UB-FoK" secondAttribute="bottom" constant="8" id="5LY-xA-5ML"/>
329
+                                                <constraint firstItem="iwc-4j-TRS" firstAttribute="top" secondItem="str-tM-QWN" secondAttribute="bottom" constant="6" id="2qq-j5-mKw"/>
475 330
                                                 <constraint firstItem="MsN-4R-lt2" firstAttribute="centerY" secondItem="u6l-UB-FoK" secondAttribute="centerY" id="9qq-6O-Vhb"/>
476
-                                                <constraint firstAttribute="trailing" secondItem="iwc-4j-TRS" secondAttribute="trailing" constant="10" id="TMI-os-LvC"/>
477
-                                                <constraint firstItem="str-tM-QWN" firstAttribute="width" secondItem="str-tM-QWN" secondAttribute="height" id="c0x-MP-hUB"/>
331
+                                                <constraint firstAttribute="trailing" secondItem="iwc-4j-TRS" secondAttribute="trailing" constant="10" id="VFy-cP-ts4"/>
332
+                                                <constraint firstItem="iwc-4j-TRS" firstAttribute="leading" secondItem="ncE-AG-MIP" secondAttribute="leading" constant="15" id="bmd-Ve-5aG"/>
478 333
                                                 <constraint firstItem="str-tM-QWN" firstAttribute="width" secondItem="str-tM-QWN" secondAttribute="height" id="d0r-sR-lUW"/>
479
-                                                <constraint firstItem="MsN-4R-lt2" firstAttribute="trailing" secondItem="iwc-4j-TRS" secondAttribute="trailing" id="gas-t9-dV6"/>
334
+                                                <constraint firstAttribute="bottom" secondItem="iwc-4j-TRS" secondAttribute="bottom" id="ggt-ZE-bzP"/>
480 335
                                                 <constraint firstItem="u6l-UB-FoK" firstAttribute="leading" secondItem="str-tM-QWN" secondAttribute="trailing" constant="10" id="hIT-CX-JbS"/>
481 336
                                                 <constraint firstItem="u6l-UB-FoK" firstAttribute="top" secondItem="str-tM-QWN" secondAttribute="top" id="pca-XM-Z1U"/>
482
-                                                <constraint firstItem="iwc-4j-TRS" firstAttribute="leading" secondItem="u6l-UB-FoK" secondAttribute="leading" id="rlX-um-t0a"/>
337
+                                                <constraint firstAttribute="trailing" secondItem="MsN-4R-lt2" secondAttribute="trailing" constant="10" id="rmu-5a-Rld"/>
483 338
                                                 <constraint firstItem="str-tM-QWN" firstAttribute="leading" secondItem="ncE-AG-MIP" secondAttribute="leading" constant="15" id="ucZ-2U-sZg"/>
484 339
                                                 <constraint firstItem="str-tM-QWN" firstAttribute="top" secondItem="ncE-AG-MIP" secondAttribute="top" constant="10" id="xc5-Fo-t4y"/>
485 340
                                             </constraints>
@@ -492,34 +347,42 @@
492 347
                                         </connections>
493 348
                                     </tableViewCell>
494 349
                                 </prototypes>
495
-                                <connections>
496
-                                    <outlet property="dataSource" destination="qsT-Pc-Bhh" id="Ftm-VU-vAa"/>
497
-                                    <outlet property="delegate" destination="qsT-Pc-Bhh" id="Aej-45-W3l"/>
498
-                                </connections>
499 350
                             </tableView>
500
-                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="lID-7p-0oW">
501
-                                <rect key="frame" x="-70.5" y="430" width="516" height="192"/>
351
+                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="lID-7p-0oW" userLabel="button group">
352
+                                <rect key="frame" x="46.5" y="859" width="282" height="96"/>
502 353
                                 <subviews>
503
-                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Tvg-6N-vtJ" userLabel="thumbup">
504
-                                        <rect key="frame" x="162" y="0.0" width="192" height="192"/>
354
+                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="0wu-p1-ehU" userLabel="mes">
355
+                                        <rect key="frame" x="0.0" y="13.5" width="69" height="69"/>
505 356
                                         <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
506
-                                        <state key="normal" image="Oval 491"/>
357
+                                        <constraints>
358
+                                            <constraint firstAttribute="width" constant="69" id="Kl9-19-ofx"/>
359
+                                            <constraint firstAttribute="height" constant="69" id="NE5-HG-g9l"/>
360
+                                        </constraints>
361
+                                        <state key="normal" image="BTN-comment"/>
507 362
                                         <connections>
508
-                                            <action selector="zan" destination="qsT-Pc-Bhh" eventType="touchUpInside" id="m0i-Im-CcG"/>
363
+                                            <action selector="comment" destination="qsT-Pc-Bhh" eventType="touchUpInside" id="CAa-E8-57D"/>
509 364
                                         </connections>
510 365
                                     </button>
511
-                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="0wu-p1-ehU" userLabel="mes">
512
-                                        <rect key="frame" x="0.0" y="27" width="138" height="138"/>
366
+                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Tvg-6N-vtJ" userLabel="thumbup">
367
+                                        <rect key="frame" x="93" y="0.0" width="96" height="96"/>
513 368
                                         <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
514
-                                        <state key="normal" image="BTN-评论"/>
369
+                                        <constraints>
370
+                                            <constraint firstAttribute="height" constant="96" id="pQ4-9p-4wb"/>
371
+                                            <constraint firstAttribute="width" constant="96" id="uaf-UW-oXr"/>
372
+                                        </constraints>
373
+                                        <state key="normal" image="BTN-thumbup"/>
515 374
                                         <connections>
516
-                                            <action selector="comment" destination="qsT-Pc-Bhh" eventType="touchUpInside" id="CAa-E8-57D"/>
375
+                                            <action selector="thumbup" destination="qsT-Pc-Bhh" eventType="touchUpInside" id="yGL-Ix-DyZ"/>
517 376
                                         </connections>
518 377
                                     </button>
519 378
                                     <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="BWu-RT-ZEQ" userLabel="share">
520
-                                        <rect key="frame" x="378" y="27" width="138" height="138"/>
379
+                                        <rect key="frame" x="213" y="13.5" width="69" height="69"/>
521 380
                                         <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
522
-                                        <state key="normal" image="BTN-分享"/>
381
+                                        <constraints>
382
+                                            <constraint firstAttribute="width" constant="69" id="EcS-81-GGx"/>
383
+                                            <constraint firstAttribute="height" constant="69" id="Mpf-VI-Pej"/>
384
+                                        </constraints>
385
+                                        <state key="normal" image="BTN-share"/>
523 386
                                         <connections>
524 387
                                             <action selector="share" destination="qsT-Pc-Bhh" eventType="touchUpInside" id="ai8-KT-pxy"/>
525 388
                                         </connections>
@@ -538,8 +401,8 @@
538 401
                                     <constraint firstItem="BWu-RT-ZEQ" firstAttribute="centerY" secondItem="Tvg-6N-vtJ" secondAttribute="centerY" id="yEr-l3-Y6R"/>
539 402
                                 </constraints>
540 403
                             </view>
541
-                            <view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="M7B-fx-M7f">
542
-                                <rect key="frame" x="0.0" y="611" width="375" height="56"/>
404
+                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="M7B-fx-M7f">
405
+                                <rect key="frame" x="0.0" y="1000" width="375" height="56"/>
543 406
                                 <subviews>
544 407
                                     <textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="我也说一句..." textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="1va-ae-Juh">
545 408
                                         <rect key="frame" x="12" y="10" width="285" height="36"/>
@@ -558,25 +421,15 @@
558 421
                                             </userDefinedRuntimeAttribute>
559 422
                                         </userDefinedRuntimeAttributes>
560 423
                                     </textField>
561
-                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="oef-gW-sEK">
424
+                                    <button opaque="NO" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="oef-gW-sEK">
562 425
                                         <rect key="frame" x="303" y="10" width="60" height="36"/>
426
+                                        <color key="backgroundColor" red="0.9995151162147522" green="0.16799759864807129" blue="0.24112263321876526" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
563 427
                                         <constraints>
564 428
                                             <constraint firstAttribute="width" constant="60" id="lDb-Za-vrb"/>
565 429
                                         </constraints>
566 430
                                         <state key="normal" title="发表">
567 431
                                             <color key="titleColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
568 432
                                         </state>
569
-                                        <userDefinedRuntimeAttributes>
570
-                                            <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
571
-                                                <real key="value" value="4"/>
572
-                                            </userDefinedRuntimeAttribute>
573
-                                            <userDefinedRuntimeAttribute type="color" keyPath="normalStatusBackgroundColor">
574
-                                                <color key="value" red="0.98431372549999996" green="0.31372549020000001" blue="0.31372549020000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
575
-                                            </userDefinedRuntimeAttribute>
576
-                                            <userDefinedRuntimeAttribute type="color" keyPath="pressedStatusBackgroundColor">
577
-                                                <color key="value" red="0.98431372549999996" green="0.31372549020000001" blue="0.31372549020000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
578
-                                            </userDefinedRuntimeAttribute>
579
-                                        </userDefinedRuntimeAttributes>
580 433
                                         <connections>
581 434
                                             <action selector="sendComment" destination="qsT-Pc-Bhh" eventType="touchUpInside" id="utb-Y9-GCy"/>
582 435
                                         </connections>
@@ -602,38 +455,37 @@
602 455
                                 </userDefinedRuntimeAttributes>
603 456
                             </view>
604 457
                             <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="n3n-4c-ZrJ">
605
-                                <rect key="frame" x="0.0" y="619" width="375" height="48"/>
458
+                                <rect key="frame" x="0.0" y="952" width="375" height="48"/>
606 459
                                 <subviews>
607 460
                                     <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="lCw-zE-NnH">
608 461
                                         <rect key="frame" x="0.0" y="0.0" width="375" height="48"/>
609 462
                                         <subviews>
610
-                                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="IpC-PG-FTb">
611
-                                                <rect key="frame" x="0.0" y="0.0" width="375" height="48"/>
612
-                                                <state key="normal" backgroundImage="去除水印"/>
613
-                                                <connections>
614
-                                                    <action selector="waterMarkPay:" destination="qsT-Pc-Bhh" eventType="touchUpInside" id="Jms-KW-CI9"/>
615
-                                                </connections>
616
-                                            </button>
617 463
                                             <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="购买-去水印" translatesAutoresizingMaskIntoConstraints="NO" id="pvN-rn-4Jn">
618
-                                                <rect key="frame" x="99.5" y="-24" width="96" height="96"/>
464
+                                                <rect key="frame" x="123.5" y="0.0" width="48" height="48"/>
465
+                                                <constraints>
466
+                                                    <constraint firstAttribute="height" constant="48" id="2VC-Nk-MXl"/>
467
+                                                    <constraint firstAttribute="width" constant="48" id="RbZ-dr-qL8"/>
468
+                                                </constraints>
619 469
                                             </imageView>
620 470
                                             <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="去除水印" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Xot-0B-lcT">
621
-                                                <rect key="frame" x="200.5" y="15" width="62" height="18"/>
471
+                                                <rect key="frame" x="176.5" y="15" width="62" height="18"/>
622 472
                                                 <fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
623 473
                                                 <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
624 474
                                                 <nil key="highlightedColor"/>
625 475
                                             </label>
626 476
                                         </subviews>
477
+                                        <accessibility key="accessibilityConfiguration">
478
+                                            <accessibilityTraits key="traits" image="YES"/>
479
+                                        </accessibility>
627 480
                                         <constraints>
628
-                                            <constraint firstItem="IpC-PG-FTb" firstAttribute="leading" secondItem="lCw-zE-NnH" secondAttribute="leading" id="AI8-0A-5NM"/>
629 481
                                             <constraint firstItem="pvN-rn-4Jn" firstAttribute="centerY" secondItem="lCw-zE-NnH" secondAttribute="centerY" id="IiE-3R-qra"/>
630
-                                            <constraint firstAttribute="trailing" secondItem="IpC-PG-FTb" secondAttribute="trailing" id="JXk-xw-Ijl"/>
631 482
                                             <constraint firstItem="Xot-0B-lcT" firstAttribute="leading" secondItem="pvN-rn-4Jn" secondAttribute="trailing" constant="5" id="iEp-95-h2T"/>
632 483
                                             <constraint firstItem="pvN-rn-4Jn" firstAttribute="centerX" secondItem="lCw-zE-NnH" secondAttribute="centerX" constant="-40" id="ldO-mF-P0P"/>
633
-                                            <constraint firstAttribute="bottom" secondItem="IpC-PG-FTb" secondAttribute="bottom" id="w9Z-mh-Lbw"/>
634
-                                            <constraint firstItem="IpC-PG-FTb" firstAttribute="top" secondItem="lCw-zE-NnH" secondAttribute="top" id="wZx-uT-kRL"/>
635 484
                                             <constraint firstItem="Xot-0B-lcT" firstAttribute="centerY" secondItem="pvN-rn-4Jn" secondAttribute="centerY" id="z6Y-h0-ffK"/>
636 485
                                         </constraints>
486
+                                        <connections>
487
+                                            <outletCollection property="gestureRecognizers" destination="thN-TP-atT" appends="YES" id="gKW-D0-5pd"/>
488
+                                        </connections>
637 489
                                     </view>
638 490
                                 </subviews>
639 491
                                 <constraints>
@@ -648,54 +500,64 @@
648 500
                         <color key="backgroundColor" red="0.94117647059999998" green="0.94117647059999998" blue="0.94117647059999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
649 501
                         <gestureRecognizers/>
650 502
                         <constraints>
651
-                            <constraint firstItem="9RM-c8-CL6" firstAttribute="top" secondItem="BJb-h6-o9s" secondAttribute="bottom" id="73f-0Y-3Vq"/>
652
-                            <constraint firstItem="9RM-c8-CL6" firstAttribute="top" secondItem="lID-7p-0oW" secondAttribute="bottom" constant="45" id="8XF-qX-tug"/>
653
-                            <constraint firstAttribute="trailing" secondItem="BJb-h6-o9s" secondAttribute="trailing" id="FVI-fa-ovF"/>
654
-                            <constraint firstAttribute="bottom" secondItem="M7B-fx-M7f" secondAttribute="bottom" id="HPO-e8-4k2"/>
655
-                            <constraint firstItem="BJb-h6-o9s" firstAttribute="leading" secondItem="DXj-L8-o9b" secondAttribute="leading" id="LUw-LA-s32"/>
656
-                            <constraint firstAttribute="trailing" secondItem="M7B-fx-M7f" secondAttribute="trailing" id="N1J-SK-PaC"/>
657
-                            <constraint firstItem="9RM-c8-CL6" firstAttribute="top" secondItem="n3n-4c-ZrJ" secondAttribute="bottom" id="WmR-iX-rxE"/>
658
-                            <constraint firstItem="lID-7p-0oW" firstAttribute="centerX" secondItem="DXj-L8-o9b" secondAttribute="centerX" id="X50-n0-wZy"/>
659
-                            <constraint firstItem="BJb-h6-o9s" firstAttribute="top" secondItem="aUY-hC-XIK" secondAttribute="bottom" id="YxH-gE-R4v"/>
660
-                            <constraint firstAttribute="trailing" secondItem="n3n-4c-ZrJ" secondAttribute="trailing" id="c7G-xk-PsT"/>
661
-                            <constraint firstItem="n3n-4c-ZrJ" firstAttribute="leading" secondItem="DXj-L8-o9b" secondAttribute="leading" id="cYR-vc-dTi"/>
662
-                            <constraint firstItem="M7B-fx-M7f" firstAttribute="leading" secondItem="DXj-L8-o9b" secondAttribute="leading" id="svX-zh-yc8"/>
503
+                            <constraint firstItem="fD2-Ow-gtt" firstAttribute="leading" secondItem="9gv-8b-ehH" secondAttribute="leading" id="05m-kg-yB6"/>
504
+                            <constraint firstItem="9gv-8b-ehH" firstAttribute="bottom" secondItem="lID-7p-0oW" secondAttribute="bottom" constant="45" id="8XF-qX-tug"/>
505
+                            <constraint firstAttribute="bottom" secondItem="M7B-fx-M7f" secondAttribute="bottom" constant="-56" id="HPO-e8-4k2"/>
506
+                            <constraint firstItem="fD2-Ow-gtt" firstAttribute="top" secondItem="9gv-8b-ehH" secondAttribute="top" id="IeE-A7-ZV9"/>
507
+                            <constraint firstItem="9gv-8b-ehH" firstAttribute="trailing" secondItem="M7B-fx-M7f" secondAttribute="trailing" id="N1J-SK-PaC"/>
508
+                            <constraint firstAttribute="bottom" secondItem="n3n-4c-ZrJ" secondAttribute="bottom" id="WmR-iX-rxE"/>
509
+                            <constraint firstItem="lID-7p-0oW" firstAttribute="centerX" secondItem="9gv-8b-ehH" secondAttribute="centerX" id="X50-n0-wZy"/>
510
+                            <constraint firstAttribute="bottom" secondItem="fD2-Ow-gtt" secondAttribute="bottom" id="XB5-IP-h70"/>
511
+                            <constraint firstItem="9gv-8b-ehH" firstAttribute="trailing" secondItem="n3n-4c-ZrJ" secondAttribute="trailing" id="c7G-xk-PsT"/>
512
+                            <constraint firstItem="n3n-4c-ZrJ" firstAttribute="leading" secondItem="9gv-8b-ehH" secondAttribute="leading" id="cYR-vc-dTi"/>
513
+                            <constraint firstItem="9gv-8b-ehH" firstAttribute="trailing" secondItem="fD2-Ow-gtt" secondAttribute="trailing" id="riO-3O-KZ0"/>
514
+                            <constraint firstItem="M7B-fx-M7f" firstAttribute="leading" secondItem="9gv-8b-ehH" secondAttribute="leading" id="svX-zh-yc8"/>
663 515
                         </constraints>
664
-                        <connections>
665
-                            <outletCollection property="gestureRecognizers" destination="thN-TP-atT" appends="YES" id="fm4-zR-Gbm"/>
666
-                        </connections>
516
+                        <viewLayoutGuide key="safeArea" id="9gv-8b-ehH"/>
667 517
                     </view>
518
+                    <size key="freeformSize" width="375" height="1000"/>
668 519
                     <connections>
669 520
                         <outlet property="buyView" destination="n3n-4c-ZrJ" id="SXL-nK-eIX"/>
521
+                        <outlet property="commentCount" destination="cuT-s1-NnA" id="m50-cn-Kcn"/>
522
+                        <outlet property="commentEditYConstraint" destination="HPO-e8-4k2" id="tfy-Rg-aiz"/>
523
+                        <outlet property="commentEditingView" destination="M7B-fx-M7f" id="ZRf-mw-WVD"/>
670 524
                         <outlet property="commentHeight" destination="HPO-e8-4k2" id="gpG-te-dKf"/>
525
+                        <outlet property="commentTableView" destination="fD2-Ow-gtt" id="jog-WM-p44"/>
671 526
                         <outlet property="commentTextField" destination="1va-ae-Juh" id="TpO-kE-PhT"/>
672
-                        <outlet property="commentView" destination="M7B-fx-M7f" id="c0Q-vg-7C9"/>
527
+                        <outlet property="enterGroupView" destination="gSr-Cm-y1W" id="bNT-Z4-eOG"/>
528
+                        <outlet property="groupAvatar" destination="nng-M9-7cj" id="Y42-Tc-QnV"/>
529
+                        <outlet property="groupName" destination="XM7-FX-tOk" id="ifP-h1-72j"/>
530
+                        <outlet property="photoCollectionView" destination="dtf-M8-otl" id="VlY-wa-ekc"/>
531
+                        <outlet property="photoTime" destination="QpI-Mp-URP" id="rJM-TG-fZW"/>
673 532
                         <outlet property="sendBtn" destination="oef-gW-sEK" id="3RV-uD-3q1"/>
674
-                        <outlet property="shuiyinImage" destination="pvN-rn-4Jn" id="SOk-vE-uCT"/>
675
-                        <outlet property="shuiyinLabel" destination="Xot-0B-lcT" id="eWR-Rv-Fzl"/>
676
-                        <outlet property="tableView" destination="BJb-h6-o9s" id="XS2-bS-jiH"/>
677
-                        <outlet property="thumbupView" destination="lID-7p-0oW" id="nBh-5u-Utm"/>
678
-                        <outlet property="waterMarkView" destination="lCw-zE-NnH" id="CPm-lw-FQT"/>
533
+                        <outlet property="thumbupCount" destination="h88-PP-cvG" id="hwu-eb-a1J"/>
534
+                        <outlet property="thumbupView" destination="Zde-8U-5Bl" id="ceB-lf-AhY"/>
535
+                        <outlet property="thumbupViewHeightConstraint" destination="fc8-6l-lEC" id="E8M-of-NHC"/>
536
+                        <outlet property="userAvatar" destination="Zj6-ve-uzJ" id="3Un-We-QLK"/>
537
+                        <outlet property="userName" destination="jmc-9F-Uzr" id="1Z5-wc-83i"/>
538
+                        <outlet property="waterMarkImage" destination="pvN-rn-4Jn" id="TaO-CX-590"/>
539
+                        <outlet property="waterMarkLabel" destination="Xot-0B-lcT" id="Jok-hr-Eeb"/>
540
+                        <outlet property="waterMarkView" destination="lCw-zE-NnH" id="z8i-gh-ZRG"/>
679 541
                     </connections>
680 542
                 </viewController>
681 543
                 <placeholder placeholderIdentifier="IBFirstResponder" id="MFn-pn-5Jb" userLabel="First Responder" sceneMemberID="firstResponder"/>
682
-                <tapGestureRecognizer id="thN-TP-atT">
544
+                <tapGestureRecognizer id="thN-TP-atT" userLabel="purchaseGestureRecognizer">
683 545
                     <connections>
684
-                        <action selector="ReturnKeyboard:" destination="qsT-Pc-Bhh" id="uVf-UL-SuP"/>
685
-                        <outlet property="delegate" destination="qsT-Pc-Bhh" id="vFS-jJ-MlO"/>
546
+                        <action selector="purchase:" destination="qsT-Pc-Bhh" id="qjs-Ot-XwA"/>
547
+                    </connections>
548
+                </tapGestureRecognizer>
549
+                <tapGestureRecognizer id="EHE-XX-kIE" userLabel="enterGroupGestureRecognizer">
550
+                    <connections>
551
+                        <action selector="enterGroup:" destination="qsT-Pc-Bhh" id="dZE-ok-iUM"/>
686 552
                     </connections>
687 553
                 </tapGestureRecognizer>
688 554
             </objects>
689
-            <point key="canvasLocation" x="-1927.2" y="1267.0164917541231"/>
555
+            <point key="canvasLocation" x="-1452" y="1137.9310344827586"/>
690 556
         </scene>
691 557
         <!--ShareController-->
692 558
         <scene sceneID="UTe-rv-qoO">
693 559
             <objects>
694 560
                 <viewController storyboardIdentifier="ShareController" automaticallyAdjustsScrollViewInsets="NO" id="KnW-jg-4H5" userLabel="ShareController" customClass="ShareController" customModule="Paiai_iOS" customModuleProvider="target" sceneMemberID="viewController">
695
-                    <layoutGuides>
696
-                        <viewControllerLayoutGuide type="top" id="hPQ-yS-v0d"/>
697
-                        <viewControllerLayoutGuide type="bottom" id="xwf-Eg-SuC"/>
698
-                    </layoutGuides>
699 561
                     <view key="view" contentMode="scaleToFill" id="rN5-Zb-vwm">
700 562
                         <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
701 563
                         <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
@@ -795,10 +657,11 @@
795 657
                         </subviews>
796 658
                         <gestureRecognizers/>
797 659
                         <constraints>
798
-                            <constraint firstItem="xUW-ue-yp5" firstAttribute="leading" secondItem="rN5-Zb-vwm" secondAttribute="leading" id="Afy-jR-Jec"/>
799
-                            <constraint firstItem="xwf-Eg-SuC" firstAttribute="top" secondItem="xUW-ue-yp5" secondAttribute="bottom" constant="127" id="hco-RD-Kle"/>
800
-                            <constraint firstAttribute="trailing" secondItem="xUW-ue-yp5" secondAttribute="trailing" id="lAd-Wf-RcE"/>
660
+                            <constraint firstItem="xUW-ue-yp5" firstAttribute="leading" secondItem="C6P-7J-fWs" secondAttribute="leading" id="Afy-jR-Jec"/>
661
+                            <constraint firstItem="C6P-7J-fWs" firstAttribute="bottom" secondItem="xUW-ue-yp5" secondAttribute="bottom" constant="127" id="hco-RD-Kle"/>
662
+                            <constraint firstItem="C6P-7J-fWs" firstAttribute="trailing" secondItem="xUW-ue-yp5" secondAttribute="trailing" id="lAd-Wf-RcE"/>
801 663
                         </constraints>
664
+                        <viewLayoutGuide key="safeArea" id="C6P-7J-fWs"/>
802 665
                     </view>
803 666
                     <connections>
804 667
                         <outlet property="shareView" destination="xUW-ue-yp5" id="I5g-Zk-9uW"/>
@@ -806,22 +669,18 @@
806 669
                 </viewController>
807 670
                 <placeholder placeholderIdentifier="IBFirstResponder" id="9LO-35-FRH" userLabel="First Responder" sceneMemberID="firstResponder"/>
808 671
             </objects>
809
-            <point key="canvasLocation" x="649.60000000000002" y="1430.2848575712146"/>
672
+            <point key="canvasLocation" x="-513" y="1287"/>
810 673
         </scene>
811
-        <!--ShowFullPicController-->
674
+        <!--PhotoPreviewViewController-->
812 675
         <scene sceneID="yhk-2u-fiu">
813 676
             <objects>
814
-                <viewController storyboardIdentifier="ShowFullPicController" automaticallyAdjustsScrollViewInsets="NO" id="p3y-A2-QU1" userLabel="ShowFullPicController" customClass="ShowFullPicController" customModule="PaiAi" sceneMemberID="viewController">
815
-                    <layoutGuides>
816
-                        <viewControllerLayoutGuide type="top" id="LdU-do-COB"/>
817
-                        <viewControllerLayoutGuide type="bottom" id="uHN-Ad-PoZ"/>
818
-                    </layoutGuides>
677
+                <viewController storyboardIdentifier="PhotoPreviewViewController" automaticallyAdjustsScrollViewInsets="NO" id="p3y-A2-QU1" userLabel="PhotoPreviewViewController" customClass="PhotoPreviewViewController" customModule="Paiai_iOS" customModuleProvider="target" sceneMemberID="viewController">
819 678
                     <view key="view" contentMode="scaleToFill" id="MdC-Fu-zFL">
820 679
                         <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
821 680
                         <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
822 681
                         <subviews>
823
-                            <collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" pagingEnabled="YES" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" indicatorStyle="white" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="cvI-jg-TrD">
824
-                                <rect key="frame" x="0.0" y="20" width="395" height="647"/>
682
+                            <collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" pagingEnabled="YES" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" indicatorStyle="white" dataMode="prototypes" prefetchingEnabled="NO" translatesAutoresizingMaskIntoConstraints="NO" id="cvI-jg-TrD">
683
+                                <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
825 684
                                 <collectionViewFlowLayout key="collectionViewLayout" scrollDirection="horizontal" minimumLineSpacing="0.0" minimumInteritemSpacing="0.0" id="nE7-Ce-1KB">
826 685
                                     <size key="itemSize" width="237.5" height="357"/>
827 686
                                     <size key="headerReferenceSize" width="0.0" height="0.0"/>
@@ -830,7 +689,7 @@
830 689
                                 </collectionViewFlowLayout>
831 690
                                 <cells>
832 691
                                     <collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="imageCell" id="PAU-eQ-c9k" customClass="ImageCell" customModule="Paiai_iOS" customModuleProvider="target">
833
-                                        <rect key="frame" x="0.0" y="145" width="237.5" height="357"/>
692
+                                        <rect key="frame" x="0.0" y="155" width="237.5" height="357"/>
834 693
                                         <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
835 694
                                         <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
836 695
                                             <rect key="frame" x="0.0" y="0.0" width="237.5" height="357"/>
@@ -856,35 +715,31 @@
856 715
                                         </connections>
857 716
                                     </collectionViewCell>
858 717
                                 </cells>
859
-                                <connections>
860
-                                    <outlet property="dataSource" destination="p3y-A2-QU1" id="gOQ-91-JoU"/>
861
-                                    <outlet property="delegate" destination="p3y-A2-QU1" id="Woc-UQ-V73"/>
862
-                                </connections>
863 718
                             </collectionView>
864
-                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="BRP-J0-WGF">
719
+                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="BRP-J0-WGF" userLabel="button group">
865 720
                                 <rect key="frame" x="0.0" y="623" width="375" height="44"/>
866 721
                                 <subviews>
867
-                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Q90-2h-mGx">
868
-                                        <rect key="frame" x="231" y="-26" width="96" height="96"/>
869
-                                        <state key="normal" image="大图模式-下载"/>
870
-                                        <connections>
871
-                                            <action selector="load" destination="p3y-A2-QU1" eventType="touchUpInside" id="tC2-9R-1be"/>
872
-                                        </connections>
873
-                                    </button>
874
-                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="2r6-s1-9be">
722
+                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="2r6-s1-9be" userLabel="back">
875 723
                                         <rect key="frame" x="43.5" y="-26" width="96" height="96"/>
876
-                                        <state key="normal" image="back"/>
724
+                                        <state key="normal" image="navigation-back"/>
877 725
                                         <connections>
878 726
                                             <action selector="back" destination="p3y-A2-QU1" eventType="touchUpInside" id="xKk-c3-Iub"/>
879 727
                                         </connections>
880 728
                                     </button>
881
-                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="aOC-mu-785">
729
+                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="aOC-mu-785" userLabel="rotate">
882 730
                                         <rect key="frame" x="139.5" y="-26" width="96" height="96"/>
883
-                                        <state key="normal" image="旋转"/>
731
+                                        <state key="normal" image="BTN-rotate"/>
884 732
                                         <connections>
885 733
                                             <action selector="rotateTheImage:" destination="p3y-A2-QU1" eventType="touchUpInside" id="LiB-TG-UYL"/>
886 734
                                         </connections>
887 735
                                     </button>
736
+                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Q90-2h-mGx" userLabel="download">
737
+                                        <rect key="frame" x="231" y="-26" width="96" height="96"/>
738
+                                        <state key="normal" image="BTN-download"/>
739
+                                        <connections>
740
+                                            <action selector="download:" destination="p3y-A2-QU1" eventType="touchUpInside" id="cEE-Yt-FWf"/>
741
+                                        </connections>
742
+                                    </button>
888 743
                                 </subviews>
889 744
                                 <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.5" colorSpace="custom" customColorSpace="sRGB"/>
890 745
                                 <constraints>
@@ -902,14 +757,15 @@
902 757
                         </subviews>
903 758
                         <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
904 759
                         <constraints>
905
-                            <constraint firstItem="uHN-Ad-PoZ" firstAttribute="top" secondItem="cvI-jg-TrD" secondAttribute="bottom" id="F0H-Mg-CBe"/>
906
-                            <constraint firstItem="uHN-Ad-PoZ" firstAttribute="top" secondItem="BRP-J0-WGF" secondAttribute="bottom" id="HMX-tH-TKt"/>
907
-                            <constraint firstItem="cvI-jg-TrD" firstAttribute="top" secondItem="MdC-Fu-zFL" secondAttribute="topMargin" id="bET-rg-QJH"/>
908
-                            <constraint firstItem="cvI-jg-TrD" firstAttribute="leading" secondItem="MdC-Fu-zFL" secondAttribute="leading" id="bxo-Ri-B7l"/>
909
-                            <constraint firstAttribute="trailing" secondItem="BRP-J0-WGF" secondAttribute="trailing" id="es7-Qg-qLl"/>
910
-                            <constraint firstItem="BRP-J0-WGF" firstAttribute="leading" secondItem="MdC-Fu-zFL" secondAttribute="leading" id="nES-ms-zxG"/>
911
-                            <constraint firstAttribute="trailing" secondItem="cvI-jg-TrD" secondAttribute="trailing" constant="-20" id="y4O-SP-QE3"/>
760
+                            <constraint firstAttribute="bottom" secondItem="cvI-jg-TrD" secondAttribute="bottom" id="F0H-Mg-CBe"/>
761
+                            <constraint firstItem="pxb-BY-KAJ" firstAttribute="bottom" secondItem="BRP-J0-WGF" secondAttribute="bottom" id="HMX-tH-TKt"/>
762
+                            <constraint firstItem="cvI-jg-TrD" firstAttribute="top" secondItem="MdC-Fu-zFL" secondAttribute="top" id="bET-rg-QJH"/>
763
+                            <constraint firstItem="cvI-jg-TrD" firstAttribute="leading" secondItem="pxb-BY-KAJ" secondAttribute="leading" id="bxo-Ri-B7l"/>
764
+                            <constraint firstItem="pxb-BY-KAJ" firstAttribute="trailing" secondItem="BRP-J0-WGF" secondAttribute="trailing" id="es7-Qg-qLl"/>
765
+                            <constraint firstItem="BRP-J0-WGF" firstAttribute="leading" secondItem="pxb-BY-KAJ" secondAttribute="leading" id="nES-ms-zxG"/>
766
+                            <constraint firstItem="pxb-BY-KAJ" firstAttribute="trailing" secondItem="cvI-jg-TrD" secondAttribute="trailing" id="y4O-SP-QE3"/>
912 767
                         </constraints>
768
+                        <viewLayoutGuide key="safeArea" id="pxb-BY-KAJ"/>
913 769
                     </view>
914 770
                     <connections>
915 771
                         <outlet property="collectionView" destination="cvI-jg-TrD" id="1eT-ax-Cma"/>
@@ -921,23 +777,22 @@
921 777
         </scene>
922 778
     </scenes>
923 779
     <resources>
924
-        <image name="BTN-分享" width="138" height="138"/>
925
-        <image name="BTN-评论" width="138" height="138"/>
926
-        <image name="Oval 491" width="192" height="192"/>
927
-        <image name="back" width="36" height="72"/>
928
-        <image name="icon-记录" width="36" height="36"/>
929
-        <image name="icon-评论" width="36" height="36"/>
930
-        <image name="icon-赞" width="36" height="36"/>
931
-        <image name="list-arrow" width="16" height="16"/>
780
+        <image name="BTN-comment" width="138" height="138"/>
781
+        <image name="BTN-download" width="96" height="96"/>
782
+        <image name="BTN-rotate" width="96" height="96"/>
783
+        <image name="BTN-share" width="138" height="138"/>
784
+        <image name="BTN-thumbup" width="192" height="192"/>
785
+        <image name="defaultAvatar" width="240" height="240"/>
786
+        <image name="icon-comment" width="36" height="36"/>
787
+        <image name="icon-thumbup" width="36" height="36"/>
788
+        <image name="icon-time" width="36" height="36"/>
789
+        <image name="list-arrow" width="24" height="36"/>
790
+        <image name="navigation-back" width="36" height="72"/>
932 791
         <image name="分享-QQ" width="162" height="162"/>
933 792
         <image name="分享-微信好友" width="162" height="162"/>
934 793
         <image name="分享-微博" width="162" height="162"/>
935 794
         <image name="分享-朋友圈" width="162" height="162"/>
936
-        <image name="去除水印" width="8" height="144"/>
937
-        <image name="大图模式-下载" width="96" height="96"/>
938
-        <image name="旋转" width="96" height="96"/>
939 795
         <image name="购买-去水印" width="96" height="96"/>
940 796
         <image name="进入群" width="114" height="60"/>
941
-        <image name="默认头像" width="80" height="80"/>
942 797
     </resources>
943 798
 </document>

+ 2 - 2
PaiAi/Paiai_iOS/App/PhotoDetail/PhotoDetailCommentCell.swift

@@ -1,5 +1,5 @@
1 1
 //
2
-//  DetailCommentCell.swift
2
+//  PhotoDetailCommentCell.swift
3 3
 //  PaiAi
4 4
 //
5 5
 //  Created by zhengjianfei on 16/4/8.
@@ -10,7 +10,7 @@ import UIKit
10 10
 import PaiaiDataKit
11 11
 import PaiaiUIKit
12 12
 
13
-class DetailCommentCell: UITableViewCell {
13
+class PhotoDetailCommentCell: UITableViewCell {
14 14
 
15 15
     // MARK: Storyboard property
16 16
     @IBOutlet weak var headImage: UIImageView!

+ 49 - 1
PaiAi/Paiai_iOS/App/PhotoDetail/PhotoDetailCoordinator.swift

@@ -7,9 +7,57 @@
7 7
 //
8 8
 
9 9
 import Foundation
10
+import PaiaiDataKit
11
+
12
+final class PhotoDetailCoordinator: Coordinator {
13
+    let navigationController: UINavigationController
14
+    let photoDetailViewController: PhotoDetailViewController
15
+    let shareListViewModel: PhotoDetailListViewModel
16
+    
17
+    fileprivate var coordinators = [String: Coordinator]()
18
+    
19
+    init(_ photoDetailVC: PhotoDetailViewController,
20
+         nav: UINavigationController,
21
+         viewModel: PhotoDetailViewModel,
22
+         listViewModel: PhotoDetailListViewModel) {
23
+        photoDetailViewController = photoDetailVC
24
+        shareListViewModel = listViewModel
25
+        navigationController = nav
26
+        photoDetailViewController.listViewModel = shareListViewModel
27
+        photoDetailViewController.viewModel = viewModel
28
+        
29
+        viewModel.delegate = self
30
+        shareListViewModel.synchronization = viewModel
31
+        shareListViewModel.delegate = self
32
+    }
33
+    
34
+    func start() {
35
+        
36
+    }
37
+}
38
+
39
+extension PhotoDetailCoordinator: PhotoDetailViewModelDelegate {
40
+    func navigateToGroup(_ item: GroupItem) {
41
+        let vc = GroupViewController.instantiate()
42
+        vc.viewModel = GroupViewModel(groupItem: item)
43
+        let coordinator = GroupCoordinator(vc,
44
+                                           navigationController: navigationController)
45
+        coordinators["group"] = coordinator
46
+        
47
+        navigationController.pushViewController(vc)
48
+    }
49
+}
50
+
51
+extension PhotoDetailCoordinator: PhotoDetailListViewModelDelegate {
52
+    func didSelected() {
53
+        let vc = UIStoryboard.photoDetail.instantiateController(PhotoPreviewViewController.self)
54
+        vc.viewModel = shareListViewModel
55
+        navigationController.pushViewController(vc, animated: true)
56
+    }
57
+}
10 58
 
11 59
 extension UIStoryboard {
12 60
     static var photoDetail: UIStoryboard {
13
-        return UIStoryboard(name: "Detail", bundle: Bundle(identifier: "com.Paiai-iOS"))
61
+        return UIStoryboard(name: "PhotoDetail", bundle: Bundle(identifier: "com.Paiai-iOS"))
14 62
     }
15 63
 }

+ 1 - 1
PaiAi/Paiai_iOS/App/PhotoDetail/PhotoDetailImageCell.swift

@@ -9,5 +9,5 @@
9 9
 import UIKit
10 10
 
11 11
 class PhotoDetailImageCell: UICollectionViewCell {
12
-    
12
+    @IBOutlet weak var imageView: UIImageView!
13 13
 }

+ 265 - 339
PaiAi/Paiai_iOS/App/PhotoDetail/PhotoDetailViewController.swift

@@ -9,6 +9,7 @@
9 9
 import UIKit
10 10
 import RxSwift
11 11
 import RxCocoa
12
+import RxDataSources
12 13
 import PaiaiDataKit
13 14
 import PaiaiUIKit
14 15
 
@@ -17,6 +18,7 @@ let kPhotographerMark = 1
17 18
 
18 19
 final class PhotoDetailViewController: UIViewController {
19 20
 
21
+    @IBOutlet weak var enterGroupView: UIView!
20 22
     @IBOutlet weak var groupAvatar: UIImageView!
21 23
     @IBOutlet weak var groupName: UILabel!
22 24
     
@@ -30,409 +32,333 @@ final class PhotoDetailViewController: UIViewController {
30 32
     @IBOutlet weak var thumbupView: UIView!
31 33
     
32 34
     @IBOutlet weak var commentCount: UILabel!
33
-    @IBOutlet weak var tableView: UITableView!
35
+    @IBOutlet weak var commentTableView: UITableView!
34 36
     
35
-    @IBOutlet weak var commentView: UIView!
37
+    @IBOutlet weak var commentEditingView: UIView!
36 38
     @IBOutlet weak var commentHeight: NSLayoutConstraint!
37 39
     @IBOutlet weak var commentTextField: UITextField!
38 40
     @IBOutlet weak var sendBtn: UIButton!
39 41
     
40 42
     @IBOutlet weak var buyView: UIView!
43
+    @IBOutlet weak var waterMarkView: UIView!
41 44
     @IBOutlet weak var waterMarkImage: UIImageView!
42 45
     @IBOutlet weak var waterMarkLabel: UILabel!
43
-
46
+    
47
+    @IBOutlet weak var thumbupViewHeightConstraint: NSLayoutConstraint!
48
+    @IBOutlet weak var commentEditYConstraint: NSLayoutConstraint!
49
+    
44 50
     // MARK: data property
45 51
     var viewModel: PhotoDetailViewModel!
46
-    lazy var datas = [PhotoItem]()
47
-    lazy var currentPhotoIndex = 0
48
-    var isHiddenEnterView = false
52
+    var listViewModel: PhotoDetailListViewModel!
53
+    
49 54
     let disposeBag = DisposeBag()
50
-    static let storyboardCtl = UIStoryboard.photoDetail.instantiateInitialViewController() as! PhotoDetailViewController
51 55
 
52 56
     // MARK: view function
53 57
     override func viewDidLoad() {
54 58
         super.viewDidLoad()
55
-//        detailPageViewModel.tipDelegate = self
56
-//        tableView.tableFooterView = UIView()
57
-        configureNotification()
58
-        
59
-        commentTextField.rx.text
60
-                        .map {!($0?.isEmpty)!}
61
-                        .bind(to: sendBtn.rx.isEnabled)
62
-                        .disposed(by: disposeBag)
59
+        binding()
60
+        setup()
61
+    }
62
+    
63
+    func setup() {
64
+        setupCommentTextField()
65
+        setupWaterMarkView()
63 66
     }
64 67
     
65 68
     func setupCommentTextField() {
66 69
         commentTextField.addLeftPadding(7)
67 70
     }
71
+    
72
+    func setupWaterMarkView() {
73
+        guard let image = UIImage.PhotoDetail.purchaseBackground else { return }
74
+        waterMarkView.backgroundColor = UIColor(patternImage: image)
75
+    }
68 76
 
69 77
     override func viewWillAppear(_ animated: Bool) {
70 78
         super.viewWillAppear(true)
71
-//        titleWithbackBar = "详情"
72
-        navigationController?.isNavigationBarHidden = false
73
-//        refreshUI(index: currentPhotoIndex)
74
-    }
75
-
76
-//    override func backToController() {
77
-//        navigationController?.popViewController(animated: true)
78
-//        if let last = navigationController?.viewControllers[(navigationController?.viewControllers.count)! - 1] as? HomeViewController {
79
-////            last.mainViewModel.models.value = datas
80
-//        }
81
-//
82
-//        if let last = navigationController?.viewControllers[(navigationController?.viewControllers.count)! - 1] as? GroupViewController {
83
-////            last.MineGroupViewModel.models.value = datas
84
-//        }
85
-//    }
86
-
87
-    func configureNotification() {
88
-        do {
89
-//            NotificationCenter.default.rx.notification(Notification.Name(rawValue: WXPayDidFinishNotification)).asObservable().subscribe { (notification) in
90
-//                FFToastView.showLoadingToast(inView: UIApplication.shared.keyWindow!, blockSuperView: true)
91
-//                self.detailPageViewModel.handleResult(errorCode: 0, success: {[weak self](PhotoItem) in
92
-//                    if let weakself = self {
93
-//                        weakself.datas[weakself.currentPhotoIndex].murl = PhotoItem.murl
94
-//                        weakself.datas[weakself.currentPhotoIndex].rurl = PhotoItem.rurl
95
-////                        weakself.showBuyView()
96
-//                        weakself.tableView.reloadRows(at: [IndexPath(item: 0, section: 1)], with: .none)
97
-//                        let fullPicCtl = UIStoryboard.detailBoard.instantiateController(ShowFullPicController.self)
98
-//                        fullPicCtl.datas = weakself.datas
99
-//                        fullPicCtl.currentPhotoIndex = weakself.currentPhotoIndex
100
-//                        fullPicCtl.showNomark = weakself.detailPageViewModel.watermarkPrice != -1
101
-//                        fullPicCtl.showHD = weakself.detailPageViewModel.hdPrice != -1
102
-//                        weakself.navigationController?.pushViewController(fullPicCtl, animated: true)
103
-//                   }
104
-//                })
105
-//                }.disposed(by: disposeBag)
106
-        }
107
-        do {
108
-//            NotificationCenter.default.rx.notification(Notification.Name.UIKeyboardWillShow)
109
-//                .asObservable()
110
-//                .subscribe({ (notification) in
111
-//                    guard let info = notification.element?.userInfo, let avalue = info[UIKeyboardFrameEndUserInfoKey] else {
112
-//                        return
113
-//                    }
114
-//
115
-//                    let height = (avalue as AnyObject).cgRectValue.size.height
116
-//                    self.returnKeyboarAction(notification.element!, height: height)
117
-//                }).disposed(by: disposeBag)
118
-        }
119
-
120
-        do {
121
-            NotificationCenter.default.rx.notification(UIResponder.keyboardWillHideNotification)
122
-                .asObservable()
123
-                .subscribe({ (notification) in
124
-                    self.returnKeyboarAction(notification.element!, height: 0)
125
-                }).disposed(by: disposeBag)
126
-        }
127
-
79
+        viewModel.viewWillAppear.accept(())
128 80
     }
81
+}
129 82
 
130
-    // MARK: refresh interface
131
-    func refreshUI(index: Int) {
132
-        currentPhotoIndex = index
133
-//        detailPageViewModel.currentPhoto = datas[index]
134
-
135
-//        detailPageViewModel.fetchThumbup(success: {[weak self] in
136
-//            if let weakself = self {
137
-//                var model = weakself.datas[index]
138
-//                model.thumbup_num =  weakself.detailPageViewModel.thumbups.count
139
-//                weakself.datas[index] = model
140
-////                PhotoLocalStorage.instance.updateLocalData(PhotoItem: model)
141
-//                weakself.reloadSection(inter: 3)
142
-//            }
143
-//        })
144
-//        detailPageViewModel.fetchComment(success: {[weak self] in
145
-//            if let weakself = self {
146
-//
147
-//                var model = weakself.datas[index]
148
-//                model.comment_num =  weakself.detailPageViewModel.comments.count
149
-//                weakself.datas[index] = model
150
-////                PhotoLocalStorage.instance.updateLocalData(PhotoItem: model)
151
-//                weakself.reloadSection(inter: 4)
152
-//            }
153
-//        })
83
+//MARK textField delegate
84
+extension PhotoDetailViewController: UIGestureRecognizerDelegate {
85
+    // MARK: textField
154 86
 
155
-//        reloadSection(inter: 0)
156
-//        reloadSection(inter: 2)
157
-        showBuyView()
87
+    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
88
+        return commentTextField.isFirstResponder
158 89
     }
90
+    
91
+}
159 92
 
160
-    func showBuyView() {
161
-//        if detailPageViewModel.currentPhoto.photo_from == kPhotographerMark && detailPageViewModel.currentPhoto.display_payment_btn == 1 {
162
-//            buyView.isHidden = false
163
-//            detailPageViewModel.hdPrice = -0.01
164
-//            detailPageViewModel.watermarkPrice = -0.01
165
-//            shuiyinLabel.text = !detailPageViewModel.currentPhoto.murl.isEmpty ? "查看无水印图" : "去除水印"
166
-//            shuiyinImage.isHidden = false
167
-//        } else {
168
-//            buyView.isHidden = true
169
-//        }
170
-    }
171 93
 
172
-    // MARK: Storyboard  button function
173
-    @IBAction func HDPay(_ sender: UIButton) {
174
-//        detailPageViewModel.getHD (getPriceSuccess: { [weak self] (isExist) in
175
-//            if let weakself = self {
176
-//                if isExist {
177
-//                    let fullPicCtl = UIStoryboard(name: "Detail", bundle: nil).instantiateController(ShowFullPicController.self)
178
-//                    fullPicCtl.datas = [weakself.datas[weakself.currentPhotoIndex]]
179
-//                    fullPicCtl.showNomark = true
180
-//                    fullPicCtl.currentPhotoIndex = weakself.currentPhotoIndex
181
-//                    weakself.navigationController?.pushViewController(fullPicCtl, animated: true)
182
-//                } else {
183
-//                }
184
-//            }
185
-//        })
94
+/// bind storyboard gesture action
95
+extension PhotoDetailViewController {
96
+    @IBAction func purchase(_ sender: UITapGestureRecognizer) {
97
+        
186 98
     }
187
-
188
-    @IBAction func waterMarkPay(_ sender: UIButton) {
189
-
190
-//        detailPageViewModel.getWatermark (getPriceSuccess: { [weak self] (isExist) in
191
-//            if let weakself = self {
192
-//                if isExist {
193
-//                    let fullPicCtl = UIStoryboard(name: "Detail", bundle: nil).instantiateController(ShowFullPicController.self)
194
-//                    fullPicCtl.datas = weakself.datas
195
-//                    fullPicCtl.showNomark = true
196
-//                    fullPicCtl.currentPhotoIndex = weakself.currentPhotoIndex
197
-//                    weakself.navigationController?.pushViewController(fullPicCtl, animated: true)
198
-//                } else {
199
-//                    weakself.shuiyinImage.isHidden = true
200
-//                    weakself.shuiyinLabel.text = "¥\((weakself.detailPageViewModel.watermarkPrice/100))"
201
-//                }
202
-//            }
203
-//        })
99
+    
100
+    @IBAction func enterGroup(_ sender: UITapGestureRecognizer) {
101
+        self.viewModel.navigateToGroup()
204 102
     }
103
+}
205 104
 
105
+/// bind storyboard button action
106
+extension PhotoDetailViewController {
206 107
     @IBAction func share() {
207 108
         let ctl = UIStoryboard.photoDetail.instantiateController(ShareController.self)
208 109
         ctl.shareContent = "我使用拍爱分享了一张美图,你也快来试试吧"
209
-        ctl.shareImgUrlThumb = datas[currentPhotoIndex].photo_thumbnail_url
210
-        ctl.shareUrl = datas[currentPhotoIndex].photo_share_url
110
+        //        ctl.shareImgUrlThumb = datas[currentPhotoIndex].photo_thumbnail_url
111
+        //        ctl.shareUrl = datas[currentPhotoIndex].photo_share_url
211 112
         presentController(ctl)
212 113
     }
213
-
114
+    
214 115
     @IBAction func comment() {
215
-        commentView.isHidden = false
216 116
         commentTextField.becomeFirstResponder()
117
+        
217 118
     }
218
-
119
+    
219 120
     @IBAction func sendComment() {
220
-        guard let text = commentTextField.text else {
221
-            return
222
-        }
223
-//        detailPageViewModel.sendComment(content: text, success: { [weak self] in
224
-//            if let weakself = self {
225
-//                weakself.commentTextField.text = ""
226
-//                weakself.commentTextField.resignFirstResponder()
227
-//                weakself.commentView.isHidden = true
228
-//                weakself.refreshUI(index: weakself.currentPhotoIndex)
229
-//            }
230
-//        })
121
+        guard let text = commentTextField.text else { return }
122
+        
123
+        viewModel.submitComment(text: text)
124
+        commentTextField.resignFirstResponder()
231 125
     }
232
-
126
+    
233 127
     @IBAction func thumbup() {
234
-//        detailPageViewModel.sendThumbup(success: {[weak self]  in
235
-//            if let weakself = self {
236
-//                weakself.refreshUI(index: weakself.currentPhotoIndex)
237
-//            }
238
-//
239
-//        })
128
+        viewModel.submitThumbup()
240 129
     }
130
+}
241 131
 
242
-    // MARK: custom function
243
-    func reloadSection(inter: Int) {
244
-        tableView.beginUpdates()
245
-        let indexSet = IndexSet(integer: inter)
246
-        tableView.reloadSections(indexSet, with: .none)
247
-        tableView.endUpdates()
132
+/// bind rx
133
+extension PhotoDetailViewController {
134
+    
135
+    var commentTableViewDataSource: RxTableViewSectionedAnimatedDataSource<AnimatableSectionModel<Int, PhotoCommentItem>> {
136
+        return RxTableViewSectionedAnimatedDataSource<AnimatableSectionModel<Int, PhotoCommentItem>>(configureCell: { (dataSource, tableView, indexPath, item) in
137
+            let cell = tableView.dequeueReusableCell(withIdentifier: "photoDetailCommentCell", for: indexPath) as! PhotoDetailCommentCell
138
+            cell.setInfo(item)
139
+            return cell
140
+        })
248 141
     }
249
-
250
-    @objc func showThumps() {
251
-//        if detailPageViewModel.thumbupsCount > 0 {
252
-//            detailPageViewModel.thumbupsCount = 0
253
-//        } else {
254
-//            detailPageViewModel.thumbupsCount = detailPageViewModel.thumbups.count
255
-//        }
256
-        reloadSection(inter: 3)
257
-    }
258
-    @objc func showComments() {
259
-//        if detailPageViewModel.commentsCount > 0 {
260
-//            detailPageViewModel.commentsCount = 0
261
-//        } else {
262
-//            detailPageViewModel.commentsCount = detailPageViewModel.comments.count
263
-//        }
264
-        reloadSection(inter: 4)
142
+    
143
+    var photoCollectionViewDataSource: RxCollectionViewSectionedAnimatedDataSource<AnimatableSectionModel<Int, PhotoItem>> {
144
+        return RxCollectionViewSectionedAnimatedDataSource<AnimatableSectionModel<Int, PhotoItem>>(configureCell: { (dataSource, collectionView, indexPath, item) in
145
+            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "photoDetailImageCell", for: indexPath) as! PhotoDetailImageCell
146
+            cell.imageView.setImage(item.photo_thumbnail2_url, placeholder: UIImage.photoPlaceholder)
147
+            return cell
148
+        })
265 149
     }
266
-
267
-    @objc func loadReportController() {
268
-//        let ctl = UIStoryboard.photoDetail.instantiateController(ReportController.self)
269
-//        presentController(ctl)
150
+    
151
+    func binding() {
152
+        bindEnterGroupViewHiddenState()
153
+        bindViewModelToGroupName()
154
+        bindViewModelToGroupAvatar()
155
+        
156
+        bindViewModelToUserName()
157
+        bindgingViewModelToUserAvatar()
158
+        
159
+        bindViewModelToThumbupCount()
160
+        bindViewModelToThumbupView()
161
+        
162
+        bindViewModelToCommentCount()
163
+        bindViewModelToCommentTableView()
164
+        
165
+        bindBuyViewIsVisiable()
166
+        
167
+        bindCommentTextFieldToSendBtn()
168
+        
169
+        bindCollectionViewDelegate()
170
+        bindCollectionViewSelected()
171
+        bindCollectionViewToListViewModel()
172
+        bindListViewModelToCollectionView()
173
+        
174
+        bindViewWillAppear()
175
+        
176
+        monitorKeyboardWillShow()
177
+        monitorKeyboardWillHide()
270 178
     }
271
-
272
-    func returnKeyboarAction(_ notification: Notification, height: CGFloat) {
273
-        guard let info = (notification as NSNotification).userInfo, let duration = info[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval else {
274
-            return
275
-        }
276
-
277
-        UIView.animate(withDuration: duration, animations: {() -> Void in
278
-                self.commentHeight.constant = height
279
-                self.commentView.superview!.layoutIfNeeded()
280
-        })
179
+    
180
+    func bindEnterGroupViewHiddenState() {
181
+        viewModel.isHiddenEnterGroupBtn.bind(to: enterGroupView.rx.isHidden).disposed(by: disposeBag)
281 182
     }
282
-
283
-    // MARK: deinit
284
-    deinit {
285
-        NotificationCenter.default.removeObserver(self)
183
+    
184
+    func bindViewModelToGroupName() {
185
+        viewModel.groupName.bind(to: groupName.rx.text).disposed(by: disposeBag)
286 186
     }
287
-
288
-}
289
-
290
-// MARK: custom delegate function
291
-extension PhotoDetailViewController: CellDelegate {
292
-    func selectIndex(indexpath: IndexPath) {
293
-        let ctl = UIStoryboard.photoDetail.instantiateController(ShowFullPicController.self)
294
-        ctl.datas = datas
295
-        ctl.currentPhotoIndex = currentPhotoIndex
296
-        show(ctl, sender: nil)
187
+    
188
+    func bindViewModelToGroupAvatar() {
189
+        viewModel.groupAvatar
190
+            .subscribe(onNext: {[weak self] (avatar) in
191
+            guard let `self` = self else { return }
192
+            self.groupAvatar.setImage(avatar)
193
+        }).disposed(by: disposeBag)
297 194
     }
298
-
299
-    func returnCurrentIndex(index: Int) {
300
-        refreshUI(index: index)
195
+    
196
+    func bindgingViewModelToUserAvatar() {
197
+        viewModel.userAvatar
198
+            .subscribe(onNext: {[weak self] (avatar) in
199
+                guard let `self` = self else { return }
200
+                self.userAvatar.setImage(avatar)
201
+            }).disposed(by: disposeBag)
301 202
     }
302
-
303
-    func pushNext() {
304
-        let ctl = UIStoryboard.main.instantiateController(GroupViewController.self)
305
-
306
-//        ctl.groupModel = GroupModel(map: Map(mappingType: .fromJSON, JSON: datas[currentPhotoIndex].toJSON()))
307
-        show(ctl, sender: nil)
203
+    
204
+    func bindViewModelToUserName() {
205
+        viewModel.groupName.bind(to: userName.rx.text).disposed(by: disposeBag)
308 206
     }
309
-}
310
-
311
-//MARK textField delegate
312
-extension PhotoDetailViewController: UIGestureRecognizerDelegate {
313
-    // MARK: textField
314
-
315
-    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
316
-        return commentTextField.isFirstResponder
207
+    
208
+    func bindViewModelToPhotoTime() {
209
+        viewModel.photoTime.bind(to: photoTime.rx.text).disposed(by: disposeBag)
317 210
     }
318
-
319
-    @IBAction func ReturnKeyboard(_ sender: UITapGestureRecognizer) {
320
-        if !commentView.isHidden {
321
-            commentTextField.resignFirstResponder()
322
-            commentView.isHidden = true
323
-        }
211
+    
212
+    func bindViewModelToThumbupCount() {
213
+        viewModel.thumbupCount.bind(to: thumbupCount.rx.text).disposed(by: disposeBag)
324 214
     }
325
-
326
-}
327
-
328
-// MARK: UITableView delegate
329
-extension PhotoDetailViewController: UITableViewDataSource, UITableViewDelegate {
330
-
331
-    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
332
-        if section == 3 {
333
-            let cell = tableView.dequeueReusableCell(withIdentifier: "thumbupHeadCell")
334
-            if let label = cell?.viewWithTag(1001) as? UILabel {
335
-//                label.text = "(\(detailPageViewModel.thumbups.count))"
336
-            }
337
-            if let button = cell?.viewWithTag(1011) as? UIButton {
338
-                button.addTarget(self, action: #selector(showThumps), for: .touchUpInside)
339
-            }
340
-            if let imageView = cell?.viewWithTag(1008) as? UIImageView {
341
-//                let imageName = detailPageViewModel.thumbupsCount <= 0 ? "收起" : "list-arrow"
342
-//                imageView.image = UIImage(named : imageName)
343
-            }
344
-            return cell?.contentView
345
-        } else if section == 4 {
346
-            let cell = tableView.dequeueReusableCell(withIdentifier: "comentHeadCell")
347
-            if let label = cell?.viewWithTag(1002) as? UILabel {
348
-//                label.text = "(\(detailPageViewModel.comments.count))"
349
-            }
350
-            if let button = cell?.viewWithTag(1012) as? UIButton {
351
-                button.addTarget(self, action: #selector(showComments), for: .touchUpInside)
352
-            }
353
-            if let imageView = cell?.viewWithTag(1009) as? UIImageView {
354
-//                let imageName = detailPageViewModel.commentsCount <= 0 ? "收起" : "list-arrow"
355
-//                imageView.image = UIImage(named : imageName)
356
-            }
357
-            return cell?.contentView
358
-        }
359
-        return nil
215
+    
216
+    func bindViewModelToThumbupView() {
217
+        viewModel.thumbupItems
218
+            .asDriver(onErrorJustReturn: [])
219
+            .drive(onNext: { [weak self] (items) in
220
+                self?.setupThumbupView(items: items)
221
+            }).disposed(by: disposeBag)
360 222
     }
361
-
362
-    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
363
-        if section == 3 || section == 4 {
364
-            return 44
365
-        }
366
-        return 0
223
+    
224
+    func bindViewModelToCommentCount() {
225
+        viewModel.commentCount.bind(to: commentCount.rx.text).disposed(by: disposeBag)
367 226
     }
368
-
369
-    func numberOfSections(in tableView: UITableView) -> Int {
370
-        return 0
227
+    
228
+    func bindViewModelToCommentTableView() {
229
+        viewModel.commentItems
230
+            .bind(to: commentTableView.rx.items(dataSource: commentTableViewDataSource))
231
+            .disposed(by: disposeBag)
371 232
     }
372
-
373
-    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
374
-//        if section == 3 {
375
-//            return detailPageViewModel.thumbupsCount > 0 ? 1 : 0
376
-//        } else if section == 4 {
377
-//            return detailPageViewModel.commentsCount
378
-//        }
379
-        return 0
233
+    
234
+    func bindBuyViewIsVisiable() {
235
+        viewModel.canBuy.map { !$0 }.bind(to: buyView.rx.isHidden).disposed(by: disposeBag)
380 236
     }
381
-
382
-    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
383
-        if indexPath.section == 0 {
384
-            let cell = tableView.dequeueReusableCell(withIdentifier: "headCell", for: indexPath) as! DetailPageHeadCell
385
-            cell.enterView.isHidden = isHiddenEnterView
386
-//            cell.setInfo(datas[currentPhotoIndex])
387
-            cell.delegate = self
388
-            if let reportBtn = cell.viewWithTag(40001) as? UIButton {
389
-
390
-                reportBtn.addTarget(self, action: #selector(loadReportController), for: .touchUpInside)
391
-//                reportBtn.isHidden = !(UserDefaults.Account.bool(forKey: .isAudit))
392
-            }
393
-            return cell
394
-        } else if indexPath.section == 1 {
395
-            let cell = tableView.dequeueReusableCell(withIdentifier: "detailPagePhotoCell", for: indexPath) as! DetailPagePhotoCell
396
-//            cell.datas = datas
397
-//            cell.currentPhotoIndex = currentPhotoIndex
398
-            cell.delegate = self
399
-            cell.first = true
400
-            cell.collectionView.reloadData()
401
-            return cell
402
-        } else if indexPath.section == 2 {
403
-            let cell = tableView.dequeueReusableCell(withIdentifier: "nameCell", for: indexPath) as! DetailPageNameCell
404
-//            cell.setInfo(datas[currentPhotoIndex])
405
-            return cell
406
-        } else if indexPath.section == 3 {
407
-            let cell = tableView.dequeueReusableCell(withIdentifier: "thumbupCell", for: indexPath) as! DetailthumbupImagesCell
408
-//            if detailPageViewModel.thumbups.count > 0 {
409
-//                let headers = detailPageViewModel.thumbups.map {$0.avatar}
410
-//                cell.setInfo(content: headers)
411
-//            }
412
-            return cell
413
-        } else {
414
-            let cell = tableView.dequeueReusableCell(withIdentifier: "comentCell", for: indexPath) as! DetailCommentCell
415
-//            cell.setInfo(detailPageViewModel.comments[indexPath.row])
416
-            return cell
417
-        }
237
+    
238
+    func bindCommentTextFieldToSendBtn() {
239
+        commentTextField.rx.text
240
+            .map { !($0?.isEmpty)! }
241
+            .bind(to: sendBtn.rx.isEnabled)
242
+            .disposed(by: disposeBag)
243
+    }
244
+    
245
+    func bindCollectionViewDelegate() {
246
+        photoCollectionView.rx.setDelegate(self).disposed(by: disposeBag)
247
+        
248
+    }
249
+    
250
+    func bindListViewModelToCollectionView() {
251
+        listViewModel.content
252
+            .bind(to: photoCollectionView.rx.items(dataSource: photoCollectionViewDataSource))
253
+            .disposed(by: disposeBag)
254
+    }
255
+    
256
+    func bindCollectionViewToListViewModel() {
257
+        photoCollectionView.rx.willDisplayCell
258
+            .asDriver()
259
+            .drive(onNext: { [unowned self] in
260
+                self.listViewModel.willShow(index: $0.at.row)
261
+            })
262
+            .disposed(by: disposeBag)
263
+    }
264
+    
265
+    func bindCollectionViewSelected() {
266
+        photoCollectionView.rx.itemSelected
267
+            .asDriver(onErrorJustReturn: IndexPath(item: 0, section: 0))
268
+            .drive(onNext: { [unowned self] _ in self.listViewModel.didSelected() })
269
+            .disposed(by: disposeBag)
270
+    }
271
+    
272
+    func bindViewWillAppear() {
273
+        viewModel.viewWillAppear
274
+            .asDriver()
275
+            .drive(onNext: { [unowned self] _ in
276
+                self.photoCollectionView.scrollToItem(at: IndexPath(item: self.listViewModel.currIndex, section: 0), at: .right, animated: false)
277
+            })
278
+            .disposed(by: disposeBag)
418 279
     }
280
+    
281
+    func monitorKeyboardWillShow() {
282
+        NotificationCenter.default.rx
283
+            .notification(UIResponder.keyboardWillShowNotification)
284
+            .takeUntil(self.rx.deallocated)
285
+            .subscribe { [unowned self] notification in
286
+                guard let userInfo = notification.element?.userInfo,
287
+                    let keyboardFrame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect,
288
+                    let timeInterval = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval,
289
+                    let curve = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? Int
290
+                    else { return }
291
+                UIView.setAnimationCurve(UIView.AnimationCurve(rawValue: curve) ?? .linear)
292
+                UIView.animate(withDuration: timeInterval, animations: {
293
+                    self.commentEditYConstraint.constant = keyboardFrame.height
294
+                    self.view.layoutIfNeeded()
295
+                })
296
+        }.disposed(by: disposeBag)
297
+    }
298
+    
299
+    func monitorKeyboardWillHide() {
300
+        NotificationCenter.default.rx
301
+            .notification(UIResponder.keyboardWillHideNotification)
302
+            .takeUntil(self.rx.deallocated)
303
+            .subscribe { [unowned self] notification in
304
+                guard let userInfo = notification.element?.userInfo,
305
+                    let timeInterval = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval,
306
+                    let curve = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? Int
307
+                    else { return }
308
+                UIView.setAnimationCurve(UIView.AnimationCurve(rawValue: curve) ?? .linear)
309
+                UIView.animate(withDuration: timeInterval, animations: {
310
+                    self.commentEditYConstraint.constant = -56
311
+                    self.view.layoutIfNeeded()
312
+                })
313
+                self.commentTextField.clear()
314
+        }.disposed(by: disposeBag)
315
+    }
316
+}
419 317
 
420
-    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
421
-        if indexPath.section == 0 {
422
-            return 48
423
-        } else if indexPath.section == 1 {
424
-            return 360
425
-        } else if indexPath.section == 2 {
426
-            return 36
427
-        } else if indexPath.section == 3 {
428
-            return 40
429
-        } else {
430
-//            return 40 + detailPageViewModel.comments[indexPath.row].cellHeigth
431
-            return 40
318
+extension PhotoDetailViewController {
319
+    func setupThumbupView(items: [PhotoThumbupUserItem]) {
320
+        thumbupView.subviews.forEach { $0.removeFromSuperview() }
321
+        
322
+        let row = (Int(kScreenWidth) - 6) / 34
323
+        var topConstraint: CGFloat = 6
324
+        var last: UIImageView?
325
+        
326
+        for (index, item) in items.enumerated() {
327
+            let imageView = UIImageView()
328
+            imageView.cornerRadius = 5
329
+            imageView.translatesAutoresizingMaskIntoConstraints = false
330
+            imageView.setImage(item.avatar, placeholder: UIImage.defaultAvatar)
331
+            thumbupView.addSubview(imageView)
332
+            
333
+            if index % row == 0 && index != 0 {
334
+                topConstraint += 28 + 6
335
+                last = nil
336
+            }
337
+            
338
+            NSLayoutConstraint.activate([
339
+                imageView.widthAnchor.constraint(equalToConstant: 28),
340
+                imageView.heightAnchor.constraint(equalToConstant: 28),
341
+                imageView.topAnchor.constraint(equalTo: thumbupView.topAnchor, constant: topConstraint),
342
+                imageView.leadingAnchor.constraint(equalTo: last?.trailingAnchor ?? thumbupView.leadingAnchor, constant: 6)
343
+                ])
344
+            
345
+            last = imageView
432 346
         }
347
+        thumbupViewHeightConstraint.constant = items.isEmpty ? 1 : topConstraint + 34
348
+        commentTableView.tableHeaderView?.height = 532 + thumbupViewHeightConstraint.constant
433 349
     }
434 350
 }
435 351
 
436
-extension PhotoDetailViewController {
352
+extension PhotoDetailViewController: UICollectionViewDelegateFlowLayout {
353
+    func collectionView(_ collectionView: UICollectionView,
354
+                        layout collectionViewLayout: UICollectionViewLayout,
355
+                        sizeForItemAt indexPath: IndexPath) -> CGSize {
356
+        return CGSize(width: kScreenWidth, height: 359)
357
+    }
437 358
     
359
+    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
360
+        return 0
361
+    }
438 362
 }
363
+
364
+extension PhotoDetailViewController: NavigationBackViewController {}

+ 90 - 75
PaiAi/Paiai_iOS/App/PhotoDetail/PhotoPreviewViewController.swift

@@ -1,5 +1,5 @@
1 1
 //
2
-//  ShowFullPicController.swift
2
+//  PhotoPreviewViewController.swift
3 3
 //  PaiAi
4 4
 //
5 5
 //  Created by zhengjianfei on 16/4/9.
@@ -9,49 +9,104 @@
9 9
 import UIKit
10 10
 import PaiaiDataKit
11 11
 import PaiaiUIKit
12
+import RxCocoa
13
+import RxSwift
14
+import RxDataSources
12 15
 
13
-final class ShowFullPicController: UIViewController {
16
+final class PhotoPreviewViewController: UIViewController {
14 17
 
15
-    // MARK: Storyboard property
18
+    /// MARK: Storyboard property
16 19
     @IBOutlet weak var collectionView: UICollectionView!
17
-//    @IBOutlet weak var progressView: FFProgress!
18
-   // MARK: parameter property
19
-    lazy var datas = [PhotoItem]()
20
-    lazy var currentPhotoIndex = 0
21
-    lazy var firstLayout = true
22
-    lazy var currentPageIndex = 0
23
-    lazy var showNomark = false
24
-    lazy var showHD = false
25
-
26
-    var shufflingImage = [String]()
27
-
28
-    // MARK: Controller fucntion
20
+    var viewModel: PhotoDetailListViewModel!
21
+    var disposeBag = DisposeBag()
22
+    
23
+    override var prefersStatusBarHidden: Bool {
24
+        return true
25
+    }
26
+    
29 27
     override func viewDidLoad() {
30 28
         super.viewDidLoad()
31
-        navigationController?.isNavigationBarHidden = true
32
-//        titleWithbackBar = ""
33
-
34
-     }
35
-
36
-    override func viewDidLayoutSubviews() {
37
-        if firstLayout {
38
-            collectionView.contentOffset = CGPoint(x: (CGFloat(currentPhotoIndex) * (collectionView.width)), y: 0)
39
-            firstLayout = false
29
+        binding()
30
+        scrollToSpecifiedImage()
31
+        navigationController?.setNavigationBarHidden(true, animated: true)
32
+        
33
+    }
34
+    
35
+    func scrollToSpecifiedImage() {
36
+        collectionView.layoutIfNeeded()
37
+        collectionView.scrollToItem(at: IndexPath(item: viewModel.currIndex, section: 0), at: .right, animated: false)
38
+    }
39
+    
40
+    override func viewDidAppear(_ animated: Bool) {
41
+        super.viewWillAppear(animated)
42
+        bindCollectionViewToViewModel()
43
+    }
44
+    
45
+    override func viewWillDisappear(_ animated: Bool) {
46
+        super.viewWillDisappear(animated)
47
+        navigationController?.setNavigationBarHidden(false, animated: true)
48
+    }
49
+    
50
+    @IBAction func download(_ sender: UIButton) {
51
+        guard let cell = collectionView.cellForItem(at: IndexPath(item: viewModel.currIndex, section: 0)) as? ImageCell,
52
+            let image = cell.photoImage.image else {
53
+                //            FFToastView.showToast(inView: view, withText: "未检测到图片")
54
+                return
40 55
         }
41
-        super.viewDidLayoutSubviews()
56
+        
57
+        UIImageWriteToSavedPhotosAlbum(image, self, #selector(image(_:didFinishSavingWithError:contextInfo:)), nil)
42 58
     }
59
+    
60
+    @objc func image(_ image: UIImage?, didFinishSavingWithError error: NSError?, contextInfo info: UnsafeMutableRawPointer) {
61
+        if error != nil {
62
+            //            FFToastView.showToast(inView: view, withText: "保存图片失败")
63
+        } else {
64
+            //            FFToastView.showImageToast(inView: view, withText: "已保存图片到相册", withImage: "提示弹窗-勾")
65
+        }
66
+    }
67
+}
68
+
69
+/// binding
70
+extension PhotoPreviewViewController {
71
+    
72
+    var dataSource: RxCollectionViewSectionedAnimatedDataSource<AnimatableSectionModel<Int, PhotoItem>> {
73
+        return RxCollectionViewSectionedAnimatedDataSource<AnimatableSectionModel<Int, PhotoItem>>(configureCell: { (dataSource, collectionView, indexPath, item) in
74
+            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "imageCell", for: indexPath) as! ImageCell
75
+            cell.setModel(url: item.murl.isEmpty ? item.photo_url : item.murl)
76
+            return cell
77
+        })
78
+    }
79
+    
80
+    func binding() {
81
+        bindViewModelToCollectionView()
82
+        bindCollectionViewDelegate()
83
+    }
84
+    
85
+    func bindViewModelToCollectionView() {
86
+        viewModel.content
87
+            .bind(to: collectionView.rx.items(dataSource: dataSource))
88
+            .disposed(by: disposeBag)
89
+    }
90
+    
91
+    func bindCollectionViewDelegate() {
92
+        collectionView.rx.setDelegate(self).disposed(by: disposeBag)
93
+    }
94
+    
95
+    func bindCollectionViewToViewModel() {
96
+        collectionView.rx.willDisplayCell
97
+            .asDriver()
98
+            .drive(onNext: { [unowned self] in self.viewModel.willShow(index: $0.at.row) })
99
+            .disposed(by: disposeBag)
100
+    }
101
+}
43 102
 
44
-    // MARK: Storyboard  button function
103
+/// storyboard button action
104
+extension PhotoPreviewViewController {
45 105
     @IBAction  func back() {
46
-         _ = navigationController?.popViewController(animated: true)
47
-        guard let ctl = navigationController?.viewControllers.last as? PhotoDetailViewController else {
48
-            return
49
-        }
50
-//        ctl.currentPhotoIndex = currentPageIndex
51
-//        ctl.tableView.reloadData()
106
+        navigationController?.popViewController(animated: true)
52 107
     }
53 108
     @IBAction func rotateTheImage(_ sender: UIButton) {
54
-        guard let cell = collectionView.cellForItem(at: IndexPath(item: currentPageIndex, section: 0)) as? ImageCell else {
109
+        guard let cell = collectionView.cellForItem(at: IndexPath(item: viewModel.currIndex, section: 0)) as? ImageCell else {
55 110
             return
56 111
         }
57 112
         UIView.beginAnimations("image.rotate", context: nil)
@@ -71,54 +126,14 @@ final class ShowFullPicController: UIViewController {
71 126
             }
72 127
         }
73 128
     }
129
+    
74 130
 
75
-    @IBAction func load() {
76
-        guard let cell = collectionView.cellForItem(at: IndexPath(item: currentPageIndex, section: 0)) as? ImageCell else {
77
-//            FFToastView.showToast(inView: view, withText: "未检测到图片")
78
-            return
79
-        }
80
-        guard let image = cell.photoImage.image else {
81
-//            FFToastView.showToast(inView: view, withText: "未检测到图片")
82
-            return
83
-        }
84
-        UIImageWriteToSavedPhotosAlbum(image, self, #selector(image(_:didFinishSavingWithError:contextInfo:)), nil)
85
-    }
86
-
87
-    @objc func image(_ image: UIImage?, didFinishSavingWithError error: NSError?, contextInfo info: UnsafeMutableRawPointer) {
88
-        if error != nil {
89
-//            FFToastView.showToast(inView: view, withText: "保存图片失败")
90
-        } else {
91
-//            FFToastView.showImageToast(inView: view, withText: "已保存图片到相册", withImage: "提示弹窗-勾")
92
-        }
93
-
94
-    }
95 131
 }
96 132
 
97 133
 // MARK: UICollectionView delegate
98
-extension ShowFullPicController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
99
-
100
-    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
101
-        return shufflingImage.count > datas.count ? shufflingImage.count : datas.count
102
-    }
103
-
104
-    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
105
-        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "imageCell", for: indexPath) as! ImageCell
106
-        if shufflingImage.count <= 0 {
107
-            let data = datas[indexPath.item]
108
-            let urlStr = data.murl.isEmpty ? data.photo_url : data.murl
109
-            cell.setModel(url: urlStr)
110
-        } else {
111
-            cell.setModel(url: shufflingImage[indexPath.row])
112
-        }
113
-        return cell
114
-    }
134
+extension PhotoPreviewViewController: UICollectionViewDelegateFlowLayout {
115 135
 
116 136
     func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
117 137
         return CGSize(width: collectionView.width, height: collectionView.height - 20)
118 138
     }
119
-
120
-    func scrollViewDidScroll(_ scrollView: UIScrollView) {
121
-        let page = Int(scrollView.contentOffset.x / (collectionView.width))
122
-        currentPageIndex = page
123
-    }
124 139
 }

+ 15 - 7
PaiAi/Paiai_iOS/App/PhotoDetail/ShareView.swift

@@ -9,13 +9,21 @@
9 9
 import UIKit
10 10
 
11 11
 class ShareView: UIView {
12
+    
13
+}
14
+
12 15
 
13
-    /*
14
-    // Only override draw() if you perform custom drawing.
15
-    // An empty implementation adversely affects performance during animation.
16
-    override func draw(_ rect: CGRect) {
17
-        // Drawing code
18
-    }
19
-    */
20 16
 
17
+extension ShareView {
18
+    
19
+    func activateConstraints() {
20
+        
21
+    }
22
+    
23
+    func activateConstraintsShareView() {
24
+        guard let superView = superview else { return }
25
+        
26
+        self.translatesAutoresizingMaskIntoConstraints = false
27
+        
28
+    }
21 29
 }

+ 0 - 15
PaiAi/launch_multiple_simulators.sh

@@ -1,15 +0,0 @@
1
-#!/bin/bash
2
-
3
-xcrun simctl shutdown all
4
-
5
-path=$(find ~/Library/Developer/Xcode/DerivedData/Paiai-*/Build/Products/Debug-iphonesimulator -name "Paiai.app" | head -n 1)
6
-echo "${path}"
7
-
8
-filename=MultiSimConfig.txt
9
-grep -v '^#' $filename | while read -r line
10
-do
11
-  echo $line
12
-  xcrun instruments -w "$line"
13
-  xcrun simctl install booted $path
14
-  xcrun simctl launch booted com.Paiai.Paiai
15
-done

kodo - Gogs: Go Git Service

Geen omschrijving

admin.py 13KB

    # -*- coding: utf-8 -*- import monetary from django.conf import settings from django.contrib import admin from django.contrib.auth.hashers import make_password from django_admin import AdvancedActionsModelAdmin, AdvancedExportExcelModelAdmin, DeleteModelAdmin, ReadOnlyModelAdmin from django_models_ext import ProvinceShortModelMixin from pysnippets.strsnippets import strip from integral.models import SaleclerkSubmitLogInfo from mch.models import (ActivityInfo, AdministratorInfo, BrandInfo, CameraModelInfo, ConsumeInfoSubmitLogInfo, DistributorInfo, LatestAppInfo, LatestAppScreenInfo, MaintenancemanInfo, ModelCameraBodyInfo, ModelInfo, OperatorInfo, SaleclerkInfo, ConsumeShotUnbindingInfo) from utils.redis.rshot import update_member_shot_data class AdministratorInfoAdmin(admin.ModelAdmin): list_display = ('admin_id', 'admin_type', 'phone', 'password', 'encryption', 'name', 'brand_id', 'brand_name', 'user_status', 'status', 'created_at', 'updated_at') list_filter = ('admin_type', 'user_status', 'status', 'brand_name') readonly_fields = ('encryption', 'brand_name') def save_model(self, request, obj, form, change): obj.phone = strip(obj.phone) obj.password = strip(obj.password) if obj.password: obj.encryption = make_password(obj.password, settings.MAKE_PASSWORD_SALT, settings.MAKE_PASSWORD_HASHER) obj.password = '' obj.brand_id = strip(obj.brand_id) try: obj.brand_name = BrandInfo.objects.get(brand_id=obj.brand_id).brand_name except BrandInfo.DoesNotExist: obj.brand_name = '' obj.save() class OperatorInfoAdmin(admin.ModelAdmin): list_display = ('operator_id', 'phone', 'password', 'encryption', 'name', 'brand_id', 'brand_name', 'brand_domain', 'user_status', 'status', 'created_at', 'updated_at') list_filter = ('user_status', 'status', 'brand_name') readonly_fields = ('brand_domain', 'encryption', 'brand_name') def save_model(self, request, obj, form, change): obj.phone = strip(obj.phone) obj.password = strip(obj.password) if obj.password: obj.encryption = make_password(obj.password, settings.MAKE_PASSWORD_SALT, settings.MAKE_PASSWORD_HASHER) obj.password = '' obj.brand_id = strip(obj.brand_id) try: obj.brand_name = BrandInfo.objects.get(brand_id=obj.brand_id).brand_name except BrandInfo.DoesNotExist: obj.brand_name = '' obj.save() class BrandInfoAdmin(admin.ModelAdmin): list_display = ('brand_id', 'brand_name', 'brand_descr', 'brand_logo', 'brand_domain', 'position', 'status', 'created_at', 'updated_at') def save_model(self, request, obj, form, change): obj.save() OperatorInfo.objects.filter(brand_id=obj.brand_id).update(brand_domain=obj.brand_domain) class ModelInfoAdmin(AdvancedExportExcelModelAdmin, DeleteModelAdmin, admin.ModelAdmin): list_display = ('pk', 'brand_id', 'brand_name', 'jancode', 'model_id', 'model_name', 'model_uni_name', 'model_full_name', 'model_descr', 'image', 'url', 'image2', 'integral', 'position', 'display', 'is_important', 'shot_type_id', 'shot_member_integral', 'shot_member_image', 'shot_member_name', 'status', 'created_at', 'updated_at') list_filter = ('brand_name', 'shot_type_id', 'display', 'status') readonly_fields = ('brand_name', ) search_fields = ('brand_id', 'brand_name', 'jancode', 'model_id', 'model_name', 'model_uni_name', 'model_full_name', 'model_descr') def save_model(self, request, obj, form, change): obj.brand_id = strip(obj.brand_id) try: obj.brand_name = BrandInfo.objects.get(brand_id=obj.brand_id).brand_name except BrandInfo.DoesNotExist: obj.brand_name = '' obj.is_show_shot = True obj.save() ModelInfo.objects.filter(brand_id=obj.brand_id, model_uni_name=obj.model_uni_name).exclude(model_id=obj.model_id).update(is_show_shot=False) update_member_shot_data() # 更新用户提交列表和销售员提交列表 SaleclerkSubmitLogInfo.objects.filter(model_pk=obj.pk).update( model_name=obj.model_name, model_uni_name=obj.model_uni_name, ) ConsumeInfoSubmitLogInfo.objects.filter(model_id=obj.model_id).update( model_uni_name=obj.model_uni_name, model_name=obj.model_name, ) # 更新"型号机身适配"型号图片 ModelCameraBodyInfo.objects.filter( brand_id=obj.brand_id, model_name=obj.model_uni_name, ).update( model_image=obj.shot_member_image, is_important=obj.is_important, ) def delete_model(self, request, obj): obj.delete() update_member_shot_data() class CameraModelInfoAdmin(admin.ModelAdmin): list_display = ('brand_id', 'brand_name', 'camera_brand_name', 'camera_name', 'camera_image', 'camera_market_time', 'status', 'created_at', 'updated_at') list_filter = ('brand_name', 'camera_brand_name', 'status') def save_model(self, request, obj, form, change): obj.save() # 更新"型号机身适配"机身图片 ModelCameraBodyInfo.objects.filter( brand_id=obj.brand_id, camera_brand_name=obj.camera_brand_name, camera_name=obj.camera_name, ).update( camera_image=obj.camera_image, camera_market_time=obj.camera_market_time, ) class ModelCameraBodyInfoAdmin(DeleteModelAdmin, admin.ModelAdmin): list_display = ('brand_id', 'brand_name', 'model_name', 'model_full_name', 'model_image', 'is_important', 'camera_brand_name', 'camera_name', 'camera_image', 'camera_market_time', 'status', 'created_at', 'updated_at') list_filter = ('brand_name', 'model_name', 'is_important', 'camera_brand_name', 'status') readonly_fields = ('model_image', 'camera_image', 'camera_market_time') search_fields = ('brand_id', 'brand_name', 'model_name', 'model_full_name', 'camera_name') class ModelImageInfoAdmin(admin.ModelAdmin): list_display = ('model_id', 'model_name', 'image', 'url', 'position', 'status', 'created_at', 'updated_at') list_filter = ('model_name', 'status') def save_model(self, request, obj, form, change): obj.model_id = strip(obj.model_id) try: obj.model_name = ModelInfo.objects.get(model_id=obj.model_id).model_name except BrandInfo.DoesNotExist: obj.model_name = '' obj.save() class DistributorInfoAdmin(admin.ModelAdmin): list_display = ('brand_id', 'brand_name', 'department_id', 'distributor_id', 'distributor_name', 'distributor_short_name', 'distributor_province_name', 'position', 'status', 'created_at', 'updated_at') list_filter = ('brand_name', 'sr_id', 'distributor_province_name', 'status') readonly_fields = ('brand_name', 'distributor_province_code') search_fields = ('brand_id', 'brand_name', 'distributor_id', 'distributor_name', 'distributor_short_name', 'distributor_descr', 'distributor_province_code', 'distributor_province_name') def save_model(self, request, obj, form, change): obj.brand_id = strip(obj.brand_id) try: obj.brand_name = BrandInfo.objects.get(brand_id=obj.brand_id).brand_name except BrandInfo.DoesNotExist: obj.brand_name = '' obj.distributor_province_code = ProvinceShortModelMixin.PROVINCE_NAME_CODE_DICT.get(obj.distributor_province_name, '') obj.save() class SaleclerkInfoAdmin(AdvancedExportExcelModelAdmin, AdvancedActionsModelAdmin, admin.ModelAdmin): list_display = ('brand_id', 'brand_name', 'distributor_id', 'distributor_name', 'clerk_id', 'clerk_name', 'clerk_sex', 'clerk_phone', 'unionid', 'openid', 'num', 'integral', 'total_integral', 'user_status', 'test_user', 'is_online_sales', 'status', 'created_at', 'updated_at') list_filter = ('test_user', 'user_status', 'is_online_sales', 'status') search_fields = ('brand_id', 'brand_name', 'distributor_id', 'distributor_name', 'clerk_id', 'clerk_name', 'clerk_phone', 'unionid', 'openid') actions_exclude = ('delete_selected', ) class MaintenancemanInfoAdmin(AdvancedExportExcelModelAdmin, AdvancedActionsModelAdmin, admin.ModelAdmin): list_display = ('brand_name', 'wx_userid', 'maintenance_id', 'maintenance_name', 'maintenance_phone', 'status', 'created_at', 'updated_at') search_fields = ('maintenance_id', 'maintenance_name', 'maintenance_phone') actions_exclude = ('delete_selected', ) class BrandModelDistributorPriceInfoAdmin(admin.ModelAdmin): list_display = ('brand_id', 'brand_name', 'model_id', 'model_name', 'distributor_id', 'distributor_name', 'factory_yuan', 'integral', 'status', 'created_at', 'updated_at') list_filter = ('brand_name', 'model_name', 'distributor_name', 'status') readonly_fields = ('brand_name', 'model_name', 'distributor_name', 'factory_fee', ) search_fields = ('brand_id', 'brand_name', 'model_id', 'model_name', 'distributor_id', 'distributor_name') def save_model(self, request, obj, form, change): obj.brand_id = strip(obj.brand_id) try: obj.brand_name = BrandInfo.objects.get(brand_id=obj.brand_id).brand_name except BrandInfo.DoesNotExist: obj.brand_name = '' obj.model_id = strip(obj.model_id) try: obj.model_name = ModelInfo.objects.get(model_id=obj.model_id).model_name except BrandInfo.DoesNotExist: obj.model_name = '' obj.distributor_id = strip(obj.distributor_id) try: obj.distributor_name = DistributorInfo.objects.get(distributor_id=obj.distributor_id).distributor_name except BrandInfo.DoesNotExist: obj.distributor_name = '' obj.save() class LatestAppInfoAdmin(admin.ModelAdmin): list_display = ('latest_adr_version_code', 'latest_adr_version_name', 'latest_adr_app', 'latest_adr_url', 'status', 'created_at', 'updated_at') list_filter = ('status', ) readonly_fields = ('status', ) def save_model(self, request, obj, form, change): if not obj.pk and LatestAppInfo.objects.filter(status=True).exists(): return obj.latest_adr_version_name = strip(obj.latest_adr_version_name) obj.latest_adr_app = strip(obj.latest_adr_app) obj.latest_adr_url = strip(obj.latest_adr_url) obj.save() class LatestAppScreenInfoAdmin(admin.ModelAdmin): list_display = ('latest_adr_version_code', 'latest_adr_version_name', 'latest_adr_app', 'latest_adr_url', 'status', 'created_at', 'updated_at') list_filter = ('status', ) readonly_fields = ('status', ) def save_model(self, request, obj, form, change): if not obj.pk and LatestAppInfo.objects.filter(status=True).exists(): return obj.latest_adr_version_name = strip(obj.latest_adr_version_name) obj.latest_adr_app = strip(obj.latest_adr_app) obj.latest_adr_url = strip(obj.latest_adr_url) obj.save() class ConsumeInfoSubmitLogInfoAdmin(AdvancedExportExcelModelAdmin, admin.ModelAdmin): list_display = ('user_id', 'phone', 'model_name', 'serialNo', 'dupload', 'submit_during_activity', 'integral', 'has_used', 'created_at') list_filter = ('brand_id', 'submit_during_activity', 'activity_id', 'dupload', 'test_user', 'verifyResult', 'has_used', 'status', 'code_version', 'created_at') excel_fields = ('created_at', 'phone', 'model_uni_name', 'model_name', 'serialNo') search_fields = ('user_id', 'phone', 'serialNo', 'model_name') class ConsumeShotUnbindingInfoAdmin(admin.ModelAdmin): list_display = ('user_id', 'submit_pk', 'submit_at', 'model_id', 'sn', 'reason', 'created_at') search_fields = ('sn', ) class ActivityInfoAdmin(admin.ModelAdmin): list_display = ('activity_id', 'activity_name', 'model_uni_names', 'start_at', 'end_at', 'coupon_expire_type', 'coupon_valid_period', 'coupon_expire_at', 'coupon_value', 'status', 'created_at', 'updated_at') list_filter = ('coupon_expire_type', 'status') admin.site.register(AdministratorInfo, AdministratorInfoAdmin) admin.site.register(OperatorInfo, OperatorInfoAdmin) admin.site.register(BrandInfo, BrandInfoAdmin) admin.site.register(ModelInfo, ModelInfoAdmin) admin.site.register(CameraModelInfo, CameraModelInfoAdmin) admin.site.register(ModelCameraBodyInfo, ModelCameraBodyInfoAdmin) # admin.site.register(ModelImageInfo, ModelImageInfoAdmin) admin.site.register(DistributorInfo, DistributorInfoAdmin) admin.site.register(SaleclerkInfo, SaleclerkInfoAdmin) # admin.site.register(BrandModelDistributorPriceInfo, BrandModelDistributorPriceInfoAdmin) admin.site.register(LatestAppInfo, LatestAppInfoAdmin) admin.site.register(LatestAppScreenInfo, LatestAppScreenInfoAdmin) admin.site.register(ConsumeInfoSubmitLogInfo, ConsumeInfoSubmitLogInfoAdmin) admin.site.register(ActivityInfo, ActivityInfoAdmin) admin.site.register(MaintenancemanInfo, MaintenancemanInfoAdmin) admin.site.register(ConsumeShotUnbindingInfo, ConsumeShotUnbindingInfoAdmin)