在大标题导航栏中添加一个按钮

Jon*_*ano 11 xcode uinavigationbar ios swift ios11

摘要

我想在App Store的帐户按钮等大型标题导航栏中添加一个按钮.

应用商店

所需流量:

  1. 仅在large titles启用时按钮才可见
  2. 当用户在视图内滚动时,允许从大标题转换为正常.

注意:我使用故事板.

Mar*_*ark 7

如果有人仍在寻找如何在 SwiftUI 中执行此操作。我制作了一个名为 NavigationBarLargeTitleItems 的包来处理这个问题。它模仿您在 AppStore 和消息应用程序中看到的行为。

\n

请注意,为了能够完成此行为,我们需要添加到“_UINavigationBarLargeTitleView”,这是一个私有类,因此您的应用程序在提交到 App Store 时可能会被拒绝。

\n

我还为那些不喜欢链接或只想复制/粘贴的人提供了完整的相关源代码。

\n

扩大:

\n
// Copyright \xc2\xa9 2020 Mark van Wijnen\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \xe2\x80\x9cSoftware\xe2\x80\x9d), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \xe2\x80\x9cAS IS\xe2\x80\x9d, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\nimport SwiftUI\n\npublic extension View {\n    func navigationBarLargeTitleItems<L>(trailing: L) -> some View where L : View {\n        overlay(NavigationBarLargeTitleItems(trailing: trailing).frame(width: 0, height: 0))\n    }\n}\n\nfileprivate struct NavigationBarLargeTitleItems<L : View>: UIViewControllerRepresentable {\n    typealias UIViewControllerType = Wrapper\n    \n    private let trailingItems: L\n    \n    init(trailing: L) {\n        self.trailingItems = trailing\n    }\n    \n    func makeUIViewController(context: Context) -> Wrapper {\n        Wrapper(representable: self)\n    }\n    \n    func updateUIViewController(_ uiViewController: Wrapper, context: Context) {\n    }\n    \n    class Wrapper: UIViewController {\n        private let representable: NavigationBarLargeTitleItems?\n        \n        init(representable: NavigationBarLargeTitleItems) {\n            self.representable = representable\n            super.init(nibName: nil, bundle: nil)\n        }\n        \n        required init?(coder: NSCoder) {\n            self.representable = nil\n            super.init(coder: coder)\n        }\n                \n        override func viewWillAppear(_ animated: Bool) {\n            guard let representable = self.representable else { return }\n            guard let navigationBar = self.navigationController?.navigationBar else { return }\n            guard let UINavigationBarLargeTitleView = NSClassFromString("_UINavigationBarLargeTitleView") else { return }\n           \n            navigationBar.subviews.forEach { subview in\n                if subview.isKind(of: UINavigationBarLargeTitleView.self) {\n                    let controller = UIHostingController(rootView: representable.trailingItems)\n                    controller.view.translatesAutoresizingMaskIntoConstraints = false\n                    subview.addSubview(controller.view)\n                    \n                    NSLayoutConstraint.activate([\n                        controller.view.bottomAnchor.constraint(\n                            equalTo: subview.bottomAnchor,\n                            constant: -15\n                        ),\n                        controller.view.trailingAnchor.constraint(\n                            equalTo: subview.trailingAnchor,\n                            constant: -view.directionalLayoutMargins.trailing\n                        )\n                    ])\n                }\n            }\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

用法:

\n
import SwiftUI\nimport NavigationBarLargeTitleItems\n\nstruct ContentView: View {\n    var body: some View {\n        NavigationView {\n            List {\n                ForEach(1..<50) { index in\n                    Text("Sample Row \\(String(index))")\n                }\n            }\n            .navigationTitle("Navigation")\n            .navigationBarLargeTitleItems(trailing: ProfileIcon())\n        }\n    }\n}\n\nstruct ContentView_Previews: PreviewProvider {\n    static var previews: some View {\n        ContentView()\n    }\n}\n\nstruct ProfileIcon: View {\n    var body: some View{\n        Button(action: {\n            print("Profile button was tapped")\n        }) {\n            Image(systemName: "person.circle.fill")\n                .resizable()\n                .aspectRatio(contentMode: .fit)\n                .foregroundColor(.red)\n                .frame(width: 36, height: 36)\n        }\n        .offset(x: -20, y: 5)\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

预览

\n

在此输入图像描述

\n


Syl*_*Ash 6

找到了董岩在这篇中型文章上的答案。

private let imageView = UIImageView(image: UIImage(named: "image_name"))

/// WARNING: Change these constants according to your project's design
private struct Const {
  /// Image height/width for Large NavBar state
  static let ImageSizeForLargeState: CGFloat = 40
  /// Margin from right anchor of safe area to right anchor of Image
  static let ImageRightMargin: CGFloat = 16
  /// Margin from bottom anchor of NavBar to bottom anchor of Image for Large NavBar state
  static let ImageBottomMarginForLargeState: CGFloat = 12
  /// Margin from bottom anchor of NavBar to bottom anchor of Image for Small NavBar state
  static let ImageBottomMarginForSmallState: CGFloat = 6
  /// Image height/width for Small NavBar state
  static let ImageSizeForSmallState: CGFloat = 32
  /// Height of NavBar for Small state. Usually it's just 44
  static let NavBarHeightSmallState: CGFloat = 44
  /// Height of NavBar for Large state. Usually it's just 96.5 but if you have a custom font for the title, please make sure to edit this value since it changes the height for Large state of NavBar
  static let NavBarHeightLargeState: CGFloat = 96.5
}

/**
 Setup the image in navbar to be on the same line as the navbar title
 */
private func setupUI() {
  navigationController?.navigationBar.prefersLargeTitles = true
  title = "Large Title"

  // Initial setup for image for Large NavBar state since the the screen always has Large NavBar once it gets opened
  guard let navigationBar = self.navigationController?.navigationBar else { return }

  navigationBar.addSubview(imageView)

  // setup constraints
  imageView.layer.cornerRadius = Const.ImageSizeForLargeState / 2
  imageView.clipsToBounds = true
  imageView.translatesAutoresizingMaskIntoConstraints = false
  NSLayoutConstraint.activate([
    imageView.rightAnchor.constraint(equalTo: navigationBar.rightAnchor, constant: -Const.ImageRightMargin),
    imageView.bottomAnchor.constraint(equalTo: navigationBar.bottomAnchor, constant: -Const.ImageBottomMarginForLargeState),
    imageView.heightAnchor.constraint(equalToConstant: Const.ImageSizeForLargeState),
    imageView.widthAnchor.constraint(equalTo: imageView.heightAnchor)
  ])
}

override func viewDidLoad() {
  super.viewDidLoad()

  // setup Settings navigation bar button
  setupUI()
}
Run Code Online (Sandbox Code Playgroud)

并且如果您要在导航到该视图控制器的子级时隐藏/显示图像,请执行以下操作:

/**
 Show or hide the image from NavBar while going to next screen or back to initial screen

 - parameter show: show or hide the image from NavBar
 */
private func showImage(_ show: Bool) {
  UIView.animate(withDuration: 0.4) {
    self.settingsImageView.alpha = show ? 1.0 : 0.0
  }
}

override func viewWillDisappear(_ animated: Bool) {
  super.viewWillDisappear(animated)
  showImage(false)
}

override func viewWillAppear(_ animated: Bool) {
  super.viewDidAppear(animated)
  showImage(true)
}
Run Code Online (Sandbox Code Playgroud)

如果这对您有帮助,您可以查看实际文章并给作者鼓掌或两个