iOS 11 大标题导航栏的图像
Posted
技术标签:
【中文标题】iOS 11 大标题导航栏的图像【英文标题】:Image for Navigation Bar with Large Title iOS 11 【发布时间】:2018-04-14 04:51:45 【问题描述】:AppStore 应用在 NabBar 的右侧有一个带有大标题的图像的图标:
如果有人知道如何实施或想法了解如何实施,将不胜感激。
顺便说一句:在 UIBarButtonItem 内为 UIButton 设置图像不起作用。已经试过了。该按钮粘在屏幕顶部:
【问题讨论】:
@GuidoLodetti 这有点复杂,所以我正在为它写一个教程。紧急吗?如果是,我可以在不解释的情况下共享代码。 @TungFam 代码就够了!只是想知道它是否是一个复杂且不稳定的解决方案,或者它是否会在 ios 的下一个版本中得到很好的支持 :) 我们需要它来更新应用程序!谢谢。 100 行代码:) 足够稳定。在这里联系我:tung.fam@uptech.team @TungFam 有关于 iOS 13 的更新吗?有人有消息吗? @Bonnke 我假设你在 Github 上创建了一个问题,我在那里回答:github.com/tungfam/ImageInNavigationBarDemo/issues/8 【参考方案1】:经过几个小时的编码,我终于设法让它工作。我还决定写一个详细教程:link。如果您喜欢非常详细的说明,请遵循它。
演示:
GitHub 上的完整项目:link。
这里有 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 步:设置用户界面:
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)
])
第四步:创建图片大小调整方法
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)
第五步:
override func scrollViewDidScroll(_ scrollView: UIScrollView)
guard let height = navigationController?.navigationBar.frame.height else return
moveAndResizeImage(for: height)
希望它清楚并帮助您! 如果您有任何其他问题,请在 cmets 中告诉我。
【讨论】:
我看到你在微积分上这么聪明。你能给我一个学习的建议吗? @seyha 对不起,你是什么意思? 我的意思是你擅长数学。你能给我一个好的学习建议吗? @seyha 哦,我明白了。谢谢:) 但老实说,我只在学校和大学里学会了它。所以也许在你的情况下,我会建议一些在线数学课程。 我想做这样的东西但是图像太大了,当用户滚动表格视图时,图像会在屏幕的中心,图像会很小,并且会像这样@TungFam跨度> 【参考方案2】:如果有人还在寻找如何在 SwiftUI 中执行此操作。我做了一个package named 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)
预览
【讨论】:
【参考方案3】:您可以使用自定义视图创建 UIBarButtonItem。此自定义视图将是一个 UIView,其中实际的 UIButton(作为子视图)放置在距离顶部 x 个像素(x=您想要将其向下移动的像素数)。
【讨论】:
感谢您提出的解决方案,但不幸的是它不起作用。它不允许在栏按钮项的底部下方移动任何东西(在大标题上方) 嗯..抱歉。有时间我再看一遍。 没问题,感谢您花时间和精力研究这个问题! 尝试在 uibutton 中使用 imageInsets,或者在 titleView 中使用自定义视图。【参考方案4】:感谢@TungFam,我想我有更好的解决方案。 check it out
两点:
根据导航栏高度改变按钮框架
// adjust topview height
override func scrollViewDidScroll(_ scrollView: UIScrollView)
guard let navBar = self.navigationController?.navigationBar else
return
// hardcoded .. to improve
if navBar.bounds.height > 44 + 40 + 10
NSLayoutConstraint.deactivate(heightConstraint)
heightConstraint = [topview.heightAnchor.constraint(equalToConstant: 40)]
NSLayoutConstraint.activate(heightConstraint)
else
NSLayoutConstraint.deactivate(heightConstraint)
var height = navBar.bounds.height - 44 - 10
if height < 0
height = 0
heightConstraint = [topview.heightAnchor.constraint(equalToConstant: height)]
NSLayoutConstraint.activate(heightConstraint)
根据弹出/推送进度更改按钮 alpha
@objc func onGesture(sender: UIGestureRecognizer)
switch sender.state
case .began, .changed:
if let ct = navigationController?.transitionCoordinator
topview.alpha = ct.percentComplete
case .cancelled, .ended:
return
case .possible, .failed:
break
【讨论】:
以上是关于iOS 11 大标题导航栏的图像的主要内容,如果未能解决你的问题,请参考以下文章