Animate nav bar title text change
If you want to animate between different title strings, use the following:
CATransition *fadeTextAnimation = [CATransition animation];
fadeTextAnimation.duration = 0.5;
fadeTextAnimation.type = kCATransitionFade;
[self.navigationController.navigationBar.layer addAnimation: fadeTextAnimation forKey: @"fadeText"];
self.navigationItem.title = "My new title";
You can adjust the duration and set a timing function to suit, of course.
There are also other types of animation that might work in different circumstances (thanks @inorganik):
kCATransitionFade
kCATransitionMoveIn
kCATransitionPush
kCATransitionReveal
Animate change to title text in Navigation Controller
So I've worked it out.
I had to find the specific layer within the UINavigationBar
's subviews that contained the title text, then animate that layer. The results are exactly what I wanted.
Here's my answer (Swift 5 iOS 12)...
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if (newTitle ?? "").isEmpty == false { // only proceed with a valid value for newTitle.
// CATransition code
let titleAnimation = CATransition()
titleAnimation.duration = 0.5
titleAnimation.type = CATransitionType.push
titleAnimation.subtype = CATransitionSubtype.fromRight
titleAnimation.timingFunction = CAMediaTimingFunction.init(name: CAMediaTimingFunctionName.easeInEaseOut)
// this is a detail view controller, so we must grab the reference
// to the parent view controller's navigation controller
// then cycle through until we find the title labels.
if let subviews = parent?.navigationController?.navigationBar.subviews {
for navigationItem in subviews {
for itemSubView in navigationItem.subviews {
if let largeLabel = itemSubView as? UILabel {
largeLabel.layer.add(titleAnimation, forKey: "changeTitle")
}
}
}
}
// finally set the title
navigationItem.title = newTitle
}
Note: there is no need to import QuartzCore
.
...and here's the process I went through to identify what I had to change...
This SO Q&A How to set multi line Large title in navigation bar? ( New feature of iOS 11)
helped me identify the process detailed below, so thanks in particular to the original post and the answer by @Krunal .
Using the same code to cycle through the UINavigationBar
's subviews (as above), I used a print to terminal to identify the various UINavigationItem
s and their subviews.
counter = 0
if let subviews = parent?.navigationController?.navigationBar.subviews {
for navigationItem in subviews {
print("____\(navigationItem)")
for itemSubView in navigationItem.subviews {
counter += 1
print("_______\(itemSubView)")
}
}
}
print("COUNTER: \(counter)")
this code yielded the following prints in terminal (for iPhone 8 Plus running iOS 12.2 in simulator)...
____<_UIBarBackground: 0x7f922740c000; frame = (0 -20; 414 116); userInteractionEnabled = NO; layer = <CALayer: 0x6000026ce1c0>>
_______<UIImageView: 0x7f922740c9c0; frame = (0 116; 414 0.333333); userInteractionEnabled = NO; layer = <CALayer: 0x6000026ce7c0>>
_______<UIVisualEffectView: 0x7f922740cbf0; frame = (0 0; 414 116); layer = <CALayer: 0x6000026ce880>>
____<_UINavigationBarLargeTitleView: 0x7f922740f390; frame = (0 44; 414 52); clipsToBounds = YES; layer = <CALayer: 0x6000026cd840>>
_______<UILabel: 0x7f9227499fe0; frame = (20.1667 3.66667; 206.333 40.6667); text = 'Event Details'; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x6000005bf250>>
____<_UINavigationBarContentView: 0x7f922740d660; frame = (0 0; 414 44); clipsToBounds = YES; layer = <CALayer: 0x6000026cea00>>
_______<_UIButtonBarStackView: 0x7f92274984a0; frame = (302 0; 100 44); layer = <CALayer: 0x60000261cc60>>
_______<_UIButtonBarButton: 0x7f922749a5c0; frame = (0 0; 82.3333 44); layer = <CALayer: 0x60000261ee60>>
_______<UILabel: 0x7f922749a2d0; frame = (155 11.6667; 104.333 20.3333); text = 'Event Details'; alpha = 0; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x6000005bfc50>>
____<_UINavigationBarModernPromptView: 0x7f9227602db0; frame = (0 0; 0 50); alpha = 0; hidden = YES; layer = <CALayer: 0x6000026e4600>>
COUNTER: 2
I've actually applied the animation twice - to the UILabel
layer
within _UINavigationBarLargeTitleView
and the UILabel
layer
within _UINavigationBarContentView
. This does not seem to matter however because, when the large title first appears, the label within content view (which I assume is for the "old style" title in the navigation bar when the large title is scrolled off screen) is hidden on viewDidAppear
.
Incidentally, if you drop in the following two lines, you'll also have multi-line large titles:
largeLabel.numberOfLines = 0
largeLabel.lineBreakMode = .byWordWrapping
BUT, I've not yet figured out how to animate the increase in size of the large title frame, so a change to two or more lines is immediate and IMHO ruins the animation of the title change.
Not yet tested on device, but does seem to work OK for both iPhone and iPad sims.
If you find any bugs, let me know and I'll update my answer.
How can I animate the change of the navigation bar title
The title text is accessible as topItem.text
. There is no way to directly access the label which is displaying this text.
So if you want to animate this label, you first have to search for it in the subview of the NavigationBar.
Then, you can apply animations on this label.
See below for an example that fades in the new title from the right.
/// Fades in the new title from the right
///
/// - Parameter newTitle: New title to display on the navigation item
func animateTitle(newTitle: String) {
// Title animation code
let titleAnimation = CATransition()
titleAnimation.duration = 0.25
titleAnimation.type = CATransitionType.push
titleAnimation.subtype = CATransitionSubtype.fromRight
titleAnimation.timingFunction = CAMediaTimingFunction.init(name: CAMediaTimingFunctionName.easeInEaseOut)
// Find the Label which contains the topitem title
if let subviews = navigationController?.navigationBar.subviews {
for navigationItem in subviews {
for itemSubView in navigationItem.subviews {
if let largeLabel = itemSubView as? UILabel {
largeLabel.layer.add(titleAnimation, forKey: "changeTitle")
}
}
}
}
navigationItem.title = newTitle
}
Transition Navigation Bar Title
You can animate the title changing by using a CATransition... however, because the title itself is a private property on the navigation bar, you need to first create a custom label and attach that to the navigation item.
Setup the title label (this would override the default navigation bar's title):
UILabel *titleLabelView = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 100.0f, 44.0f) /* auto-sized anyway */];
titleLabelView.backgroundColor = [UIColor clearColor];
titleLabelView.textAlignment = NSTextAlignmentCenter;
titleLabelView.textColor = [UIColor blackColor];
titleLabelView.font = [UIFont systemFontOfSize:16.0f];
titleLabelView.adjustsFontSizeToFitWidth = YES;
titleLabelView.text = @"@cracy123";
self.navigationItem.titleView = titleLabelView;
Then whenever you want to animate the title changing (assume on a scroll view delegate action), add a CAAnimation layer and presto:
CATransition *animation = [CATransition animation];
animation.duration = 1.0;
animation.type = kCATransitionPush;
animation.subtype = kCATransitionFromTop;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[self.navigationItem.titleView.layer addAnimation:animation forKey:@"changeTitle"];
((UILabel*)self.navigationItem.titleView).text = @"JACOB K";
You can obviously change the CATransition animation properties to get the effect you're after, but those will give you the 'push-up' effect.
Animate title in navigation bar
You could solve it like this
var spread = document.getElementsByClassName('spread');[].forEach.call(spread, function(el) { // replace the content width divs el.innerHTML = '<span>' + el.innerText.split('').join('</span><span>') + '</span>' // custom :hover el.onmouseenter = function(e) { var childern = e.target.childNodes var width = e.target.offsetWidth / childern.length for (var i = 0, child; child = childern[i]; i++) child.style.minWidth = width + 'px' } // remove custom style again el.onmouseleave = function(e) { var childern = e.target.childNodes for (var i = 0, child; child = childern[i]; i++) child.style.minWidth = '0' }})
.spread { text-align:center;}.spread span { display: inline-block; transition: all .5s ease; text-align:center; min-width: 0;}
<h1 class="spread"> Title</h1>
Navigation Bar Large Title - Animation Issue
for second issue:
in SceneDelegate: var window: UIWindow?
in the function scene put this line:
window?.backgroundColor = .yourColor
like this:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
guard let _ = (scene as? UIWindowScene) else { return }
window?.backgroundColor = .white
}
set the color you want and that's it :)
Animating a navigation bar color
Details
xCode 8.3.2, swift 3.1
Solution
override func viewWillAppear(_ animated: Bool) {
if let navigationBar = self.navigationController?.navigationBar {
navigationBar.backgroundColor = .blue
}
}
Full sample
ViewController
import UIKit
class ViewController: UIViewController {
override func viewWillAppear(_ animated: Bool) {
if let navigationBar = self.navigationController?.navigationBar {
navigationBar.barTintColor = UIColor(red: 244/255, green: 67/255, blue: 54/255, alpha: 1.0)
navigationBar.tintColor = .white
navigationBar.titleTextAttributes = [NSForegroundColorAttributeName : UIColor.white]
navigationBar.isTranslucent = false
}
}
}
ViewController2
import UIKit
class ViewController2: UIViewController {
override func viewWillAppear(_ animated: Bool) {
if let navigationBar = self.navigationController?.navigationBar {
let color = UIColor(red: 1, green: 153/255, blue: 0, alpha: 1.0)
navigationBar.setBackgroundImage(UIImage.imageWithColor(color: color), for: .default)
navigationBar.shadowImage = UIImage()
navigationBar.isTranslucent = true
}
}
override func viewWillDisappear(_ animated: Bool) {
if let navigationBar = self.navigationController?.navigationBar {
navigationBar.setBackgroundImage(nil, for: .default)
navigationBar.shadowImage = nil
navigationBar.isTranslucent = false
}
}
}
extension UIImage
import UIKit
extension UIImage {
class func imageWithColor(color: UIColor) -> UIImage {
let rect = CGRect(x: 0, y: 0, width: 1, height: 1)
UIGraphicsBeginImageContextWithOptions(CGSize(width: 1, height: 1), false, 0)
color.setFill()
UIRectFill(rect)
let image = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return image
}
}
Main.storyboard
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12120" systemVersion="16F73" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="Tzy-ol-uu0">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12088"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="ViewController" customModule="stackowerflow_44343355" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="eIC-Nm-Ex7">
<rect key="frame" x="164" y="318" width="46" height="30"/>
<state key="normal" title="Button"/>
<connections>
<segue destination="QHs-H4-fAS" kind="show" id="Rff-Eq-K6g"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="eIC-Nm-Ex7" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="G1g-VM-MAn"/>
<constraint firstItem="eIC-Nm-Ex7" firstAttribute="centerY" secondItem="8bC-Xf-vdC" secondAttribute="centerY" id="mdZ-GP-EQw"/>
</constraints>
</view>
<navigationItem key="navigationItem" id="hq3-zt-U4K"/>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="977" y="791"/>
</scene>
<!--View Controller2-->
<scene sceneID="F9C-Nz-6dd">
<objects>
<viewController id="QHs-H4-fAS" customClass="ViewController2" customModule="stackowerflow_44343355" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="AV0-X8-nhX"/>
<viewControllerLayoutGuide type="bottom" id="AsY-Gl-67v"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="1fA-pX-rzR">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="Uzd-Tb-KRO" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1770" y="789"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="Jff-OO-3e7">
<objects>
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="Tzy-ol-uu0" sceneMemberID="viewController">
<toolbarItems/>
<navigationBar key="navigationBar" contentMode="scaleToFill" id="804-YF-T6T">
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<nil name="viewControllers"/>
<connections>
<segue destination="BYZ-38-t0r" kind="relationship" relationship="rootViewController" id="BRB-ym-7I2"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="M6i-ib-61I" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="140" y="791.15442278860576"/>
</scene>
</scenes>
</document>
Wrong large title animation with custom back button font
I could solve this problem on my own, but since I could not find a similar topic/problem/solution on the web I wanted to share the solution, just in case someone else has the same problem one day.
To solve this strange behavior I had to set the custom font for the normal UIControlState and additionally for the highlighted one.
UIBarButtonItem.appearance().setTitleTextAttributes([kCTFontAttributeName as NSAttributedStringKey: UIFont(name: "Futura", size: 17)!], for: .normal)
UIBarButtonItem.appearance().setTitleTextAttributes([kCTFontAttributeName as NSAttributedStringKey: UIFont(name: "Futura", size: 17)!], for: .highlighted)
Hope this helps! :)
Related Topics
Pulling Data from a Cmsamplebuffer in Order to Create a Deep Copy
Has Anyone Found a Good Way of Using the New iOS5 Keyboard Events
How to Detect If the Currently Running App Was Installed from the App Store
How to Activate Tcp Keepalive on Apple iOS Devices
Afnetworking - Do Not Cache Response
How to Set the Tab Order in iOS
Ios11 Causing Cors Issues in All Mobile Browsers
Play Audio Through Upper (Phone Call) Speaker
New Itunes User Interface Unable to Find "Ready to Upload Binary"
Uitableview Scrolls to Top When Reloading Cells with Changing Cell Heights
Is the Function 'Dlopen()' Private API
How to Upload Video to Server from Iphone
Swift Calculate Md5 Checksum for Large Files
Unknown Class in Interface Builder
Override a Method in Objective C via Category
Get Current iOS Device Orientation Even If Device's Orientation Locked