【问题标题】:iTunes Song Title Scrolling in CocoaiTunes 歌曲标题在 Cocoa 中滚动
【发布时间】:2011-03-15 01:14:53
【问题描述】:

我进行了广泛的搜索,如果文本在 Cocoa 中太大,我终生无法找到有关如何实现与 iTunes 歌曲标题滚动类似效果的任何信息。我尝试在 NSTextField 上设置边界无济于事。我尝试过使用 NSTextView 以及使用 NSScrollView 的各种尝试。我确信我错过了一些简单的东西,但任何帮助将不胜感激。如果可能的话,我也希望不必使用 CoreGraphics。

Example,注意“Base.FM http://www”。文本已滚动。如果您需要一个更好的示例,请打开 iTunes,其中有一首标题相当大的歌曲,然后观看它来回滚动。

我认为肯定有一种简单的方法可以使用 NSTextField 和 NSTimer 创建选取框类型效果,但是唉。

【问题讨论】:

    标签: objective-c cocoa itunes nstextfield nsscrollview


    【解决方案1】:

    如果您试图将功能硬塞到现有控件中,我可以看到这将是多么困难。但是,如果你只是从一个普通的 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
    

    这显然是非常初级的,但它可以包含您正在寻找的东西,并且是一个不错的起点。

    【讨论】:

    • 像冠军一样工作。我不敢相信我在互联网上找不到任何类似的例子。也许现在我看到你的解决方案太简单了。
    • 这可能是因为它是一种 UI 反模式。出于几个原因,主要是在任何给定时间,如果用户查看它,它会显示他们想要的信息吗?可能不会。
    【解决方案2】:

    对于在 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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多