簡體   English   中英

向大標題導航欄添加按鈕

[英]Add a button to large title navigation bar

總結

我想在像 App Store 的帳戶按鈕這樣的大標題導航欄中添加一個按鈕。

應用商店

所需流量:

  1. 按鈕僅在啟用large titles時可見
  2. 當用戶在視圖內滾動時,允許從大標題過渡到普通標題。

注意:我使用故事板。

在 Tung Fam 的這篇 Medium 文章中找到了答案。

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()
}

如果您想在導航到此視圖控制器的子項時隱藏/顯示圖像:

/**
 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)
}

如果這對您有幫助,您可以查看實際文章並給作者一兩掌

如果有人仍在尋找如何在 SwiftUI 中執行此操作。 我制作了一個 名為 NavigationBarLargeTitleItems來處理這個問題。 它模仿您在 AppStore 和 Messages-app 中看到的行為。

請注意,為了能夠完成此行為,我們需要將“_UINavigationBarLargeTitleView”添加到私有類中,因此在提交到 App Store 時可能會拒絕您的應用程序。

我還為那些不喜歡鏈接或只想復制/粘貼的人提供了完整的相關源代碼。

擴展:

// Copyright © 2020 Mark van Wijnen
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the “Software”), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import SwiftUI

public extension View {
    func navigationBarLargeTitleItems<L>(trailing: L) -> some View where L : View {
        overlay(NavigationBarLargeTitleItems(trailing: trailing).frame(width: 0, height: 0))
    }
}

fileprivate struct NavigationBarLargeTitleItems<L : View>: UIViewControllerRepresentable {
    typealias UIViewControllerType = Wrapper
    
    private let trailingItems: L
    
    init(trailing: L) {
        self.trailingItems = trailing
    }
    
    func makeUIViewController(context: Context) -> Wrapper {
        Wrapper(representable: self)
    }
    
    func updateUIViewController(_ uiViewController: Wrapper, context: Context) {
    }
    
    class Wrapper: UIViewController {
        private let representable: NavigationBarLargeTitleItems?
        
        init(representable: NavigationBarLargeTitleItems) {
            self.representable = representable
            super.init(nibName: nil, bundle: nil)
        }
        
        required init?(coder: NSCoder) {
            self.representable = nil
            super.init(coder: coder)
        }
                
        override func viewWillAppear(_ animated: Bool) {
            guard let representable = self.representable else { return }
            guard let navigationBar = self.navigationController?.navigationBar else { return }
            guard let UINavigationBarLargeTitleView = NSClassFromString("_UINavigationBarLargeTitleView") else { return }
           
            navigationBar.subviews.forEach { subview in
                if subview.isKind(of: UINavigationBarLargeTitleView.self) {
                    let controller = UIHostingController(rootView: representable.trailingItems)
                    controller.view.translatesAutoresizingMaskIntoConstraints = false
                    subview.addSubview(controller.view)
                    
                    NSLayoutConstraint.activate([
                        controller.view.bottomAnchor.constraint(
                            equalTo: subview.bottomAnchor,
                            constant: -15
                        ),
                        controller.view.trailingAnchor.constraint(
                            equalTo: subview.trailingAnchor,
                            constant: -view.directionalLayoutMargins.trailing
                        )
                    ])
                }
            }
        }
    }
}

用法:

import SwiftUI
import NavigationBarLargeTitleItems

struct ContentView: View {
    var body: some View {
        NavigationView {
            List {
                ForEach(1..<50) { index in
                    Text("Sample Row \(String(index))")
                }
            }
            .navigationTitle("Navigation")
            .navigationBarLargeTitleItems(trailing: ProfileIcon())
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

struct ProfileIcon: View {
    var body: some View{
        Button(action: {
            print("Profile button was tapped")
        }) {
            Image(systemName: "person.circle.fill")
                .resizable()
                .aspectRatio(contentMode: .fit)
                .foregroundColor(.red)
                .frame(width: 36, height: 36)
        }
        .offset(x: -20, y: 5)
    }
}

預覽

在此處輸入圖片說明

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM