我已经进行了广泛的搜索,并且在我的生活中找不到任何关于如果在Cocoa中文本太大而如何实现与iTunes歌曲标题滚动类似效果的信息。我试过在NSTextField上设置边界无济于事。我尝试过使用NSTextView以及使用NSScrollView的各种尝试。我相信我错过了一些简单的东西,但任何帮助都会非常感激。我也希望如果可能的话不必使用CoreGraphics。
Example,请注意“Base.FM http://www。”文本已滚动。如果您需要一个更好的示例,请使用标题较大的歌曲打开iTunes,并观看它来回滚动。
我认为有一种简单的方法可以用NSTextField和NSTimer创建一个字幕类型效果,但是唉。
答案 0 :(得分:19)
如果你试图将功能强加到一个存在的控件中,我可以看到这将是多么困难。但是,如果你只是从一个简单的NSView开始,它就不那么糟糕了。我在大约10分钟内掀起了这个......
//ScrollingTextView.h:
#import <Cocoa/Cocoa.h>
@interface ScrollingTextView : NSView {
NSTimer * scroller;
NSPoint point;
NSString * text;
NSTimeInterval speed;
CGFloat stringWidth;
}
@property (nonatomic, copy) NSString * text;
@property (nonatomic) NSTimeInterval speed;
@end
//ScrollingTextView.m
#import "ScrollingTextView.h"
@implementation ScrollingTextView
@synthesize text;
@synthesize speed;
- (void) dealloc {
[text release];
[scroller invalidate];
[super dealloc];
}
- (void) setText:(NSString *)newText {
[text release];
text = [newText copy];
point = NSZeroPoint;
stringWidth = [newText sizeWithAttributes:nil].width;
if (scroller == nil && speed > 0 && text != nil) {
scroller = [NSTimer scheduledTimerWithTimeInterval:speed target:self selector:@selector(moveText:) userInfo:nil repeats:YES];
}
}
- (void) setSpeed:(NSTimeInterval)newSpeed {
if (newSpeed != speed) {
speed = newSpeed;
[scroller invalidate];
scroller == nil;
if (speed > 0 && text != nil) {
scroller = [NSTimer scheduledTimerWithTimeInterval:speed target:self selector:@selector(moveText:) userInfo:nil repeats:YES];
}
}
}
- (void) moveText:(NSTimer *)timer {
point.x = point.x - 1.0f;
[self setNeedsDisplay:YES];
}
- (void)drawRect:(NSRect)dirtyRect {
// Drawing code here.
if (point.x + stringWidth < 0) {
point.x += dirtyRect.size.width;
}
[text drawAtPoint:point withAttributes:nil];
if (point.x < 0) {
NSPoint otherPoint = point;
otherPoint.x += dirtyRect.size.width;
[text drawAtPoint:otherPoint withAttributes:nil];
}
}
@end
只需将一个NSView拖到Interface Builder中的窗口上,然后将其类更改为“ScrollingTextView”。然后(在代码中),你做:
[myScrollingTextView setText:@"This is the text I want to scroll"];
[myScrollingTextView setSpeed:0.01]; //redraws every 1/100th of a second
这显然是非常简陋的,但它确实包含了你正在寻找的东西,并且是一个不错的起点。
答案 1 :(得分:0)
对于在Swift 4中寻找此内容的任何人,我都转换了Dave的答案并添加了更多功能。
import Cocoa
open class ScrollingTextView: NSView {
/// Text to scroll
open var text: NSString?
/// Font for scrolling text
open var font: NSFont?
/// Scrolling text color
open var textColor: NSColor = .headerTextColor
/// Determines if the text should be delayed before starting scroll
open var isDelayed: Bool = true
/// Spacing between the tail and head of the scrolling text
open var spacing: CGFloat = 20
/// Amount of time the text is delayed before scrolling
open var delay: TimeInterval = 2 {
didSet {
updateTraits()
}
}
/// Length of the scrolling text view
open var length: CGFloat = 0 {
didSet {
updateTraits()
}
}
/// Speed at which the text scrolls. This number is divided by 100.
open var speed: Double = 4 {
didSet {
updateTraits()
}
}
private var timer: Timer?
private var point = NSPoint(x: 0, y: 0)
private var timeInterval: TimeInterval?
private(set) var stringSize = NSSize(width: 0, height: 0) {
didSet {
point.x = 0
}
}
private var timerSpeed: Double? {
return speed / 100
}
private lazy var textFontAttributes: [NSAttributedString.Key: Any] = {
return [NSAttributedString.Key.font: font ?? NSFont.systemFont(ofSize: 14)]
}()
/**
Sets up the scrolling text view
- Parameters:
- string: The string that will be used as the text in the view
*/
open func setup(string: String) {
text = string as NSString
stringSize = text?.size(withAttributes: textFontAttributes) ?? NSSize(width: 0, height: 0)
setNeedsDisplay(NSRect(x: 0, y: 0, width: frame.width, height: frame.height))
updateTraits()
}
}
private extension ScrollingTextView {
func setSpeed(newInterval: TimeInterval) {
clearTimer()
timeInterval = newInterval
guard let timeInterval = timeInterval else { return }
if timer == nil, timeInterval > 0.0, text != nil {
if #available(OSX 10.12, *) {
timer = Timer.scheduledTimer(timeInterval: newInterval, target: self, selector: #selector(update(_:)), userInfo: nil, repeats: true)
guard let timer = timer else { return }
RunLoop.main.add(timer, forMode: .commonModes)
} else {
// Fallback on earlier versions
}
} else {
clearTimer()
point.x = 0
}
}
func updateTraits() {
clearTimer()
if stringSize.width > length {
guard let speed = timerSpeed else { return }
if #available(OSX 10.12, *), isDelayed {
timer = Timer.scheduledTimer(withTimeInterval: delay, repeats: false, block: { [weak self] timer in
self?.setSpeed(newInterval: speed)
})
} else {
setSpeed(newInterval: speed)
}
} else {
setSpeed(newInterval: 0.0)
}
}
func clearTimer() {
timer?.invalidate()
timer = nil
}
@objc
func update(_ sender: Timer) {
point.x = point.x - 1
setNeedsDisplay(NSRect(x: 0, y: 0, width: frame.width, height: frame.height))
}
}
extension ScrollingTextView {
override open func draw(_ dirtyRect: NSRect) {
if point.x + stringSize.width < 0 {
point.x += stringSize.width + spacing
}
textFontAttributes[NSAttributedString.Key.foregroundColor] = textColor
text?.draw(at: point, withAttributes: textFontAttributes)
if point.x < 0 {
var otherPoint = point
otherPoint.x += stringSize.width + spacing
text?.draw(at: otherPoint, withAttributes: textFontAttributes)
}
}
override open func layout() {
super.layout()
point.y = (frame.height - stringSize.height) / 2
}
}
参考要点: https://gist.github.com/NicholasBellucci/b5e9d31c47f335c36aa043f5f39eedb2