Compare commits
No commits in common. "fcab6818b05737c8bd9e3d679850eb0e08bc8638" and "d9517047d731249919c61fee7b41607ca5fb254b" have entirely different histories.
fcab6818b0
...
d9517047d7
|
@ -223,8 +223,6 @@
|
||||||
D6D4DDF0212518A200E1C4BB /* TuskerUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D4DDEF212518A200E1C4BB /* TuskerUITests.swift */; };
|
D6D4DDF0212518A200E1C4BB /* TuskerUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D4DDEF212518A200E1C4BB /* TuskerUITests.swift */; };
|
||||||
D6DD353D22F28CD000A9563A /* ContentWarningCopyMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DD353C22F28CD000A9563A /* ContentWarningCopyMode.swift */; };
|
D6DD353D22F28CD000A9563A /* ContentWarningCopyMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DD353C22F28CD000A9563A /* ContentWarningCopyMode.swift */; };
|
||||||
D6DD353F22F502EC00A9563A /* Preferences+Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DD353E22F502EC00A9563A /* Preferences+Notification.swift */; };
|
D6DD353F22F502EC00A9563A /* Preferences+Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DD353E22F502EC00A9563A /* Preferences+Notification.swift */; };
|
||||||
D6DFC69E242C490400ACC392 /* TrackpadScrollGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DFC69D242C490400ACC392 /* TrackpadScrollGestureRecognizer.swift */; };
|
|
||||||
D6DFC6A0242C4CCC00ACC392 /* WeakArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DFC69F242C4CCC00ACC392 /* WeakArray.swift */; };
|
|
||||||
D6E0DC8E216EDF1E00369478 /* Previewing.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E0DC8D216EDF1E00369478 /* Previewing.swift */; };
|
D6E0DC8E216EDF1E00369478 /* Previewing.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E0DC8D216EDF1E00369478 /* Previewing.swift */; };
|
||||||
D6E6F26321603F8B006A8599 /* CharacterCounter.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E6F26221603F8B006A8599 /* CharacterCounter.swift */; };
|
D6E6F26321603F8B006A8599 /* CharacterCounter.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E6F26221603F8B006A8599 /* CharacterCounter.swift */; };
|
||||||
D6E6F26521604242006A8599 /* CharacterCounterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E6F26421604242006A8599 /* CharacterCounterTests.swift */; };
|
D6E6F26521604242006A8599 /* CharacterCounterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E6F26421604242006A8599 /* CharacterCounterTests.swift */; };
|
||||||
|
@ -512,8 +510,6 @@
|
||||||
D6D4DDF1212518A200E1C4BB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
D6D4DDF1212518A200E1C4BB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
D6DD353C22F28CD000A9563A /* ContentWarningCopyMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentWarningCopyMode.swift; sourceTree = "<group>"; };
|
D6DD353C22F28CD000A9563A /* ContentWarningCopyMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentWarningCopyMode.swift; sourceTree = "<group>"; };
|
||||||
D6DD353E22F502EC00A9563A /* Preferences+Notification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Preferences+Notification.swift"; sourceTree = "<group>"; };
|
D6DD353E22F502EC00A9563A /* Preferences+Notification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Preferences+Notification.swift"; sourceTree = "<group>"; };
|
||||||
D6DFC69D242C490400ACC392 /* TrackpadScrollGestureRecognizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackpadScrollGestureRecognizer.swift; sourceTree = "<group>"; };
|
|
||||||
D6DFC69F242C4CCC00ACC392 /* WeakArray.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeakArray.swift; sourceTree = "<group>"; };
|
|
||||||
D6E0DC8D216EDF1E00369478 /* Previewing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Previewing.swift; sourceTree = "<group>"; };
|
D6E0DC8D216EDF1E00369478 /* Previewing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Previewing.swift; sourceTree = "<group>"; };
|
||||||
D6E6F26221603F8B006A8599 /* CharacterCounter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CharacterCounter.swift; sourceTree = "<group>"; };
|
D6E6F26221603F8B006A8599 /* CharacterCounter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CharacterCounter.swift; sourceTree = "<group>"; };
|
||||||
D6E6F26421604242006A8599 /* CharacterCounterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CharacterCounterTests.swift; sourceTree = "<group>"; };
|
D6E6F26421604242006A8599 /* CharacterCounterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CharacterCounterTests.swift; sourceTree = "<group>"; };
|
||||||
|
@ -1163,7 +1159,6 @@
|
||||||
D68FEC4E232C5BC300C84F23 /* SegmentedPageViewController.swift */,
|
D68FEC4E232C5BC300C84F23 /* SegmentedPageViewController.swift */,
|
||||||
D693DE5623FE1A6A0061E07D /* EnhancedNavigationViewController.swift */,
|
D693DE5623FE1A6A0061E07D /* EnhancedNavigationViewController.swift */,
|
||||||
D693DE5823FE24300061E07D /* InteractivePushTransition.swift */,
|
D693DE5823FE24300061E07D /* InteractivePushTransition.swift */,
|
||||||
D6DFC69D242C490400ACC392 /* TrackpadScrollGestureRecognizer.swift */,
|
|
||||||
);
|
);
|
||||||
path = Utilities;
|
path = Utilities;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -1215,7 +1210,6 @@
|
||||||
D6945C2E23AC47C3005C403C /* SavedDataManager.swift */,
|
D6945C2E23AC47C3005C403C /* SavedDataManager.swift */,
|
||||||
D6028B9A2150811100F223B9 /* MastodonCache.swift */,
|
D6028B9A2150811100F223B9 /* MastodonCache.swift */,
|
||||||
D6C693EE216192C2007D6A6D /* TuskerNavigationDelegate.swift */,
|
D6C693EE216192C2007D6A6D /* TuskerNavigationDelegate.swift */,
|
||||||
D6DFC69F242C4CCC00ACC392 /* WeakArray.swift */,
|
|
||||||
D6F1F84E2193B9BE00F5FE67 /* Caching */,
|
D6F1F84E2193B9BE00F5FE67 /* Caching */,
|
||||||
D6757A7A2157E00100721E32 /* XCallbackURL */,
|
D6757A7A2157E00100721E32 /* XCallbackURL */,
|
||||||
D62D241E217AA46B005076CC /* Shortcuts */,
|
D62D241E217AA46B005076CC /* Shortcuts */,
|
||||||
|
@ -1695,7 +1689,6 @@
|
||||||
D627943523A5525100D38C68 /* StatusActivity.swift in Sources */,
|
D627943523A5525100D38C68 /* StatusActivity.swift in Sources */,
|
||||||
D663626C21361C6700C9CBA2 /* Account+Preferences.swift in Sources */,
|
D663626C21361C6700C9CBA2 /* Account+Preferences.swift in Sources */,
|
||||||
D6333B372137838300CE884A /* AttributedString+Helpers.swift in Sources */,
|
D6333B372137838300CE884A /* AttributedString+Helpers.swift in Sources */,
|
||||||
D6DFC69E242C490400ACC392 /* TrackpadScrollGestureRecognizer.swift in Sources */,
|
|
||||||
D61AC1D8232EA42D00C54D2D /* InstanceTableViewCell.swift in Sources */,
|
D61AC1D8232EA42D00C54D2D /* InstanceTableViewCell.swift in Sources */,
|
||||||
D63F9C68241C4F79004C03CF /* AddAttachmentTableViewCell.swift in Sources */,
|
D63F9C68241C4F79004C03CF /* AddAttachmentTableViewCell.swift in Sources */,
|
||||||
D6B8DB342182A59300424AF7 /* UIAlertController+Visibility.swift in Sources */,
|
D6B8DB342182A59300424AF7 /* UIAlertController+Visibility.swift in Sources */,
|
||||||
|
@ -1710,7 +1703,6 @@
|
||||||
D6945C3223AC4D36005C403C /* HashtagTimelineViewController.swift in Sources */,
|
D6945C3223AC4D36005C403C /* HashtagTimelineViewController.swift in Sources */,
|
||||||
D647D92824257BEB0005044F /* AttachmentPreviewViewController.swift in Sources */,
|
D647D92824257BEB0005044F /* AttachmentPreviewViewController.swift in Sources */,
|
||||||
D66362752137068A00C9CBA2 /* Visibility+Helpers.swift in Sources */,
|
D66362752137068A00C9CBA2 /* Visibility+Helpers.swift in Sources */,
|
||||||
D6DFC6A0242C4CCC00ACC392 /* WeakArray.swift in Sources */,
|
|
||||||
D6C693FC2162FE6F007D6A6D /* LoadingViewController.swift in Sources */,
|
D6C693FC2162FE6F007D6A6D /* LoadingViewController.swift in Sources */,
|
||||||
D646C95A213B5D0500269FB5 /* LargeImageInteractionController.swift in Sources */,
|
D646C95A213B5D0500269FB5 /* LargeImageInteractionController.swift in Sources */,
|
||||||
D6A3BC7C232195C600FD64D5 /* ActionNotificationGroupTableViewCell.swift in Sources */,
|
D6A3BC7C232195C600FD64D5 /* ActionNotificationGroupTableViewCell.swift in Sources */,
|
||||||
|
|
|
@ -13,7 +13,7 @@ import AVKit
|
||||||
class GalleryViewController: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate, LargeImageAnimatableViewController {
|
class GalleryViewController: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate, LargeImageAnimatableViewController {
|
||||||
|
|
||||||
let attachments: [Attachment]
|
let attachments: [Attachment]
|
||||||
let sourceViews: WeakArray<UIImageView>
|
let sourcesInfo: [LargeImageViewController.SourceInfo?]
|
||||||
let startIndex: Int
|
let startIndex: Int
|
||||||
|
|
||||||
let pages: [UIViewController]
|
let pages: [UIViewController]
|
||||||
|
@ -26,13 +26,12 @@ class GalleryViewController: UIPageViewController, UIPageViewControllerDataSourc
|
||||||
return index
|
return index
|
||||||
}
|
}
|
||||||
|
|
||||||
var animationSourceView: UIImageView? { sourceViews[currentIndex] }
|
var animationSourceInfo: LargeImageViewController.SourceInfo? { sourcesInfo[currentIndex] }
|
||||||
var animationImage: UIImage? {
|
var animationImage: UIImage? {
|
||||||
if let page = pages[currentIndex] as? LoadingLargeImageViewController,
|
if let sourceImage = sourcesInfo[currentIndex]?.image {
|
||||||
let image = page.largeImageVC?.image {
|
return sourceImage
|
||||||
return image
|
|
||||||
} else {
|
} else {
|
||||||
return animationSourceView?.image
|
return (pages[currentIndex] as? LoadingLargeImageViewController)?.largeImageVC?.image
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var animationGifData: Data? {
|
var animationGifData: Data? {
|
||||||
|
@ -60,9 +59,9 @@ class GalleryViewController: UIPageViewController, UIPageViewControllerDataSourc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init(attachments: [Attachment], sourceViews: [UIImageView?], startIndex: Int) {
|
init(attachments: [Attachment], sourcesInfo: [LargeImageViewController.SourceInfo?], startIndex: Int) {
|
||||||
self.attachments = attachments
|
self.attachments = attachments
|
||||||
self.sourceViews = WeakArray(sourceViews)
|
self.sourcesInfo = sourcesInfo
|
||||||
self.startIndex = startIndex
|
self.startIndex = startIndex
|
||||||
|
|
||||||
self.pages = attachments.map {
|
self.pages = attachments.map {
|
||||||
|
|
|
@ -233,7 +233,7 @@ class ComposeViewController: UIViewController {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
var formatButtons = StatusFormat.allCases.map { (format) -> UIBarButtonItem in
|
return StatusFormat.allCases.map { (format) in
|
||||||
let item: UIBarButtonItem
|
let item: UIBarButtonItem
|
||||||
if let image = format.image {
|
if let image = format.image {
|
||||||
item = UIBarButtonItem(image: image, style: .plain, target: self, action: #selector(formatButtonPressed(_:)))
|
item = UIBarButtonItem(image: image, style: .plain, target: self, action: #selector(formatButtonPressed(_:)))
|
||||||
|
@ -248,14 +248,6 @@ class ComposeViewController: UIViewController {
|
||||||
item.accessibilityLabel = format.accessibilityLabel
|
item.accessibilityLabel = format.accessibilityLabel
|
||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
|
|
||||||
for i in (1..<StatusFormat.allCases.count).reversed() {
|
|
||||||
let spacer = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil)
|
|
||||||
spacer.width = 8
|
|
||||||
formatButtons.insert(spacer, at: i)
|
|
||||||
}
|
|
||||||
|
|
||||||
return formatButtons
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func adjustForKeyboard(notification: NSNotification) {
|
@objc func adjustForKeyboard(notification: NSNotification) {
|
||||||
|
|
|
@ -11,8 +11,10 @@ import Gifu
|
||||||
|
|
||||||
class LargeImageViewController: UIViewController, UIScrollViewDelegate, LargeImageAnimatableViewController {
|
class LargeImageViewController: UIViewController, UIScrollViewDelegate, LargeImageAnimatableViewController {
|
||||||
|
|
||||||
weak var animationSourceView: UIImageView?
|
typealias SourceInfo = (image: UIImage?, frame: CGRect, cornerRadius: CGFloat)
|
||||||
var animationImage: UIImage? { image ?? animationSourceView?.image }
|
|
||||||
|
var animationSourceInfo: SourceInfo?
|
||||||
|
var animationImage: UIImage? { animationSourceInfo?.image ?? image }
|
||||||
var animationGifData: Data? { gifData }
|
var animationGifData: Data? { gifData }
|
||||||
var dismissInteractionController: LargeImageInteractionController?
|
var dismissInteractionController: LargeImageInteractionController?
|
||||||
|
|
||||||
|
@ -57,10 +59,10 @@ class LargeImageViewController: UIViewController, UIScrollViewDelegate, LargeIma
|
||||||
return !controlsVisible
|
return !controlsVisible
|
||||||
}
|
}
|
||||||
|
|
||||||
init(image: UIImage, description: String?, sourceView: UIImageView?) {
|
init(image: UIImage, description: String?, sourceInfo: SourceInfo?) {
|
||||||
self.image = image
|
self.image = image
|
||||||
self.imageDescription = description
|
self.imageDescription = description
|
||||||
self.animationSourceView = sourceView
|
self.animationSourceInfo = sourceInfo
|
||||||
|
|
||||||
super.init(nibName: "LargeImageViewController", bundle: nil)
|
super.init(nibName: "LargeImageViewController", bundle: nil)
|
||||||
|
|
||||||
|
@ -180,8 +182,6 @@ class LargeImageViewController: UIViewController, UIScrollViewDelegate, LargeIma
|
||||||
return zoomRect
|
return zoomRect
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Interaction
|
|
||||||
|
|
||||||
func animateZoomOut() {
|
func animateZoomOut() {
|
||||||
UIView.animate(withDuration: 0.3, animations: {
|
UIView.animate(withDuration: 0.3, animations: {
|
||||||
self.scrollView.zoomScale = self.scrollView.minimumZoomScale
|
self.scrollView.zoomScale = self.scrollView.minimumZoomScale
|
||||||
|
|
|
@ -35,8 +35,8 @@ class LoadingLargeImageViewController: UIViewController, LargeImageAnimatableVie
|
||||||
|
|
||||||
var shrinkGestureEnabled = true
|
var shrinkGestureEnabled = true
|
||||||
|
|
||||||
weak var animationSourceView: UIImageView?
|
var animationSourceInfo: LargeImageViewController.SourceInfo?
|
||||||
var animationImage: UIImage? { largeImageVC?.image ?? animationSourceView?.image }
|
var animationImage: UIImage? { animationSourceInfo?.image ?? largeImageVC?.image }
|
||||||
var animationGifData: Data? { largeImageVC?.gifData }
|
var animationGifData: Data? { largeImageVC?.gifData }
|
||||||
var dismissInteractionController: LargeImageInteractionController?
|
var dismissInteractionController: LargeImageInteractionController?
|
||||||
|
|
||||||
|
@ -108,7 +108,7 @@ class LoadingLargeImageViewController: UIViewController, LargeImageAnimatableVie
|
||||||
|
|
||||||
func createLargeImage(data: Data) {
|
func createLargeImage(data: Data) {
|
||||||
guard let image = UIImage(data: data) else { return }
|
guard let image = UIImage(data: data) else { return }
|
||||||
largeImageVC = LargeImageViewController(image: image, description: imageDescription, sourceView: animationSourceView)
|
largeImageVC = LargeImageViewController(image: image, description: imageDescription, sourceInfo: nil)
|
||||||
largeImageVC!.initialControlsVisible = initialControlsVisible
|
largeImageVC!.initialControlsVisible = initialControlsVisible
|
||||||
largeImageVC!.shrinkGestureEnabled = false
|
largeImageVC!.shrinkGestureEnabled = false
|
||||||
if url.pathExtension == "gif" {
|
if url.pathExtension == "gif" {
|
||||||
|
|
|
@ -10,30 +10,12 @@ import UIKit
|
||||||
import Gifu
|
import Gifu
|
||||||
|
|
||||||
protocol LargeImageAnimatableViewController: UIViewController {
|
protocol LargeImageAnimatableViewController: UIViewController {
|
||||||
var animationSourceView: UIImageView? { get }
|
var animationSourceInfo: LargeImageViewController.SourceInfo? { get }
|
||||||
var animationImage: UIImage? { get }
|
var animationImage: UIImage? { get }
|
||||||
var animationGifData: Data? { get }
|
var animationGifData: Data? { get }
|
||||||
var dismissInteractionController: LargeImageInteractionController? { get }
|
var dismissInteractionController: LargeImageInteractionController? { get }
|
||||||
}
|
}
|
||||||
|
|
||||||
extension LargeImageAnimatableViewController {
|
|
||||||
func sourceViewFrame(in coordinateSpace: UIView) -> CGRect? {
|
|
||||||
guard let sourceView = animationSourceView else { return nil }
|
|
||||||
|
|
||||||
var sourceFrame = sourceView.convert(sourceView.bounds, to: coordinateSpace)
|
|
||||||
if let scrollView = coordinateSpace as? UIScrollView {
|
|
||||||
let scale = scrollView.zoomScale
|
|
||||||
let width = sourceFrame.width * scale
|
|
||||||
let height = sourceFrame.height * scale
|
|
||||||
let x = sourceFrame.minX * scale - scrollView.contentOffset.x + scrollView.frame.minX
|
|
||||||
let y = sourceFrame.minY * scale - scrollView.contentOffset.y + scrollView.frame.minY
|
|
||||||
sourceFrame = CGRect(x: x, y: y, width: width, height: height)
|
|
||||||
}
|
|
||||||
|
|
||||||
return sourceFrame
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class LargeImageExpandAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
|
class LargeImageExpandAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
|
||||||
|
|
||||||
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
|
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
|
||||||
|
@ -50,35 +32,25 @@ class LargeImageExpandAnimationController: NSObject, UIViewControllerAnimatedTra
|
||||||
containerView.addSubview(toVC.view)
|
containerView.addSubview(toVC.view)
|
||||||
|
|
||||||
let finalVCFrame = transitionContext.finalFrame(for: toVC)
|
let finalVCFrame = transitionContext.finalFrame(for: toVC)
|
||||||
guard let sourceView = toVC.animationSourceView,
|
guard let sourceInfo = toVC.animationSourceInfo,
|
||||||
let sourceFrame = toVC.sourceViewFrame(in: fromVC.view),
|
|
||||||
let image = toVC.animationImage else {
|
let image = toVC.animationImage else {
|
||||||
toVC.view.frame = finalVCFrame
|
toVC.view.frame = finalVCFrame
|
||||||
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
|
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// use alpha, becaus isHidden makes stack views re-layout
|
let ratio = image.size.width / image.size.height
|
||||||
sourceView.alpha = 0
|
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)
|
||||||
|
|
||||||
var finalFrameSize = finalVCFrame.inset(by: fromVC.view.safeAreaInsets).size
|
let imageView = GIFImageView(frame: sourceInfo.frame)
|
||||||
let newWidth = finalFrameSize.width / image.size.width
|
|
||||||
let newHeight = finalFrameSize.height / image.size.height
|
|
||||||
if newHeight < newWidth {
|
|
||||||
finalFrameSize.width = newHeight * image.size.width
|
|
||||||
} else {
|
|
||||||
finalFrameSize.height = newWidth * image.size.height
|
|
||||||
}
|
|
||||||
let finalFrame = CGRect(origin: CGPoint(x: finalVCFrame.midX - finalFrameSize.width / 2, y: finalVCFrame.midY - finalFrameSize.height / 2), size: finalFrameSize)
|
|
||||||
|
|
||||||
let imageView = GIFImageView(frame: sourceFrame)
|
|
||||||
imageView.image = image
|
imageView.image = image
|
||||||
if let gifData = toVC.animationGifData {
|
if let gifData = toVC.animationGifData {
|
||||||
imageView.animate(withGIFData: gifData)
|
imageView.animate(withGIFData: gifData)
|
||||||
}
|
}
|
||||||
imageView.contentMode = .scaleAspectFill
|
imageView.contentMode = .scaleAspectFill
|
||||||
imageView.layer.cornerRadius = sourceView.layer.cornerRadius
|
imageView.layer.cornerRadius = sourceInfo.cornerRadius
|
||||||
imageView.layer.maskedCorners = sourceView.layer.maskedCorners
|
|
||||||
imageView.layer.masksToBounds = true
|
imageView.layer.masksToBounds = true
|
||||||
|
|
||||||
let blackView = UIView(frame: finalVCFrame)
|
let blackView = UIView(frame: finalVCFrame)
|
||||||
|
@ -107,9 +79,6 @@ class LargeImageExpandAnimationController: NSObject, UIViewControllerAnimatedTra
|
||||||
fromVC.view.isHidden = false
|
fromVC.view.isHidden = false
|
||||||
blackView.removeFromSuperview()
|
blackView.removeFromSuperview()
|
||||||
imageView.removeFromSuperview()
|
imageView.removeFromSuperview()
|
||||||
|
|
||||||
sourceView.alpha = 1
|
|
||||||
|
|
||||||
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
|
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,11 +19,7 @@ class LargeImageInteractionController: UIPercentDrivenInteractiveTransition {
|
||||||
init(viewController: UIViewController) {
|
init(viewController: UIViewController) {
|
||||||
super.init()
|
super.init()
|
||||||
self.viewController = viewController
|
self.viewController = viewController
|
||||||
let panRecognizer = UIPanGestureRecognizer(target: self, action: #selector(handleGesture(_:)))
|
viewController.view.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(handleGesture(_:))))
|
||||||
if #available(iOS 13.4, *) {
|
|
||||||
panRecognizer.allowedScrollTypesMask = .all
|
|
||||||
}
|
|
||||||
viewController.view.addGestureRecognizer(panRecognizer)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func handleGesture(_ recognizer: UIPanGestureRecognizer) {
|
@objc func handleGesture(_ recognizer: UIPanGestureRecognizer) {
|
||||||
|
|
|
@ -27,28 +27,19 @@ class LargeImageShrinkAnimationController: NSObject, UIViewControllerAnimatedTra
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let sourceView = fromVC.animationSourceView,
|
guard let sourceInfo = fromVC.animationSourceInfo,
|
||||||
let sourceFrame = fromVC.sourceViewFrame(in: toVC.view),
|
|
||||||
let image = fromVC.animationImage else {
|
let image = fromVC.animationImage else {
|
||||||
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
|
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// use alpha, becaus isHidden makes stack views re-layout
|
let originalVCFrame = fromVC.view.frame
|
||||||
sourceView.alpha = 0
|
|
||||||
|
|
||||||
let containerView = transitionContext.containerView
|
let containerView = transitionContext.containerView
|
||||||
|
let ratio = image.size.width / image.size.height
|
||||||
let originalVCFrame = fromVC.view.frame
|
let width = originalVCFrame.width
|
||||||
var originalFrameSize = originalVCFrame.inset(by: fromVC.view.safeAreaInsets).size
|
let height = width / ratio
|
||||||
let newWidth = originalFrameSize.width / image.size.width
|
let originalFrame = CGRect(x: originalVCFrame.midX - width / 2, y: originalVCFrame.midY - height / 2, width: width, height: height)
|
||||||
let newHeight = originalFrameSize.height / image.size.height
|
|
||||||
if newHeight < newWidth {
|
|
||||||
originalFrameSize.width = newHeight * image.size.width
|
|
||||||
} else {
|
|
||||||
originalFrameSize.height = newWidth * image.size.height
|
|
||||||
}
|
|
||||||
let originalFrame = CGRect(origin: CGPoint(x: originalVCFrame.midX - originalFrameSize.width / 2, y: originalVCFrame.midY - originalFrameSize.height / 2), size: originalFrameSize)
|
|
||||||
|
|
||||||
let imageView = GIFImageView(frame: originalFrame)
|
let imageView = GIFImageView(frame: originalFrame)
|
||||||
imageView.image = image
|
imageView.image = image
|
||||||
|
@ -69,9 +60,8 @@ class LargeImageShrinkAnimationController: NSObject, UIViewControllerAnimatedTra
|
||||||
|
|
||||||
let duration = transitionDuration(using: transitionContext)
|
let duration = transitionDuration(using: transitionContext)
|
||||||
UIView.animate(withDuration: duration, animations: {
|
UIView.animate(withDuration: duration, animations: {
|
||||||
imageView.frame = sourceFrame
|
imageView.frame = sourceInfo.frame
|
||||||
imageView.layer.cornerRadius = sourceView.layer.cornerRadius
|
imageView.layer.cornerRadius = sourceInfo.cornerRadius
|
||||||
imageView.layer.maskedCorners = sourceView.layer.maskedCorners
|
|
||||||
blackView.alpha = 0
|
blackView.alpha = 0
|
||||||
}, completion: { _ in
|
}, completion: { _ in
|
||||||
blackView.removeFromSuperview()
|
blackView.removeFromSuperview()
|
||||||
|
@ -79,9 +69,6 @@ class LargeImageShrinkAnimationController: NSObject, UIViewControllerAnimatedTra
|
||||||
if transitionContext.transitionWasCancelled {
|
if transitionContext.transitionWasCancelled {
|
||||||
toVC.view.removeFromSuperview()
|
toVC.view.removeFromSuperview()
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceView.alpha = 1
|
|
||||||
|
|
||||||
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
|
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,12 +35,6 @@ class InteractivePushTransition: UIPercentDrivenInteractiveTransition {
|
||||||
interactivePushGestureRecognizer.edges = .right
|
interactivePushGestureRecognizer.edges = .right
|
||||||
interactivePushGestureRecognizer.require(toFail: navigationController.interactivePopGestureRecognizer!)
|
interactivePushGestureRecognizer.require(toFail: navigationController.interactivePopGestureRecognizer!)
|
||||||
navigationController.view.addGestureRecognizer(interactivePushGestureRecognizer)
|
navigationController.view.addGestureRecognizer(interactivePushGestureRecognizer)
|
||||||
|
|
||||||
if #available(iOS 13.4, *) {
|
|
||||||
let trackpadGestureRecognizer = TrackpadScrollGestureRecognizer(target: self, action: #selector(handleSwipeForward(_:)))
|
|
||||||
trackpadGestureRecognizer.require(toFail: navigationController.interactivePopGestureRecognizer!)
|
|
||||||
navigationController.view.addGestureRecognizer(trackpadGestureRecognizer)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func handleSwipeForward(_ recognizer: UIPanGestureRecognizer) {
|
@objc func handleSwipeForward(_ recognizer: UIPanGestureRecognizer) {
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
//
|
|
||||||
// TrackpadScreenEdgePanScrollGestureRecognizer.swift
|
|
||||||
// Tusker
|
|
||||||
//
|
|
||||||
// Created by Shadowfacts on 3/25/20.
|
|
||||||
// Copyright © 2020 Shadowfacts. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
@available(iOS 13.4, *)
|
|
||||||
class TrackpadScrollGestureRecognizer: UIPanGestureRecognizer {
|
|
||||||
|
|
||||||
override init(target: Any?, action: Selector?) {
|
|
||||||
super.init(target: target, action: action)
|
|
||||||
|
|
||||||
self.allowedScrollTypesMask = .all
|
|
||||||
}
|
|
||||||
|
|
||||||
override func shouldReceive(_ event: UIEvent) -> Bool {
|
|
||||||
return event.type == .scroll
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -151,14 +151,29 @@ extension TuskerNavigationDelegate where Self: UIViewController {
|
||||||
present(vc, animated: true)
|
present(vc, animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func sourceViewInfo(_ sourceView: UIImageView?) -> LargeImageViewController.SourceInfo? {
|
||||||
|
guard let sourceView = sourceView else { return nil }
|
||||||
|
|
||||||
|
var sourceFrame = sourceView.convert(sourceView.bounds, to: view)
|
||||||
|
if let scrollView = view as? UIScrollView {
|
||||||
|
let scale = scrollView.zoomScale
|
||||||
|
let width = sourceFrame.width * scale
|
||||||
|
let height = sourceFrame.height * scale
|
||||||
|
let x = sourceFrame.minX * scale - scrollView.contentOffset.x + scrollView.frame.minX
|
||||||
|
let y = sourceFrame.minY * scale - scrollView.contentOffset.y + scrollView.frame.minY
|
||||||
|
sourceFrame = CGRect(x: x, y: y, width: width, height: height)
|
||||||
|
}
|
||||||
|
return (image: sourceView.image, frame: sourceFrame, cornerRadius: sourceView.layer.cornerRadius)
|
||||||
|
}
|
||||||
|
|
||||||
func largeImage(_ image: UIImage, description: String?, sourceView: UIImageView) -> LargeImageViewController {
|
func largeImage(_ image: UIImage, description: String?, sourceView: UIImageView) -> LargeImageViewController {
|
||||||
let vc = LargeImageViewController(image: image, description: description, sourceView: sourceView)
|
let vc = LargeImageViewController(image: image, description: description, sourceInfo: sourceViewInfo(sourceView))
|
||||||
vc.transitioningDelegate = self
|
vc.transitioningDelegate = self
|
||||||
return vc
|
return vc
|
||||||
}
|
}
|
||||||
|
|
||||||
func largeImage(gifData: Data, description: String?, sourceView: UIImageView) -> LargeImageViewController {
|
func largeImage(gifData: Data, description: String?, sourceView: UIImageView) -> LargeImageViewController {
|
||||||
let vc = LargeImageViewController(image: UIImage(data: gifData)!, description: description, sourceView: sourceView)
|
let vc = LargeImageViewController(image: UIImage(data: gifData)!, description: description, sourceInfo: sourceViewInfo(sourceView))
|
||||||
vc.transitioningDelegate = self
|
vc.transitioningDelegate = self
|
||||||
vc.gifData = gifData
|
vc.gifData = gifData
|
||||||
return vc
|
return vc
|
||||||
|
@ -174,7 +189,7 @@ extension TuskerNavigationDelegate where Self: UIViewController {
|
||||||
|
|
||||||
func loadingLargeImage(url: URL, cache: ImageCache, description: String?, animatingFrom sourceView: UIImageView) -> LoadingLargeImageViewController {
|
func loadingLargeImage(url: URL, cache: ImageCache, description: String?, animatingFrom sourceView: UIImageView) -> LoadingLargeImageViewController {
|
||||||
let vc = LoadingLargeImageViewController(url: url, cache: cache, imageDescription: description)
|
let vc = LoadingLargeImageViewController(url: url, cache: cache, imageDescription: description)
|
||||||
vc.animationSourceView = sourceView
|
vc.animationSourceInfo = sourceViewInfo(sourceView)
|
||||||
vc.transitioningDelegate = self
|
vc.transitioningDelegate = self
|
||||||
return vc
|
return vc
|
||||||
}
|
}
|
||||||
|
@ -184,7 +199,8 @@ extension TuskerNavigationDelegate where Self: UIViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
func gallery(attachments: [Attachment], sourceViews: [UIImageView?], startIndex: Int) -> GalleryViewController {
|
func gallery(attachments: [Attachment], sourceViews: [UIImageView?], startIndex: Int) -> GalleryViewController {
|
||||||
let vc = GalleryViewController(attachments: attachments, sourceViews: sourceViews, startIndex: startIndex)
|
let sourcesInfo = sourceViews.map(sourceViewInfo)
|
||||||
|
let vc = GalleryViewController(attachments: attachments, sourcesInfo: sourcesInfo, startIndex: startIndex)
|
||||||
vc.transitioningDelegate = self
|
vc.transitioningDelegate = self
|
||||||
return vc
|
return vc
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,10 +52,6 @@ class ProfileHeaderTableViewCell: UITableViewCell {
|
||||||
maskLayer.path = CGPath(ellipseIn: moreButtonVisualEffectView.bounds, transform: nil)
|
maskLayer.path = CGPath(ellipseIn: moreButtonVisualEffectView.bounds, transform: nil)
|
||||||
moreButtonVisualEffectView.layer.mask = maskLayer
|
moreButtonVisualEffectView.layer.mask = maskLayer
|
||||||
|
|
||||||
if #available(iOS 13.4, *) {
|
|
||||||
moreButtonVisualEffectView.addInteraction(UIPointerInteraction(delegate: self))
|
|
||||||
}
|
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(updateUIForPreferences), name: .preferencesChanged, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(updateUIForPreferences), name: .preferencesChanged, object: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,12 +159,3 @@ class ProfileHeaderTableViewCell: UITableViewCell {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ProfileHeaderTableViewCell: UIPointerInteractionDelegate {
|
|
||||||
@available(iOS 13.4, *)
|
|
||||||
func pointerInteraction(_ interaction: UIPointerInteraction, styleFor region: UIPointerRegion) -> UIPointerStyle? {
|
|
||||||
let preview = UITargetedPreview(view: moreButtonVisualEffectView)
|
|
||||||
let rect = CGRect(x: moreButtonVisualEffectView.frame.minX - 4, y: moreButtonVisualEffectView.frame.minY - 4, width: moreButtonVisualEffectView.frame.width + 8, height: moreButtonVisualEffectView.frame.height + 8)
|
|
||||||
return UIPointerStyle(effect: .highlight(preview), shape: .roundedRect(rect, radius: 4))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16096" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16092.1" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16086"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16082.1"/>
|
||||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
@ -144,7 +144,7 @@
|
||||||
<stackView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" distribution="equalSpacing" translatesAutoresizingMaskIntoConstraints="NO" id="3Bg-XP-d13">
|
<stackView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" distribution="equalSpacing" translatesAutoresizingMaskIntoConstraints="NO" id="3Bg-XP-d13">
|
||||||
<rect key="frame" x="0.0" y="257" width="343" height="18"/>
|
<rect key="frame" x="0.0" y="257" width="343" height="18"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="leading" contentVerticalAlignment="center" lineBreakMode="middleTruncation" pointerInteraction="YES" translatesAutoresizingMaskIntoConstraints="NO" id="2cc-lE-AdG">
|
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="leading" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="2cc-lE-AdG">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="21" height="18"/>
|
<rect key="frame" x="0.0" y="0.0" width="21" height="18"/>
|
||||||
<accessibility key="accessibilityConfiguration" label="Reply"/>
|
<accessibility key="accessibilityConfiguration" label="Reply"/>
|
||||||
<state key="normal" image="arrowshape.turn.up.left.fill" catalog="system"/>
|
<state key="normal" image="arrowshape.turn.up.left.fill" catalog="system"/>
|
||||||
|
@ -152,7 +152,7 @@
|
||||||
<action selector="replyPressed" destination="iN0-l3-epB" eventType="touchUpInside" id="RxZ-zv-lkN"/>
|
<action selector="replyPressed" destination="iN0-l3-epB" eventType="touchUpInside" id="RxZ-zv-lkN"/>
|
||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" pointerInteraction="YES" translatesAutoresizingMaskIntoConstraints="NO" id="DhN-rJ-jdA">
|
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="DhN-rJ-jdA">
|
||||||
<rect key="frame" x="107" y="0.0" width="22" height="18"/>
|
<rect key="frame" x="107" y="0.0" width="22" height="18"/>
|
||||||
<accessibility key="accessibilityConfiguration" label="Favorite"/>
|
<accessibility key="accessibilityConfiguration" label="Favorite"/>
|
||||||
<state key="normal" image="star.fill" catalog="system"/>
|
<state key="normal" image="star.fill" catalog="system"/>
|
||||||
|
@ -160,7 +160,7 @@
|
||||||
<action selector="favoritePressed" destination="iN0-l3-epB" eventType="touchUpInside" id="NCA-iR-VMt"/>
|
<action selector="favoritePressed" destination="iN0-l3-epB" eventType="touchUpInside" id="NCA-iR-VMt"/>
|
||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" pointerInteraction="YES" translatesAutoresizingMaskIntoConstraints="NO" id="GUG-f7-Hdy">
|
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="GUG-f7-Hdy">
|
||||||
<rect key="frame" x="215.5" y="0.0" width="22.5" height="18"/>
|
<rect key="frame" x="215.5" y="0.0" width="22.5" height="18"/>
|
||||||
<accessibility key="accessibilityConfiguration" label="Reblog"/>
|
<accessibility key="accessibilityConfiguration" label="Reblog"/>
|
||||||
<state key="normal" image="repeat" catalog="system"/>
|
<state key="normal" image="repeat" catalog="system"/>
|
||||||
|
@ -168,7 +168,7 @@
|
||||||
<action selector="reblogPressed" destination="iN0-l3-epB" eventType="touchUpInside" id="iIu-Vv-U0I"/>
|
<action selector="reblogPressed" destination="iN0-l3-epB" eventType="touchUpInside" id="iIu-Vv-U0I"/>
|
||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" pointerInteraction="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Ujo-Ap-dmK">
|
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Ujo-Ap-dmK">
|
||||||
<rect key="frame" x="324" y="0.0" width="19" height="18"/>
|
<rect key="frame" x="324" y="0.0" width="19" height="18"/>
|
||||||
<accessibility key="accessibilityConfiguration" label="More Actions"/>
|
<accessibility key="accessibilityConfiguration" label="More Actions"/>
|
||||||
<state key="normal" image="ellipsis" catalog="system"/>
|
<state key="normal" image="ellipsis" catalog="system"/>
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16096" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16092.1" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16086"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16082.1"/>
|
||||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
@ -135,7 +135,7 @@
|
||||||
<stackView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" distribution="equalSpacing" alignment="bottom" translatesAutoresizingMaskIntoConstraints="NO" id="Zlb-yt-NTw">
|
<stackView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" distribution="equalSpacing" alignment="bottom" translatesAutoresizingMaskIntoConstraints="NO" id="Zlb-yt-NTw">
|
||||||
<rect key="frame" x="0.0" y="202" width="343" height="22"/>
|
<rect key="frame" x="0.0" y="202" width="343" height="22"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<button opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" contentHorizontalAlignment="leading" contentVerticalAlignment="center" lineBreakMode="middleTruncation" pointerInteraction="YES" translatesAutoresizingMaskIntoConstraints="NO" id="rKF-yF-KIa">
|
<button opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" contentHorizontalAlignment="leading" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="rKF-yF-KIa">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="21" height="22"/>
|
<rect key="frame" x="0.0" y="0.0" width="21" height="22"/>
|
||||||
<accessibility key="accessibilityConfiguration" label="Reply"/>
|
<accessibility key="accessibilityConfiguration" label="Reply"/>
|
||||||
<state key="normal" image="arrowshape.turn.up.left.fill" catalog="system"/>
|
<state key="normal" image="arrowshape.turn.up.left.fill" catalog="system"/>
|
||||||
|
@ -143,7 +143,7 @@
|
||||||
<action selector="replyPressed" destination="iN0-l3-epB" eventType="touchUpInside" id="ybz-3W-jAa"/>
|
<action selector="replyPressed" destination="iN0-l3-epB" eventType="touchUpInside" id="ybz-3W-jAa"/>
|
||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" pointerInteraction="YES" translatesAutoresizingMaskIntoConstraints="NO" id="x0t-TR-jJ4">
|
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="x0t-TR-jJ4">
|
||||||
<rect key="frame" x="107" y="0.0" width="22" height="22"/>
|
<rect key="frame" x="107" y="0.0" width="22" height="22"/>
|
||||||
<accessibility key="accessibilityConfiguration" label="Favorite"/>
|
<accessibility key="accessibilityConfiguration" label="Favorite"/>
|
||||||
<state key="normal" image="star.fill" catalog="system"/>
|
<state key="normal" image="star.fill" catalog="system"/>
|
||||||
|
@ -151,7 +151,7 @@
|
||||||
<action selector="favoritePressed" destination="iN0-l3-epB" eventType="touchUpInside" id="8Q8-Rz-k02"/>
|
<action selector="favoritePressed" destination="iN0-l3-epB" eventType="touchUpInside" id="8Q8-Rz-k02"/>
|
||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" pointerInteraction="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6tW-z8-Qh9">
|
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="6tW-z8-Qh9">
|
||||||
<rect key="frame" x="215.5" y="0.0" width="22.5" height="22"/>
|
<rect key="frame" x="215.5" y="0.0" width="22.5" height="22"/>
|
||||||
<accessibility key="accessibilityConfiguration" label="Reblog"/>
|
<accessibility key="accessibilityConfiguration" label="Reblog"/>
|
||||||
<state key="normal" image="repeat" catalog="system"/>
|
<state key="normal" image="repeat" catalog="system"/>
|
||||||
|
@ -159,7 +159,7 @@
|
||||||
<action selector="reblogPressed" destination="iN0-l3-epB" eventType="touchUpInside" id="Wa2-ZA-TBo"/>
|
<action selector="reblogPressed" destination="iN0-l3-epB" eventType="touchUpInside" id="Wa2-ZA-TBo"/>
|
||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" pointerInteraction="YES" translatesAutoresizingMaskIntoConstraints="NO" id="982-J4-NGl">
|
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="982-J4-NGl">
|
||||||
<rect key="frame" x="324" y="0.0" width="19" height="22"/>
|
<rect key="frame" x="324" y="0.0" width="19" height="22"/>
|
||||||
<accessibility key="accessibilityConfiguration" label="More Actions"/>
|
<accessibility key="accessibilityConfiguration" label="More Actions"/>
|
||||||
<state key="normal" image="ellipsis" catalog="system"/>
|
<state key="normal" image="ellipsis" catalog="system"/>
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
//
|
|
||||||
// WeakArray.swift
|
|
||||||
// Tusker
|
|
||||||
//
|
|
||||||
// Created by Shadowfacts on 3/25/20.
|
|
||||||
// Copyright © 2020 Shadowfacts. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
fileprivate class WeakWrapper<T: AnyObject> {
|
|
||||||
weak var value: T?
|
|
||||||
|
|
||||||
init(_ value: T?) {
|
|
||||||
self.value = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct WeakArray<Element: AnyObject>: Collection {
|
|
||||||
private var array: [WeakWrapper<Element>]
|
|
||||||
|
|
||||||
var startIndex: Int { array.startIndex }
|
|
||||||
var endIndex: Int { array.endIndex }
|
|
||||||
|
|
||||||
init(_ elements: [Element]) {
|
|
||||||
array = elements.map { WeakWrapper($0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
init(_ elements: [Element?]) {
|
|
||||||
array = elements.map { WeakWrapper($0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
subscript(_ index: Int) -> Element? {
|
|
||||||
return array[index].value
|
|
||||||
}
|
|
||||||
|
|
||||||
func index(after i: Int) -> Int {
|
|
||||||
return array.index(after: i)
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue