Gallery pager style changes

This commit is contained in:
Michael Kirk 2018-11-14 10:09:24 -06:00
parent c5bcba45be
commit 47a7114317
10 changed files with 379 additions and 251 deletions

View File

@ -451,6 +451,7 @@
4C948FF72146EB4800349F0D /* BlockListCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C948FF62146EB4800349F0D /* BlockListCache.swift */; };
4C9CA25D217E676900607C63 /* ZXingObjC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C9CA25C217E676900607C63 /* ZXingObjC.framework */; };
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 */; };
4CB5F26720F6E1E2004D1B42 /* MenuActionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF4C0920F55BBA005DA313 /* MenuActionsViewController.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>"; };
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>"; };
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>"; };
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>"; };
@ -2271,6 +2273,7 @@
34B6A902218B3F62007C4606 /* TypingIndicatorView.swift */,
4C1885D1218F8E1C00B67051 /* PhotoGridViewCell.swift */,
4CA46F49219C78050038ABDE /* GalleryRailView.swift */,
4CA46F4B219CCC630038ABDE /* CaptionView.swift */,
);
name = Views;
path = views;
@ -3464,6 +3467,7 @@
452C468F1E427E200087B011 /* OutboundCallInitiator.swift in Sources */,
34D2CCDA2062E7D000CB1A14 /* OWSScreenLockUI.m in Sources */,
45F170BB1E2FC5D3003FC1F2 /* CallAudioService.swift in Sources */,
4CA46F4C219CCC630038ABDE /* CaptionView.swift in Sources */,
345BC30C2047030700257B7C /* OWS2FASettingsViewController.m in Sources */,
340FC8CA20517B84007AEB0F /* OWSBackupImportJob.m in Sources */,
340FC8B7204DAC8D007AEB0F /* OWSConversationSettingsViewController.m in Sources */,

View File

