20

AppStore アプリには、大きなタイトルの NabBar の右側に画像付きのアイコンがあります。 ここに画像の説明を入力

誰かがそれを実装する方法またはそれを行う方法についてのアイデアを知っているなら、本当に感謝します.

ところで: UIBarButtonItem 内で UIButton の画像を設定しても機能しません。すでに試しました。ボタンは画面の上部にくっつきます: ここに画像の説明を入力

4

4 に答える 4

60

数時間のコーディングの後、ようやく機能するようになりました。また、詳細なチュートリアルを作成することにしました:リンク. 非常に詳細な手順が必要な場合は、それに従ってください。

デモ: ここに画像の説明を入力

GitHub の完全なプロジェクト:リンク

それを達成するための5つのステップは次のとおりです。

ステップ 1: イメージを作成する

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

ステップ 2: 定数を追加する

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

ステップ 3: UI のセットアップ:

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

ステップ 4: 画像のサイズ変更メソッドを作成する

private func moveAndResizeImage(for height: CGFloat) {
    let coeff: CGFloat = {
        let delta = height - Const.NavBarHeightSmallState
        let heightDifferenceBetweenStates = (Const.NavBarHeightLargeState - Const.NavBarHeightSmallState)
        return delta / heightDifferenceBetweenStates
    }()

    let factor = Const.ImageSizeForSmallState / Const.ImageSizeForLargeState

    let scale: CGFloat = {
        let sizeAddendumFactor = coeff * (1.0 - factor)
        return min(1.0, sizeAddendumFactor + factor)
    }()

    // Value of difference between icons for large and small states
    let sizeDiff = Const.ImageSizeForLargeState * (1.0 - factor) // 8.0

    let yTranslation: CGFloat = {
        /// This value = 14. It equals to difference of 12 and 6 (bottom margin for large and small states). Also it adds 8.0 (size difference when the image gets smaller size)
        let maxYTranslation = Const.ImageBottomMarginForLargeState - Const.ImageBottomMarginForSmallState + sizeDiff
        return max(0, min(maxYTranslation, (maxYTranslation - coeff * (Const.ImageBottomMarginForSmallState + sizeDiff))))
    }()

    let xTranslation = max(0, sizeDiff - coeff * sizeDiff)

    imageView.transform = CGAffineTransform.identity
        .scaledBy(x: scale, y: scale)
        .translatedBy(x: xTranslation, y: yTranslation)
}

ステップ 5:

override func scrollViewDidScroll(_ scrollView: UIScrollView) {
    guard let height = navigationController?.navigationBar.frame.height else { return }
    moveAndResizeImage(for: height)
}

それが明確で、あなたを助けることを願っています! 追加の質問がある場合は、コメントでお知らせください。

于 2017-11-21T16:11:26.083 に答える
1

誰かがまだSwiftUIでこれを行う方法を探している場合. これに対処するために、NavigationBarLargeTitleItems という名前のパッケージを作成しました。これは、AppStore およびメッセージ アプリで見られる動作を模倣しています。

この動作を実現するには、プライベート クラスである「_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)
    }
}

プレビュー

ここに画像の説明を入力

于 2020-09-04T21:06:29.957 に答える