forked from shadowfacts/Tusker
Large image view
This commit is contained in:
parent
834b6cfc42
commit
e523d69e33
|
@ -13,6 +13,9 @@
|
|||
D6333B372137838300CE884A /* AttributedString+Trim.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6333B362137838300CE884A /* AttributedString+Trim.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 */; };
|
||||
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 */; };
|
||||
D64D0AAD2128D88B005A6F37 /* LocalData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64D0AAC2128D88B005A6F37 /* LocalData.swift */; };
|
||||
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 */; };
|
||||
D667E5EB21349EF80057A976 /* ProfileHeaderTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D667E5EA21349EF80057A976 /* ProfileHeaderTableViewCell.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 */; };
|
||||
D667E5F52135BCD50057A976 /* ConversationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D667E5F42135BCD50057A976 /* ConversationViewController.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 */; };
|
||||
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 */; };
|
||||
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 */; };
|
||||
D6D4DDD5212518A000E1C4BB /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D6D4DDD3212518A000E1C4BB /* Main.storyboard */; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
|
@ -117,12 +126,15 @@
|
|||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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; };
|
||||
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>"; };
|
||||
|
@ -168,6 +180,16 @@
|
|||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
D646C954213B364600269FB5 /* Transitions */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D646C955213B365700269FB5 /* LargeImageExpandAnimationController.swift */,
|
||||
D646C957213B367000269FB5 /* LargeImageShrinkAnimationController.swift */,
|
||||
D646C959213B5D0500269FB5 /* LargeImageInteractionController.swift */,
|
||||
);
|
||||
path = Transitions;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D663626021360A9600C9CBA2 /* Preferences */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -181,7 +203,7 @@
|
|||
D667E5F62135C2ED0057A976 /* Extensions */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D667E5F02134D5050057A976 /* UIViewController+StatusTableViewCellDelegate.swift */,
|
||||
D667E5F02134D5050057A976 /* UIViewController+Delegates.swift */,
|
||||
D667E5F72135C3040057A976 /* Mastodon+Equatable.swift */,
|
||||
D663626B21361C6700C9CBA2 /* Account+Preferences.swift */,
|
||||
D66362722136FFC600C9CBA2 /* UITextView+Placeholder.swift */,
|
||||
|
@ -204,6 +226,7 @@
|
|||
D663625C2135C74800C9CBA2 /* ConversationMainStatusTableViewCell.xib */,
|
||||
D663625E2135C75500C9CBA2 /* ConversationMainStatusTableViewCell.swift */,
|
||||
D6333B762138D94E00CE884A /* ComposeMediaView.swift */,
|
||||
D6C94D882139E6EC00CB5196 /* AttachmentView.swift */,
|
||||
);
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
|
@ -272,6 +295,7 @@
|
|||
D6F953E9212519B800CF0F2B /* View Controllers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D646C954213B364600269FB5 /* Transitions */,
|
||||
D6F953EB212519E700CF0F2B /* TimelineTableViewController.swift */,
|
||||
D64D0AB02128D9AE005A6F37 /* OnboardingViewController.swift */,
|
||||
04DACE8B212CB14B009840C4 /* MainTabBarViewController.swift */,
|
||||
|
@ -279,6 +303,7 @@
|
|||
D667E5F42135BCD50057A976 /* ConversationViewController.swift */,
|
||||
D663626721360E2C00C9CBA2 /* PreferencesTableViewController.swift */,
|
||||
D66362702136338600C9CBA2 /* ComposeViewController.swift */,
|
||||
D6C94D862139E62700CB5196 /* LargeImageViewController.swift */,
|
||||
);
|
||||
path = "View Controllers";
|
||||
sourceTree = "<group>";
|
||||
|
@ -293,6 +318,7 @@
|
|||
D667E5F22135BC260057A976 /* Conversation.storyboard */,
|
||||
D663626521360DD700C9CBA2 /* Preferences.storyboard */,
|
||||
D663626E213632A000C9CBA2 /* Compose.storyboard */,
|
||||
D6C94D842139DFD800CB5196 /* LargeImage.storyboard */,
|
||||
);
|
||||
path = Storyboards;
|
||||
sourceTree = "<group>";
|
||||
|
@ -418,6 +444,7 @@
|
|||
D663625D2135C74800C9CBA2 /* ConversationMainStatusTableViewCell.xib in Resources */,
|
||||
D6F953EE21251A0700CF0F2B /* Timeline.storyboard in Resources */,
|
||||
D663626F213632A000C9CBA2 /* Compose.storyboard in Resources */,
|
||||
D6C94D852139DFD800CB5196 /* LargeImage.storyboard in Resources */,
|
||||
D6D4DDD5212518A000E1C4BB /* Main.storyboard in Resources */,
|
||||
D667E5E3213499F70057A976 /* Profile.storyboard in Resources */,
|
||||
D663626621360DD700C9CBA2 /* Preferences.storyboard in Resources */,
|
||||
|
@ -451,12 +478,16 @@
|
|||
D667E5F52135BCD50057A976 /* ConversationViewController.swift in Sources */,
|
||||
D6F953F021251A2900CF0F2B /* MastodonController.swift in Sources */,
|
||||
D66362712136338600C9CBA2 /* ComposeViewController.swift in Sources */,
|
||||
D646C958213B367000269FB5 /* LargeImageShrinkAnimationController.swift in Sources */,
|
||||
D646C956213B365700269FB5 /* LargeImageExpandAnimationController.swift in Sources */,
|
||||
D667E5F82135C3040057A976 /* Mastodon+Equatable.swift in Sources */,
|
||||
04DACE8E212CC7CC009840C4 /* AvatarCache.swift in Sources */,
|
||||
D6333B772138D94E00CE884A /* ComposeMediaView.swift in Sources */,
|
||||
D663626421360D2300C9CBA2 /* AvatarStyle.swift in Sources */,
|
||||
D6BED174212667E900F02DA0 /* StatusTableViewCell.swift in Sources */,
|
||||
D64D0AAD2128D88B005A6F37 /* LocalData.swift in Sources */,
|
||||
D6C94D892139E6EC00CB5196 /* AttachmentView.swift in Sources */,
|
||||
D6C94D872139E62700CB5196 /* LargeImageViewController.swift in Sources */,
|
||||
D663626221360B1900C9CBA2 /* Preferences.swift in Sources */,
|
||||
D6333B792139AEFD00CE884A /* Date+TimeAgo.swift in Sources */,
|
||||
D663626C21361C6700C9CBA2 /* Account+Preferences.swift in Sources */,
|
||||
|
@ -466,11 +497,12 @@
|
|||
D663626821360E2C00C9CBA2 /* PreferencesTableViewController.swift in Sources */,
|
||||
D66362732136FFC600C9CBA2 /* UITextView+Placeholder.swift in Sources */,
|
||||
D66362752137068A00C9CBA2 /* Visibility+Helpers.swift in Sources */,
|
||||
D646C95A213B5D0500269FB5 /* LargeImageInteractionController.swift in Sources */,
|
||||
D6F953EC212519E700CF0F2B /* TimelineTableViewController.swift in Sources */,
|
||||
D663626A2136163000C9CBA2 /* PreferencesAdaptive.swift in Sources */,
|
||||
D667E5EB21349EF80057A976 /* ProfileHeaderTableViewCell.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 */,
|
||||
D667E5E721349D4C0057A976 /* ProfileTableViewController.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 {
|
||||
requestCallbacks[url] = [completion]
|
||||
let task = URLSession.shared.dataTask(with: url) { data, response, error in
|
||||
guard error == nil,
|
||||
let data = data,
|
||||
let image = UIImage(data: data) else {
|
||||
guard error == nil, let data = data, let image = UIImage(data: data) else {
|
||||
let callbacks = self.requestCallbacks.removeValue(forKey: url)
|
||||
callbacks?.forEach({ callback in
|
||||
// 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">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSPhotoLibraryAddUsageDescription</key>
|
||||
<string>Save photos directly from other people's posts.</string>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>To post photos from the camera.</string>
|
||||
<string>Post photos from the camera.</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>To post photos from the photo library.</string>
|
||||
<string>Post photos from the photo library.</string>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<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 showLargeAttachment(for attachmentView: AttachmentView)
|
||||
|
||||
}
|
||||
|
||||
class StatusTableViewCell: UITableViewCell, PreferencesAdaptive {
|
||||
|
@ -35,6 +37,7 @@ class StatusTableViewCell: UITableViewCell, PreferencesAdaptive {
|
|||
@IBOutlet weak var avatarImageView: UIImageView!
|
||||
@IBOutlet weak var reblogLabel: UILabel!
|
||||
@IBOutlet weak var timestampLabel: UILabel!
|
||||
@IBOutlet weak var attachmentsView: UIView!
|
||||
|
||||
var status: Status!
|
||||
var account: Account!
|
||||
|
@ -44,6 +47,8 @@ class StatusTableViewCell: UITableViewCell, PreferencesAdaptive {
|
|||
|
||||
var updateTimestampWorkItem: DispatchWorkItem?
|
||||
|
||||
var attachmentDataTasks: [URLSessionDataTask] = []
|
||||
|
||||
override func awakeFromNib() {
|
||||
displayNameLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(accountPressed)))
|
||||
displayNameLabel.isUserInteractionEnabled = true
|
||||
|
@ -54,6 +59,8 @@ class StatusTableViewCell: UITableViewCell, PreferencesAdaptive {
|
|||
avatarImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(accountPressed)))
|
||||
avatarImageView.isUserInteractionEnabled = true
|
||||
avatarImageView.layer.masksToBounds = true
|
||||
attachmentsView.layer.cornerRadius = 5
|
||||
attachmentsView.layer.masksToBounds = true
|
||||
}
|
||||
|
||||
func updateUIForPreferences() {
|
||||
|
@ -95,6 +102,35 @@ class StatusTableViewCell: UITableViewCell, PreferencesAdaptive {
|
|||
}
|
||||
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.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() {
|
||||
if let url = avatarURL {
|
||||
AvatarCache.shared.cancel(url)
|
||||
}
|
||||
updateTimestampWorkItem?.cancel()
|
||||
updateTimestampWorkItem = nil
|
||||
attachmentsView.subviews.forEach { view in
|
||||
(view as? AttachmentView)?.task?.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
@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"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<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"/>
|
||||
<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">
|
||||
<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"/>
|
||||
<color key="textColor" white="0.33333333333333331" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
|
@ -78,11 +78,18 @@
|
|||
<constraint firstItem="QMP-j2-HLn" firstAttribute="leading" secondItem="ve3-Y1-NQH" secondAttribute="leading" id="zeW-tQ-uJl"/>
|
||||
</constraints>
|
||||
</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">
|
||||
<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>
|
||||
<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"/>
|
||||
<connections>
|
||||
<action selector="replyPressed:" destination="iN0-l3-epB" eventType="touchUpInside" id="Ohg-uU-d3Z"/>
|
||||
|
@ -91,6 +98,9 @@
|
|||
</subviews>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="nbq-yr-2mA" firstAttribute="width" secondItem="yNh-ac-v6c" secondAttribute="width" id="3Ag-HE-h4m"/>
|
||||
</constraints>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
|
@ -103,6 +113,7 @@
|
|||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
|
||||
<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="contentLabel" destination="HrJ-t9-KcD" id="tbD-3T-nNP"/>
|
||||
<outlet property="displayNameLabel" destination="gll-xe-FSr" id="63y-He-xy1"/>
|
||||
|
|
Loading…
Reference in New Issue