@ -223,14 +223,6 @@ protocol MediaGalleryDataSourceDelegate: class {
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 retainUntilDismissed: MediaGallery?
@ -254,6 +246,10 @@ class MediaGalleryNavigationController: OWSNavigationController {
// MARK: View Lifecycle
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
override func viewDidLoad() {
super.viewDidLoad()
@ -273,6 +269,23 @@ class MediaGalleryNavigationController: OWSNavigationController {
presentationView.layer.minificationFilter = kCAFilterTrilinear
presentationView.layer.magnificationFilter = kCAFilterTrilinear
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
@ -437,7 +450,7 @@ class MediaGallery: NSObject, MediaGalleryDataSource, MediaTileViewControllerDel
// fade out content behind the pageViewController
// and behind the presentation view
self.navigationController.view.backgroundColor = Theme.backgroundColor
self.navigationController.view.backgroundColor = Theme.galleryBackgroundColor
},
completion: { (_: Bool) in
// 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
UIApplication.shared.isStatusBarHidden = false
guard let detailView = mediaPageViewController.view else {
owsFailDebug("detailView was unexpectedly nil")
@ -583,12 +607,6 @@ class MediaGallery: NSObject, MediaGalleryDataSource, MediaTileViewControllerDel
} else {
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
@ -598,7 +616,7 @@ class MediaGallery: NSObject, MediaGalleryDataSource, MediaTileViewControllerDel
animations: {
guard let replacingView = self.replacingView else {
owsFailDebug("replacingView was unexpectedly nil")
self.navigationController.presentingViewController?.dismiss(animated: false, completion: completion)
presentingViewController.dismiss(animated: false, completion: completion)
return
}
// fade out content and toolbars
@ -606,16 +624,16 @@ class MediaGallery: NSObject, MediaGalleryDataSource, MediaTileViewControllerDel
replacingView.alpha = 1.0
},
completion: { (_: Bool) in
self.navigationController.presentingViewController?.dismiss(animated: false, completion: completion)
presentingViewController.dismiss(animated: false, completion: completion)
})
} else {
guard let replacingView = self.replacingView else {
owsFailDebug("replacingView was unexpectedly nil")
navigationController.presentingViewController?.dismiss(animated: false, completion: completion)
presentingViewController.dismiss(animated: false, completion: completion)
return
}
replacingView.alpha = 1.0
navigationController.presentingViewController?.dismiss(animated: false, completion: completion)
presentingViewController.dismiss(animated: false, completion: completion)
}
}

View File

@ -99,36 +99,17 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
Logger.debug("deinit")
}
// MARK: - Subview
// MARK: Bottom Bar
var bottomContainer: UIView!
var footerBar: UIToolbar!
var videoPlayBarButton: UIBarButtonItem!
var videoPauseBarButton: UIBarButtonItem!
let captionContainerView: CaptionContainerView = CaptionContainerView()
var galleryRailView: GalleryRailView = GalleryRailView()
var pagerScrollView: UIScrollView!
// MARK: Caption
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:
// MARK: UIViewController overrides
override func viewDidLoad() {
super.viewDidLoad()
@ -171,51 +152,27 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
// Views
view.backgroundColor = Theme.backgroundColor
view.backgroundColor = Theme.galleryBackgroundColor
let captionViewsContainer = UIView()
captionViewsContainer.setContentHuggingHigh()
captionViewsContainer.setCompressionResistanceHigh()
captionContainerView.delegate = self
updateCaptionContainerVisibility()
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.autoSetDimension(.height, toSize: 60)
self.galleryRailView = galleryRailView
galleryRailView.autoSetDimension(.height, toSize: 72)
let footerBar = self.makeClearToolbar()
self.footerBar = footerBar
footerBar.tintColor = .white
let bottomContainer = UIView()
self.bottomContainer = bottomContainer
bottomContainer.backgroundColor = UIColor.ows_black.withAlphaComponent(0.4)
let toolbarStack = UIStackView(arrangedSubviews: [galleryRailView, footerBar])
toolbarStack.axis = .vertical
let toolbarBarBlurView = UIVisualEffectView(effect: Theme.barBlurEffect)
toolbarStack.insertSubview(toolbarBarBlurView, at: 0)
toolbarBarBlurView.autoPinEdgesToSuperviewEdges()
let bottomStack = UIStackView(arrangedSubviews: [captionViewsContainer, toolbarStack])
let bottomStack = UIStackView(arrangedSubviews: [captionContainerView, galleryRailView, footerBar])
bottomStack.axis = .vertical
bottomContainer.addSubview(bottomStack)
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)
bottomContainer.autoPinWidthToSuperview()
bottomContainer.autoPinEdge(toSuperviewEdge: .bottom)
@ -223,6 +180,7 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
footerBar.autoSetDimension(.height, toSize: 44)
updateTitle()
updateCaption(item: currentItem)
updateMediaRail()
updateFooterBarButtonItems(isPlayingVideo: true)
@ -233,6 +191,19 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
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
var pagerScrollViewContentOffsetObservation: NSKeyValueObservation?
@ -246,36 +217,8 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
guard width > 0 else {
return
}
let ratioComplete = abs((newValue.x - width) / width)
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 = [:]
captionContainerView.updatePagerTransition(ratioComplete: ratioComplete)
}
// 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 {
didSet {
if (oldValue == shouldHideToolbars) {
@ -303,10 +260,6 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
UIApplication.shared.setStatusBarHidden(shouldHideToolbars, with: .none)
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) {
self.currentViewController.setShouldHideToolbars(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) {
// TODO do we still need this? seems like a vestige
// from when media detail view was used for attachment approval
@ -323,20 +309,18 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
}
var toolbarItems: [UIBarButtonItem] = [
UIBarButtonItem(barButtonSystemItem: .action, target: self, action: #selector(didPressShare)),
UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
shareBarButton,
buildFlexibleSpace()
]
if (self.currentItem.isVideo) {
toolbarItems += [
isPlayingVideo ? self.videoPauseBarButton : self.videoPlayBarButton,
UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
buildFlexibleSpace()
]
}
toolbarItems.append(UIBarButtonItem(barButtonSystemItem: .trash,
target: self,
action: #selector(didPressDelete)))
toolbarItems.append(deleteBarButton)
self.footerBar.setItems(toolbarItems, animated: false)
}
@ -482,16 +466,11 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
}
self.pendingViewController = pendingViewController
CATransaction.begin()
CATransaction.disableActions()
if let pendingCaptionText = pendingViewController.galleryItem.captionForDisplay, pendingCaptionText.count > 0 {
self.pendingCaptionView.text = pendingCaptionText
self.captionContainerView.pendingText = pendingCaptionText
} 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
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
// In that case, we don't want to change the captionView.
if (previousPage != currentViewController) {
updatePagerTransition(ratioComplete: 1)
// promote "pending" to "current" caption view.
let oldCaptionView = self.currentCaptionView
self.currentCaptionView = self.pendingCaptionView
self.pendingCaptionView = oldCaptionView
self.pendingCaptionView.text = nil
captionContainerView.completePagerTransition()
}
updateTitle()
@ -529,6 +502,8 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
previousPage.zoomOut(animated: false)
previousPage.stopAnyVideo()
updateFooterBarButtonItems(isPlayingVideo: false)
} else {
captionContainerView.pendingText = nil
}
}
}
@ -703,7 +678,7 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
lazy private var portraitHeaderNameLabel: UILabel = {
let label = UILabel()
label.textColor = Theme.navbarTitleColor
label.textColor = Theme.galleryIconColor
label.font = UIFont.ows_regularFont(withSize: 17)
label.textAlignment = .center
label.adjustsFontSizeToFitWidth = true
@ -714,7 +689,7 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
lazy private var portraitHeaderDateLabel: UILabel = {
let label = UILabel()
label.textColor = Theme.navbarTitleColor
label.textColor = Theme.galleryIconColor
label.font = UIFont.ows_regularFont(withSize: 12)
label.textAlignment = .center
label.adjustsFontSizeToFitWidth = true
@ -756,7 +731,7 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
}
private func updateCaption(item: MediaGalleryItem) {
self.currentCaptionView.text = item.captionForDisplay
captionContainerView.currentText = item.captionForDisplay
}
private func updateTitle(item: MediaGalleryItem) {
@ -802,103 +777,25 @@ extension MediaPageViewController: GalleryRailViewDelegate {
}
}
class CaptionView: UIView {
extension MediaPageViewController: CaptionContainerViewDelegate {
var text: String? {
get { return textView.text }
set { textView.text = newValue }
func captionContainerViewDidUpdateText(_ captionContainerView: CaptionContainerView) {
updateCaptionContainerVisibility()
}
// MARK: Subviews
// MARK: Helpers
let backgroundGradientView = GradientView(from: .clear, to: .black)
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(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()
}
func updateCaptionContainerVisibility() {
if let currentText = captionContainerView.currentText, currentText.count > 0 {
captionContainerView.isHidden = false
return
}
override var font: UIFont? {
didSet {
invalidateIntrinsicContentSize()
}
if let pendingText = captionContainerView.pendingText, pendingText.count > 0 {
captionContainerView.isHidden = false
return
}
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
}
captionContainerView.isHidden = true
}
}

View File

@ -66,7 +66,7 @@ public class MediaTileViewController: UICollectionViewController, MediaGalleryDa
return
}
collectionView.backgroundColor = Theme.backgroundColor
collectionView.backgroundColor = Theme.galleryBackgroundColor
collectionView.register(PhotoGridViewCell.self, forCellWithReuseIdentifier: PhotoGridViewCell.reuseIdentifier)
collectionView.register(MediaGallerySectionHeader.self, forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: MediaGallerySectionHeader.reuseIdentifier)

View 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
}
}
}

