Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Nextcloud.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -6246,7 +6246,7 @@
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 2;
CURRENT_PROJECT_VERSION = 3;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = NKUJUXUJ3B;
Expand Down Expand Up @@ -6314,7 +6314,7 @@
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 2;
CURRENT_PROJECT_VERSION = 3;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = NKUJUXUJ3B;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,45 +5,64 @@
import UIKit

final class NCMediaViewerFloatingTitleView: UIView {
private let primaryLabel = UILabel()
private let secondaryLabel = UILabel()
private let stackView = UIStackView()
private let blurView = UIVisualEffectView(effect: nil)
private weak var navigationBar: UINavigationBar?
private var navigationBarConstraints: [NSLayoutConstraint] = []
private var centerXConstraint: NSLayoutConstraint?
private var heightConstraint: NSLayoutConstraint?

private let titleButton: UIButton = {
let button: UIButton

if #available(iOS 26.0, *) {
var configuration = UIButton.Configuration.glass()
button = UIButton(configuration: configuration)
} else {
var configuration = UIButton.Configuration.plain()
button = UIButton(configuration: configuration)
}

button.translatesAutoresizingMaskIntoConstraints = false
button.isUserInteractionEnabled = false
button.adjustsImageSizeForAccessibilityContentSizeCategory = false

return button
}()

init() {
super.init(frame: .zero)

translatesAutoresizingMaskIntoConstraints = false
backgroundColor = .clear
layoutMargins = UIEdgeInsets(top: 6, left: 14, bottom: 6, right: 14)
clipsToBounds = false
isAccessibilityElement = true

configureLabels()
configureBlurView()
configureStackView()
updateAppearance()
addSubview(titleButton)

registerForTraitChanges([UITraitUserInterfaceStyle.self]) { [weak self] (_: NCMediaViewerFloatingTitleView, _: UITraitCollection) in
self?.updateAppearance()
}
NSLayoutConstraint.activate([
titleButton.leadingAnchor.constraint(equalTo: leadingAnchor),
titleButton.trailingAnchor.constraint(equalTo: trailingAnchor),
titleButton.topAnchor.constraint(equalTo: topAnchor),
titleButton.bottomAnchor.constraint(equalTo: bottomAnchor)
])
}

@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

// Attach directly to the navigation bar to match real button layout.
func attach(
to navigationBar: UINavigationBar,
widthMultiplier: CGFloat = 0.36,
verticalOffset: CGFloat = 0
) {
override func layoutSubviews() {
super.layoutSubviews()

var configuration = titleButton.configuration
configuration?.titleAlignment = .center
configuration?.titleLineBreakMode = .byTruncatingMiddle

titleButton.configuration = configuration
titleButton.titleLabel?.numberOfLines = 1
}

func attach(to navigationBar: UINavigationBar, widthMultiplier: CGFloat = 0.36, verticalOffset: CGFloat = 0) {
if self.navigationBar !== navigationBar || superview !== navigationBar {
navigationBarConstraints.forEach { $0.isActive = false }
navigationBarConstraints.removeAll()
Expand All @@ -52,14 +71,18 @@ final class NCMediaViewerFloatingTitleView: UIView {

let centerXConstraint = centerXAnchor.constraint(equalTo: navigationBar.centerXAnchor)
let heightConstraint = heightAnchor.constraint(equalToConstant: navigationItemHeight(in: navigationBar))
let topConstraint = topAnchor.constraint(equalTo: navigationBar.topAnchor, constant: verticalOffset)
self.centerXConstraint = centerXConstraint
self.heightConstraint = heightConstraint

navigationBarConstraints = [
centerXConstraint,
topAnchor.constraint(equalTo: navigationBar.topAnchor, constant: verticalOffset),
topConstraint,
heightConstraint,
widthAnchor.constraint(lessThanOrEqualTo: navigationBar.widthAnchor, multiplier: widthMultiplier)
widthAnchor.constraint(
lessThanOrEqualTo: navigationBar.widthAnchor,
multiplier: widthMultiplier
)
]
NSLayoutConstraint.activate(navigationBarConstraints)
self.navigationBar = navigationBar
Expand All @@ -81,75 +104,53 @@ final class NCMediaViewerFloatingTitleView: UIView {

let height = navigationItemHeight(in: navigationBar)
heightConstraint?.constant = height
blurView.layer.cornerRadius = height / 2
}

// Use visible bar item height when possible.
private func navigationItemHeight(in navigationBar: UINavigationBar) -> CGFloat {
let heights = navigationBar.subviews.flatMap { subview in
navigationItemHeights(
from: subview,
in: navigationBar
)
}
min(44, navigationBar.bounds.height)
}

return heights.max() ?? navigationBar.bounds.height
func clear() {
update(
primaryText: nil,
secondaryText: nil
)
}

private func navigationItemHeights(
from view: UIView,
in navigationBar: UINavigationBar
) -> [CGFloat] {
guard view !== self,
!view.isDescendant(of: self),
!view.isHidden,
view.alpha > 0.01,
view.bounds.width > 0,
view.bounds.height > 0 else {
return []
}
func update(primaryText: String?, secondaryText: String?) {
var configuration = titleButton.configuration

configuration?.attributedTitle = AttributedString(
primaryText ?? "",
attributes: AttributeContainer([
.font: UIFont.systemFont(
ofSize: 13,
weight: .semibold
)
])
)

let frame = view.convert(view.bounds, to: navigationBar)
let isVisibleNavigationFrame = frame.minY >= -1 &&
frame.maxY <= navigationBar.bounds.height + 1 &&
frame.height > 20 &&
frame.width > 20 &&
frame.width < navigationBar.bounds.width * 0.6

let childHeights = view.subviews.flatMap { subview in
navigationItemHeights(
from: subview,
in: navigationBar
if let secondaryText,
!secondaryText.isEmpty {
configuration?.attributedSubtitle = AttributedString(
secondaryText,
attributes: AttributeContainer([
.font: UIFont.systemFont(
ofSize: 11,
weight: .regular
)
])
)
} else {
configuration?.attributedSubtitle = nil
}
titleButton.configuration = configuration

if isVisibleNavigationFrame {
return childHeights + [frame.height]
}

return childHeights
}

func update(
primaryText: String?,
secondaryText: String?
) {
let normalizedPrimaryText = primaryText?
.trimmingCharacters(in: .whitespacesAndNewlines)

let normalizedSecondaryText = secondaryText?
.trimmingCharacters(in: .whitespacesAndNewlines)

primaryLabel.text = normalizedPrimaryText
secondaryLabel.text = normalizedSecondaryText
secondaryLabel.isHidden = normalizedSecondaryText?.isEmpty ?? true
isHidden = normalizedPrimaryText?.isEmpty ?? true

updateAppearance()
isHidden = primaryText?.isEmpty ?? true

accessibilityLabel = [
normalizedPrimaryText,
normalizedSecondaryText
primaryText,
secondaryText
]
.compactMap { text in
guard let text, !text.isEmpty else {
Expand All @@ -160,78 +161,4 @@ final class NCMediaViewerFloatingTitleView: UIView {
}
.joined(separator: ", ")
}

func clear() {
update(
primaryText: nil,
secondaryText: nil
)
}

private func configureLabels() {
primaryLabel.font = .preferredFont(forTextStyle: .footnote)
primaryLabel.textColor = .white
primaryLabel.textAlignment = .center
primaryLabel.adjustsFontForContentSizeCategory = true
primaryLabel.lineBreakMode = .byTruncatingMiddle
primaryLabel.numberOfLines = 1

secondaryLabel.font = .preferredFont(forTextStyle: .caption2)
secondaryLabel.textColor = .white.withAlphaComponent(0.82)
secondaryLabel.textAlignment = .center
secondaryLabel.adjustsFontForContentSizeCategory = true
secondaryLabel.lineBreakMode = .byTruncatingTail
secondaryLabel.numberOfLines = 1
}

private func configureBlurView() {
blurView.translatesAutoresizingMaskIntoConstraints = false
blurView.isUserInteractionEnabled = false
blurView.clipsToBounds = true
blurView.layer.cornerCurve = .continuous
addSubview(blurView)

NSLayoutConstraint.activate([
blurView.leadingAnchor.constraint(equalTo: leadingAnchor),
blurView.trailingAnchor.constraint(equalTo: trailingAnchor),
blurView.topAnchor.constraint(equalTo: topAnchor),
blurView.bottomAnchor.constraint(equalTo: bottomAnchor)
])
}

private func configureStackView() {
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .vertical
stackView.alignment = .center
stackView.distribution = .fill
stackView.spacing = 2

stackView.addArrangedSubview(primaryLabel)
stackView.addArrangedSubview(secondaryLabel)
addSubview(stackView)
bringSubviewToFront(stackView)

NSLayoutConstraint.activate([
stackView.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor),
stackView.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor),
stackView.centerYAnchor.constraint(equalTo: centerYAnchor),
stackView.topAnchor.constraint(greaterThanOrEqualTo: layoutMarginsGuide.topAnchor),
stackView.bottomAnchor.constraint(lessThanOrEqualTo: layoutMarginsGuide.bottomAnchor)
])
}

private func updateAppearance() {
let isDarkMode = traitCollection.userInterfaceStyle == .dark

blurView.effect = UIBlurEffect(
style: isDarkMode
? .systemChromeMaterialDark
: .systemChromeMaterialLight
)

let textColor: UIColor = isDarkMode ? .white : .black

primaryLabel.textColor = textColor
secondaryLabel.textColor = textColor.withAlphaComponent(0.82)
}
}
Loading