Large image view
This commit is contained in:
parent
42db93db03
commit
72b0540c1b
|
@ -13,6 +13,9 @@
|
||||||
D6333B372137838300CE884A /* AttributedString+Trim.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6333B362137838300CE884A /* AttributedString+Trim.swift */; };
|
D6333B372137838300CE884A /* AttributedString+Trim.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6333B362137838300CE884A /* AttributedString+Trim.swift */; };
|
||||||
D6333B772138D94E00CE884A /* ComposeMediaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6333B762138D94E00CE884A /* ComposeMediaView.swift */; };
|
D6333B772138D94E00CE884A /* ComposeMediaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6333B762138D94E00CE884A /* ComposeMediaView.swift */; };
|
||||||
D6333B792139AEFD00CE884A /* Date+TimeAgo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6333B782139AEFD00CE884A /* Date+TimeAgo.swift */; };
|
D6333B792139AEFD00CE884A /* Date+TimeAgo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6333B782139AEFD00CE884A /* Date+TimeAgo.swift */; };
|
||||||
|
D646C956213B365700269FB5 /* LargeImageExpandAnimationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D646C955213B365700269FB5 /* LargeImageExpandAnimationController.swift */; };
|
||||||
|
D646C958213B367000269FB5 /* LargeImageShrinkAnimationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D646C957213B367000269FB5 /* LargeImageShrinkAnimationController.swift */; };
|
||||||
|
D646C95A213B5D0500269FB5 /* LargeImageInteractionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D646C959213B5D0500269FB5 /* LargeImageInteractionController.swift */; };
|
||||||
D64A0CD32132153900640E3B /* HTMLContentLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64A0CD22132153900640E3B /* HTMLContentLabel.swift */; };
|
D64A0CD32132153900640E3B /* HTMLContentLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64A0CD22132153900640E3B /* HTMLContentLabel.swift */; };
|
||||||
D64D0AAD2128D88B005A6F37 /* LocalData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64D0AAC2128D88B005A6F37 /* LocalData.swift */; };
|
D64D0AAD2128D88B005A6F37 /* LocalData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64D0AAC2128D88B005A6F37 /* LocalData.swift */; };
|
||||||
D64D0AAF2128D954005A6F37 /* Onboarding.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D64D0AAE2128D954005A6F37 /* Onboarding.storyboard */; };
|
D64D0AAF2128D954005A6F37 /* Onboarding.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D64D0AAE2128D954005A6F37 /* Onboarding.storyboard */; };
|
||||||
|
@ -35,13 +38,16 @@
|
||||||
D667E5E921349EE50057A976 /* ProfileHeaderTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D667E5E821349EE50057A976 /* ProfileHeaderTableViewCell.xib */; };
|
D667E5E921349EE50057A976 /* ProfileHeaderTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D667E5E821349EE50057A976 /* ProfileHeaderTableViewCell.xib */; };
|
||||||
D667E5EB21349EF80057A976 /* ProfileHeaderTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D667E5EA21349EF80057A976 /* ProfileHeaderTableViewCell.swift */; };
|
D667E5EB21349EF80057A976 /* ProfileHeaderTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D667E5EA21349EF80057A976 /* ProfileHeaderTableViewCell.swift */; };
|
||||||
D667E5EF2134C39F0057A976 /* StatusContentLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D667E5EE2134C39F0057A976 /* StatusContentLabel.swift */; };
|
D667E5EF2134C39F0057A976 /* StatusContentLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D667E5EE2134C39F0057A976 /* StatusContentLabel.swift */; };
|
||||||
D667E5F12134D5050057A976 /* UIViewController+StatusTableViewCellDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D667E5F02134D5050057A976 /* UIViewController+StatusTableViewCellDelegate.swift */; };
|
D667E5F12134D5050057A976 /* UIViewController+Delegates.swift in Sources */ = {isa = PBXBuildFile; fileRef = D667E5F02134D5050057A976 /* UIViewController+Delegates.swift */; };
|
||||||
D667E5F32135BC260057A976 /* Conversation.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D667E5F22135BC260057A976 /* Conversation.storyboard */; };
|
D667E5F32135BC260057A976 /* Conversation.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D667E5F22135BC260057A976 /* Conversation.storyboard */; };
|
||||||
D667E5F52135BCD50057A976 /* ConversationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D667E5F42135BCD50057A976 /* ConversationViewController.swift */; };
|
D667E5F52135BCD50057A976 /* ConversationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D667E5F42135BCD50057A976 /* ConversationViewController.swift */; };
|
||||||
D667E5F82135C3040057A976 /* Mastodon+Equatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D667E5F72135C3040057A976 /* Mastodon+Equatable.swift */; };
|
D667E5F82135C3040057A976 /* Mastodon+Equatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D667E5F72135C3040057A976 /* Mastodon+Equatable.swift */; };
|
||||||
D6BED16F212663DA00F02DA0 /* SwiftSoup.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D6BED16E212663DA00F02DA0 /* SwiftSoup.framework */; };
|
D6BED16F212663DA00F02DA0 /* SwiftSoup.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D6BED16E212663DA00F02DA0 /* SwiftSoup.framework */; };
|
||||||
D6BED170212663DA00F02DA0 /* SwiftSoup.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D6BED16E212663DA00F02DA0 /* SwiftSoup.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
D6BED170212663DA00F02DA0 /* SwiftSoup.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D6BED16E212663DA00F02DA0 /* SwiftSoup.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||||
D6BED174212667E900F02DA0 /* StatusTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6BED173212667E900F02DA0 /* StatusTableViewCell.swift */; };
|
D6BED174212667E900F02DA0 /* StatusTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6BED173212667E900F02DA0 /* StatusTableViewCell.swift */; };
|
||||||
|
D6C94D852139DFD800CB5196 /* LargeImage.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D6C94D842139DFD800CB5196 /* LargeImage.storyboard */; };
|
||||||
|
D6C94D872139E62700CB5196 /* LargeImageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6C94D862139E62700CB5196 /* LargeImageViewController.swift */; };
|
||||||
|
D6C94D892139E6EC00CB5196 /* AttachmentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6C94D882139E6EC00CB5196 /* AttachmentView.swift */; };
|
||||||
D6D4DDD0212518A000E1C4BB /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D4DDCF212518A000E1C4BB /* AppDelegate.swift */; };
|
D6D4DDD0212518A000E1C4BB /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D4DDCF212518A000E1C4BB /* AppDelegate.swift */; };
|
||||||
D6D4DDD5212518A000E1C4BB /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D6D4DDD3212518A000E1C4BB /* Main.storyboard */; };
|
D6D4DDD5212518A000E1C4BB /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D6D4DDD3212518A000E1C4BB /* Main.storyboard */; };
|
||||||
D6D4DDD7212518A200E1C4BB /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D6D4DDD6212518A200E1C4BB /* Assets.xcassets */; };
|
D6D4DDD7212518A200E1C4BB /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D6D4DDD6212518A200E1C4BB /* Assets.xcassets */; };
|
||||||
|
@ -94,6 +100,9 @@
|
||||||
D6333B362137838300CE884A /* AttributedString+Trim.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AttributedString+Trim.swift"; sourceTree = "<group>"; };
|
D6333B362137838300CE884A /* AttributedString+Trim.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AttributedString+Trim.swift"; sourceTree = "<group>"; };
|
||||||
D6333B762138D94E00CE884A /* ComposeMediaView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeMediaView.swift; sourceTree = "<group>"; };
|
D6333B762138D94E00CE884A /* ComposeMediaView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeMediaView.swift; sourceTree = "<group>"; };
|
||||||
D6333B782139AEFD00CE884A /* Date+TimeAgo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+TimeAgo.swift"; sourceTree = "<group>"; };
|
D6333B782139AEFD00CE884A /* Date+TimeAgo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+TimeAgo.swift"; sourceTree = "<group>"; };
|
||||||
|
D646C955213B365700269FB5 /* LargeImageExpandAnimationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LargeImageExpandAnimationController.swift; sourceTree = "<group>"; };
|
||||||
|
D646C957213B367000269FB5 /* LargeImageShrinkAnimationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LargeImageShrinkAnimationController.swift; sourceTree = "<group>"; };
|
||||||
|
D646C959213B5D0500269FB5 /* LargeImageInteractionController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LargeImageInteractionController.swift; sourceTree = "<group>"; };
|
||||||
D64A0CD22132153900640E3B /* HTMLContentLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTMLContentLabel.swift; sourceTree = "<group>"; };
|
D64A0CD22132153900640E3B /* HTMLContentLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTMLContentLabel.swift; sourceTree = "<group>"; };
|
||||||
D64D0AAC2128D88B005A6F37 /* LocalData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalData.swift; sourceTree = "<group>"; };
|
D64D0AAC2128D88B005A6F37 /* LocalData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalData.swift; sourceTree = "<group>"; };
|
||||||
D64D0AAE2128D954005A6F37 /* Onboarding.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Onboarding.storyboard; sourceTree = "<group>"; };
|
D64D0AAE2128D954005A6F37 /* Onboarding.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Onboarding.storyboard; sourceTree = "<group>"; };
|
||||||
|
@ -117,12 +126,15 @@
|
||||||
D667E5E821349EE50057A976 /* ProfileHeaderTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ProfileHeaderTableViewCell.xib; sourceTree = "<group>"; };
|
D667E5E821349EE50057A976 /* ProfileHeaderTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ProfileHeaderTableViewCell.xib; sourceTree = "<group>"; };
|
||||||
D667E5EA21349EF80057A976 /* ProfileHeaderTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileHeaderTableViewCell.swift; sourceTree = "<group>"; };
|
D667E5EA21349EF80057A976 /* ProfileHeaderTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileHeaderTableViewCell.swift; sourceTree = "<group>"; };
|
||||||
D667E5EE2134C39F0057A976 /* StatusContentLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusContentLabel.swift; sourceTree = "<group>"; };
|
D667E5EE2134C39F0057A976 /* StatusContentLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusContentLabel.swift; sourceTree = "<group>"; };
|
||||||
D667E5F02134D5050057A976 /* UIViewController+StatusTableViewCellDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+StatusTableViewCellDelegate.swift"; sourceTree = "<group>"; };
|
D667E5F02134D5050057A976 /* UIViewController+Delegates.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+Delegates.swift"; sourceTree = "<group>"; };
|
||||||
D667E5F22135BC260057A976 /* Conversation.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Conversation.storyboard; sourceTree = "<group>"; };
|
D667E5F22135BC260057A976 /* Conversation.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Conversation.storyboard; sourceTree = "<group>"; };
|
||||||
D667E5F42135BCD50057A976 /* ConversationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationViewController.swift; sourceTree = "<group>"; };
|
D667E5F42135BCD50057A976 /* ConversationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationViewController.swift; sourceTree = "<group>"; };
|
||||||
D667E5F72135C3040057A976 /* Mastodon+Equatable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Mastodon+Equatable.swift"; sourceTree = "<group>"; };
|
D667E5F72135C3040057A976 /* Mastodon+Equatable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Mastodon+Equatable.swift"; sourceTree = "<group>"; };
|
||||||
D6BED16E212663DA00F02DA0 /* SwiftSoup.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SwiftSoup.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
D6BED16E212663DA00F02DA0 /* SwiftSoup.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SwiftSoup.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
D6BED173212667E900F02DA0 /* StatusTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusTableViewCell.swift; sourceTree = "<group>"; };
|
D6BED173212667E900F02DA0 /* StatusTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusTableViewCell.swift; sourceTree = "<group>"; };
|
||||||
|
D6C94D842139DFD800CB5196 /* LargeImage.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = LargeImage.storyboard; sourceTree = "<group>"; };
|
||||||
|
D6C94D862139E62700CB5196 /* LargeImageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LargeImageViewController.swift; sourceTree = "<group>"; };
|
||||||
|
D6C94D882139E6EC00CB5196 /* AttachmentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentView.swift; sourceTree = "<group>"; };
|
||||||
D6D4DDCC212518A000E1C4BB /* Tusker.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Tusker.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
D6D4DDCC212518A000E1C4BB /* Tusker.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Tusker.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
D6D4DDCF212518A000E1C4BB /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
D6D4DDCF212518A000E1C4BB /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
D6D4DDD4212518A000E1C4BB /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
D6D4DDD4212518A000E1C4BB /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||||
|
@ -168,6 +180,16 @@
|
||||||
/* End PBXFrameworksBuildPhase section */
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
|
D646C954213B364600269FB5 /* Transitions */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
D646C955213B365700269FB5 /* LargeImageExpandAnimationController.swift */,
|
||||||
|
D646C957213B367000269FB5 /* LargeImageShrinkAnimationController.swift */,
|
||||||
|
D646C959213B5D0500269FB5 /* LargeImageInteractionController.swift */,
|
||||||
|
);
|
||||||
|
path = Transitions;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
D663626021360A9600C9CBA2 /* Preferences */ = {
|
D663626021360A9600C9CBA2 /* Preferences */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -181,7 +203,7 @@
|
||||||
D667E5F62135C2ED0057A976 /* Extensions */ = {
|
D667E5F62135C2ED0057A976 /* Extensions */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
D667E5F02134D5050057A976 /* UIViewController+StatusTableViewCellDelegate.swift */,
|
D667E5F02134D5050057A976 /* UIViewController+Delegates.swift */,
|
||||||
D667E5F72135C3040057A976 /* Mastodon+Equatable.swift */,
|
D667E5F72135C3040057A976 /* Mastodon+Equatable.swift */,
|
||||||
D663626B21361C6700C9CBA2 /* Account+Preferences.swift */,
|
D663626B21361C6700C9CBA2 /* Account+Preferences.swift */,
|
||||||
D66362722136FFC600C9CBA2 /* UITextView+Placeholder.swift */,
|
D66362722136FFC600C9CBA2 /* UITextView+Placeholder.swift */,
|
||||||
|
@ -204,6 +226,7 @@
|
||||||
D663625C2135C74800C9CBA2 /* ConversationMainStatusTableViewCell.xib */,
|
D663625C2135C74800C9CBA2 /* ConversationMainStatusTableViewCell.xib */,
|
||||||
D663625E2135C75500C9CBA2 /* ConversationMainStatusTableViewCell.swift */,
|
D663625E2135C75500C9CBA2 /* ConversationMainStatusTableViewCell.swift */,
|
||||||
D6333B762138D94E00CE884A /* ComposeMediaView.swift */,
|
D6333B762138D94E00CE884A /* ComposeMediaView.swift */,
|
||||||
|
D6C94D882139E6EC00CB5196 /* AttachmentView.swift */,
|
||||||
);
|
);
|
||||||
path = Views;
|
path = Views;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -272,6 +295,7 @@
|
||||||
D6F953E9212519B800CF0F2B /* View Controllers */ = {
|
D6F953E9212519B800CF0F2B /* View Controllers */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
D646C954213B364600269FB5 /* Transitions */,
|
||||||
D6F953EB212519E700CF0F2B /* TimelineTableViewController.swift */,
|
D6F953EB212519E700CF0F2B /* TimelineTableViewController.swift */,
|
||||||
D64D0AB02128D9AE005A6F37 /* OnboardingViewController.swift */,
|
D64D0AB02128D9AE005A6F37 /* OnboardingViewController.swift */,
|
||||||
04DACE8B212CB14B009840C4 /* MainTabBarViewController.swift */,
|
04DACE8B212CB14B009840C4 /* MainTabBarViewController.swift */,
|
||||||
|
@ -279,6 +303,7 @@
|
||||||
D667E5F42135BCD50057A976 /* ConversationViewController.swift */,
|
D667E5F42135BCD50057A976 /* ConversationViewController.swift */,
|
||||||
D663626721360E2C00C9CBA2 /* PreferencesTableViewController.swift */,
|
D663626721360E2C00C9CBA2 /* PreferencesTableViewController.swift */,
|
||||||
D66362702136338600C9CBA2 /* ComposeViewController.swift */,
|
D66362702136338600C9CBA2 /* ComposeViewController.swift */,
|
||||||
|
D6C94D862139E62700CB5196 /* LargeImageViewController.swift */,
|
||||||
);
|
);
|
||||||
path = "View Controllers";
|
path = "View Controllers";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -293,6 +318,7 @@
|
||||||
D667E5F22135BC260057A976 /* Conversation.storyboard */,
|
D667E5F22135BC260057A976 /* Conversation.storyboard */,
|
||||||
D663626521360DD700C9CBA2 /* Preferences.storyboard */,
|
D663626521360DD700C9CBA2 /* Preferences.storyboard */,
|
||||||
D663626E213632A000C9CBA2 /* Compose.storyboard */,
|
D663626E213632A000C9CBA2 /* Compose.storyboard */,
|
||||||
|
D6C94D842139DFD800CB5196 /* LargeImage.storyboard */,
|
||||||
);
|
);
|
||||||
path = Storyboards;
|
path = Storyboards;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -418,6 +444,7 @@
|
||||||
D663625D2135C74800C9CBA2 /* ConversationMainStatusTableViewCell.xib in Resources */,
|
D663625D2135C74800C9CBA2 /* ConversationMainStatusTableViewCell.xib in Resources */,
|
||||||
D6F953EE21251A0700CF0F2B /* Timeline.storyboard in Resources */,
|
D6F953EE21251A0700CF0F2B /* Timeline.storyboard in Resources */,
|
||||||
D663626F213632A000C9CBA2 /* Compose.storyboard in Resources */,
|
D663626F213632A000C9CBA2 /* Compose.storyboard in Resources */,
|
||||||
|
D6C94D852139DFD800CB5196 /* LargeImage.storyboard in Resources */,
|
||||||
D6D4DDD5212518A000E1C4BB /* Main.storyboard in Resources */,
|
D6D4DDD5212518A000E1C4BB /* Main.storyboard in Resources */,
|
||||||
D667E5E3213499F70057A976 /* Profile.storyboard in Resources */,
|
D667E5E3213499F70057A976 /* Profile.storyboard in Resources */,
|
||||||
D663626621360DD700C9CBA2 /* Preferences.storyboard in Resources */,
|
D663626621360DD700C9CBA2 /* Preferences.storyboard in Resources */,
|
||||||
|
@ -451,12 +478,16 @@
|
||||||
D667E5F52135BCD50057A976 /* ConversationViewController.swift in Sources */,
|
D667E5F52135BCD50057A976 /* ConversationViewController.swift in Sources */,
|
||||||
D6F953F021251A2900CF0F2B /* MastodonController.swift in Sources */,
|
D6F953F021251A2900CF0F2B /* MastodonController.swift in Sources */,
|
||||||
D66362712136338600C9CBA2 /* ComposeViewController.swift in Sources */,
|
D66362712136338600C9CBA2 /* ComposeViewController.swift in Sources */,
|
||||||
|
D646C958213B367000269FB5 /* LargeImageShrinkAnimationController.swift in Sources */,
|
||||||
|
D646C956213B365700269FB5 /* LargeImageExpandAnimationController.swift in Sources */,
|
||||||
D667E5F82135C3040057A976 /* Mastodon+Equatable.swift in Sources */,
|
D667E5F82135C3040057A976 /* Mastodon+Equatable.swift in Sources */,
|
||||||
04DACE8E212CC7CC009840C4 /* AvatarCache.swift in Sources */,
|
04DACE8E212CC7CC009840C4 /* AvatarCache.swift in Sources */,
|
||||||
D6333B772138D94E00CE884A /* ComposeMediaView.swift in Sources */,
|
D6333B772138D94E00CE884A /* ComposeMediaView.swift in Sources */,
|
||||||
D663626421360D2300C9CBA2 /* AvatarStyle.swift in Sources */,
|
D663626421360D2300C9CBA2 /* AvatarStyle.swift in Sources */,
|
||||||
D6BED174212667E900F02DA0 /* StatusTableViewCell.swift in Sources */,
|
D6BED174212667E900F02DA0 /* StatusTableViewCell.swift in Sources */,
|
||||||
D64D0AAD2128D88B005A6F37 /* LocalData.swift in Sources */,
|
D64D0AAD2128D88B005A6F37 /* LocalData.swift in Sources */,
|
||||||
|
D6C94D892139E6EC00CB5196 /* AttachmentView.swift in Sources */,
|
||||||
|
D6C94D872139E62700CB5196 /* LargeImageViewController.swift in Sources */,
|
||||||
D663626221360B1900C9CBA2 /* Preferences.swift in Sources */,
|
D663626221360B1900C9CBA2 /* Preferences.swift in Sources */,
|
||||||
D6333B792139AEFD00CE884A /* Date+TimeAgo.swift in Sources */,
|
D6333B792139AEFD00CE884A /* Date+TimeAgo.swift in Sources */,
|
||||||
D663626C21361C6700C9CBA2 /* Account+Preferences.swift in Sources */,
|
D663626C21361C6700C9CBA2 /* Account+Preferences.swift in Sources */,
|
||||||
|
@ -466,11 +497,12 @@
|
||||||
D663626821360E2C00C9CBA2 /* PreferencesTableViewController.swift in Sources */,
|
D663626821360E2C00C9CBA2 /* PreferencesTableViewController.swift in Sources */,
|
||||||
D66362732136FFC600C9CBA2 /* UITextView+Placeholder.swift in Sources */,
|
D66362732136FFC600C9CBA2 /* UITextView+Placeholder.swift in Sources */,
|
||||||
D66362752137068A00C9CBA2 /* Visibility+Helpers.swift in Sources */,
|
D66362752137068A00C9CBA2 /* Visibility+Helpers.swift in Sources */,
|
||||||
|
D646C95A213B5D0500269FB5 /* LargeImageInteractionController.swift in Sources */,
|
||||||
D6F953EC212519E700CF0F2B /* TimelineTableViewController.swift in Sources */,
|
D6F953EC212519E700CF0F2B /* TimelineTableViewController.swift in Sources */,
|
||||||
D663626A2136163000C9CBA2 /* PreferencesAdaptive.swift in Sources */,
|
D663626A2136163000C9CBA2 /* PreferencesAdaptive.swift in Sources */,
|
||||||
D667E5EB21349EF80057A976 /* ProfileHeaderTableViewCell.swift in Sources */,
|
D667E5EB21349EF80057A976 /* ProfileHeaderTableViewCell.swift in Sources */,
|
||||||
D64A0CD32132153900640E3B /* HTMLContentLabel.swift in Sources */,
|
D64A0CD32132153900640E3B /* HTMLContentLabel.swift in Sources */,
|
||||||
D667E5F12134D5050057A976 /* UIViewController+StatusTableViewCellDelegate.swift in Sources */,
|
D667E5F12134D5050057A976 /* UIViewController+Delegates.swift in Sources */,
|
||||||
D663625F2135C75500C9CBA2 /* ConversationMainStatusTableViewCell.swift in Sources */,
|
D663625F2135C75500C9CBA2 /* ConversationMainStatusTableViewCell.swift in Sources */,
|
||||||
D667E5E721349D4C0057A976 /* ProfileTableViewController.swift in Sources */,
|
D667E5E721349D4C0057A976 /* ProfileTableViewController.swift in Sources */,
|
||||||
D6D4DDD0212518A000E1C4BB /* AppDelegate.swift in Sources */,
|
D6D4DDD0212518A000E1C4BB /* AppDelegate.swift in Sources */,
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
%PDF-1.5
|
||||||
|
%µí®û
|
||||||
|
3 0 obj
|
||||||
|
<< /Length 4 0 R
|
||||||
|
/Filter /FlateDecode
|
||||||
|
>>
|
||||||
|
stream
|
||||||
|
xœ]‘KÂ0D÷>…/€‰óÏ18ªÔ²(àþI[uQM:Ïs_àp<ï
¯w‡Û"qŽè#y×ð‰\#¥ÔлHÙEÜ‘›#怞û;¥®¹Ä¦Å™À"Ëü”§5œô¸ÊkD†e‰1¿à(`”{àÂjÅF…“
|
||||||
|
)|8{ð8,‡£¨8ÂIŒ h>þYW=˜ÅŸs<C5B8>‘J •Ž'̉!ŽÚ*
TJ;µku]ääÒ=‹ûj—®º²Ý`çNMÊÊÈàLo°+Üà¹e¤
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
4 0 obj
|
||||||
|
206
|
||||||
|
endobj
|
||||||
|
2 0 obj
|
||||||
|
<<
|
||||||
|
/ExtGState <<
|
||||||
|
/a0 << /CA 1 /ca 1 >>
|
||||||
|
>>
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
5 0 obj
|
||||||
|
<< /Type /Page
|
||||||
|
/Parent 1 0 R
|
||||||
|
/MediaBox [ 0 0 208.76799 208.76799 ]
|
||||||
|
/Contents 3 0 R
|
||||||
|
/Group <<
|
||||||
|
/Type /Group
|
||||||
|
/S /Transparency
|
||||||
|
/I true
|
||||||
|
/CS /DeviceRGB
|
||||||
|
>>
|
||||||
|
/Resources 2 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
1 0 obj
|
||||||
|
<< /Type /Pages
|
||||||
|
/Kids [ 5 0 R ]
|
||||||
|
/Count 1
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
6 0 obj
|
||||||
|
<< /Creator (cairo 1.14.8 (http://cairographics.org))
|
||||||
|
/Producer (cairo 1.14.8 (http://cairographics.org))
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
7 0 obj
|
||||||
|
<< /Type /Catalog
|
||||||
|
/Pages 1 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
xref
|
||||||
|
0 8
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000618 00000 n
|
||||||
|
0000000320 00000 n
|
||||||
|
0000000015 00000 n
|
||||||
|
0000000298 00000 n
|
||||||
|
0000000392 00000 n
|
||||||
|
0000000683 00000 n
|
||||||
|
0000000810 00000 n
|
||||||
|
trailer
|
||||||
|
<< /Size 8
|
||||||
|
/Root 7 0 R
|
||||||
|
/Info 6 0 R
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
862
|
||||||
|
%%EOF
|
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "Close.pdf",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"template-rendering-intent" : "template"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "Download.pdf",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"template-rendering-intent" : "template"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
%PDF-1.5
|
||||||
|
%µí®û
|
||||||
|
3 0 obj
|
||||||
|
<< /Length 4 0 R
|
||||||
|
/Filter /FlateDecode
|
||||||
|
>>
|
||||||
|
stream
|
||||||
|
xœ}RKnC1Üû¾@¨ÁàÏ1z„êIIÉ¢íý¥Â³<C382>ÕDªÞ†™7†áóR´ïûß>R¼ýd@n‘d®ñ©tÈXq<>T+ Ð"¨eà"‘R‡gž.ñpf&¾;Q4/yºKòRK~IAŸéÅœŽ0ÿǵÔ]Ãéb¦ÿtôÅçK“We:`k±ØV*Ô®<C394>[Éxö
<0A>p§ˆšºvÈÐÔÂT[‘=«¼
|
||||||
|
Hå…JiÅÕ… <E280A6>Í35Œ\€²Íó…Èã‡z¢ºáp;õš@Ì·DAEßÐduÉÃênŒà©a<C2A9><61>¶(ÒµÓ-·ÀÚÍ×$¸–¦63PR[‚Z–ö°°m±ªCv<43>´–ýù„3ýáÄt`«ß”΋gÜguÖt|¸)WÏa§xêóÇKï×@ZªöxŒ=Õº†fóÐS#ŸóDs Sö–ÆVÿ]*ï¥R±ãñ3¹‡Ó<E280A1>9Y‡äxžÙz1
[Š³ß)É™+lâ©G›Åkß×ð~dˆÎÉ
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
4 0 obj
|
||||||
|
395
|
||||||
|
endobj
|
||||||
|
2 0 obj
|
||||||
|
<<
|
||||||
|
/ExtGState <<
|
||||||
|
/a0 << /CA 1 /ca 1 >>
|
||||||
|
>>
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
5 0 obj
|
||||||
|
<< /Type /Page
|
||||||
|
/Parent 1 0 R
|
||||||
|
/MediaBox [ 0 0 283.464569 283.464569 ]
|
||||||
|
/Contents 3 0 R
|
||||||
|
/Group <<
|
||||||
|
/Type /Group
|
||||||
|
/S /Transparency
|
||||||
|
/I true
|
||||||
|
/CS /DeviceRGB
|
||||||
|
>>
|
||||||
|
/Resources 2 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
1 0 obj
|
||||||
|
<< /Type /Pages
|
||||||
|
/Kids [ 5 0 R ]
|
||||||
|
/Count 1
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
6 0 obj
|
||||||
|
<< /Creator (cairo 1.14.8 (http://cairographics.org))
|
||||||
|
/Producer (cairo 1.14.8 (http://cairographics.org))
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
7 0 obj
|
||||||
|
<< /Type /Catalog
|
||||||
|
/Pages 1 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
xref
|
||||||
|
0 8
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000809 00000 n
|
||||||
|
0000000509 00000 n
|
||||||
|
0000000015 00000 n
|
||||||
|
0000000487 00000 n
|
||||||
|
0000000581 00000 n
|
||||||
|
0000000874 00000 n
|
||||||
|
0000001001 00000 n
|
||||||
|
trailer
|
||||||
|
<< /Size 8
|
||||||
|
/Root 7 0 R
|
||||||
|
/Info 6 0 R
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
1053
|
||||||
|
%%EOF
|
|
@ -32,9 +32,7 @@ class AvatarCache {
|
||||||
} else {
|
} else {
|
||||||
requestCallbacks[url] = [completion]
|
requestCallbacks[url] = [completion]
|
||||||
let task = URLSession.shared.dataTask(with: url) { data, response, error in
|
let task = URLSession.shared.dataTask(with: url) { data, response, error in
|
||||||
guard error == nil,
|
guard error == nil, let data = data, let image = UIImage(data: data) else {
|
||||||
let data = data,
|
|
||||||
let image = UIImage(data: data) else {
|
|
||||||
let callbacks = self.requestCallbacks.removeValue(forKey: url)
|
let callbacks = self.requestCallbacks.removeValue(forKey: url)
|
||||||
callbacks?.forEach({ callback in
|
callbacks?.forEach({ callback in
|
||||||
// todo: default avatar for failed requests
|
// todo: default avatar for failed requests
|
||||||
|
|
|
@ -0,0 +1,113 @@
|
||||||
|
//
|
||||||
|
// UIViewController+StatusTableViewCellDelegate.swift
|
||||||
|
// Tusker
|
||||||
|
//
|
||||||
|
// Created by Shadowfacts on 8/27/18.
|
||||||
|
// Copyright © 2018 Shadowfacts. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import MastodonKit
|
||||||
|
import SafariServices
|
||||||
|
|
||||||
|
extension UIViewController: StatusTableViewCellDelegate {
|
||||||
|
|
||||||
|
func selected(account: Account) {
|
||||||
|
// don't open if the account is the same as the current one
|
||||||
|
if let profileController = self as? ProfileTableViewController,
|
||||||
|
profileController.account == account {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let navigationController = navigationController else {
|
||||||
|
fatalError("Can't show profile VC when not in navigation controller")
|
||||||
|
}
|
||||||
|
let vc = ProfileTableViewController.create(for: account)
|
||||||
|
navigationController.pushViewController(vc, animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func selected(mention: Mention) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func selected(tag: Tag) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func selected(url: URL) {
|
||||||
|
let vc = SFSafariViewController(url: url)
|
||||||
|
present(vc, animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func selected(status: Status) {
|
||||||
|
// don't open if the conversation is the same as the current one
|
||||||
|
if let conversationController = self as? ConversationViewController,
|
||||||
|
conversationController.mainStatus == status {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let navigationController = navigationController else {
|
||||||
|
fatalError("Can't show conversation VC when not in navigation controller")
|
||||||
|
}
|
||||||
|
let vc = ConversationViewController.create(for: status)
|
||||||
|
navigationController.pushViewController(vc, animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func reply(to status: Status) {
|
||||||
|
let vc = ComposeViewController.create(inReplyTo: status)
|
||||||
|
present(vc, animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func showLargeAttachment(for attachmentView: AttachmentView) {
|
||||||
|
let vc = LargeImageViewController.create(image: attachmentView.image!, description: attachmentView.attachment.description)
|
||||||
|
vc.delegate = self
|
||||||
|
var frame = attachmentView.convert(attachmentView.bounds, to: view)
|
||||||
|
if let scrollView = view as? UIScrollView {
|
||||||
|
let scale = scrollView.zoomScale
|
||||||
|
let width = frame.width * scale
|
||||||
|
let height = frame.height * scale
|
||||||
|
let x = frame.minX * scale - scrollView.contentOffset.x + scrollView.frame.minX
|
||||||
|
let y = frame.minY * scale - scrollView.contentOffset.y + scrollView.frame.minY
|
||||||
|
frame = CGRect(x: x, y: y, width: width, height: height)
|
||||||
|
}
|
||||||
|
vc.originFrame = frame
|
||||||
|
vc.transitioningDelegate = self
|
||||||
|
present(vc, animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension UIViewController: LargeImageViewControllerDelegate {
|
||||||
|
func closeLargeImage() {
|
||||||
|
dismiss(animated: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension UIViewController: UIViewControllerTransitioningDelegate {
|
||||||
|
public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
|
||||||
|
if presented is LargeImageViewController {
|
||||||
|
return LargeImageExpandAnimationController()
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
|
||||||
|
if let dismissed = dismissed as? LargeImageViewController {
|
||||||
|
return LargeImageShrinkAnimationController(interactionController: dismissed.dismissInteractionController)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
|
||||||
|
if let animator = animator as? LargeImageShrinkAnimationController,
|
||||||
|
let interactionController = animator.interactionController,
|
||||||
|
interactionController.inProgress {
|
||||||
|
|
||||||
|
return interactionController
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,61 +0,0 @@
|
||||||
//
|
|
||||||
// UIViewController+StatusTableViewCellDelegate.swift
|
|
||||||
// Tusker
|
|
||||||
//
|
|
||||||
// Created by Shadowfacts on 8/27/18.
|
|
||||||
// Copyright © 2018 Shadowfacts. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
import MastodonKit
|
|
||||||
import SafariServices
|
|
||||||
|
|
||||||
extension UIViewController: StatusTableViewCellDelegate {
|
|
||||||
|
|
||||||
func selected(account: Account) {
|
|
||||||
// don't open if the account is the same as the current one
|
|
||||||
if let profileController = self as? ProfileTableViewController,
|
|
||||||
profileController.account == account {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let navigationController = navigationController else {
|
|
||||||
fatalError("Can't show profile VC when not in navigation controller")
|
|
||||||
}
|
|
||||||
let vc = ProfileTableViewController.create(for: account)
|
|
||||||
navigationController.pushViewController(vc, animated: true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func selected(mention: Mention) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func selected(tag: Tag) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func selected(url: URL) {
|
|
||||||
let vc = SFSafariViewController(url: url)
|
|
||||||
present(vc, animated: true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func selected(status: Status) {
|
|
||||||
// don't open if the conversation is the same as the current one
|
|
||||||
if let conversationController = self as? ConversationViewController,
|
|
||||||
conversationController.mainStatus == status {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let navigationController = navigationController else {
|
|
||||||
fatalError("Can't show conversation VC when not in navigation controller")
|
|
||||||
}
|
|
||||||
let vc = ConversationViewController.create(for: status)
|
|
||||||
navigationController.pushViewController(vc, animated: true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func reply(to status: Status) {
|
|
||||||
let vc = ComposeViewController.create(inReplyTo: status)
|
|
||||||
present(vc, animated: true)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -2,10 +2,12 @@
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
|
<key>NSPhotoLibraryAddUsageDescription</key>
|
||||||
|
<string>Save photos directly from other people's posts.</string>
|
||||||
<key>NSCameraUsageDescription</key>
|
<key>NSCameraUsageDescription</key>
|
||||||
<string>To post photos from the camera.</string>
|
<string>Post photos from the camera.</string>
|
||||||
<key>NSPhotoLibraryUsageDescription</key>
|
<key>NSPhotoLibraryUsageDescription</key>
|
||||||
<string>To post photos from the photo library.</string>
|
<string>Post photos from the photo library.</string>
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
|
|
|
@ -0,0 +1,152 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14313.13.2" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="8pH-Ao-p7G">
|
||||||
|
<device id="retina4_7" orientation="portrait">
|
||||||
|
<adaptation id="fullscreen"/>
|
||||||
|
</device>
|
||||||
|
<dependencies>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14283.9"/>
|
||||||
|
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
</dependencies>
|
||||||
|
<scenes>
|
||||||
|
<!--Large Image View Controller-->
|
||||||
|
<scene sceneID="jFU-tD-Rk9">
|
||||||
|
<objects>
|
||||||
|
<viewController id="8pH-Ao-p7G" customClass="LargeImageViewController" customModule="Tusker" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
|
<view key="view" contentMode="scaleToFill" id="w1M-d4-X1A">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" ambiguous="YES" minimumZoomScale="0.25" maximumZoomScale="2" translatesAutoresizingMaskIntoConstraints="NO" id="2wV-8N-EFe">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||||
|
<subviews>
|
||||||
|
<imageView contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Pd6-Mn-fcS">
|
||||||
|
<rect key="frame" x="0.0" y="-10" width="375" height="647"/>
|
||||||
|
<gestureRecognizers/>
|
||||||
|
</imageView>
|
||||||
|
</subviews>
|
||||||
|
<gestureRecognizers/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="Pd6-Mn-fcS" secondAttribute="bottom" id="4DC-Cz-yrd"/>
|
||||||
|
<constraint firstItem="Pd6-Mn-fcS" firstAttribute="leading" secondItem="2wV-8N-EFe" secondAttribute="leading" id="8NV-u8-aEP"/>
|
||||||
|
<constraint firstItem="Pd6-Mn-fcS" firstAttribute="top" secondItem="2wV-8N-EFe" secondAttribute="top" id="HLI-4J-tF7"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="Pd6-Mn-fcS" secondAttribute="trailing" id="eYw-ke-jQP"/>
|
||||||
|
</constraints>
|
||||||
|
<connections>
|
||||||
|
<outletCollection property="gestureRecognizers" destination="vji-Fm-RJ6" appends="YES" id="62b-Mz-RSo"/>
|
||||||
|
</connections>
|
||||||
|
</scrollView>
|
||||||
|
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="1EF-uu-YnK">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="375" height="36"/>
|
||||||
|
<subviews>
|
||||||
|
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Jjh-Jw-5E9">
|
||||||
|
<rect key="frame" x="16" y="16" width="20" height="20"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="20" id="cBe-e6-Eng"/>
|
||||||
|
<constraint firstAttribute="height" constant="20" id="uYQ-fp-4x9"/>
|
||||||
|
</constraints>
|
||||||
|
<color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
<state key="normal" image="Download"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="downloadPressed:" destination="8pH-Ao-p7G" eventType="touchUpInside" id="4LG-gn-IWR"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Z1X-K2-dVx">
|
||||||
|
<rect key="frame" x="339" y="16" width="20" height="20"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="20" id="1Oj-Ht-ytC"/>
|
||||||
|
<constraint firstAttribute="height" constant="20" id="6VA-5F-Y8k"/>
|
||||||
|
</constraints>
|
||||||
|
<color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
<state key="normal" image="Close"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="closeButtonPressed:" destination="8pH-Ao-p7G" eventType="touchUpInside" id="o1j-v4-hr8"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="Jjh-Jw-5E9" firstAttribute="top" secondItem="1EF-uu-YnK" secondAttribute="top" constant="16" id="9zK-pO-i0Z"/>
|
||||||
|
<constraint firstAttribute="height" secondItem="Z1X-K2-dVx" secondAttribute="height" constant="16" id="IYQ-ii-Ii4"/>
|
||||||
|
<constraint firstItem="Jjh-Jw-5E9" firstAttribute="leading" secondItem="1EF-uu-YnK" secondAttribute="leading" constant="16" id="P1u-fC-6Ln"/>
|
||||||
|
<constraint firstItem="Z1X-K2-dVx" firstAttribute="top" secondItem="1EF-uu-YnK" secondAttribute="top" constant="16" id="Tsg-Oc-1jH"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="Z1X-K2-dVx" secondAttribute="trailing" constant="16" id="wEw-jP-Teu"/>
|
||||||
|
</constraints>
|
||||||
|
</view>
|
||||||
|
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="8yv-cK-4o1">
|
||||||
|
<rect key="frame" x="0.0" y="630.5" width="375" height="36.5"/>
|
||||||
|
<subviews>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gvy-hM-kiM">
|
||||||
|
<rect key="frame" x="16" y="0.0" width="343" height="20.5"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
|
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="gvy-hM-kiM" secondAttribute="trailing" constant="16" id="3YX-UN-rIa"/>
|
||||||
|
<constraint firstAttribute="height" secondItem="gvy-hM-kiM" secondAttribute="height" constant="16" id="WrI-2J-9Z5"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="gvy-hM-kiM" secondAttribute="bottom" constant="16" id="bB4-b5-pmK"/>
|
||||||
|
<constraint firstItem="gvy-hM-kiM" firstAttribute="leading" secondItem="8yv-cK-4o1" secondAttribute="leading" constant="16" id="giM-Iz-Owe"/>
|
||||||
|
</constraints>
|
||||||
|
</view>
|
||||||
|
</subviews>
|
||||||
|
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
<gestureRecognizers/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="2wV-8N-EFe" firstAttribute="centerX" secondItem="w1M-d4-X1A" secondAttribute="centerX" id="5tb-e1-ObD"/>
|
||||||
|
<constraint firstItem="MqP-FN-iGs" firstAttribute="trailing" secondItem="1EF-uu-YnK" secondAttribute="trailing" id="8Aw-Lq-1Pa"/>
|
||||||
|
<constraint firstItem="8yv-cK-4o1" firstAttribute="leading" secondItem="MqP-FN-iGs" secondAttribute="leading" id="9qz-OC-7av"/>
|
||||||
|
<constraint firstItem="2wV-8N-EFe" firstAttribute="centerY" secondItem="w1M-d4-X1A" secondAttribute="centerY" id="Z9C-TF-I3s"/>
|
||||||
|
<constraint firstItem="1EF-uu-YnK" firstAttribute="top" secondItem="w1M-d4-X1A" secondAttribute="top" id="Zgr-Ck-Ly5"/>
|
||||||
|
<constraint firstItem="2wV-8N-EFe" firstAttribute="height" secondItem="w1M-d4-X1A" secondAttribute="height" id="adX-Xo-gwg"/>
|
||||||
|
<constraint firstItem="MqP-FN-iGs" firstAttribute="bottom" secondItem="8yv-cK-4o1" secondAttribute="bottom" id="bn5-Qt-ggu"/>
|
||||||
|
<constraint firstItem="MqP-FN-iGs" firstAttribute="trailing" secondItem="8yv-cK-4o1" secondAttribute="trailing" id="eSp-qn-PwQ"/>
|
||||||
|
<constraint firstItem="2wV-8N-EFe" firstAttribute="width" secondItem="w1M-d4-X1A" secondAttribute="width" id="mZZ-WZ-o8E"/>
|
||||||
|
<constraint firstItem="1EF-uu-YnK" firstAttribute="leading" secondItem="MqP-FN-iGs" secondAttribute="leading" id="xLf-u4-n14"/>
|
||||||
|
</constraints>
|
||||||
|
<viewLayoutGuide key="safeArea" id="MqP-FN-iGs"/>
|
||||||
|
<connections>
|
||||||
|
<outletCollection property="gestureRecognizers" destination="EgN-eL-bDf" appends="YES" id="CeU-w7-YSb"/>
|
||||||
|
</connections>
|
||||||
|
</view>
|
||||||
|
<nil key="simulatedTopBarMetrics"/>
|
||||||
|
<nil key="simulatedBottomBarMetrics"/>
|
||||||
|
<connections>
|
||||||
|
<outlet property="bottomControlsView" destination="8yv-cK-4o1" id="iA6-0y-hiN"/>
|
||||||
|
<outlet property="closeButton" destination="Z1X-K2-dVx" id="kR7-e7-Q8T"/>
|
||||||
|
<outlet property="closeButtonTopConstraint" destination="Tsg-Oc-1jH" id="07w-Ob-ker"/>
|
||||||
|
<outlet property="closeButtonTrailingConstraint" destination="wEw-jP-Teu" id="j0N-dF-qzf"/>
|
||||||
|
<outlet property="descriptionLabel" destination="gvy-hM-kiM" id="cbi-iB-Lob"/>
|
||||||
|
<outlet property="downloadButton" destination="Jjh-Jw-5E9" id="EAp-fo-yxs"/>
|
||||||
|
<outlet property="downloadButtonLeadingConstraint" destination="P1u-fC-6Ln" id="v2c-eL-7Pa"/>
|
||||||
|
<outlet property="downloadButtonTopConstraint" destination="9zK-pO-i0Z" id="eSj-yq-jk2"/>
|
||||||
|
<outlet property="imageView" destination="Pd6-Mn-fcS" id="Eh4-q1-L4S"/>
|
||||||
|
<outlet property="imageViewBottomConstraint" destination="4DC-Cz-yrd" id="X7o-5I-sQT"/>
|
||||||
|
<outlet property="imageViewLeadingConstraint" destination="8NV-u8-aEP" id="Hvu-dn-5O9"/>
|
||||||
|
<outlet property="imageViewTopConstraint" destination="HLI-4J-tF7" id="vpw-JN-YHC"/>
|
||||||
|
<outlet property="imageViewTrailingConstraint" destination="eYw-ke-jQP" id="7Ln-IV-VZW"/>
|
||||||
|
<outlet property="scrollView" destination="2wV-8N-EFe" id="dLq-Dq-32G"/>
|
||||||
|
<outlet property="topControlsHeightConstraint" destination="IYQ-ii-Ii4" id="Mht-sy-5fZ"/>
|
||||||
|
<outlet property="topControlsView" destination="1EF-uu-YnK" id="Syt-Ib-He6"/>
|
||||||
|
</connections>
|
||||||
|
</viewController>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="6Ga-cx-zlp" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
|
<tapGestureRecognizer id="vji-Fm-RJ6">
|
||||||
|
<connections>
|
||||||
|
<action selector="scrollViewPressed:" destination="8pH-Ao-p7G" id="kYC-qd-03P"/>
|
||||||
|
</connections>
|
||||||
|
</tapGestureRecognizer>
|
||||||
|
<tapGestureRecognizer numberOfTapsRequired="2" id="EgN-eL-bDf">
|
||||||
|
<connections>
|
||||||
|
<action selector="scrollViewDoubleTapped:" destination="8pH-Ao-p7G" id="JZe-G5-okr"/>
|
||||||
|
</connections>
|
||||||
|
</tapGestureRecognizer>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="-434.39999999999998" y="139.880059970015"/>
|
||||||
|
</scene>
|
||||||
|
</scenes>
|
||||||
|
<resources>
|
||||||
|
<image name="Close" width="209" height="209"/>
|
||||||
|
<image name="Download" width="284" height="284"/>
|
||||||
|
</resources>
|
||||||
|
</document>
|
|
@ -0,0 +1,206 @@
|
||||||
|
//
|
||||||
|
// LargeImageViewController.swift
|
||||||
|
// Tusker
|
||||||
|
//
|
||||||
|
// Created by Shadowfacts on 8/31/18.
|
||||||
|
// Copyright © 2018 Shadowfacts. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import Photos
|
||||||
|
|
||||||
|
protocol LargeImageViewControllerDelegate {
|
||||||
|
func closeLargeImage()
|
||||||
|
}
|
||||||
|
|
||||||
|
class LargeImageViewController: UIViewController, UIScrollViewDelegate {
|
||||||
|
|
||||||
|
static func create(image: UIImage, description: String?) -> LargeImageViewController {
|
||||||
|
guard let vc = UIStoryboard(name: "LargeImage", bundle: nil).instantiateInitialViewController() as? LargeImageViewController else { fatalError() }
|
||||||
|
vc.image = image
|
||||||
|
vc.imageDescription = description
|
||||||
|
return vc
|
||||||
|
}
|
||||||
|
|
||||||
|
var delegate: LargeImageViewControllerDelegate?
|
||||||
|
|
||||||
|
var originFrame: CGRect?
|
||||||
|
var dismissInteractionController: LargeImageInteractionController?
|
||||||
|
|
||||||
|
@IBOutlet weak var scrollView: UIScrollView!
|
||||||
|
@IBOutlet weak var imageView: UIImageView!
|
||||||
|
@IBOutlet weak var imageViewLeadingConstraint: NSLayoutConstraint!
|
||||||
|
@IBOutlet weak var imageViewTrailingConstraint: NSLayoutConstraint!
|
||||||
|
@IBOutlet weak var imageViewTopConstraint: NSLayoutConstraint!
|
||||||
|
@IBOutlet weak var imageViewBottomConstraint: NSLayoutConstraint!
|
||||||
|
|
||||||
|
@IBOutlet weak var topControlsView: UIView!
|
||||||
|
@IBOutlet weak var topControlsHeightConstraint: NSLayoutConstraint!
|
||||||
|
@IBOutlet weak var downloadButton: UIButton!
|
||||||
|
@IBOutlet weak var downloadButtonTopConstraint: NSLayoutConstraint!
|
||||||
|
@IBOutlet weak var downloadButtonLeadingConstraint: NSLayoutConstraint!
|
||||||
|
@IBOutlet weak var closeButton: UIButton!
|
||||||
|
@IBOutlet weak var closeButtonTopConstraint: NSLayoutConstraint!
|
||||||
|
@IBOutlet weak var closeButtonTrailingConstraint: NSLayoutConstraint!
|
||||||
|
|
||||||
|
@IBOutlet weak var bottomControlsView: UIView!
|
||||||
|
@IBOutlet weak var descriptionLabel: UILabel!
|
||||||
|
|
||||||
|
var image: UIImage?
|
||||||
|
var imageDescription: String?
|
||||||
|
|
||||||
|
var controlsVisible = true {
|
||||||
|
didSet {
|
||||||
|
UIView.animate(withDuration: 0.2) {
|
||||||
|
let topOffset = self.controlsVisible ? 0 : -self.topControlsView.bounds.height
|
||||||
|
self.topControlsView.transform = CGAffineTransform(translationX: 0, y: topOffset)
|
||||||
|
if self.imageDescription != nil {
|
||||||
|
let bottomOffset = self.controlsVisible ? 0 : self.bottomControlsView.bounds.height + self.view.safeAreaInsets.bottom
|
||||||
|
self.bottomControlsView.transform = CGAffineTransform(translationX: 0, y: bottomOffset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var prevZoomScale: CGFloat?
|
||||||
|
|
||||||
|
override var prefersStatusBarHidden: Bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
imageView.image = image
|
||||||
|
scrollView.delegate = self
|
||||||
|
imageView.bounds = CGRect(origin: .zero, size: image!.size)
|
||||||
|
|
||||||
|
if let imageDescription = imageDescription {
|
||||||
|
descriptionLabel.text = imageDescription
|
||||||
|
} else {
|
||||||
|
bottomControlsView.isHidden = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// if running on iPhone X
|
||||||
|
if UIScreen.main.nativeBounds.height == 2436 {
|
||||||
|
topControlsHeightConstraint.constant = 35
|
||||||
|
downloadButtonLeadingConstraint.constant = 35
|
||||||
|
closeButtonTrailingConstraint.constant = 35
|
||||||
|
}
|
||||||
|
|
||||||
|
dismissInteractionController = LargeImageInteractionController(viewController: self)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewDidLayoutSubviews() {
|
||||||
|
let widthScale = view.bounds.size.width / imageView.bounds.width
|
||||||
|
let heightScale = view.bounds.size.height / imageView.bounds.height
|
||||||
|
let minScale = min(widthScale, heightScale)
|
||||||
|
scrollView.minimumZoomScale = minScale
|
||||||
|
scrollView.zoomScale = minScale
|
||||||
|
scrollView.maximumZoomScale = minScale >= 1 ? minScale + 2 : 2
|
||||||
|
}
|
||||||
|
|
||||||
|
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
|
||||||
|
return imageView
|
||||||
|
}
|
||||||
|
|
||||||
|
func scrollViewDidZoom(_ scrollView: UIScrollView) {
|
||||||
|
centerImage()
|
||||||
|
|
||||||
|
let prevZoomScale = self.prevZoomScale ?? scrollView.minimumZoomScale
|
||||||
|
if scrollView.zoomScale <= scrollView.minimumZoomScale {
|
||||||
|
controlsVisible = true
|
||||||
|
} else if scrollView.zoomScale > prevZoomScale {
|
||||||
|
controlsVisible = false
|
||||||
|
}
|
||||||
|
self.prevZoomScale = scrollView.zoomScale
|
||||||
|
}
|
||||||
|
|
||||||
|
func centerImage() {
|
||||||
|
let yOffset = max(0, (view.bounds.size.height - imageView.frame.height) / 2)
|
||||||
|
imageViewTopConstraint.constant = yOffset
|
||||||
|
imageViewBottomConstraint.constant = yOffset
|
||||||
|
|
||||||
|
let xOffset = max(0, (view.bounds.size.width - imageView.frame.width) / 2)
|
||||||
|
imageViewLeadingConstraint.constant = xOffset
|
||||||
|
imageViewTrailingConstraint.constant = xOffset
|
||||||
|
}
|
||||||
|
|
||||||
|
func zoomRectFor(scale: CGFloat, center: CGPoint) -> CGRect {
|
||||||
|
var zoomRect = CGRect.zero
|
||||||
|
zoomRect.size.width = imageView.frame.width / scale
|
||||||
|
zoomRect.size.height = imageView.frame.height / scale
|
||||||
|
let newCenter = scrollView.convert(center, to: imageView)
|
||||||
|
zoomRect.origin.x = newCenter.x - (zoomRect.width / 2)
|
||||||
|
zoomRect.origin.y = newCenter.y - (zoomRect.height / 2)
|
||||||
|
return zoomRect
|
||||||
|
}
|
||||||
|
|
||||||
|
func animateZoomOut() {
|
||||||
|
UIView.animate(withDuration: 0.3, animations: {
|
||||||
|
self.scrollView.zoomScale = self.scrollView.minimumZoomScale
|
||||||
|
self.view.layoutIfNeeded()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// MARK: - Navigation
|
||||||
|
|
||||||
|
// In a storyboard-based application, you will often want to do a little preparation before navigation
|
||||||
|
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
||||||
|
// Get the new view controller using segue.destination.
|
||||||
|
// Pass the selected object to the new view controller.
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
@IBAction func scrollViewPressed(_ sender: UITapGestureRecognizer) {
|
||||||
|
if scrollView.zoomScale > scrollView.minimumZoomScale {
|
||||||
|
animateZoomOut()
|
||||||
|
} else {
|
||||||
|
controlsVisible = !controlsVisible
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func scrollViewDoubleTapped(_ recognizer: UITapGestureRecognizer) {
|
||||||
|
if scrollView.zoomScale <= scrollView.minimumZoomScale {
|
||||||
|
let point = recognizer.location(in: recognizer.view)
|
||||||
|
let scale: CGFloat
|
||||||
|
if scrollView.minimumZoomScale < 1 {
|
||||||
|
if 1 - scrollView.zoomScale <= 0.5 {
|
||||||
|
scale = scrollView.zoomScale + 1
|
||||||
|
} else {
|
||||||
|
scale = 1
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
scale = scrollView.maximumZoomScale
|
||||||
|
}
|
||||||
|
let rect = zoomRectFor(scale: scale, center: point)
|
||||||
|
UIView.animate(withDuration: 0.3) {
|
||||||
|
self.scrollView.zoom(to: rect, animated: false)
|
||||||
|
self.view.layoutIfNeeded()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
animateZoomOut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func closeButtonPressed(_ sender: Any) {
|
||||||
|
delegate?.closeLargeImage()
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func downloadPressed(_ sender: Any) {
|
||||||
|
guard let image = image else { return }
|
||||||
|
PHPhotoLibrary.shared().performChanges({
|
||||||
|
PHAssetChangeRequest.creationRequestForAsset(from: image)
|
||||||
|
}, completionHandler: { success, error in
|
||||||
|
if success {
|
||||||
|
return
|
||||||
|
} else if let error = error {
|
||||||
|
print("Couldn't save photo: \(error)")
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
//
|
||||||
|
// LargeImageExpandAnimationController.swift
|
||||||
|
// Tusker
|
||||||
|
//
|
||||||
|
// Created by Shadowfacts on 9/1/18.
|
||||||
|
// Copyright © 2018 Shadowfacts. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class LargeImageExpandAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
|
||||||
|
|
||||||
|
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
|
||||||
|
return 0.2
|
||||||
|
}
|
||||||
|
|
||||||
|
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
|
||||||
|
guard let fromVC = transitionContext.viewController(forKey: .from),
|
||||||
|
let toVC = transitionContext.viewController(forKey: .to) as? LargeImageViewController,
|
||||||
|
let originFrame = toVC.originFrame else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let containerView = transitionContext.containerView
|
||||||
|
let finalVCFrame = transitionContext.finalFrame(for: toVC)
|
||||||
|
let image = toVC.image!
|
||||||
|
let ratio = image.size.width / image.size.height
|
||||||
|
let width = finalVCFrame.width
|
||||||
|
let height = width / ratio
|
||||||
|
let finalFrame = CGRect(x: finalVCFrame.midX - width / 2, y: finalVCFrame.midY - height / 2, width: width, height: height)
|
||||||
|
|
||||||
|
let imageView = UIImageView(frame: originFrame)
|
||||||
|
imageView.image = toVC.image!
|
||||||
|
imageView.contentMode = .scaleAspectFill
|
||||||
|
imageView.layer.cornerRadius = 5
|
||||||
|
imageView.layer.masksToBounds = true
|
||||||
|
|
||||||
|
let blackView = UIView(frame: finalVCFrame)
|
||||||
|
blackView.backgroundColor = .black
|
||||||
|
blackView.alpha = 0
|
||||||
|
|
||||||
|
containerView.addSubview(toVC.view)
|
||||||
|
containerView.addSubview(blackView)
|
||||||
|
containerView.addSubview(imageView)
|
||||||
|
|
||||||
|
toVC.view.isHidden = true
|
||||||
|
|
||||||
|
let duration = transitionDuration(using: transitionContext)
|
||||||
|
UIView.animate(withDuration: duration, animations: {
|
||||||
|
imageView.frame = finalFrame
|
||||||
|
blackView.alpha = 1
|
||||||
|
}, completion: { _ in
|
||||||
|
toVC.view.isHidden = false
|
||||||
|
fromVC.view.isHidden = false
|
||||||
|
blackView.removeFromSuperview()
|
||||||
|
imageView.removeFromSuperview()
|
||||||
|
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
//
|
||||||
|
// LargeImageInteractionController.swift
|
||||||
|
// Tusker
|
||||||
|
//
|
||||||
|
// Created by Shadowfacts on 9/1/18.
|
||||||
|
// Copyright © 2018 Shadowfacts. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class LargeImageInteractionController: UIPercentDrivenInteractiveTransition {
|
||||||
|
|
||||||
|
var inProgress = false
|
||||||
|
var direction: CGFloat?
|
||||||
|
|
||||||
|
var shouldCompleteTransition = false
|
||||||
|
private weak var viewController: UIViewController!
|
||||||
|
|
||||||
|
init(viewController: UIViewController) {
|
||||||
|
super.init()
|
||||||
|
self.viewController = viewController
|
||||||
|
viewController.view.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(handleGesture(_:))))
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func handleGesture(_ recognizer: UIPanGestureRecognizer) {
|
||||||
|
let translation = recognizer.translation(in: recognizer.view!.superview!)
|
||||||
|
var progress = translation.y / 200
|
||||||
|
if let direction = direction {
|
||||||
|
progress *= direction
|
||||||
|
} else {
|
||||||
|
direction = progress > 0 ? 1 : -1
|
||||||
|
progress = abs(progress)
|
||||||
|
}
|
||||||
|
progress = min(max(progress, 0), 1)
|
||||||
|
let velocity = abs(recognizer.velocity(in: recognizer.view!.superview!).y)
|
||||||
|
|
||||||
|
switch recognizer.state {
|
||||||
|
case .began:
|
||||||
|
inProgress = true
|
||||||
|
viewController.dismiss(animated: true)
|
||||||
|
case .changed:
|
||||||
|
shouldCompleteTransition = progress > 0.5 || velocity > 1000
|
||||||
|
update(progress)
|
||||||
|
case .cancelled:
|
||||||
|
inProgress = false
|
||||||
|
cancel()
|
||||||
|
case .ended:
|
||||||
|
inProgress = false
|
||||||
|
direction = nil
|
||||||
|
if shouldCompleteTransition {
|
||||||
|
finish()
|
||||||
|
} else {
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
//
|
||||||
|
// LargeImageShrinkAnimationController.swift
|
||||||
|
// Tusker
|
||||||
|
//
|
||||||
|
// Created by Shadowfacts on 9/1/18.
|
||||||
|
// Copyright © 2018 Shadowfacts. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class LargeImageShrinkAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
|
||||||
|
|
||||||
|
let interactionController: LargeImageInteractionController?
|
||||||
|
|
||||||
|
init(interactionController: LargeImageInteractionController?) {
|
||||||
|
self.interactionController = interactionController
|
||||||
|
}
|
||||||
|
|
||||||
|
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
|
||||||
|
return 0.2
|
||||||
|
}
|
||||||
|
|
||||||
|
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
|
||||||
|
guard let fromVC = transitionContext.viewController(forKey: .from) as? LargeImageViewController,
|
||||||
|
let toVC = transitionContext.viewController(forKey: .to),
|
||||||
|
let finalFrame = fromVC.originFrame else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let originalVCFrame = fromVC.view.frame
|
||||||
|
|
||||||
|
let containerView = transitionContext.containerView
|
||||||
|
let image = fromVC.image!
|
||||||
|
let ratio = image.size.width / image.size.height
|
||||||
|
let width = originalVCFrame.width
|
||||||
|
let height = width / ratio
|
||||||
|
let originalFrame = CGRect(x: originalVCFrame.midX - width / 2, y: originalVCFrame.midY - height / 2, width: width, height: height)
|
||||||
|
|
||||||
|
let imageView = UIImageView(frame: originalFrame)
|
||||||
|
imageView.image = fromVC.image!
|
||||||
|
imageView.contentMode = .scaleAspectFill
|
||||||
|
imageView.layer.cornerRadius = 5
|
||||||
|
imageView.layer.masksToBounds = true
|
||||||
|
|
||||||
|
let blackView = UIView(frame: originalVCFrame)
|
||||||
|
blackView.backgroundColor = .black
|
||||||
|
blackView.alpha = 1
|
||||||
|
|
||||||
|
containerView.addSubview(toVC.view)
|
||||||
|
containerView.addSubview(blackView)
|
||||||
|
containerView.addSubview(imageView)
|
||||||
|
|
||||||
|
let duration = transitionDuration(using: transitionContext)
|
||||||
|
UIView.animate(withDuration: duration, animations: {
|
||||||
|
imageView.frame = finalFrame
|
||||||
|
blackView.alpha = 0
|
||||||
|
}, completion: { _ in
|
||||||
|
blackView.removeFromSuperview()
|
||||||
|
imageView.removeFromSuperview()
|
||||||
|
if transitionContext.transitionWasCancelled {
|
||||||
|
toVC.view.removeFromSuperview()
|
||||||
|
}
|
||||||
|
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
//
|
||||||
|
// AttachmentView.swift
|
||||||
|
// Tusker
|
||||||
|
//
|
||||||
|
// Created by Shadowfacts on 8/31/18.
|
||||||
|
// Copyright © 2018 Shadowfacts. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import MastodonKit
|
||||||
|
|
||||||
|
protocol AttachmentViewDelegate {
|
||||||
|
func showLargeAttachment(for attachmentView: AttachmentView)
|
||||||
|
}
|
||||||
|
|
||||||
|
class AttachmentView: UIImageView {
|
||||||
|
|
||||||
|
var delegate: AttachmentViewDelegate?
|
||||||
|
|
||||||
|
var attachment: Attachment!
|
||||||
|
|
||||||
|
var task: URLSessionDataTask?
|
||||||
|
|
||||||
|
required init?(coder aDecoder: NSCoder) {
|
||||||
|
super.init(coder: aDecoder)
|
||||||
|
commonInit()
|
||||||
|
}
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
commonInit()
|
||||||
|
}
|
||||||
|
|
||||||
|
convenience init(frame: CGRect, attachment: Attachment) {
|
||||||
|
self.init(frame: frame)
|
||||||
|
self.attachment = attachment
|
||||||
|
loadImage()
|
||||||
|
}
|
||||||
|
|
||||||
|
func commonInit() {
|
||||||
|
contentMode = .scaleAspectFill
|
||||||
|
layer.masksToBounds = true
|
||||||
|
isUserInteractionEnabled = true
|
||||||
|
addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(imagePressed)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadImage() {
|
||||||
|
guard let url = URL(string: attachment.url) else { fatalError("Invalid URL: \(attachment.url)") }
|
||||||
|
task = URLSession.shared.dataTask(with: url) { data, response, error in
|
||||||
|
guard error == nil, let data = data, let image = UIImage(data: data) else { return }
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.image = image
|
||||||
|
}
|
||||||
|
}
|
||||||
|
task!.resume()
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func imagePressed() {
|
||||||
|
if image != nil {
|
||||||
|
delegate?.showLargeAttachment(for: self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -23,6 +23,8 @@ protocol StatusTableViewCellDelegate {
|
||||||
|
|
||||||
func reply(to status: Status)
|
func reply(to status: Status)
|
||||||
|
|
||||||
|
func showLargeAttachment(for attachmentView: AttachmentView)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class StatusTableViewCell: UITableViewCell, PreferencesAdaptive {
|
class StatusTableViewCell: UITableViewCell, PreferencesAdaptive {
|
||||||
|
@ -35,6 +37,7 @@ class StatusTableViewCell: UITableViewCell, PreferencesAdaptive {
|
||||||
@IBOutlet weak var avatarImageView: UIImageView!
|
@IBOutlet weak var avatarImageView: UIImageView!
|
||||||
@IBOutlet weak var reblogLabel: UILabel!
|
@IBOutlet weak var reblogLabel: UILabel!
|
||||||
@IBOutlet weak var timestampLabel: UILabel!
|
@IBOutlet weak var timestampLabel: UILabel!
|
||||||
|
@IBOutlet weak var attachmentsView: UIView!
|
||||||
|
|
||||||
var status: Status!
|
var status: Status!
|
||||||
var account: Account!
|
var account: Account!
|
||||||
|
@ -44,6 +47,8 @@ class StatusTableViewCell: UITableViewCell, PreferencesAdaptive {
|
||||||
|
|
||||||
var updateTimestampWorkItem: DispatchWorkItem?
|
var updateTimestampWorkItem: DispatchWorkItem?
|
||||||
|
|
||||||
|
var attachmentDataTasks: [URLSessionDataTask] = []
|
||||||
|
|
||||||
override func awakeFromNib() {
|
override func awakeFromNib() {
|
||||||
displayNameLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(accountPressed)))
|
displayNameLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(accountPressed)))
|
||||||
displayNameLabel.isUserInteractionEnabled = true
|
displayNameLabel.isUserInteractionEnabled = true
|
||||||
|
@ -54,6 +59,8 @@ class StatusTableViewCell: UITableViewCell, PreferencesAdaptive {
|
||||||
avatarImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(accountPressed)))
|
avatarImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(accountPressed)))
|
||||||
avatarImageView.isUserInteractionEnabled = true
|
avatarImageView.isUserInteractionEnabled = true
|
||||||
avatarImageView.layer.masksToBounds = true
|
avatarImageView.layer.masksToBounds = true
|
||||||
|
attachmentsView.layer.cornerRadius = 5
|
||||||
|
attachmentsView.layer.masksToBounds = true
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateUIForPreferences() {
|
func updateUIForPreferences() {
|
||||||
|
@ -95,6 +102,35 @@ class StatusTableViewCell: UITableViewCell, PreferencesAdaptive {
|
||||||
}
|
}
|
||||||
updateTimestamp()
|
updateTimestamp()
|
||||||
|
|
||||||
|
attachmentsView.subviews.forEach { $0.removeFromSuperview() }
|
||||||
|
let attachments = status.mediaAttachments.filter({ $0.type == .image })
|
||||||
|
if attachments.count > 0 {
|
||||||
|
attachmentsView.isHidden = false
|
||||||
|
let width = attachmentsView.bounds.width
|
||||||
|
let height: CGFloat = 200
|
||||||
|
switch attachments.count {
|
||||||
|
case 1:
|
||||||
|
addAttachmentView(frame: CGRect(x: 0, y: 0, width: width, height: height), attachment: attachments[0])
|
||||||
|
case 2:
|
||||||
|
addAttachmentView(frame: CGRect(x: 0, y: 0, width: width / 2 - 4, height: height), attachment: attachments[0])
|
||||||
|
addAttachmentView(frame: CGRect(x: width / 2 + 4, y: 0, width: width / 2 - 4, height: height), attachment: attachments[1])
|
||||||
|
case 3:
|
||||||
|
addAttachmentView(frame: CGRect(x: 0, y: 0, width: width / 2 - 4, height: height), attachment: attachments[0])
|
||||||
|
addAttachmentView(frame: CGRect(x: width / 2 + 4, y: 0, width: width / 2 - 4, height: height / 2 - 4), attachment: attachments[1])
|
||||||
|
addAttachmentView(frame: CGRect(x: width / 2 + 4, y: height / 2 + 4, width: width / 2 - 4, height: height / 2 - 4), attachment: attachments[2])
|
||||||
|
case 4:
|
||||||
|
addAttachmentView(frame: CGRect(x: 0, y: 0, width: width / 2 - 4, height: height / 2 - 4), attachment: attachments[0])
|
||||||
|
addAttachmentView(frame: CGRect(x: 0, y: height / 2 + 4, width: width / 2 - 4, height: height / 2 - 4), attachment: attachments[1])
|
||||||
|
addAttachmentView(frame: CGRect(x: width / 2 + 4, y: 0, width: width / 2 - 4, height: height / 2 - 4), attachment: attachments[2])
|
||||||
|
addAttachmentView(frame: CGRect(x: width / 2 + 4, y: height / 2 + 4, width: width / 2 - 4, height: height / 2 - 4), attachment: attachments[3])
|
||||||
|
default:
|
||||||
|
fatalError("Too many attachments")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
attachmentsView.isHidden = true
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
contentLabel.status = status
|
contentLabel.status = status
|
||||||
contentLabel.delegate = self
|
contentLabel.delegate = self
|
||||||
}
|
}
|
||||||
|
@ -120,12 +156,21 @@ class StatusTableViewCell: UITableViewCell, PreferencesAdaptive {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func addAttachmentView(frame: CGRect, attachment: Attachment) {
|
||||||
|
let attachmentView = AttachmentView(frame: frame, attachment: attachment)
|
||||||
|
attachmentView.delegate = self
|
||||||
|
attachmentsView.addSubview(attachmentView)
|
||||||
|
}
|
||||||
|
|
||||||
override func prepareForReuse() {
|
override func prepareForReuse() {
|
||||||
if let url = avatarURL {
|
if let url = avatarURL {
|
||||||
AvatarCache.shared.cancel(url)
|
AvatarCache.shared.cancel(url)
|
||||||
}
|
}
|
||||||
updateTimestampWorkItem?.cancel()
|
updateTimestampWorkItem?.cancel()
|
||||||
updateTimestampWorkItem = nil
|
updateTimestampWorkItem = nil
|
||||||
|
attachmentsView.subviews.forEach { view in
|
||||||
|
(view as? AttachmentView)?.task?.cancel()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func replyPressed(_ sender: Any) {
|
@IBAction func replyPressed(_ sender: Any) {
|
||||||
|
@ -162,3 +207,9 @@ extension StatusTableViewCell: HTMLContentLabelDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension StatusTableViewCell: AttachmentViewDelegate {
|
||||||
|
func showLargeAttachment(for attachmentView: AttachmentView) {
|
||||||
|
delegate?.showLargeAttachment(for: attachmentView)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -15,11 +15,11 @@
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="150"/>
|
<rect key="frame" x="0.0" y="0.0" width="375" height="150"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="yNh-ac-v6c">
|
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="top" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="yNh-ac-v6c">
|
||||||
<rect key="frame" x="16" y="8" width="343" height="142"/>
|
<rect key="frame" x="16" y="8" width="343" height="142"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Reblogged by Person" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="lDH-50-AJZ">
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Reblogged by Person" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="lDH-50-AJZ">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="343" height="20.5"/>
|
<rect key="frame" x="0.0" y="0.0" width="163.5" height="20.5"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
<color key="textColor" white="0.33333333333333331" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
<color key="textColor" white="0.33333333333333331" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
|
@ -78,11 +78,18 @@
|
||||||
<constraint firstItem="QMP-j2-HLn" firstAttribute="leading" secondItem="ve3-Y1-NQH" secondAttribute="leading" id="zeW-tQ-uJl"/>
|
<constraint firstItem="QMP-j2-HLn" firstAttribute="leading" secondItem="ve3-Y1-NQH" secondAttribute="leading" id="zeW-tQ-uJl"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
</view>
|
</view>
|
||||||
|
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="nbq-yr-2mA">
|
||||||
|
<rect key="frame" x="0.0" y="108" width="343" height="0.0"/>
|
||||||
|
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" priority="999" constant="200" id="J42-49-2MU"/>
|
||||||
|
</constraints>
|
||||||
|
</view>
|
||||||
<stackView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="Zlb-yt-NTw">
|
<stackView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="Zlb-yt-NTw">
|
||||||
<rect key="frame" x="0.0" y="112" width="343" height="30"/>
|
<rect key="frame" x="0.0" y="112" width="39" height="30"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<button opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" contentHorizontalAlignment="leading" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="rKF-yF-KIa">
|
<button opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" contentHorizontalAlignment="leading" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="rKF-yF-KIa">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="343" height="30"/>
|
<rect key="frame" x="0.0" y="0.0" width="39" height="30"/>
|
||||||
<state key="normal" title="Reply"/>
|
<state key="normal" title="Reply"/>
|
||||||
<connections>
|
<connections>
|
||||||
<action selector="replyPressed:" destination="iN0-l3-epB" eventType="touchUpInside" id="Ohg-uU-d3Z"/>
|
<action selector="replyPressed:" destination="iN0-l3-epB" eventType="touchUpInside" id="Ohg-uU-d3Z"/>
|
||||||
|
@ -91,6 +98,9 @@
|
||||||
</subviews>
|
</subviews>
|
||||||
</stackView>
|
</stackView>
|
||||||
</subviews>
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="nbq-yr-2mA" firstAttribute="width" secondItem="yNh-ac-v6c" secondAttribute="width" id="3Ag-HE-h4m"/>
|
||||||
|
</constraints>
|
||||||
</stackView>
|
</stackView>
|
||||||
</subviews>
|
</subviews>
|
||||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
@ -103,6 +113,7 @@
|
||||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||||
<viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
|
<viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
|
||||||
<connections>
|
<connections>
|
||||||
|
<outlet property="attachmentsView" destination="nbq-yr-2mA" id="GkU-Xk-pc0"/>
|
||||||
<outlet property="avatarImageView" destination="QMP-j2-HLn" id="CAl-hK-i3j"/>
|
<outlet property="avatarImageView" destination="QMP-j2-HLn" id="CAl-hK-i3j"/>
|
||||||
<outlet property="contentLabel" destination="HrJ-t9-KcD" id="tbD-3T-nNP"/>
|
<outlet property="contentLabel" destination="HrJ-t9-KcD" id="tbD-3T-nNP"/>
|
||||||
<outlet property="displayNameLabel" destination="gll-xe-FSr" id="63y-He-xy1"/>
|
<outlet property="displayNameLabel" destination="gll-xe-FSr" id="63y-He-xy1"/>
|
||||||
|
|
Loading…
Reference in New Issue