diff --git a/Nextcloud.xcodeproj/project.pbxproj b/Nextcloud.xcodeproj/project.pbxproj index dabfedb6ef..52e8502a2f 100644 --- a/Nextcloud.xcodeproj/project.pbxproj +++ b/Nextcloud.xcodeproj/project.pbxproj @@ -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; @@ -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; diff --git a/iOSClient/Viewer/NCViewerMedia/Views/NCMediaViewerFloatingTitleView.swift b/iOSClient/Viewer/NCViewerMedia/Views/NCMediaViewerFloatingTitleView.swift index 65d0b9b3b7..6cba29d834 100644 --- a/iOSClient/Viewer/NCViewerMedia/Views/NCMediaViewerFloatingTitleView.swift +++ b/iOSClient/Viewer/NCViewerMedia/Views/NCMediaViewerFloatingTitleView.swift @@ -5,32 +5,45 @@ 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) @@ -38,12 +51,18 @@ final class NCMediaViewerFloatingTitleView: UIView { 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() @@ -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 @@ -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 { @@ -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) - } }