Merge commit 'develop' into release/0.4.0
This commit is contained in:
commit
ebc25be6a5
|
@ -8,7 +8,7 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import CoreData
|
import CoreData
|
||||||
|
|
||||||
public protocol Managed: class, NSFetchRequestResult {
|
public protocol Managed: AnyObject, NSFetchRequestResult {
|
||||||
static var entityName: String { get }
|
static var entityName: String { get }
|
||||||
static var defaultSortDescriptors: [NSSortDescriptor] { get }
|
static var defaultSortDescriptors: [NSSortDescriptor] { get }
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
protocol NeedsDependency: class {
|
protocol NeedsDependency: AnyObject {
|
||||||
var context: AppContext! { get set }
|
var context: AppContext! { get set }
|
||||||
var coordinator: SceneCoordinator! { get set }
|
var coordinator: SceneCoordinator! { get set }
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,7 +50,7 @@ extension ComposeStatusItem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protocol ComposePollAttributeDelegate: class {
|
protocol ComposePollAttributeDelegate: AnyObject {
|
||||||
func composePollAttribute(_ attribute: ComposeStatusItem.ComposePollOptionAttribute, pollOptionDidChange: String?)
|
func composePollAttribute(_ attribute: ComposeStatusItem.ComposePollOptionAttribute, pollOptionDidChange: String?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -197,6 +197,20 @@ extension StatusSection {
|
||||||
emojiDict: (status.reblog ?? status).emojiDict
|
emojiDict: (status.reblog ?? status).emojiDict
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// set visibility
|
||||||
|
if let visibility = (status.reblog ?? status).visibility {
|
||||||
|
cell.statusView.updateVisibility(visibility: visibility)
|
||||||
|
|
||||||
|
cell.statusView.revealContentWarningButton.publisher(for: \.isHidden)
|
||||||
|
.receive(on: DispatchQueue.main)
|
||||||
|
.sink { [weak cell] isHidden in
|
||||||
|
cell?.statusView.visibilityImageView.isHidden = !isHidden
|
||||||
|
}
|
||||||
|
.store(in: &cell.disposeBag)
|
||||||
|
} else {
|
||||||
|
cell.statusView.visibilityImageView.isHidden = true
|
||||||
|
}
|
||||||
|
|
||||||
// prepare media attachments
|
// prepare media attachments
|
||||||
let mediaAttachments = Array((status.reblog ?? status).mediaAttachments ?? []).sorted { $0.index.compare($1.index) == .orderedAscending }
|
let mediaAttachments = Array((status.reblog ?? status).mediaAttachments ?? []).sorted { $0.index.compare($1.index) == .orderedAscending }
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
protocol ContentOffsetAdjustableTimelineViewControllerDelegate: class {
|
protocol ContentOffsetAdjustableTimelineViewControllerDelegate: AnyObject {
|
||||||
func navigationBar() -> UINavigationBar?
|
func navigationBar() -> UINavigationBar?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,6 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import Combine
|
import Combine
|
||||||
|
|
||||||
protocol DisposeBagCollectable: class {
|
protocol DisposeBagCollectable: AnyObject {
|
||||||
var disposeBag: Set<AnyCancellable> { get set }
|
var disposeBag: Set<AnyCancellable> { get set }
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import os.log
|
||||||
import UIKit
|
import UIKit
|
||||||
import Combine
|
import Combine
|
||||||
|
|
||||||
protocol ComposeStatusAttachmentCollectionViewCellDelegate: class {
|
protocol ComposeStatusAttachmentCollectionViewCellDelegate: AnyObject {
|
||||||
func composeStatusAttachmentCollectionViewCell(_ cell: ComposeStatusAttachmentCollectionViewCell, removeButtonDidPressed button: UIButton)
|
func composeStatusAttachmentCollectionViewCell(_ cell: ComposeStatusAttachmentCollectionViewCell, removeButtonDidPressed button: UIButton)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ import os.log
|
||||||
import UIKit
|
import UIKit
|
||||||
import Combine
|
import Combine
|
||||||
|
|
||||||
protocol ComposeStatusPollExpiresOptionCollectionViewCellDelegate: class {
|
protocol ComposeStatusPollExpiresOptionCollectionViewCellDelegate: AnyObject {
|
||||||
func composeStatusPollExpiresOptionCollectionViewCell(_ cell: ComposeStatusPollExpiresOptionCollectionViewCell, didSelectExpiresOption expiresOption: ComposeStatusItem.ComposePollExpiresOptionAttribute.ExpiresOption)
|
func composeStatusPollExpiresOptionCollectionViewCell(_ cell: ComposeStatusPollExpiresOptionCollectionViewCell, didSelectExpiresOption expiresOption: ComposeStatusItem.ComposePollExpiresOptionAttribute.ExpiresOption)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
import os.log
|
import os.log
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
protocol ComposeStatusPollOptionAppendEntryCollectionViewCellDelegate: class {
|
protocol ComposeStatusPollOptionAppendEntryCollectionViewCellDelegate: AnyObject {
|
||||||
func composeStatusPollOptionAppendEntryCollectionViewCellDidPressed(_ cell: ComposeStatusPollOptionAppendEntryCollectionViewCell)
|
func composeStatusPollOptionAppendEntryCollectionViewCellDidPressed(_ cell: ComposeStatusPollOptionAppendEntryCollectionViewCell)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ import os.log
|
||||||
import UIKit
|
import UIKit
|
||||||
import Combine
|
import Combine
|
||||||
|
|
||||||
protocol ComposeStatusPollOptionCollectionViewCellDelegate: class {
|
protocol ComposeStatusPollOptionCollectionViewCellDelegate: AnyObject {
|
||||||
func composeStatusPollOptionCollectionViewCell(_ cell: ComposeStatusPollOptionCollectionViewCell, textFieldDidBeginEditing textField: UITextField)
|
func composeStatusPollOptionCollectionViewCell(_ cell: ComposeStatusPollOptionCollectionViewCell, textFieldDidBeginEditing textField: UITextField)
|
||||||
func composeStatusPollOptionCollectionViewCell(_ cell: ComposeStatusPollOptionCollectionViewCell, textBeforeDeleteBackward text: String?)
|
func composeStatusPollOptionCollectionViewCell(_ cell: ComposeStatusPollOptionCollectionViewCell, textBeforeDeleteBackward text: String?)
|
||||||
func composeStatusPollOptionCollectionViewCell(_ cell: ComposeStatusPollOptionCollectionViewCell, pollOptionTextFieldDidReturn: UITextField)
|
func composeStatusPollOptionCollectionViewCell(_ cell: ComposeStatusPollOptionCollectionViewCell, pollOptionTextFieldDidReturn: UITextField)
|
||||||
|
|
|
@ -9,7 +9,7 @@ import os.log
|
||||||
import UIKit
|
import UIKit
|
||||||
import MastodonSDK
|
import MastodonSDK
|
||||||
|
|
||||||
protocol ComposeToolbarViewDelegate: class {
|
protocol ComposeToolbarViewDelegate: AnyObject {
|
||||||
func composeToolbarView(_ composeToolbarView: ComposeToolbarView, cameraButtonDidPressed sender: UIButton, mediaSelectionType type: ComposeToolbarView.MediaSelectionType)
|
func composeToolbarView(_ composeToolbarView: ComposeToolbarView, cameraButtonDidPressed sender: UIButton, mediaSelectionType type: ComposeToolbarView.MediaSelectionType)
|
||||||
func composeToolbarView(_ composeToolbarView: ComposeToolbarView, pollButtonDidPressed sender: UIButton)
|
func composeToolbarView(_ composeToolbarView: ComposeToolbarView, pollButtonDidPressed sender: UIButton)
|
||||||
func composeToolbarView(_ composeToolbarView: ComposeToolbarView, emojiButtonDidPressed sender: UIButton)
|
func composeToolbarView(_ composeToolbarView: ComposeToolbarView, emojiButtonDidPressed sender: UIButton)
|
||||||
|
@ -181,6 +181,15 @@ extension ComposeToolbarView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func imageNameForTimeline() -> String {
|
||||||
|
switch self {
|
||||||
|
case .public: return "person.3"
|
||||||
|
case .unlisted: return "eye.slash"
|
||||||
|
case .private: return "person.crop.circle.badge.plus"
|
||||||
|
case .direct: return "at"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var visibility: Mastodon.Entity.Status.Visibility {
|
var visibility: Mastodon.Entity.Status.Visibility {
|
||||||
switch self {
|
switch self {
|
||||||
case .public: return .public
|
case .public: return .public
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
import os.log
|
import os.log
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
protocol HomeTimelineNavigationBarTitleViewDelegate: class {
|
protocol HomeTimelineNavigationBarTitleViewDelegate: AnyObject {
|
||||||
func homeTimelineNavigationBarTitleView(_ titleView: HomeTimelineNavigationBarTitleView, buttonDidPressed sender: UIButton)
|
func homeTimelineNavigationBarTitleView(_ titleView: HomeTimelineNavigationBarTitleView, buttonDidPressed sender: UIButton)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ import MastodonSDK
|
||||||
import AlamofireImage
|
import AlamofireImage
|
||||||
import Kanna
|
import Kanna
|
||||||
|
|
||||||
protocol PickServerCellDelegate: class {
|
protocol PickServerCellDelegate: AnyObject {
|
||||||
func pickServerCell(_ cell: PickServerCell, expandButtonPressed button: UIButton)
|
func pickServerCell(_ cell: PickServerCell, expandButtonPressed button: UIButton)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
protocol PickServerSearchCellDelegate: class {
|
protocol PickServerSearchCellDelegate: AnyObject {
|
||||||
func pickServerSearchCell(_ cell: PickServerSearchCell, searchTextDidChange searchText: String?)
|
func pickServerSearchCell(_ cell: PickServerSearchCell, searchTextDidChange searchText: String?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ import AlamofireImage
|
||||||
import CropViewController
|
import CropViewController
|
||||||
import TwitterTextEditor
|
import TwitterTextEditor
|
||||||
|
|
||||||
protocol ProfileHeaderViewControllerDelegate: class {
|
protocol ProfileHeaderViewControllerDelegate: AnyObject {
|
||||||
func profileHeaderViewController(_ viewController: ProfileHeaderViewController, viewLayoutDidUpdate view: UIView)
|
func profileHeaderViewController(_ viewController: ProfileHeaderViewController, viewLayoutDidUpdate view: UIView)
|
||||||
func profileHeaderViewController(_ viewController: ProfileHeaderViewController, pageSegmentedControlValueChanged segmentedControl: UISegmentedControl, selectedSegmentIndex index: Int)
|
func profileHeaderViewController(_ viewController: ProfileHeaderViewController, pageSegmentedControlValueChanged segmentedControl: UISegmentedControl, selectedSegmentIndex index: Int)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
import os.log
|
import os.log
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
protocol ProfileStatusDashboardViewDelegate: class {
|
protocol ProfileStatusDashboardViewDelegate: AnyObject {
|
||||||
func profileStatusDashboardView(_ dashboardView: ProfileStatusDashboardView, postDashboardMeterViewDidPressed dashboardMeterView: ProfileStatusDashboardMeterView)
|
func profileStatusDashboardView(_ dashboardView: ProfileStatusDashboardView, postDashboardMeterViewDidPressed dashboardMeterView: ProfileStatusDashboardMeterView)
|
||||||
func profileStatusDashboardView(_ dashboardView: ProfileStatusDashboardView, followingDashboardMeterViewDidPressed dashboardMeterView: ProfileStatusDashboardMeterView)
|
func profileStatusDashboardView(_ dashboardView: ProfileStatusDashboardView, followingDashboardMeterViewDidPressed dashboardMeterView: ProfileStatusDashboardMeterView)
|
||||||
func profileStatusDashboardView(_ dashboardView: ProfileStatusDashboardView, followersDashboardMeterViewDidPressed dashboardMeterView: ProfileStatusDashboardMeterView)
|
func profileStatusDashboardView(_ dashboardView: ProfileStatusDashboardView, followersDashboardMeterViewDidPressed dashboardMeterView: ProfileStatusDashboardMeterView)
|
||||||
|
|
|
@ -10,7 +10,7 @@ import UIKit
|
||||||
import Pageboy
|
import Pageboy
|
||||||
import Tabman
|
import Tabman
|
||||||
|
|
||||||
protocol ProfilePagingViewControllerDelegate: class {
|
protocol ProfilePagingViewControllerDelegate: AnyObject {
|
||||||
func profilePagingViewController(_ viewController: ProfilePagingViewController, didScrollToPostCustomScrollViewContainerController customScrollViewContainerController: ScrollViewContainer, atIndex index: Int)
|
func profilePagingViewController(_ viewController: ProfilePagingViewController, didScrollToPostCustomScrollViewContainerController customScrollViewContainerController: ScrollViewContainer, atIndex index: Int)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
import UIKit
|
import UIKit
|
||||||
import Combine
|
import Combine
|
||||||
|
|
||||||
protocol SettingsAppearanceTableViewCellDelegate: class {
|
protocol SettingsAppearanceTableViewCellDelegate: AnyObject {
|
||||||
func settingsAppearanceCell(_ cell: SettingsAppearanceTableViewCell, didSelectAppearanceMode appearanceMode: SettingsItem.AppearanceMode)
|
func settingsAppearanceCell(_ cell: SettingsAppearanceTableViewCell, didSelectAppearanceMode appearanceMode: SettingsItem.AppearanceMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
import UIKit
|
import UIKit
|
||||||
import Combine
|
import Combine
|
||||||
|
|
||||||
protocol SettingsToggleCellDelegate: class {
|
protocol SettingsToggleCellDelegate: AnyObject {
|
||||||
func settingsToggleCell(_ cell: SettingsToggleTableViewCell, switchValueDidChange switch: UISwitch)
|
func settingsToggleCell(_ cell: SettingsToggleTableViewCell, switchValueDidChange switch: UISwitch)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ import os.log
|
||||||
import AVKit
|
import AVKit
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
protocol PlayerContainerViewDelegate: class {
|
protocol PlayerContainerViewDelegate: AnyObject {
|
||||||
func playerContainerView(_ playerContainerView: PlayerContainerView, contentWarningOverlayViewDidPressed contentWarningOverlayView: ContentWarningOverlayView)
|
func playerContainerView(_ playerContainerView: PlayerContainerView, contentWarningOverlayViewDidPressed contentWarningOverlayView: ContentWarningOverlayView)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ import os.log
|
||||||
import Foundation
|
import Foundation
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
protocol ContentWarningOverlayViewDelegate: class {
|
protocol ContentWarningOverlayViewDelegate: AnyObject {
|
||||||
func contentWarningOverlayViewDidPressed(_ contentWarningOverlayView: ContentWarningOverlayView)
|
func contentWarningOverlayViewDidPressed(_ contentWarningOverlayView: ContentWarningOverlayView)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -122,6 +122,13 @@ final class StatusView: UIView {
|
||||||
return button
|
return button
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
let visibilityImageView: UIImageView = {
|
||||||
|
let imageView = UIImageView()
|
||||||
|
imageView.tintColor = Asset.Colors.Label.secondary.color
|
||||||
|
imageView.contentMode = .scaleAspectFit
|
||||||
|
return imageView
|
||||||
|
}()
|
||||||
|
|
||||||
let statusContainerStackView = UIStackView()
|
let statusContainerStackView = UIStackView()
|
||||||
let statusMosaicImageViewContainer = MosaicImageViewContainer()
|
let statusMosaicImageViewContainer = MosaicImageViewContainer()
|
||||||
|
|
||||||
|
@ -317,6 +324,10 @@ extension StatusView {
|
||||||
authorContainerStackView.addArrangedSubview(revealContentWarningButton)
|
authorContainerStackView.addArrangedSubview(revealContentWarningButton)
|
||||||
revealContentWarningButton.setContentHuggingPriority(.required - 2, for: .horizontal)
|
revealContentWarningButton.setContentHuggingPriority(.required - 2, for: .horizontal)
|
||||||
|
|
||||||
|
// visibility ImageView
|
||||||
|
authorContainerStackView.addArrangedSubview(visibilityImageView)
|
||||||
|
visibilityImageView.setContentHuggingPriority(.required - 2, for: .horizontal)
|
||||||
|
|
||||||
authorContainerStackView.translatesAutoresizingMaskIntoConstraints = false
|
authorContainerStackView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
authorContainerView.addSubview(authorContainerStackView)
|
authorContainerView.addSubview(authorContainerStackView)
|
||||||
NSLayoutConstraint.activate([
|
NSLayoutConstraint.activate([
|
||||||
|
@ -479,6 +490,11 @@ extension StatusView {
|
||||||
// TODO: a11y
|
// TODO: a11y
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateVisibility(visibility: String) {
|
||||||
|
guard let visibility = ComposeToolbarView.VisibilitySelectionType(rawValue: visibility) else { return }
|
||||||
|
visibilityImageView.image = UIImage(systemName: visibility.imageNameForTimeline(), withConfiguration: UIImage.SymbolConfiguration(pointSize: 13, weight: .regular))
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension StatusView {
|
extension StatusView {
|
||||||
|
|
|
@ -9,7 +9,7 @@ import os.log
|
||||||
import UIKit
|
import UIKit
|
||||||
import Combine
|
import Combine
|
||||||
|
|
||||||
protocol ThreadReplyLoaderTableViewCellDelegate: class {
|
protocol ThreadReplyLoaderTableViewCellDelegate: AnyObject {
|
||||||
func threadReplyLoaderTableViewCell(_ cell: ThreadReplyLoaderTableViewCell, loadMoreButtonDidPressed button: UIButton)
|
func threadReplyLoaderTableViewCell(_ cell: ThreadReplyLoaderTableViewCell, loadMoreButtonDidPressed button: UIButton)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ import CoreData
|
||||||
import os.log
|
import os.log
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
protocol TimelineMiddleLoaderTableViewCellDelegate: class {
|
protocol TimelineMiddleLoaderTableViewCellDelegate: AnyObject {
|
||||||
func configure(cell: TimelineMiddleLoaderTableViewCell, upperTimelineStatusID: String?, timelineIndexobjectID:NSManagedObjectID?)
|
func configure(cell: TimelineMiddleLoaderTableViewCell, upperTimelineStatusID: String?, timelineIndexobjectID:NSManagedObjectID?)
|
||||||
func timelineMiddleLoaderTableViewCell(_ cell: TimelineMiddleLoaderTableViewCell, loadMoreButtonDidPressed button: UIButton)
|
func timelineMiddleLoaderTableViewCell(_ cell: TimelineMiddleLoaderTableViewCell, loadMoreButtonDidPressed button: UIButton)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
protocol DeleteBackwardResponseTextFieldDelegate: class {
|
protocol DeleteBackwardResponseTextFieldDelegate: AnyObject {
|
||||||
func deleteBackwardResponseTextField(_ textField: DeleteBackwardResponseTextField, textBeforeDelete: String?)
|
func deleteBackwardResponseTextField(_ textField: DeleteBackwardResponseTextField, textBeforeDelete: String?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
import os.log
|
import os.log
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
protocol ActionToolbarContainerDelegate: class {
|
protocol ActionToolbarContainerDelegate: AnyObject {
|
||||||
func actionToolbarContainer(_ actionToolbarContainer: ActionToolbarContainer, replayButtonDidPressed sender: UIButton)
|
func actionToolbarContainer(_ actionToolbarContainer: ActionToolbarContainer, replayButtonDidPressed sender: UIButton)
|
||||||
func actionToolbarContainer(_ actionToolbarContainer: ActionToolbarContainer, reblogButtonDidPressed sender: UIButton)
|
func actionToolbarContainer(_ actionToolbarContainer: ActionToolbarContainer, reblogButtonDidPressed sender: UIButton)
|
||||||
func actionToolbarContainer(_ actionToolbarContainer: ActionToolbarContainer, starButtonDidPressed sender: UIButton)
|
func actionToolbarContainer(_ actionToolbarContainer: ActionToolbarContainer, starButtonDidPressed sender: UIButton)
|
||||||
|
|
|
@ -81,16 +81,28 @@ extension MediaHostToMediaPreviewViewControllerAnimatedTransitioning {
|
||||||
transitionItem.imageView = transitionImageView
|
transitionItem.imageView = transitionImageView
|
||||||
transitionContext.containerView.addSubview(transitionImageView)
|
transitionContext.containerView.addSubview(transitionImageView)
|
||||||
|
|
||||||
let animator = MediaHostToMediaPreviewViewControllerAnimatedTransitioning.animator(initialVelocity: .zero)
|
toVC.closeButtonBackground.alpha = 0
|
||||||
|
|
||||||
|
if UIAccessibility.isReduceTransparencyEnabled {
|
||||||
|
toVC.visualEffectView.alpha = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
let animator = MediaHostToMediaPreviewViewControllerAnimatedTransitioning.animator(initialVelocity: .zero)
|
||||||
|
|
||||||
animator.addAnimations {
|
animator.addAnimations {
|
||||||
transitionImageView.frame = transitionTargetFrame
|
transitionImageView.frame = transitionTargetFrame
|
||||||
toView.alpha = 1
|
toView.alpha = 1
|
||||||
|
if UIAccessibility.isReduceTransparencyEnabled {
|
||||||
|
toVC.visualEffectView.alpha = 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
animator.addCompletion { position in
|
animator.addCompletion { position in
|
||||||
toVC.pagingViewConttroller.view.alpha = 1
|
toVC.pagingViewConttroller.view.alpha = 1
|
||||||
transitionImageView.removeFromSuperview()
|
transitionImageView.removeFromSuperview()
|
||||||
|
UIView.animate(withDuration: 0.33, delay: 0, options: [.curveEaseInOut]) {
|
||||||
|
toVC.closeButtonBackground.alpha = 1
|
||||||
|
}
|
||||||
transitionContext.completeTransition(position == .end)
|
transitionContext.completeTransition(position == .end)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,9 +133,20 @@ extension MediaHostToMediaPreviewViewControllerAnimatedTransitioning {
|
||||||
transitionContext.completeTransition(false)
|
transitionContext.completeTransition(false)
|
||||||
fatalError()
|
fatalError()
|
||||||
}
|
}
|
||||||
mediaPreviewImageViewController.view.insertSubview(snapshot, aboveSubview: mediaPreviewImageViewController.previewImageView)
|
|
||||||
|
let transitionMaskView = UIView(frame: transitionContext.containerView.bounds)
|
||||||
snapshot.center = transitionContext.containerView.center
|
transitionMaskView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||||
|
transitionContext.containerView.addSubview(transitionMaskView)
|
||||||
|
|
||||||
|
let maskLayer = CAShapeLayer()
|
||||||
|
maskLayer.frame = transitionMaskView.bounds
|
||||||
|
let maskLayerFromPath = UIBezierPath(rect: maskLayer.bounds).cgPath
|
||||||
|
maskLayer.path = maskLayerFromPath
|
||||||
|
transitionMaskView.layer.mask = maskLayer
|
||||||
|
|
||||||
|
transitionMaskView.addSubview(snapshot)
|
||||||
|
snapshot.center = transitionMaskView.center
|
||||||
|
fromVC.view.bringSubviewToFront(fromVC.closeButtonBackground)
|
||||||
|
|
||||||
transitionItem.imageView = imageView
|
transitionItem.imageView = imageView
|
||||||
transitionItem.snapshotTransitioning = snapshot
|
transitionItem.snapshotTransitioning = snapshot
|
||||||
|
@ -136,6 +159,38 @@ extension MediaHostToMediaPreviewViewControllerAnimatedTransitioning {
|
||||||
let animator = popInteractiveTransitionAnimator
|
let animator = popInteractiveTransitionAnimator
|
||||||
|
|
||||||
self.transitionItem.snapshotRaw?.alpha = 0.0
|
self.transitionItem.snapshotRaw?.alpha = 0.0
|
||||||
|
|
||||||
|
var needsMaskWithAnimation = true
|
||||||
|
let maskLayerToRect: CGRect? = {
|
||||||
|
guard case .mosaic = transitionItem.source else { return nil }
|
||||||
|
guard let navigationBar = toVC.navigationController?.navigationBar else { return nil }
|
||||||
|
let navigationBarFrameInWindow = toVC.view.convert(navigationBar.frame, to: nil)
|
||||||
|
var rect = transitionMaskView.frame
|
||||||
|
rect.origin.y = navigationBarFrameInWindow.maxY + UIView.separatorLineHeight(of: toVC.view) // extra hairline
|
||||||
|
|
||||||
|
if rect.minY < snapshot.frame.minY {
|
||||||
|
needsMaskWithAnimation = false
|
||||||
|
}
|
||||||
|
|
||||||
|
return rect
|
||||||
|
}()
|
||||||
|
let maskLayerToPath = maskLayerToRect.flatMap { UIBezierPath(rect: $0) }?.cgPath
|
||||||
|
let maskLayerToFinalRect: CGRect? = {
|
||||||
|
guard case .mosaic = transitionItem.source else { return nil }
|
||||||
|
guard let tabBarController = toVC.tabBarController else { return nil }
|
||||||
|
let tabBarFrameInWindow = toVC.view.convert(tabBarController.tabBar.frame, to: nil)
|
||||||
|
var rect = maskLayerToRect ?? transitionMaskView.frame
|
||||||
|
let offset = rect.maxY - tabBarFrameInWindow.minY
|
||||||
|
guard offset > 0 else { return rect }
|
||||||
|
rect.size.height -= offset
|
||||||
|
return rect
|
||||||
|
}()
|
||||||
|
let maskLayerToFinalPath = maskLayerToFinalRect.flatMap { UIBezierPath(rect: $0) }?.cgPath
|
||||||
|
|
||||||
|
if !needsMaskWithAnimation, let maskLayerToPath = maskLayerToPath {
|
||||||
|
maskLayer.path = maskLayerToPath
|
||||||
|
}
|
||||||
|
|
||||||
animator.addAnimations {
|
animator.addAnimations {
|
||||||
if let targetFrame = targetFrame {
|
if let targetFrame = targetFrame {
|
||||||
self.transitionItem.snapshotTransitioning?.frame = targetFrame
|
self.transitionItem.snapshotTransitioning?.frame = targetFrame
|
||||||
|
@ -145,6 +200,12 @@ extension MediaHostToMediaPreviewViewControllerAnimatedTransitioning {
|
||||||
self.transitionItem.sourceImageViewCornerRadius.flatMap { self.transitionItem.snapshotTransitioning?.layer.cornerRadius = $0 }
|
self.transitionItem.sourceImageViewCornerRadius.flatMap { self.transitionItem.snapshotTransitioning?.layer.cornerRadius = $0 }
|
||||||
fromVC.closeButtonBackground.alpha = 0
|
fromVC.closeButtonBackground.alpha = 0
|
||||||
fromVC.visualEffectView.effect = nil
|
fromVC.visualEffectView.effect = nil
|
||||||
|
if let maskLayerToFinalPath = maskLayerToFinalPath {
|
||||||
|
maskLayer.path = maskLayerToFinalPath
|
||||||
|
}
|
||||||
|
if UIAccessibility.isReduceTransparencyEnabled {
|
||||||
|
fromVC.visualEffectView.alpha = 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
animator.addCompletion { position in
|
animator.addCompletion { position in
|
||||||
|
@ -199,9 +260,21 @@ extension MediaHostToMediaPreviewViewControllerAnimatedTransitioning {
|
||||||
transitionContext.completeTransition(false)
|
transitionContext.completeTransition(false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
mediaPreviewImageViewController.view.insertSubview(snapshot, aboveSubview: mediaPreviewImageViewController.previewImageView)
|
|
||||||
|
let transitionMaskView = UIView(frame: transitionContext.containerView.bounds)
|
||||||
snapshot.center = transitionContext.containerView.center
|
transitionMaskView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||||
|
transitionContext.containerView.addSubview(transitionMaskView)
|
||||||
|
transitionItem.interactiveTransitionMaskView = transitionMaskView
|
||||||
|
|
||||||
|
let maskLayer = CAShapeLayer()
|
||||||
|
maskLayer.frame = transitionMaskView.bounds
|
||||||
|
maskLayer.path = UIBezierPath(rect: maskLayer.bounds).cgPath
|
||||||
|
transitionMaskView.layer.mask = maskLayer
|
||||||
|
transitionItem.interactiveTransitionMaskLayer = maskLayer
|
||||||
|
|
||||||
|
transitionMaskView.addSubview(snapshot)
|
||||||
|
snapshot.center = transitionMaskView.center
|
||||||
|
fromVC.view.bringSubviewToFront(fromVC.closeButtonBackground)
|
||||||
|
|
||||||
transitionItem.imageView = imageView
|
transitionItem.imageView = imageView
|
||||||
transitionItem.snapshotTransitioning = snapshot
|
transitionItem.snapshotTransitioning = snapshot
|
||||||
|
@ -216,6 +289,10 @@ extension MediaHostToMediaPreviewViewControllerAnimatedTransitioning {
|
||||||
let blurEffect = fromVC.visualEffectView.effect
|
let blurEffect = fromVC.visualEffectView.effect
|
||||||
self.transitionItem.snapshotRaw?.alpha = 0.0
|
self.transitionItem.snapshotRaw?.alpha = 0.0
|
||||||
|
|
||||||
|
UIView.animate(withDuration: 0.33, delay: 0, options: [.curveEaseInOut]) {
|
||||||
|
fromVC.closeButtonBackground.alpha = 0
|
||||||
|
}
|
||||||
|
|
||||||
animator.addAnimations {
|
animator.addAnimations {
|
||||||
switch self.transitionItem.source {
|
switch self.transitionItem.source {
|
||||||
case .profileBanner:
|
case .profileBanner:
|
||||||
|
@ -223,9 +300,11 @@ extension MediaHostToMediaPreviewViewControllerAnimatedTransitioning {
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
fromVC.closeButtonBackground.alpha = 0
|
|
||||||
fromVC.visualEffectView.effect = nil
|
fromVC.visualEffectView.effect = nil
|
||||||
self.transitionItem.sourceImageViewCornerRadius.flatMap { self.transitionItem.snapshotTransitioning?.layer.cornerRadius = $0 }
|
self.transitionItem.sourceImageViewCornerRadius.flatMap { self.transitionItem.snapshotTransitioning?.layer.cornerRadius = $0 }
|
||||||
|
if UIAccessibility.isReduceTransparencyEnabled {
|
||||||
|
fromVC.visualEffectView.alpha = 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
animator.addCompletion { position in
|
animator.addCompletion { position in
|
||||||
|
@ -239,6 +318,13 @@ extension MediaHostToMediaPreviewViewControllerAnimatedTransitioning {
|
||||||
self.transitionItem.source.updateAppearance(position: position, index: nil)
|
self.transitionItem.source.updateAppearance(position: position, index: nil)
|
||||||
}
|
}
|
||||||
fromVC.visualEffectView.effect = position == .end ? nil : blurEffect
|
fromVC.visualEffectView.effect = position == .end ? nil : blurEffect
|
||||||
|
transitionMaskView.removeFromSuperview()
|
||||||
|
UIView.animate(withDuration: 0.33, delay: 0, options: [.curveEaseInOut]) {
|
||||||
|
fromVC.closeButtonBackground.alpha = position == .end ? 0 : 1
|
||||||
|
}
|
||||||
|
if UIAccessibility.isReduceTransparencyEnabled {
|
||||||
|
fromVC.visualEffectView.alpha = position == .end ? 0 : 1
|
||||||
|
}
|
||||||
transitionContext.completeTransition(position == .end)
|
transitionContext.completeTransition(position == .end)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -315,8 +401,51 @@ extension MediaHostToMediaPreviewViewControllerAnimatedTransitioning {
|
||||||
let gestureVelocity = panGestureRecognizer.velocity(in: transitionContext.containerView)
|
let gestureVelocity = panGestureRecognizer.velocity(in: transitionContext.containerView)
|
||||||
let velocity = convert(gestureVelocity, for: transitionItem)
|
let velocity = convert(gestureVelocity, for: transitionItem)
|
||||||
let itemAnimator = MediaHostToMediaPreviewViewControllerAnimatedTransitioning.animator(initialVelocity: velocity)
|
let itemAnimator = MediaHostToMediaPreviewViewControllerAnimatedTransitioning.animator(initialVelocity: velocity)
|
||||||
|
|
||||||
|
var maskLayerToFinalPath: CGPath?
|
||||||
|
if toPosition == .end,
|
||||||
|
let transitionMaskView = transitionItem.interactiveTransitionMaskView,
|
||||||
|
let snapshot = transitionItem.snapshotTransitioning {
|
||||||
|
let toVC = transitionItem.previewableViewController
|
||||||
|
|
||||||
|
var needsMaskWithAnimation = true
|
||||||
|
let maskLayerToRect: CGRect? = {
|
||||||
|
guard case .mosaic = transitionItem.source else { return nil }
|
||||||
|
guard let navigationBar = toVC.navigationController?.navigationBar else { return nil }
|
||||||
|
let navigationBarFrameInWindow = toVC.view.convert(navigationBar.frame, to: nil)
|
||||||
|
var rect = transitionMaskView.frame
|
||||||
|
rect.origin.y = navigationBarFrameInWindow.maxY
|
||||||
|
|
||||||
|
if rect.minY < snapshot.frame.minY {
|
||||||
|
needsMaskWithAnimation = false
|
||||||
|
}
|
||||||
|
|
||||||
|
return rect
|
||||||
|
}()
|
||||||
|
let maskLayerToPath = maskLayerToRect.flatMap { UIBezierPath(rect: $0) }?.cgPath
|
||||||
|
|
||||||
|
if let maskLayer = transitionItem.interactiveTransitionMaskLayer, !needsMaskWithAnimation {
|
||||||
|
maskLayer.path = maskLayerToPath
|
||||||
|
}
|
||||||
|
|
||||||
|
let maskLayerToFinalRect: CGRect? = {
|
||||||
|
guard case .mosaic = transitionItem.source else { return nil }
|
||||||
|
guard let tabBarController = toVC.tabBarController else { return nil }
|
||||||
|
let tabBarFrameInWindow = toVC.view.convert(tabBarController.tabBar.frame, to: nil)
|
||||||
|
var rect = maskLayerToRect ?? transitionMaskView.frame
|
||||||
|
let offset = rect.maxY - tabBarFrameInWindow.minY
|
||||||
|
guard offset > 0 else { return rect }
|
||||||
|
rect.size.height -= offset
|
||||||
|
return rect
|
||||||
|
}()
|
||||||
|
maskLayerToFinalPath = maskLayerToFinalRect.flatMap { UIBezierPath(rect: $0) }?.cgPath
|
||||||
|
}
|
||||||
|
|
||||||
itemAnimator.addAnimations {
|
itemAnimator.addAnimations {
|
||||||
|
if let maskLayer = self.transitionItem.interactiveTransitionMaskLayer,
|
||||||
|
let maskLayerToFinalPath = maskLayerToFinalPath {
|
||||||
|
maskLayer.path = maskLayerToFinalPath
|
||||||
|
}
|
||||||
if toPosition == .end {
|
if toPosition == .end {
|
||||||
switch self.transitionItem.source {
|
switch self.transitionItem.source {
|
||||||
case .profileBanner where toPosition == .end:
|
case .profileBanner where toPosition == .end:
|
||||||
|
|
|
@ -12,7 +12,7 @@ import Kingfisher
|
||||||
import GameplayKit
|
import GameplayKit
|
||||||
import MastodonSDK
|
import MastodonSDK
|
||||||
|
|
||||||
protocol MastodonAttachmentServiceDelegate: class {
|
protocol MastodonAttachmentServiceDelegate: AnyObject {
|
||||||
func mastodonAttachmentService(_ service: MastodonAttachmentService, uploadStateDidChange state: MastodonAttachmentService.UploadState?)
|
func mastodonAttachmentService(_ service: MastodonAttachmentService, uploadStateDidChange state: MastodonAttachmentService.UploadState?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue