mirror of
https://github.com/oxen-io/session-ios.git
synced 2023-12-13 21:30:14 +01:00
Gallery pager style changes
This commit is contained in:
parent
c5bcba45be
commit
47a7114317
|
@ -451,6 +451,7 @@
|
||||||
4C948FF72146EB4800349F0D /* BlockListCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C948FF62146EB4800349F0D /* BlockListCache.swift */; };
|
4C948FF72146EB4800349F0D /* BlockListCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C948FF62146EB4800349F0D /* BlockListCache.swift */; };
|
||||||
4C9CA25D217E676900607C63 /* ZXingObjC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C9CA25C217E676900607C63 /* ZXingObjC.framework */; };
|
4C9CA25D217E676900607C63 /* ZXingObjC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C9CA25C217E676900607C63 /* ZXingObjC.framework */; };
|
||||||
4CA46F4A219C78050038ABDE /* GalleryRailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA46F49219C78050038ABDE /* GalleryRailView.swift */; };
|
4CA46F4A219C78050038ABDE /* GalleryRailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA46F49219C78050038ABDE /* GalleryRailView.swift */; };
|
||||||
|
4CA46F4C219CCC630038ABDE /* CaptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA46F4B219CCC630038ABDE /* CaptionView.swift */; };
|
||||||
4CA5F793211E1F06008C2708 /* Toast.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA5F792211E1F06008C2708 /* Toast.swift */; };
|
4CA5F793211E1F06008C2708 /* Toast.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA5F792211E1F06008C2708 /* Toast.swift */; };
|
||||||
4CB5F26720F6E1E2004D1B42 /* MenuActionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF4C0920F55BBA005DA313 /* MenuActionsViewController.swift */; };
|
4CB5F26720F6E1E2004D1B42 /* MenuActionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF4C0920F55BBA005DA313 /* MenuActionsViewController.swift */; };
|
||||||
4CB5F26920F7D060004D1B42 /* MessageActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB5F26820F7D060004D1B42 /* MessageActions.swift */; };
|
4CB5F26920F7D060004D1B42 /* MessageActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB5F26820F7D060004D1B42 /* MessageActions.swift */; };
|
||||||
|
@ -1160,6 +1161,7 @@
|
||||||
4C948FF62146EB4800349F0D /* BlockListCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockListCache.swift; sourceTree = "<group>"; };
|
4C948FF62146EB4800349F0D /* BlockListCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockListCache.swift; sourceTree = "<group>"; };
|
||||||
4C9CA25C217E676900607C63 /* ZXingObjC.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ZXingObjC.framework; path = ThirdParty/Carthage/Build/iOS/ZXingObjC.framework; sourceTree = "<group>"; };
|
4C9CA25C217E676900607C63 /* ZXingObjC.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ZXingObjC.framework; path = ThirdParty/Carthage/Build/iOS/ZXingObjC.framework; sourceTree = "<group>"; };
|
||||||
4CA46F49219C78050038ABDE /* GalleryRailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GalleryRailView.swift; sourceTree = "<group>"; };
|
4CA46F49219C78050038ABDE /* GalleryRailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GalleryRailView.swift; sourceTree = "<group>"; };
|
||||||
|
4CA46F4B219CCC630038ABDE /* CaptionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CaptionView.swift; sourceTree = "<group>"; };
|
||||||
4CA5F792211E1F06008C2708 /* Toast.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toast.swift; sourceTree = "<group>"; };
|
4CA5F792211E1F06008C2708 /* Toast.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toast.swift; sourceTree = "<group>"; };
|
||||||
4CB5F26820F7D060004D1B42 /* MessageActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageActions.swift; sourceTree = "<group>"; };
|
4CB5F26820F7D060004D1B42 /* MessageActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageActions.swift; sourceTree = "<group>"; };
|
||||||
4CB93DC12180FF07004B9764 /* ProximityMonitoringManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProximityMonitoringManager.swift; sourceTree = "<group>"; };
|
4CB93DC12180FF07004B9764 /* ProximityMonitoringManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProximityMonitoringManager.swift; sourceTree = "<group>"; };
|
||||||
|
@ -2271,6 +2273,7 @@
|
||||||
34B6A902218B3F62007C4606 /* TypingIndicatorView.swift */,
|
34B6A902218B3F62007C4606 /* TypingIndicatorView.swift */,
|
||||||
4C1885D1218F8E1C00B67051 /* PhotoGridViewCell.swift */,
|
4C1885D1218F8E1C00B67051 /* PhotoGridViewCell.swift */,
|
||||||
4CA46F49219C78050038ABDE /* GalleryRailView.swift */,
|
4CA46F49219C78050038ABDE /* GalleryRailView.swift */,
|
||||||
|
4CA46F4B219CCC630038ABDE /* CaptionView.swift */,
|
||||||
);
|
);
|
||||||
name = Views;
|
name = Views;
|
||||||
path = views;
|
path = views;
|
||||||
|
@ -3464,6 +3467,7 @@
|
||||||
452C468F1E427E200087B011 /* OutboundCallInitiator.swift in Sources */,
|
452C468F1E427E200087B011 /* OutboundCallInitiator.swift in Sources */,
|
||||||
34D2CCDA2062E7D000CB1A14 /* OWSScreenLockUI.m in Sources */,
|
34D2CCDA2062E7D000CB1A14 /* OWSScreenLockUI.m in Sources */,
|
||||||
45F170BB1E2FC5D3003FC1F2 /* CallAudioService.swift in Sources */,
|
45F170BB1E2FC5D3003FC1F2 /* CallAudioService.swift in Sources */,
|
||||||
|
4CA46F4C219CCC630038ABDE /* CaptionView.swift in Sources */,
|
||||||
345BC30C2047030700257B7C /* OWS2FASettingsViewController.m in Sources */,
|
345BC30C2047030700257B7C /* OWS2FASettingsViewController.m in Sources */,
|
||||||
340FC8CA20517B84007AEB0F /* OWSBackupImportJob.m in Sources */,
|
340FC8CA20517B84007AEB0F /* OWSBackupImportJob.m in Sources */,
|
||||||
340FC8B7204DAC8D007AEB0F /* OWSConversationSettingsViewController.m in Sources */,
|
340FC8B7204DAC8D007AEB0F /* OWSConversationSettingsViewController.m in Sources */,
|
||||||
|
|
|
@ -223,14 +223,6 @@ protocol MediaGalleryDataSourceDelegate: class {
|
||||||
|
|
||||||
class MediaGalleryNavigationController: OWSNavigationController {
|
class MediaGalleryNavigationController: OWSNavigationController {
|
||||||
|
|
||||||
// MARK: View LifeCycle
|
|
||||||
|
|
||||||
override func viewDidAppear(_ animated: Bool) {
|
|
||||||
super.viewDidAppear(animated)
|
|
||||||
// If the user's device is already rotated, try to respect that by rotating to landscape now
|
|
||||||
UIViewController.attemptRotationToDeviceOrientation()
|
|
||||||
}
|
|
||||||
|
|
||||||
var presentationView: UIImageView!
|
var presentationView: UIImageView!
|
||||||
var retainUntilDismissed: MediaGallery?
|
var retainUntilDismissed: MediaGallery?
|
||||||
|
|
||||||
|
@ -254,6 +246,10 @@ class MediaGalleryNavigationController: OWSNavigationController {
|
||||||
|
|
||||||
// MARK: View Lifecycle
|
// MARK: View Lifecycle
|
||||||
|
|
||||||
|
override var preferredStatusBarStyle: UIStatusBarStyle {
|
||||||
|
return .lightContent
|
||||||
|
}
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
@ -273,6 +269,23 @@ class MediaGalleryNavigationController: OWSNavigationController {
|
||||||
presentationView.layer.minificationFilter = kCAFilterTrilinear
|
presentationView.layer.minificationFilter = kCAFilterTrilinear
|
||||||
presentationView.layer.magnificationFilter = kCAFilterTrilinear
|
presentationView.layer.magnificationFilter = kCAFilterTrilinear
|
||||||
presentationView.contentMode = .scaleAspectFit
|
presentationView.contentMode = .scaleAspectFit
|
||||||
|
|
||||||
|
guard let navigationBar = self.navigationBar as? OWSNavigationBar else {
|
||||||
|
owsFailDebug("navigationBar had unexpected class: \(self.navigationBar)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
navigationBar.respectsTheme = false
|
||||||
|
navigationBar.barStyle = .black
|
||||||
|
navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor: Theme.galleryIconColor]
|
||||||
|
navigationBar.barTintColor = Theme.galleryBackgroundColor.withAlphaComponent(0.6)
|
||||||
|
navigationBar.tintColor = Theme.galleryIconColor
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewDidAppear(_ animated: Bool) {
|
||||||
|
super.viewDidAppear(animated)
|
||||||
|
// If the user's device is already rotated, try to respect that by rotating to landscape now
|
||||||
|
UIViewController.attemptRotationToDeviceOrientation()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Orientation
|
// MARK: Orientation
|
||||||
|
@ -437,7 +450,7 @@ class MediaGallery: NSObject, MediaGalleryDataSource, MediaTileViewControllerDel
|
||||||
|
|
||||||
// fade out content behind the pageViewController
|
// fade out content behind the pageViewController
|
||||||
// and behind the presentation view
|
// and behind the presentation view
|
||||||
self.navigationController.view.backgroundColor = Theme.backgroundColor
|
self.navigationController.view.backgroundColor = Theme.galleryBackgroundColor
|
||||||
},
|
},
|
||||||
completion: { (_: Bool) in
|
completion: { (_: Bool) in
|
||||||
// At this point our presentation view should be overlayed perfectly
|
// At this point our presentation view should be overlayed perfectly
|
||||||
|
@ -538,9 +551,20 @@ class MediaGallery: NSObject, MediaGalleryDataSource, MediaTileViewControllerDel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func dismissMediaDetailViewController(_ mediaPageViewController: MediaPageViewController, animated isAnimated: Bool, completion: (() -> Void)?) {
|
public func dismissMediaDetailViewController(_ mediaPageViewController: MediaPageViewController, animated isAnimated: Bool, completion completionParam: (() -> Void)?) {
|
||||||
|
|
||||||
|
guard let presentingViewController = self.navigationController.presentingViewController else {
|
||||||
|
owsFailDebug("presentingController was unexpectedly nil")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let completion = {
|
||||||
|
completionParam?()
|
||||||
|
UIApplication.shared.isStatusBarHidden = false
|
||||||
|
presentingViewController.setNeedsStatusBarAppearanceUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
navigationController.view.isUserInteractionEnabled = false
|
navigationController.view.isUserInteractionEnabled = false
|
||||||
UIApplication.shared.isStatusBarHidden = false
|
|
||||||
|
|
||||||
guard let detailView = mediaPageViewController.view else {
|
guard let detailView = mediaPageViewController.view else {
|
||||||
owsFailDebug("detailView was unexpectedly nil")
|
owsFailDebug("detailView was unexpectedly nil")
|
||||||
|
@ -583,12 +607,6 @@ class MediaGallery: NSObject, MediaGalleryDataSource, MediaTileViewControllerDel
|
||||||
} else {
|
} else {
|
||||||
self.navigationController.presentationView.layer.cornerRadius = kOWSMessageCellCornerRadius_Small
|
self.navigationController.presentationView.layer.cornerRadius = kOWSMessageCellCornerRadius_Small
|
||||||
}
|
}
|
||||||
},
|
|
||||||
completion: { (_: Bool) in
|
|
||||||
// In case user has hidden bars, which changes background to black.
|
|
||||||
// We don't want to change this while detailView is visible, lest
|
|
||||||
// we obscure out the presentationView
|
|
||||||
detailView.backgroundColor = Theme.backgroundColor
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// This intentionally overlaps the previous animation a bit
|
// This intentionally overlaps the previous animation a bit
|
||||||
|
@ -598,7 +616,7 @@ class MediaGallery: NSObject, MediaGalleryDataSource, MediaTileViewControllerDel
|
||||||
animations: {
|
animations: {
|
||||||
guard let replacingView = self.replacingView else {
|
guard let replacingView = self.replacingView else {
|
||||||
owsFailDebug("replacingView was unexpectedly nil")
|
owsFailDebug("replacingView was unexpectedly nil")
|
||||||
self.navigationController.presentingViewController?.dismiss(animated: false, completion: completion)
|
presentingViewController.dismiss(animated: false, completion: completion)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// fade out content and toolbars
|
// fade out content and toolbars
|
||||||
|
@ -606,16 +624,16 @@ class MediaGallery: NSObject, MediaGalleryDataSource, MediaTileViewControllerDel
|
||||||
replacingView.alpha = 1.0
|
replacingView.alpha = 1.0
|
||||||
},
|
},
|
||||||
completion: { (_: Bool) in
|
completion: { (_: Bool) in
|
||||||
self.navigationController.presentingViewController?.dismiss(animated: false, completion: completion)
|
presentingViewController.dismiss(animated: false, completion: completion)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
guard let replacingView = self.replacingView else {
|
guard let replacingView = self.replacingView else {
|
||||||
owsFailDebug("replacingView was unexpectedly nil")
|
owsFailDebug("replacingView was unexpectedly nil")
|
||||||
navigationController.presentingViewController?.dismiss(animated: false, completion: completion)
|
presentingViewController.dismiss(animated: false, completion: completion)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
replacingView.alpha = 1.0
|
replacingView.alpha = 1.0
|
||||||
navigationController.presentingViewController?.dismiss(animated: false, completion: completion)
|
presentingViewController.dismiss(animated: false, completion: completion)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -99,36 +99,17 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
|
||||||
Logger.debug("deinit")
|
Logger.debug("deinit")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Subview
|
||||||
|
|
||||||
|
// MARK: Bottom Bar
|
||||||
var bottomContainer: UIView!
|
var bottomContainer: UIView!
|
||||||
var footerBar: UIToolbar!
|
var footerBar: UIToolbar!
|
||||||
var videoPlayBarButton: UIBarButtonItem!
|
let captionContainerView: CaptionContainerView = CaptionContainerView()
|
||||||
var videoPauseBarButton: UIBarButtonItem!
|
var galleryRailView: GalleryRailView = GalleryRailView()
|
||||||
|
|
||||||
var pagerScrollView: UIScrollView!
|
var pagerScrollView: UIScrollView!
|
||||||
|
|
||||||
// MARK: Caption
|
// MARK: UIViewController overrides
|
||||||
|
|
||||||
var currentCaptionView: CaptionView!
|
|
||||||
var pendingCaptionView: CaptionView!
|
|
||||||
|
|
||||||
// MARK: ImageRail
|
|
||||||
|
|
||||||
var galleryRailView: GalleryRailView!
|
|
||||||
|
|
||||||
private func makeClearToolbar() -> UIToolbar {
|
|
||||||
let toolbar = UIToolbar()
|
|
||||||
|
|
||||||
toolbar.backgroundColor = UIColor.clear
|
|
||||||
|
|
||||||
// Making a toolbar transparent requires setting an empty uiimage
|
|
||||||
toolbar.setBackgroundImage(UIImage(), forToolbarPosition: .any, barMetrics: .default)
|
|
||||||
|
|
||||||
// hide 1px top-border
|
|
||||||
toolbar.clipsToBounds = true
|
|
||||||
|
|
||||||
return toolbar
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK:
|
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
@ -171,51 +152,27 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
|
||||||
|
|
||||||
// Views
|
// Views
|
||||||
|
|
||||||
view.backgroundColor = Theme.backgroundColor
|
view.backgroundColor = Theme.galleryBackgroundColor
|
||||||
|
|
||||||
let captionViewsContainer = UIView()
|
captionContainerView.delegate = self
|
||||||
captionViewsContainer.setContentHuggingHigh()
|
updateCaptionContainerVisibility()
|
||||||
captionViewsContainer.setCompressionResistanceHigh()
|
|
||||||
|
|
||||||
let currentCaptionView = CaptionView()
|
|
||||||
self.currentCaptionView = currentCaptionView
|
|
||||||
captionViewsContainer.addSubview(currentCaptionView)
|
|
||||||
currentCaptionView.autoPinEdgesToSuperviewEdges(with: .zero, excludingEdge: .top)
|
|
||||||
currentCaptionView.autoPinEdge(toSuperviewEdge: .top, withInset: 0, relation: .greaterThanOrEqual)
|
|
||||||
currentCaptionView.text = currentItem.captionForDisplay
|
|
||||||
|
|
||||||
let pendingCaptionView = CaptionView()
|
|
||||||
self.pendingCaptionView = pendingCaptionView
|
|
||||||
pendingCaptionView.alpha = 0
|
|
||||||
captionViewsContainer.addSubview(pendingCaptionView)
|
|
||||||
pendingCaptionView.autoPinEdgesToSuperviewEdges(with: .zero, excludingEdge: .top)
|
|
||||||
pendingCaptionView.autoPinEdge(toSuperviewEdge: .top, withInset: 0, relation: .greaterThanOrEqual)
|
|
||||||
|
|
||||||
let galleryRailView = GalleryRailView()
|
|
||||||
galleryRailView.delegate = self
|
galleryRailView.delegate = self
|
||||||
galleryRailView.autoSetDimension(.height, toSize: 60)
|
galleryRailView.autoSetDimension(.height, toSize: 72)
|
||||||
self.galleryRailView = galleryRailView
|
|
||||||
|
|
||||||
let footerBar = self.makeClearToolbar()
|
let footerBar = self.makeClearToolbar()
|
||||||
self.footerBar = footerBar
|
self.footerBar = footerBar
|
||||||
|
footerBar.tintColor = .white
|
||||||
|
|
||||||
let bottomContainer = UIView()
|
let bottomContainer = UIView()
|
||||||
self.bottomContainer = bottomContainer
|
self.bottomContainer = bottomContainer
|
||||||
|
bottomContainer.backgroundColor = UIColor.ows_black.withAlphaComponent(0.4)
|
||||||
|
|
||||||
let toolbarStack = UIStackView(arrangedSubviews: [galleryRailView, footerBar])
|
let bottomStack = UIStackView(arrangedSubviews: [captionContainerView, galleryRailView, footerBar])
|
||||||
toolbarStack.axis = .vertical
|
|
||||||
let toolbarBarBlurView = UIVisualEffectView(effect: Theme.barBlurEffect)
|
|
||||||
toolbarStack.insertSubview(toolbarBarBlurView, at: 0)
|
|
||||||
toolbarBarBlurView.autoPinEdgesToSuperviewEdges()
|
|
||||||
|
|
||||||
let bottomStack = UIStackView(arrangedSubviews: [captionViewsContainer, toolbarStack])
|
|
||||||
bottomStack.axis = .vertical
|
bottomStack.axis = .vertical
|
||||||
bottomContainer.addSubview(bottomStack)
|
bottomContainer.addSubview(bottomStack)
|
||||||
bottomStack.autoPinEdgesToSuperviewEdges()
|
bottomStack.autoPinEdgesToSuperviewEdges()
|
||||||
|
|
||||||
self.videoPlayBarButton = UIBarButtonItem(barButtonSystemItem: .play, target: self, action: #selector(didPressPlayBarButton))
|
|
||||||
self.videoPauseBarButton = UIBarButtonItem(barButtonSystemItem: .pause, target: self, action: #selector(didPressPauseBarButton))
|
|
||||||
|
|
||||||
self.view.addSubview(bottomContainer)
|
self.view.addSubview(bottomContainer)
|
||||||
bottomContainer.autoPinWidthToSuperview()
|
bottomContainer.autoPinWidthToSuperview()
|
||||||
bottomContainer.autoPinEdge(toSuperviewEdge: .bottom)
|
bottomContainer.autoPinEdge(toSuperviewEdge: .bottom)
|
||||||
|
@ -223,6 +180,7 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
|
||||||
footerBar.autoSetDimension(.height, toSize: 44)
|
footerBar.autoSetDimension(.height, toSize: 44)
|
||||||
|
|
||||||
updateTitle()
|
updateTitle()
|
||||||
|
updateCaption(item: currentItem)
|
||||||
updateMediaRail()
|
updateMediaRail()
|
||||||
updateFooterBarButtonItems(isPlayingVideo: true)
|
updateFooterBarButtonItems(isPlayingVideo: true)
|
||||||
|
|
||||||
|
@ -233,6 +191,19 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
|
||||||
view.addGestureRecognizer(verticalSwipe)
|
view.addGestureRecognizer(verticalSwipe)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
|
||||||
|
super.viewWillTransition(to: size, with: coordinator)
|
||||||
|
let isLandscape = size.width > size.height
|
||||||
|
self.navigationItem.titleView = isLandscape ? nil : self.portraitHeaderView
|
||||||
|
}
|
||||||
|
|
||||||
|
override func didReceiveMemoryWarning() {
|
||||||
|
Logger.info("")
|
||||||
|
super.didReceiveMemoryWarning()
|
||||||
|
|
||||||
|
self.cachedPages = [:]
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: KVO
|
// MARK: KVO
|
||||||
|
|
||||||
var pagerScrollViewContentOffsetObservation: NSKeyValueObservation?
|
var pagerScrollViewContentOffsetObservation: NSKeyValueObservation?
|
||||||
|
@ -246,36 +217,8 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
|
||||||
guard width > 0 else {
|
guard width > 0 else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let ratioComplete = abs((newValue.x - width) / width)
|
let ratioComplete = abs((newValue.x - width) / width)
|
||||||
updatePagerTransition(ratioComplete: ratioComplete)
|
captionContainerView.updatePagerTransition(ratioComplete: ratioComplete)
|
||||||
}
|
|
||||||
|
|
||||||
func updatePagerTransition(ratioComplete: CGFloat) {
|
|
||||||
if let currentCaptionText = currentCaptionView.text, currentCaptionText.count > 0 {
|
|
||||||
currentCaptionView.alpha = 1 - ratioComplete
|
|
||||||
} else {
|
|
||||||
currentCaptionView.alpha = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
if let pendingCaptionText = pendingCaptionView.text, pendingCaptionText.count > 0 {
|
|
||||||
pendingCaptionView.alpha = ratioComplete
|
|
||||||
} else {
|
|
||||||
pendingCaptionView.alpha = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
|
|
||||||
super.viewWillTransition(to: size, with: coordinator)
|
|
||||||
let isLandscape = size.width > size.height
|
|
||||||
self.navigationItem.titleView = isLandscape ? nil : self.portraitHeaderView
|
|
||||||
}
|
|
||||||
|
|
||||||
override func didReceiveMemoryWarning() {
|
|
||||||
Logger.info("")
|
|
||||||
super.didReceiveMemoryWarning()
|
|
||||||
|
|
||||||
self.cachedPages = [:]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: View Helpers
|
// MARK: View Helpers
|
||||||
|
@ -292,6 +235,20 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func makeClearToolbar() -> UIToolbar {
|
||||||
|
let toolbar = UIToolbar()
|
||||||
|
|
||||||
|
toolbar.backgroundColor = UIColor.clear
|
||||||
|
|
||||||
|
// Making a toolbar transparent requires setting an empty uiimage
|
||||||
|
toolbar.setBackgroundImage(UIImage(), forToolbarPosition: .any, barMetrics: .default)
|
||||||
|
|
||||||
|
// hide 1px top-border
|
||||||
|
toolbar.clipsToBounds = true
|
||||||
|
|
||||||
|
return toolbar
|
||||||
|
}
|
||||||
|
|
||||||
private var shouldHideToolbars: Bool = false {
|
private var shouldHideToolbars: Bool = false {
|
||||||
didSet {
|
didSet {
|
||||||
if (oldValue == shouldHideToolbars) {
|
if (oldValue == shouldHideToolbars) {
|
||||||
|
@ -303,10 +260,6 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
|
||||||
UIApplication.shared.setStatusBarHidden(shouldHideToolbars, with: .none)
|
UIApplication.shared.setStatusBarHidden(shouldHideToolbars, with: .none)
|
||||||
self.navigationController?.setNavigationBarHidden(shouldHideToolbars, animated: false)
|
self.navigationController?.setNavigationBarHidden(shouldHideToolbars, animated: false)
|
||||||
|
|
||||||
// We don't animate the background color change because the old color shows through momentarily
|
|
||||||
// behind where the status bar "used to be".
|
|
||||||
self.view.backgroundColor = (shouldHideToolbars ? UIColor.black : Theme.backgroundColor)
|
|
||||||
|
|
||||||
UIView.animate(withDuration: 0.1) {
|
UIView.animate(withDuration: 0.1) {
|
||||||
self.currentViewController.setShouldHideToolbars(self.shouldHideToolbars)
|
self.currentViewController.setShouldHideToolbars(self.shouldHideToolbars)
|
||||||
self.bottomContainer.isHidden = self.shouldHideToolbars
|
self.bottomContainer.isHidden = self.shouldHideToolbars
|
||||||
|
@ -314,6 +267,39 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: Bar Buttons
|
||||||
|
|
||||||
|
let shareBarButton: UIBarButtonItem = {
|
||||||
|
let shareBarButton = UIBarButtonItem(barButtonSystemItem: .action, target: self, action: #selector(didPressShare))
|
||||||
|
shareBarButton.tintColor = Theme.galleryIconColor
|
||||||
|
return shareBarButton
|
||||||
|
}()
|
||||||
|
|
||||||
|
let deleteBarButton: UIBarButtonItem = {
|
||||||
|
let deleteBarButton = UIBarButtonItem(barButtonSystemItem: .trash,
|
||||||
|
target: self,
|
||||||
|
action: #selector(didPressDelete))
|
||||||
|
deleteBarButton.tintColor = Theme.galleryIconColor
|
||||||
|
return deleteBarButton
|
||||||
|
}()
|
||||||
|
|
||||||
|
func buildFlexibleSpace() -> UIBarButtonItem {
|
||||||
|
return UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
var videoPlayBarButton: UIBarButtonItem = {
|
||||||
|
let videoPlayBarButton = UIBarButtonItem(barButtonSystemItem: .play, target: self, action: #selector(didPressPlayBarButton))
|
||||||
|
videoPlayBarButton.tintColor = Theme.galleryIconColor
|
||||||
|
return videoPlayBarButton
|
||||||
|
}()
|
||||||
|
|
||||||
|
let videoPauseBarButton: UIBarButtonItem = {
|
||||||
|
let videoPauseBarButton = UIBarButtonItem(barButtonSystemItem: .pause, target: self, action:
|
||||||
|
#selector(didPressPauseBarButton))
|
||||||
|
videoPauseBarButton.tintColor = Theme.galleryIconColor
|
||||||
|
return videoPauseBarButton
|
||||||
|
}()
|
||||||
|
|
||||||
private func updateFooterBarButtonItems(isPlayingVideo: Bool) {
|
private func updateFooterBarButtonItems(isPlayingVideo: Bool) {
|
||||||
// TODO do we still need this? seems like a vestige
|
// TODO do we still need this? seems like a vestige
|
||||||
// from when media detail view was used for attachment approval
|
// from when media detail view was used for attachment approval
|
||||||
|
@ -323,20 +309,18 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
|
||||||
}
|
}
|
||||||
|
|
||||||
var toolbarItems: [UIBarButtonItem] = [
|
var toolbarItems: [UIBarButtonItem] = [
|
||||||
UIBarButtonItem(barButtonSystemItem: .action, target: self, action: #selector(didPressShare)),
|
shareBarButton,
|
||||||
UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
|
buildFlexibleSpace()
|
||||||
]
|
]
|
||||||
|
|
||||||
if (self.currentItem.isVideo) {
|
if (self.currentItem.isVideo) {
|
||||||
toolbarItems += [
|
toolbarItems += [
|
||||||
isPlayingVideo ? self.videoPauseBarButton : self.videoPlayBarButton,
|
isPlayingVideo ? self.videoPauseBarButton : self.videoPlayBarButton,
|
||||||
UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
|
buildFlexibleSpace()
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
toolbarItems.append(UIBarButtonItem(barButtonSystemItem: .trash,
|
toolbarItems.append(deleteBarButton)
|
||||||
target: self,
|
|
||||||
action: #selector(didPressDelete)))
|
|
||||||
|
|
||||||
self.footerBar.setItems(toolbarItems, animated: false)
|
self.footerBar.setItems(toolbarItems, animated: false)
|
||||||
}
|
}
|
||||||
|
@ -482,16 +466,11 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
|
||||||
}
|
}
|
||||||
self.pendingViewController = pendingViewController
|
self.pendingViewController = pendingViewController
|
||||||
|
|
||||||
CATransaction.begin()
|
|
||||||
CATransaction.disableActions()
|
|
||||||
if let pendingCaptionText = pendingViewController.galleryItem.captionForDisplay, pendingCaptionText.count > 0 {
|
if let pendingCaptionText = pendingViewController.galleryItem.captionForDisplay, pendingCaptionText.count > 0 {
|
||||||
self.pendingCaptionView.text = pendingCaptionText
|
self.captionContainerView.pendingText = pendingCaptionText
|
||||||
} else {
|
} else {
|
||||||
self.pendingCaptionView.text = nil
|
self.captionContainerView.pendingText = nil
|
||||||
}
|
}
|
||||||
self.pendingCaptionView.sizeToFit()
|
|
||||||
self.pendingCaptionView.superview?.layoutIfNeeded()
|
|
||||||
CATransaction.commit()
|
|
||||||
|
|
||||||
// Ensure upcoming page respects current toolbar status
|
// Ensure upcoming page respects current toolbar status
|
||||||
pendingViewController.setShouldHideToolbars(self.shouldHideToolbars)
|
pendingViewController.setShouldHideToolbars(self.shouldHideToolbars)
|
||||||
|
@ -515,13 +494,7 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
|
||||||
// This can happen when trying to page past the last (or first) view controller
|
// This can happen when trying to page past the last (or first) view controller
|
||||||
// In that case, we don't want to change the captionView.
|
// In that case, we don't want to change the captionView.
|
||||||
if (previousPage != currentViewController) {
|
if (previousPage != currentViewController) {
|
||||||
updatePagerTransition(ratioComplete: 1)
|
captionContainerView.completePagerTransition()
|
||||||
|
|
||||||
// promote "pending" to "current" caption view.
|
|
||||||
let oldCaptionView = self.currentCaptionView
|
|
||||||
self.currentCaptionView = self.pendingCaptionView
|
|
||||||
self.pendingCaptionView = oldCaptionView
|
|
||||||
self.pendingCaptionView.text = nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTitle()
|
updateTitle()
|
||||||
|
@ -529,6 +502,8 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
|
||||||
previousPage.zoomOut(animated: false)
|
previousPage.zoomOut(animated: false)
|
||||||
previousPage.stopAnyVideo()
|
previousPage.stopAnyVideo()
|
||||||
updateFooterBarButtonItems(isPlayingVideo: false)
|
updateFooterBarButtonItems(isPlayingVideo: false)
|
||||||
|
} else {
|
||||||
|
captionContainerView.pendingText = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -703,7 +678,7 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
|
||||||
|
|
||||||
lazy private var portraitHeaderNameLabel: UILabel = {
|
lazy private var portraitHeaderNameLabel: UILabel = {
|
||||||
let label = UILabel()
|
let label = UILabel()
|
||||||
label.textColor = Theme.navbarTitleColor
|
label.textColor = Theme.galleryIconColor
|
||||||
label.font = UIFont.ows_regularFont(withSize: 17)
|
label.font = UIFont.ows_regularFont(withSize: 17)
|
||||||
label.textAlignment = .center
|
label.textAlignment = .center
|
||||||
label.adjustsFontSizeToFitWidth = true
|
label.adjustsFontSizeToFitWidth = true
|
||||||
|
@ -714,7 +689,7 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
|
||||||
|
|
||||||
lazy private var portraitHeaderDateLabel: UILabel = {
|
lazy private var portraitHeaderDateLabel: UILabel = {
|
||||||
let label = UILabel()
|
let label = UILabel()
|
||||||
label.textColor = Theme.navbarTitleColor
|
label.textColor = Theme.galleryIconColor
|
||||||
label.font = UIFont.ows_regularFont(withSize: 12)
|
label.font = UIFont.ows_regularFont(withSize: 12)
|
||||||
label.textAlignment = .center
|
label.textAlignment = .center
|
||||||
label.adjustsFontSizeToFitWidth = true
|
label.adjustsFontSizeToFitWidth = true
|
||||||
|
@ -756,7 +731,7 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateCaption(item: MediaGalleryItem) {
|
private func updateCaption(item: MediaGalleryItem) {
|
||||||
self.currentCaptionView.text = item.captionForDisplay
|
captionContainerView.currentText = item.captionForDisplay
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateTitle(item: MediaGalleryItem) {
|
private func updateTitle(item: MediaGalleryItem) {
|
||||||
|
@ -802,103 +777,25 @@ extension MediaPageViewController: GalleryRailViewDelegate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CaptionView: UIView {
|
extension MediaPageViewController: CaptionContainerViewDelegate {
|
||||||
|
|
||||||
var text: String? {
|
func captionContainerViewDidUpdateText(_ captionContainerView: CaptionContainerView) {
|
||||||
get { return textView.text }
|
updateCaptionContainerVisibility()
|
||||||
set { textView.text = newValue }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Subviews
|
// MARK: Helpers
|
||||||
|
|
||||||
let backgroundGradientView = GradientView(from: .clear, to: .black)
|
func updateCaptionContainerVisibility() {
|
||||||
|
if let currentText = captionContainerView.currentText, currentText.count > 0 {
|
||||||
let textView: CaptionTextView = {
|
captionContainerView.isHidden = false
|
||||||
let textView = CaptionTextView()
|
return
|
||||||
|
|
||||||
textView.font = UIFont.ows_dynamicTypeBody
|
|
||||||
textView.textColor = .white
|
|
||||||
textView.backgroundColor = .clear
|
|
||||||
textView.isEditable = false
|
|
||||||
textView.isSelectable = false
|
|
||||||
|
|
||||||
return textView
|
|
||||||
}()
|
|
||||||
|
|
||||||
let scrollFadeView = GradientView(from: .clear, to: .black)
|
|
||||||
|
|
||||||
// MARK: Initializers
|
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
|
||||||
super.init(frame: frame)
|
|
||||||
|
|
||||||
addSubview(backgroundGradientView)
|
|
||||||
backgroundGradientView.autoPinEdgesToSuperviewEdges()
|
|
||||||
|
|
||||||
addSubview(textView)
|
|
||||||
textView.autoPinEdgesToSuperviewMargins()
|
|
||||||
|
|
||||||
addSubview(scrollFadeView)
|
|
||||||
scrollFadeView.autoPinEdgesToSuperviewEdges(with: .zero, excludingEdge: .top)
|
|
||||||
scrollFadeView.autoSetDimension(.height, toSize: 20)
|
|
||||||
}
|
|
||||||
|
|
||||||
required init?(coder aDecoder: NSCoder) {
|
|
||||||
fatalError("init(coder:) has not been implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: UIView overrides
|
|
||||||
|
|
||||||
override func layoutSubviews() {
|
|
||||||
super.layoutSubviews()
|
|
||||||
scrollFadeView.isHidden = !textView.doesContentNeedScroll
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: -
|
|
||||||
|
|
||||||
class CaptionTextView: UITextView {
|
|
||||||
|
|
||||||
var kMaxHeight: CGFloat = ScaleFromIPhone5(200)
|
|
||||||
|
|
||||||
override var text: String! {
|
|
||||||
didSet {
|
|
||||||
invalidateIntrinsicContentSize()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override var font: UIFont? {
|
if let pendingText = captionContainerView.pendingText, pendingText.count > 0 {
|
||||||
didSet {
|
captionContainerView.isHidden = false
|
||||||
invalidateIntrinsicContentSize()
|
return
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var doesContentNeedScroll: Bool {
|
captionContainerView.isHidden = true
|
||||||
return self.bounds.height == kMaxHeight
|
|
||||||
}
|
|
||||||
|
|
||||||
override func layoutSubviews() {
|
|
||||||
super.layoutSubviews()
|
|
||||||
|
|
||||||
// Enable/disable scrolling depending on wether we've clipped
|
|
||||||
// content in `intrinsicContentSize`
|
|
||||||
if doesContentNeedScroll {
|
|
||||||
if !isScrollEnabled {
|
|
||||||
isScrollEnabled = true
|
|
||||||
}
|
|
||||||
} else if isScrollEnabled {
|
|
||||||
isScrollEnabled = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override var intrinsicContentSize: CGSize {
|
|
||||||
var size = super.intrinsicContentSize
|
|
||||||
|
|
||||||
if size.height == UIViewNoIntrinsicMetric {
|
|
||||||
size.height = layoutManager.usedRect(for: textContainer).height + textContainerInset.top + textContainerInset.bottom
|
|
||||||
}
|
|
||||||
size.height = min(kMaxHeight, size.height)
|
|
||||||
|
|
||||||
return size
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,7 +66,7 @@ public class MediaTileViewController: UICollectionViewController, MediaGalleryDa
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
collectionView.backgroundColor = Theme.backgroundColor
|
collectionView.backgroundColor = Theme.galleryBackgroundColor
|
||||||
|
|
||||||
collectionView.register(PhotoGridViewCell.self, forCellWithReuseIdentifier: PhotoGridViewCell.reuseIdentifier)
|
collectionView.register(PhotoGridViewCell.self, forCellWithReuseIdentifier: PhotoGridViewCell.reuseIdentifier)
|
||||||
collectionView.register(MediaGallerySectionHeader.self, forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: MediaGallerySectionHeader.reuseIdentifier)
|
collectionView.register(MediaGallerySectionHeader.self, forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: MediaGallerySectionHeader.reuseIdentifier)
|
||||||
|
|
182
Signal/src/views/CaptionView.swift
Normal file
182
Signal/src/views/CaptionView.swift
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
public protocol CaptionContainerViewDelegate: class {
|
||||||
|
func captionContainerViewDidUpdateText(_ captionContainerView: CaptionContainerView)
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CaptionContainerView: UIView {
|
||||||
|
|
||||||
|
weak var delegate: CaptionContainerViewDelegate?
|
||||||
|
|
||||||
|
var currentText: String? {
|
||||||
|
get { return currentCaptionView.text }
|
||||||
|
set {
|
||||||
|
currentCaptionView.text = newValue
|
||||||
|
delegate?.captionContainerViewDidUpdateText(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var pendingText: String? {
|
||||||
|
get { return pendingCaptionView.text }
|
||||||
|
set {
|
||||||
|
pendingCaptionView.text = newValue
|
||||||
|
delegate?.captionContainerViewDidUpdateText(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updatePagerTransition(ratioComplete: CGFloat) {
|
||||||
|
if let currentText = self.currentText, currentText.count > 1 {
|
||||||
|
currentCaptionView.alpha = 1 - ratioComplete
|
||||||
|
} else {
|
||||||
|
currentCaptionView.alpha = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if let pendingText = self.pendingText, pendingText.count > 1 {
|
||||||
|
pendingCaptionView.alpha = ratioComplete
|
||||||
|
} else {
|
||||||
|
pendingCaptionView.alpha = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func completePagerTransition() {
|
||||||
|
updatePagerTransition(ratioComplete: 1)
|
||||||
|
|
||||||
|
// promote "pending" to "current" caption view.
|
||||||
|
let oldCaptionView = self.currentCaptionView
|
||||||
|
self.currentCaptionView = self.pendingCaptionView
|
||||||
|
self.pendingCaptionView = oldCaptionView
|
||||||
|
self.pendingText = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Initializers
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
|
||||||
|
setContentHuggingHigh()
|
||||||
|
setCompressionResistanceHigh()
|
||||||
|
|
||||||
|
addSubview(currentCaptionView)
|
||||||
|
currentCaptionView.autoPinEdgesToSuperviewEdges(with: .zero, excludingEdge: .top)
|
||||||
|
currentCaptionView.autoPinEdge(toSuperviewEdge: .top, withInset: 0, relation: .greaterThanOrEqual)
|
||||||
|
|
||||||
|
pendingCaptionView.alpha = 0
|
||||||
|
addSubview(pendingCaptionView)
|
||||||
|
pendingCaptionView.autoPinEdgesToSuperviewEdges(with: .zero, excludingEdge: .top)
|
||||||
|
pendingCaptionView.autoPinEdge(toSuperviewEdge: .top, withInset: 0, relation: .greaterThanOrEqual)
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder aDecoder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Subviews
|
||||||
|
|
||||||
|
private var pendingCaptionView: CaptionView = CaptionView()
|
||||||
|
private var currentCaptionView: CaptionView = CaptionView()
|
||||||
|
}
|
||||||
|
|
||||||
|
private class CaptionView: UIView {
|
||||||
|
|
||||||
|
var text: String? {
|
||||||
|
get { return textView.text }
|
||||||
|
|
||||||
|
set {
|
||||||
|
if let captionText = newValue, captionText.count > 0 {
|
||||||
|
textView.text = captionText
|
||||||
|
} else {
|
||||||
|
textView.text = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Subviews
|
||||||
|
|
||||||
|
let textView: CaptionTextView = {
|
||||||
|
let textView = CaptionTextView()
|
||||||
|
|
||||||
|
textView.font = UIFont.ows_dynamicTypeBody
|
||||||
|
textView.textColor = .white
|
||||||
|
textView.backgroundColor = .clear
|
||||||
|
textView.isEditable = false
|
||||||
|
textView.isSelectable = false
|
||||||
|
|
||||||
|
return textView
|
||||||
|
}()
|
||||||
|
|
||||||
|
let scrollFadeView = GradientView(from: .clear, to: .black)
|
||||||
|
|
||||||
|
// MARK: Initializers
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
|
||||||
|
addSubview(textView)
|
||||||
|
textView.autoPinEdgesToSuperviewMargins()
|
||||||
|
|
||||||
|
addSubview(scrollFadeView)
|
||||||
|
scrollFadeView.autoPinEdgesToSuperviewEdges(with: .zero, excludingEdge: .top)
|
||||||
|
scrollFadeView.autoSetDimension(.height, toSize: 20)
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder aDecoder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: UIView overrides
|
||||||
|
|
||||||
|
override func layoutSubviews() {
|
||||||
|
super.layoutSubviews()
|
||||||
|
scrollFadeView.isHidden = !textView.doesContentNeedScroll
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: -
|
||||||
|
|
||||||
|
class CaptionTextView: UITextView {
|
||||||
|
|
||||||
|
var kMaxHeight: CGFloat = ScaleFromIPhone5(200)
|
||||||
|
|
||||||
|
override var text: String! {
|
||||||
|
didSet {
|
||||||
|
invalidateIntrinsicContentSize()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override var font: UIFont? {
|
||||||
|
didSet {
|
||||||
|
invalidateIntrinsicContentSize()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var doesContentNeedScroll: Bool {
|
||||||
|
return self.bounds.height == kMaxHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
override func layoutSubviews() {
|
||||||
|
super.layoutSubviews()
|
||||||
|
|
||||||
|
// Enable/disable scrolling depending on wether we've clipped
|
||||||
|
// content in `intrinsicContentSize`
|
||||||
|
if doesContentNeedScroll {
|
||||||
|
if !isScrollEnabled {
|
||||||
|
isScrollEnabled = true
|
||||||
|
}
|
||||||
|
} else if isScrollEnabled {
|
||||||
|
isScrollEnabled = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override var intrinsicContentSize: CGSize {
|
||||||
|
var size = super.intrinsicContentSize
|
||||||
|
|
||||||
|
if size.height == UIViewNoIntrinsicMetric {
|
||||||
|
size.height = layoutManager.usedRect(for: textContainer).height + textContainerInset.top + textContainerInset.bottom
|
||||||
|
}
|
||||||
|
size.height = min(kMaxHeight, size.height)
|
||||||
|
|
||||||
|
return size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -57,7 +57,6 @@ class GalleryRailCellView: UIView {
|
||||||
|
|
||||||
layoutMargins = .zero
|
layoutMargins = .zero
|
||||||
self.clipsToBounds = true
|
self.clipsToBounds = true
|
||||||
adjustAspectRatio(isSelected: isSelected)
|
|
||||||
addSubview(imageView)
|
addSubview(imageView)
|
||||||
imageView.autoPinEdgesToSuperviewMargins()
|
imageView.autoPinEdgesToSuperviewMargins()
|
||||||
|
|
||||||
|
@ -88,7 +87,7 @@ class GalleryRailCellView: UIView {
|
||||||
guard self.item === item else { return }
|
guard self.item === item else { return }
|
||||||
|
|
||||||
self.imageView.image = image
|
self.imageView.image = image
|
||||||
}.retainUntilComplete()
|
}.retainUntilComplete()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Selected
|
// MARK: Selected
|
||||||
|
@ -97,34 +96,25 @@ class GalleryRailCellView: UIView {
|
||||||
|
|
||||||
func setIsSelected(_ isSelected: Bool) {
|
func setIsSelected(_ isSelected: Bool) {
|
||||||
self.isSelected = isSelected
|
self.isSelected = isSelected
|
||||||
adjustAspectRatio(isSelected: isSelected)
|
|
||||||
if isSelected {
|
if isSelected {
|
||||||
self.layoutMargins = UIEdgeInsets(top: 0, left: 3, bottom: 0, right: 3)
|
layoutMargins = UIEdgeInsets(top: 0, left: 6, bottom: 0, right: 6)
|
||||||
|
imageView.layer.borderColor = UIColor(rgbHex: 0x1f8fe8).cgColor
|
||||||
|
imageView.layer.borderWidth = 2
|
||||||
|
imageView.layer.cornerRadius = 2
|
||||||
} else {
|
} else {
|
||||||
self.layoutMargins = .zero
|
layoutMargins = .zero
|
||||||
|
imageView.layer.borderWidth = 0
|
||||||
|
imageView.layer.cornerRadius = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Subview Helpers
|
// MARK: Subview Helpers
|
||||||
|
|
||||||
var aspectRatioConstraint: NSLayoutConstraint?
|
|
||||||
func adjustAspectRatio(isSelected: Bool) {
|
|
||||||
if let oldConstraint = aspectRatioConstraint {
|
|
||||||
NSLayoutConstraint.deactivate([oldConstraint])
|
|
||||||
}
|
|
||||||
|
|
||||||
if isSelected, let itemAspectRatio = item?.aspectRatio {
|
|
||||||
aspectRatioConstraint = imageView.autoPin(toAspectRatio: itemAspectRatio)
|
|
||||||
} else {
|
|
||||||
// Portrait mode AR by default
|
|
||||||
let kDefaultAspectRatio: CGFloat = 9.0 / 16.0
|
|
||||||
aspectRatioConstraint = imageView.autoPin(toAspectRatio: kDefaultAspectRatio)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let imageView: UIImageView = {
|
let imageView: UIImageView = {
|
||||||
let imageView = UIImageView()
|
let imageView = UIImageView()
|
||||||
imageView.contentMode = .scaleAspectFill
|
imageView.contentMode = .scaleAspectFill
|
||||||
|
imageView.autoPinToSquareAspectRatio()
|
||||||
|
imageView.clipsToBounds = true
|
||||||
|
|
||||||
return imageView
|
return imageView
|
||||||
}()
|
}()
|
||||||
|
@ -183,15 +173,21 @@ class GalleryRailView: UIView, GalleryRailCellViewDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.itemProvider = itemProvider
|
self.itemProvider = itemProvider
|
||||||
scrollView.subviews.forEach { $0.removeFromSuperview() }
|
|
||||||
|
|
||||||
guard itemProvider.railItems.count > 1 else {
|
guard itemProvider.railItems.count > 1 else {
|
||||||
UIView.animate(withDuration: animationDuration) {
|
let cellViews = scrollView.subviews
|
||||||
self.isHidden = true
|
|
||||||
}
|
UIView.animate(withDuration: animationDuration,
|
||||||
|
animations: {
|
||||||
|
cellViews.forEach { $0.isHidden = true }
|
||||||
|
self.isHidden = true
|
||||||
|
},
|
||||||
|
completion: { _ in cellViews.forEach { $0.removeFromSuperview() } })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scrollView.subviews.forEach { $0.removeFromSuperview() }
|
||||||
|
|
||||||
UIView.animate(withDuration: animationDuration) {
|
UIView.animate(withDuration: animationDuration) {
|
||||||
self.isHidden = false
|
self.isHidden = false
|
||||||
}
|
}
|
||||||
|
@ -200,7 +196,7 @@ class GalleryRailView: UIView, GalleryRailCellViewDelegate {
|
||||||
self.cellViews = cellViews
|
self.cellViews = cellViews
|
||||||
let stackView = UIStackView(arrangedSubviews: cellViews)
|
let stackView = UIStackView(arrangedSubviews: cellViews)
|
||||||
stackView.axis = .horizontal
|
stackView.axis = .horizontal
|
||||||
stackView.spacing = 4
|
stackView.spacing = 2
|
||||||
|
|
||||||
scrollView.addSubview(stackView)
|
scrollView.addSubview(stackView)
|
||||||
stackView.autoPinEdgesToSuperviewEdges()
|
stackView.autoPinEdgesToSuperviewEdges()
|
||||||
|
|
|
@ -154,7 +154,12 @@ NS_ASSUME_NONNULL_BEGIN
|
||||||
// Status bar is overlaying the green "call banner"
|
// Status bar is overlaying the green "call banner"
|
||||||
return UIStatusBarStyleLightContent;
|
return UIStatusBarStyleLightContent;
|
||||||
} else {
|
} else {
|
||||||
return (Theme.isDarkThemeEnabled ? UIStatusBarStyleLightContent : super.preferredStatusBarStyle);
|
UIViewController *presentedViewController = self.presentedViewController;
|
||||||
|
if (presentedViewController) {
|
||||||
|
return presentedViewController.preferredStatusBarStyle;
|
||||||
|
} else {
|
||||||
|
return (Theme.isDarkThemeEnabled ? UIStatusBarStyleLightContent : super.preferredStatusBarStyle);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,12 @@ public class OWSNavigationBar: UINavigationBar {
|
||||||
// MARK: Theme
|
// MARK: Theme
|
||||||
|
|
||||||
private func applyTheme() {
|
private func applyTheme() {
|
||||||
|
guard respectsTheme else {
|
||||||
|
self.blurEffectView?.removeFromSuperview()
|
||||||
|
self.setBackgroundImage(nil, for: .default)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if UIAccessibilityIsReduceTransparencyEnabled() {
|
if UIAccessibilityIsReduceTransparencyEnabled() {
|
||||||
self.blurEffectView?.removeFromSuperview()
|
self.blurEffectView?.removeFromSuperview()
|
||||||
let color = Theme.navbarBackgroundColor
|
let color = Theme.navbarBackgroundColor
|
||||||
|
@ -107,6 +113,13 @@ public class OWSNavigationBar: UINavigationBar {
|
||||||
applyTheme()
|
applyTheme()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc
|
||||||
|
public var respectsTheme: Bool = true {
|
||||||
|
didSet {
|
||||||
|
themeDidChange()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: Layout
|
// MARK: Layout
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
|
|
|
@ -39,6 +39,9 @@ extern NSString *const ThemeDidChangeNotification;
|
||||||
@property (class, readonly, nonatomic) UIColor *cellSelectedColor;
|
@property (class, readonly, nonatomic) UIColor *cellSelectedColor;
|
||||||
@property (class, readonly, nonatomic) UIColor *cellSeparatorColor;
|
@property (class, readonly, nonatomic) UIColor *cellSeparatorColor;
|
||||||
|
|
||||||
|
@property (class, readonly, nonatomic) UIColor *galleryBackgroundColor;
|
||||||
|
@property (class, readonly, nonatomic) UIColor *galleryIconColor;
|
||||||
|
|
||||||
#pragma mark -
|
#pragma mark -
|
||||||
|
|
||||||
@property (class, readonly, nonatomic) UIBarStyle barStyle;
|
@property (class, readonly, nonatomic) UIBarStyle barStyle;
|
||||||
|
|
|
@ -118,6 +118,16 @@ NSString *const ThemeKeyThemeEnabled = @"ThemeKeyThemeEnabled";
|
||||||
return Theme.hairlineColor;
|
return Theme.hairlineColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
+ (UIColor *)galleryBackgroundColor
|
||||||
|
{
|
||||||
|
return UIColor.ows_gray95Color;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (UIColor *)galleryIconColor
|
||||||
|
{
|
||||||
|
return UIColor.ows_gray05Color;
|
||||||
|
}
|
||||||
|
|
||||||
+ (UIColor *)conversationButtonBackgroundColor
|
+ (UIColor *)conversationButtonBackgroundColor
|
||||||
{
|
{
|
||||||
return (Theme.isDarkThemeEnabled ? [UIColor colorWithWhite:0.35f alpha:1.f] : UIColor.ows_gray02Color);
|
return (Theme.isDarkThemeEnabled ? [UIColor colorWithWhite:0.35f alpha:1.f] : UIColor.ows_gray02Color);
|
||||||
|
|
Loading…
Reference in a new issue