View File

@ -57,7 +57,6 @@ class GalleryRailCellView: UIView {
layoutMargins = .zero
self.clipsToBounds = true
adjustAspectRatio(isSelected: isSelected)
addSubview(imageView)
imageView.autoPinEdgesToSuperviewMargins()
@ -88,7 +87,7 @@ class GalleryRailCellView: UIView {
guard self.item === item else { return }
self.imageView.image = image
}.retainUntilComplete()
}.retainUntilComplete()
}
// MARK: Selected
@ -97,34 +96,25 @@ class GalleryRailCellView: UIView {
func setIsSelected(_ isSelected: Bool) {
self.isSelected = isSelected
adjustAspectRatio(isSelected: 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 {
self.layoutMargins = .zero
layoutMargins = .zero
imageView.layer.borderWidth = 0
imageView.layer.cornerRadius = 0
}
}
// 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()
imageView.contentMode = .scaleAspectFill
imageView.autoPinToSquareAspectRatio()
imageView.clipsToBounds = true
return imageView
}()
@ -183,15 +173,21 @@ class GalleryRailView: UIView, GalleryRailCellViewDelegate {
}
self.itemProvider = itemProvider
scrollView.subviews.forEach { $0.removeFromSuperview() }
guard itemProvider.railItems.count > 1 else {
UIView.animate(withDuration: animationDuration) {
self.isHidden = true
}
let cellViews = scrollView.subviews
UIView.animate(withDuration: animationDuration,
animations: {
cellViews.forEach { $0.isHidden = true }
self.isHidden = true
},
completion: { _ in cellViews.forEach { $0.removeFromSuperview() } })
return
}
scrollView.subviews.forEach { $0.removeFromSuperview() }
UIView.animate(withDuration: animationDuration) {
self.isHidden = false
}
@ -200,7 +196,7 @@ class GalleryRailView: UIView, GalleryRailCellViewDelegate {
self.cellViews = cellViews
let stackView = UIStackView(arrangedSubviews: cellViews)
stackView.axis = .horizontal
stackView.spacing = 4
stackView.spacing = 2
scrollView.addSubview(stackView)
stackView.autoPinEdgesToSuperviewEdges()

View File

@ -154,7 +154,12 @@ NS_ASSUME_NONNULL_BEGIN
// Status bar is overlaying the green "call banner"
return UIStatusBarStyleLightContent;
} else {
return (Theme.isDarkThemeEnabled ? UIStatusBarStyleLightContent : super.preferredStatusBarStyle);
UIViewController *presentedViewController = self.presentedViewController;
if (presentedViewController) {
return presentedViewController.preferredStatusBarStyle;
} else {
return (Theme.isDarkThemeEnabled ? UIStatusBarStyleLightContent : super.preferredStatusBarStyle);
}
}
}

