具有弯曲边缘和透明状态栏的短 UINavigationBar
Short UINavigationBar with curved edges and transparent status bar
如何使用导航控制器实现这种效果。
我只有一个 VC 嵌入在 UINavigationController
中,它是否以模态方式显示在另一个 VC 之上。到目前为止一切顺利,但我想实现如下所示的外观,其中导航栏较短,边缘弯曲,状态栏部分透明,露出底部 viewcontroller 的状态栏。
到目前为止,这是我在情节提要中的内容。
到目前为止,我已经能够通过子类化 UINavigationController
为导航栏实现圆角边缘。但是我不知道如何使导航栏从顶部变短:
class ComposeNavigController: UINavigationController {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.definesPresentationContext = true
self.providesPresentationContextTransitionStyle = true
self.modalPresentationCapturesStatusBarAppearance = false
self.modalPresentationStyle = .overFullScreen
}
override func viewDidLoad() {
super.viewDidLoad()
view.clipsToBounds = true
view.layer.cornerRadius = 16.0
view.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
view.layer.shadowOffset = .zero
view.layer.shadowColor = UIColor.black.cgColor
view.layer.shadowRadius = 16.0
view.layer.shadowOpacity = 0.5
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
view.layer.shadowPath = UIBezierPath(rect: view.bounds).cgPath
}
}
效果很好,边缘弯曲。
好吧,除了问题中的 UINavigationController
subclass 之外,这就是我必须做的事情。我不得不利用 UIViewControllerAnimatedTransitioning
委托来实现我想要的。
我创建了两个 class subclassed NSObject
并符合上述协议。
第一个用于 present
过渡-
import UIKit
class ComposePresentTransitionController: NSObject, UIViewControllerAnimatedTransitioning {
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.6
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
let fromViewController = transitionContext.viewController(forKey: .from)!
let toViewController = transitionContext.viewController(forKey: .to)!
let containerView = transitionContext.containerView
let screenBounds = UIScreen.main.bounds
let topOffset: CGFloat = UIApplication.shared.statusBarFrame.height
var finalFrame = transitionContext.finalFrame(for: toViewController)
finalFrame.origin.y += topOffset
finalFrame.size.height -= topOffset
toViewController.view.frame = CGRect(x: 0.0, y: screenBounds.size.height,
width: finalFrame.size.width,
height: finalFrame.size.height)
containerView.addSubview(toViewController.view)
UIView.animate(withDuration: transitionDuration(using: transitionContext), delay: 0.0, usingSpringWithDamping: 0.8, initialSpringVelocity: 1.0, options: .curveEaseOut, animations: {
toViewController.view.frame = finalFrame
fromViewController.view.alpha = 0.3
}) { (finished) in
transitionContext.completeTransition(finished)
}
UIView.animate(withDuration: transitionDuration(using: transitionContext), delay: 0.0, options: .curveEaseOut,
animations: {
}) { (finished) in
}
}
}
第二个 dismiss
转换-
import UIKit
class ComposeDismissTransitionController: NSObject, UIViewControllerAnimatedTransitioning {
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.3
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
let fromViewController = transitionContext.viewController(forKey: .from)!
let toViewController = transitionContext.viewController(forKey: .to)!
let screenBounds = UIScreen.main.bounds
var finalFrame = fromViewController.view.frame
finalFrame.origin.y = screenBounds.size.height
UIView.animate(withDuration: transitionDuration(using: transitionContext), delay: 0.0, options: .curveEaseIn,
animations: {
fromViewController.view.frame = finalFrame
toViewController.view.alpha = 1.0
}) { (finished) in
fromViewController.view.removeFromSuperview()
transitionContext.completeTransition(finished)
}
}
}
然后,从将要呈现的视图控制器中,这个 viewcontroller 需要遵守 UIViewControllerTransitioningDelegate
协议并实现 animationController:forPresented
和 animationController:forDismissed
方法,就像这样-
extension PresentingVC: UIViewControllerTransitioningDelegate {
func animationController(forPresented presented: UIViewController,
presenting: UIViewController,
source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return ComposePresentTransitionController()
}
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return ComposeDismissTransitionController()
}
}
完成后,PresentingVC
需要将 UIViewControllerTransitioningDelegate
设置为 self
,这是在 prepareForSegue
-
中完成的
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
segue.destination.transitioningDelegate = self
}
如果你像我一样讨厌 segue,那么你也可以通过代码中的 IBAction
按钮来完成此操作。
@IBAction func composeTap(_ sender: Any) {
let composeVC = self.storyboard?.instantiateViewController(withIdentifier: "compose") as! ComposeNavigController //this should be the navigation controller itself not the embedded viewcontroller
composeVC.transitioningDelegate = self
self.present(composeVC, animated: true, completion: nil)
}
就是这样...现在只要点击按钮以模态方式显示撰写屏幕,它就会以我想要的效果模态动画。我添加了一点弹跳效果只是为了好玩:)
如何使用导航控制器实现这种效果。
我只有一个 VC 嵌入在 UINavigationController
中,它是否以模态方式显示在另一个 VC 之上。到目前为止一切顺利,但我想实现如下所示的外观,其中导航栏较短,边缘弯曲,状态栏部分透明,露出底部 viewcontroller 的状态栏。
到目前为止,这是我在情节提要中的内容。
到目前为止,我已经能够通过子类化 UINavigationController
为导航栏实现圆角边缘。但是我不知道如何使导航栏从顶部变短:
class ComposeNavigController: UINavigationController {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.definesPresentationContext = true
self.providesPresentationContextTransitionStyle = true
self.modalPresentationCapturesStatusBarAppearance = false
self.modalPresentationStyle = .overFullScreen
}
override func viewDidLoad() {
super.viewDidLoad()
view.clipsToBounds = true
view.layer.cornerRadius = 16.0
view.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
view.layer.shadowOffset = .zero
view.layer.shadowColor = UIColor.black.cgColor
view.layer.shadowRadius = 16.0
view.layer.shadowOpacity = 0.5
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
view.layer.shadowPath = UIBezierPath(rect: view.bounds).cgPath
}
}
效果很好,边缘弯曲。
好吧,除了问题中的 UINavigationController
subclass 之外,这就是我必须做的事情。我不得不利用 UIViewControllerAnimatedTransitioning
委托来实现我想要的。
我创建了两个 class subclassed NSObject
并符合上述协议。
第一个用于 present
过渡-
import UIKit
class ComposePresentTransitionController: NSObject, UIViewControllerAnimatedTransitioning {
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.6
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
let fromViewController = transitionContext.viewController(forKey: .from)!
let toViewController = transitionContext.viewController(forKey: .to)!
let containerView = transitionContext.containerView
let screenBounds = UIScreen.main.bounds
let topOffset: CGFloat = UIApplication.shared.statusBarFrame.height
var finalFrame = transitionContext.finalFrame(for: toViewController)
finalFrame.origin.y += topOffset
finalFrame.size.height -= topOffset
toViewController.view.frame = CGRect(x: 0.0, y: screenBounds.size.height,
width: finalFrame.size.width,
height: finalFrame.size.height)
containerView.addSubview(toViewController.view)
UIView.animate(withDuration: transitionDuration(using: transitionContext), delay: 0.0, usingSpringWithDamping: 0.8, initialSpringVelocity: 1.0, options: .curveEaseOut, animations: {
toViewController.view.frame = finalFrame
fromViewController.view.alpha = 0.3
}) { (finished) in
transitionContext.completeTransition(finished)
}
UIView.animate(withDuration: transitionDuration(using: transitionContext), delay: 0.0, options: .curveEaseOut,
animations: {
}) { (finished) in
}
}
}
第二个 dismiss
转换-
import UIKit
class ComposeDismissTransitionController: NSObject, UIViewControllerAnimatedTransitioning {
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.3
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
let fromViewController = transitionContext.viewController(forKey: .from)!
let toViewController = transitionContext.viewController(forKey: .to)!
let screenBounds = UIScreen.main.bounds
var finalFrame = fromViewController.view.frame
finalFrame.origin.y = screenBounds.size.height
UIView.animate(withDuration: transitionDuration(using: transitionContext), delay: 0.0, options: .curveEaseIn,
animations: {
fromViewController.view.frame = finalFrame
toViewController.view.alpha = 1.0
}) { (finished) in
fromViewController.view.removeFromSuperview()
transitionContext.completeTransition(finished)
}
}
}
然后,从将要呈现的视图控制器中,这个 viewcontroller 需要遵守 UIViewControllerTransitioningDelegate
协议并实现 animationController:forPresented
和 animationController:forDismissed
方法,就像这样-
extension PresentingVC: UIViewControllerTransitioningDelegate {
func animationController(forPresented presented: UIViewController,
presenting: UIViewController,
source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return ComposePresentTransitionController()
}
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return ComposeDismissTransitionController()
}
}
完成后,PresentingVC
需要将 UIViewControllerTransitioningDelegate
设置为 self
,这是在 prepareForSegue
-
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
segue.destination.transitioningDelegate = self
}
如果你像我一样讨厌 segue,那么你也可以通过代码中的 IBAction
按钮来完成此操作。
@IBAction func composeTap(_ sender: Any) {
let composeVC = self.storyboard?.instantiateViewController(withIdentifier: "compose") as! ComposeNavigController //this should be the navigation controller itself not the embedded viewcontroller
composeVC.transitioningDelegate = self
self.present(composeVC, animated: true, completion: nil)
}
就是这样...现在只要点击按钮以模态方式显示撰写屏幕,它就会以我想要的效果模态动画。我添加了一点弹跳效果只是为了好玩:)