У меня есть два контроллера представления, встроенных в UINavigationController
. Первый контроллер вида имеет UISearchController
, установленный на его элемент навигации. Вот полный код, где я настраиваю контроллер поиска:
private func configureSearchController() {
searchController.searchResultsUpdater = self
searchController.obscuresBackgroundDuringPresentation = false
searchController.hidesNavigationBarDuringPresentation = false
//searchController.dimsBackgroundDuringPresentation = false
searchController.searchBar.tintColor = .white
searchController.searchBar.delegate = self
//White search text
UITextField.appearance(whenContainedInInstancesOf: [UISearchBar.self]).defaultTextAttributes = [NSAttributedStringKey.foregroundColor.rawValue: UIColor.white]
//White placeholder
UITextField.appearance(whenContainedInInstancesOf: [UISearchBar.self]).attributedPlaceholder = NSAttributedString(string: NSLocalizedString("Search", comment: "search bar placeholder"), attributes: [NSAttributedStringKey.foregroundColor: UIColor.white])
//searchController.searchBar.sizeToFit()
navigationItem.searchController = searchController
navigationItem.hidesSearchBarWhenScrolling = false
navigationController?.navigationBar.prefersLargeTitles = false
//
definesPresentationContext = true
}
Я вызываю этот метод из viewDidLoad
.
Как уже упоминалось в заголовке вопроса, я использую пользовательский переход контроллера навигации. Вот полный код аниматора перехода.
class RevealViewControllerAnimator: NSObject, UIViewControllerAnimatedTransitioning {
private let animationDuration = 1.5
var operation: UINavigationControllerOperation = .push
var isShowing = true
private weak var storedContext: UIViewControllerContextTransitioning?
var snapshot: UIView?
private lazy var viewOnTopOfSnapshot: UIView? = {
let view = UIView()
view.frame = self.snapshot!.frame
if isShowing {
view.backgroundColor = .clear
} else {
view.backgroundColor = UIColor(white: 0.3, alpha: 0.4)
}
return view
}()
private var backgroundViewBackgroundDarkColor = UIColor(white: 0.2, alpha: 0.4)
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return animationDuration
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
storedContext = transitionContext
print ("OPERATION", operation.rawValue)
//If we are presenting a view controller
if isShowing {
let fromVC = transitionContext.viewController(forKey: .from) as! ViewController1
let toVC = transitionContext.viewController(forKey: .to) as! ViewController2
snapshot = UIApplication.shared.keyWindow?.snapshotView(afterScreenUpdates: false)
let containerView = transitionContext.containerView
//Adding a view on top of a snapshot and animating its bacground color
if let snapshot = snapshot, let viewOnTopOfSnapshot = viewOnTopOfSnapshot {
containerView.addSubview(self.snapshot!)
containerView.insertSubview(viewOnTopOfSnapshot, aboveSubview: snapshot)
UIView.animate(withDuration: animationDuration - 1.0, animations: {
viewOnTopOfSnapshot.backgroundColor = self.backgroundViewBackgroundDarkColor
}, completion: nil)
}
containerView.addSubview(toVC.view)
toVC.view.frame = transitionContext.finalFrame(for: toVC)
animate(toView: toVC.view, fromTriggerButton: fromVC.filterButton)
} else {
//If we are dismissing the view controller
let fromVC = transitionContext.viewController(forKey: .from) as! ViewController2
let toVC = transitionContext.viewController(forKey: .to) as! ViewController1
let containerView = transitionContext.containerView
//Animating the background color change to clear
if let viewOnTopOfSnapshot = viewOnTopOfSnapshot {
UIView.animate(withDuration: animationDuration, animations: {
viewOnTopOfSnapshot.backgroundColor = .clear
}, completion: {_ in
self.snapshot?.removeFromSuperview()
viewOnTopOfSnapshot.removeFromSuperview()
})
}
//containerView.addSubview(fromVC.view)
containerView.insertSubview(toVC.view!, belowSubview: snapshot!)
animateDismisss(fromView: fromVC.view, toTriggerButton: fromVC.saveButton)
}
}
//MARK: Animation for pushing
private func animate(toView: UIView, fromTriggerButton button: UIButton) {
let rect = CGRect(x: toView.frame.maxX, y: toView.frame.minY, width: button.frame.width, height: button.frame.height)
let circleMaskPathInitial = UIBezierPath(ovalIn: rect)
let fullHeight = toView.bounds.height
let extremePoint = CGPoint(x: button.center.x, y: button.center.y - fullHeight)
let radius = sqrt((extremePoint.x * extremePoint.x) + (extremePoint.y * extremePoint.y))
let circleMaskPathFinal = UIBezierPath(ovalIn: button.frame.insetBy(dx: -radius - 1000, dy: -radius - 1000))
let maskLayer = CAShapeLayer()
maskLayer.path = circleMaskPathFinal.cgPath
toView.layer.mask = maskLayer
let maskLayerAnimation = CABasicAnimation(keyPath: "path")
maskLayerAnimation.fromValue = circleMaskPathInitial.cgPath
maskLayerAnimation.toValue = circleMaskPathFinal.cgPath
maskLayerAnimation.duration = animationDuration
maskLayerAnimation.delegate = self
maskLayer.add(maskLayerAnimation, forKey: "path")
}
//MARK: Animation for pop (dismiss)
private func animateDismisss(fromView: UIView, toTriggerButton button: UIButton) {
let rect = CGRect(x: button.frame.origin.x, y: button.frame.midY, width: button.frame.width, height: button.frame.width)
let finalCircleMaskPath = UIBezierPath(ovalIn: rect)
let fullHeight = fromView.bounds.height
let extremePoint = CGPoint(x: button.center.x, y: button.center.y - fullHeight)
let radius = sqrt((extremePoint.x * extremePoint.x) + (extremePoint.y * extremePoint.y))
let initialCircleMaskPath = UIBezierPath(ovalIn: button.frame.insetBy(dx: -radius, dy: -radius))
let maskLayer = CAShapeLayer()
maskLayer.path = finalCircleMaskPath.cgPath
fromView.layer.mask = maskLayer
let maskLayerAnimation = CABasicAnimation(keyPath: "path")
maskLayerAnimation.fromValue = initialCircleMaskPath.cgPath
maskLayerAnimation.toValue = finalCircleMaskPath.cgPath
maskLayerAnimation.duration = 0.8
maskLayerAnimation.delegate = self
maskLayer.add(maskLayerAnimation, forKey: "path")
}
extension RevealFilterViewControllerAnimator : CAAnimationDelegate {
func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
if let context = storedContext {
storedContext?.completeTransition(!context.transitionWasCancelled)
} else {
storedContext = nil
}
}
}
Итак, в двух словах я получаю снимок ViewController1
, вставляю его в containerView
и поверх него вставляю другой вид, цвет фона которого я изменяю во время анимации. При получении сообщения я избавляюсь от снимка и представления, а также вставляю представление ViewController1
в containerView
.
Как я уже упоминал в начале вопроса, у меня есть UISearchController
с панелью поиска в первом контроллере представления.
Проблема в том, что после отклонения ViewController2
контроллер поиска удаляется из иерархии, и я получаю пустой пробел. Вот демонстрация этого:
Когда я печатаю UISearchController
или строку поиска на консоли, я получаю информацию об объекте, однако, как вы можете видеть, она исчезает из иерархии представлений (в отладчике иерархии представлений я не могу ее найти) ,
Почему это происходит и как это можно решить?