View File

@ -58,6 +58,12 @@ public class OWSNavigationBar: UINavigationBar {
// MARK: Theme
private func applyTheme() {
guard respectsTheme else {
self.blurEffectView?.removeFromSuperview()
self.setBackgroundImage(nil, for: .default)
return
}
if UIAccessibilityIsReduceTransparencyEnabled() {
self.blurEffectView?.removeFromSuperview()
let color = Theme.navbarBackgroundColor
@ -107,6 +113,13 @@ public class OWSNavigationBar: UINavigationBar {
applyTheme()
}
@objc
public var respectsTheme: Bool = true {
didSet {
themeDidChange()
}
}
// MARK: Layout
@objc

View File

@ -39,6 +39,9 @@ extern NSString *const ThemeDidChangeNotification;
@property (class, readonly, nonatomic) UIColor *cellSelectedColor;
@property (class, readonly, nonatomic) UIColor *cellSeparatorColor;
@property (class, readonly, nonatomic) UIColor *galleryBackgroundColor;
@property (class, readonly, nonatomic) UIColor *galleryIconColor;
#pragma mark -
@property (class, readonly, nonatomic) UIBarStyle barStyle;

View File

@ -118,6 +118,16 @@ NSString *const ThemeKeyThemeEnabled = @"ThemeKeyThemeEnabled";
return Theme.hairlineColor;
}
+ (UIColor *)galleryBackgroundColor
{
return UIColor.ows_gray95Color;
}
+ (UIColor *)galleryIconColor
{
return UIColor.ows_gray05Color;
}
+ (UIColor *)conversationButtonBackgroundColor
{
return (Theme.isDarkThemeEnabled ? [UIColor colorWithWhite:0.35f alpha:1.f] : UIColor.ows_gray02Color);