A drop-in replacement for UILabel, which automatically adds a scrolling marquee effect when the label's text does not fit inside the specified frame
MarqueeLabel is a UILabel subclass adds a scrolling marquee effect when the text of the label outgrows the available width. The label scrolling direction and speed/rate can be specified as well. All standard UILabel properties (where it makes sense) are available in MarqueeLabel, with the intent of MarqueeLabel behaving just like a UILabel.
MarqueeLabel will be maintained in Swift only starting with release 4.0!
MarqueeLabel is compatible with both iOS and tvOS, and currently works with Swift 5.0 and the iOS 12.2 SDK! (But if you’re looking for prior version Swift compatibility, you can check the older releases)
MarqueeLabel.swift
source.To use MarqueeLabel in a project, add the following to your project’s Podfile:
pod 'MarqueeLabel'
Add the following to your project’s Cartfile:
github "cbpowell/MarqueeLabel"
See the Special Note below on supporting Cocoapods and Carthage simultaneously in a Swift framework!
MarqueeLabel automatically scrolls its text, at either a defined rate (points per second) or over a duration (seconds), whenever the length of the label’s text exceeds the space available given the label’s frame.
There are several options for the Marquee type, and the default is Continuous
(which looks just like what Apple typically uses). The animation curve of this scroll can be defined, and defaults to UIViewAnimationOptionCurveLinear
.
There are also several optional features to help with your integration of the scrolling nature of MarqueeLabel:
See the included demo project for several use case examples!
These lines of code create a MarqueeLabel that will scroll across its content in 8.0 seconds, and adds 10.0 point long fade at the left and right boundaries.
Replace:
var lengthyLabel = UILabel.init(frame:aFrame)
With:
var lengthyLabel = MarqueeLabel.init(frame: aFrame, duration: 8.0, fadeLength: 10.0)
If you’re using Storyboards/Interface Builder you can create a MarqueeLabel instance by adding a normal UILabel view to your Storyboard, and then manually changing the view’s class to MarqueeLabel
in the “Custom Class” field of the Identity Inspector tab on the Utilities panel (the right-side panel).
Note: If you forget to change the Custom Class field to MarqueeLabel
and then try to access/set MarqueeLabel-specific properties in your code, you will get crashes!
You can then configure the normal UILabel properties, as well as most of the MarqueeLabel configuration properties, via the Attributes tab of the Utility panel!
Check out the MarqueeLabel documentation for more about all the features, including:
Also check out the Extras folder, a collection of subclasses, extensions, and modifications for MarqueeLabel to implement various functionality that has been requested or suggested, but not merged into the MarqueeLabel code.
Starting with release 4.1, MarqueeLabel allows setting the adjustsFontSizeToWidth
to true
. When configured this way, MarqueeLabel will check to see if the text string
(non-attributed or attributed) will fit within the frame when adjusted to the specified minimum scale factor, and:
Previously MarqueeLabel would override any attempts to set adjustsFontSizetoWidth
and minimumScaleFactor
to the default settings used by UILabel (false
, and 0.0
, respectively). As such the default behavior remains the same: the label will not adjust it’s font size to ‘avoid’ scrolling.
MarqueeLabel includes support for IBInspectable and IBDesignable, to allow configuration of the label inside Interface Builder/Storyboards. However, if you see these warnings when building:
IB Designables: Failed to update auto layout status: Failed to load designables from path (null)
IB Designables: Failed to render instance of MarqueeLabel: Failed to load designables from path (null)
…then you are likely using MarqueeLabel as a static library, which does not support IBInspectable/IBDesignable. Some workarounds include:
MarqueeLabel tries its best to automatically begin scrolling when appropriate, but sometimes the way your view/view controller appears onscreen can trip it up.
To combat this, you can try:
restartLabel
instance method to manually start scrolling on a MarqueeLabelAs noted above, MarqueeLabel can sometimes have trouble detecting when the scroll animation should start when used in UITableViews and UICollectionViews - although recent reviews have improved this.
Usually you’ll configure the MarqueeLabel instance when building the cell in tableView:cellForRowAtIndexPath:
(or similar for UICollectionView), but at this point the cell is not onscreen so MarqueeLabel will not begin the scrolling animation. Even when the cell is eventually placed onscreen as the user scrolls, due to timing it’s possible that the animation will not fire.
To make sure the scrolling animation does begin as the cell scrolls onscreen, you can use the the restartLabel
method on your MarqueeLabels inside the tableView:willDisplayCell:forRowAtIndexPath:
delegate method (or similar for UICollectionView).
That said - the UITableView/UICollectionView best practice is to minimize things like excessive animation, subviews, and custom drawing in your cells, in order to get glassy smooth scrolling. In general I would recommend against allowing your labels to automatically animate during user scrolling of the UITableView/UICollectionView. I suggest holding scrolling or labelizing the labels while the user scrolls. See the table view example in the demo!
MarqueeLabel is based on Core Animation, which does cause some problems when views appear and disappear and the repeating animation is stopped by iOS and does not automatically restart.
To address this, MarqueeLabel provides a few class methods that allow easy “restarting” of all MarqueeLabels associated with a UIViewController. Specifically, the class method restartLabelsOfController:
should be called by your view controller (which passes in self
for the controller
parameter) when it is revealed or about to be revealed. Keep in mind that presenting a modal view controller can pause repeating UIView animations in the controller that is being covered!
controllerLabelsLabelize:
and controllerLabelsAnimate:
are for convenience, allowing labelizing and re-animating all labels of a UIViewController. Labelizing can be useful for performance, such as labelizing all MarqueeLabels when a UITableView/UIScrollView starts scrolling.
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
MarqueeLabel.controllerViewDidAppear(self)
}
Charles Powell
Give me a shout if you’re using this in your project!