cbpowell / MarqueeLabel

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
MIT License
4.22k stars 564 forks source link

Question/Suggestion: is there any way to get the current displayed text? #249

Closed LucaGaspa closed 3 years ago

LucaGaspa commented 4 years ago

Usage Details

I want to show some strings (news titles) in this component like a flash news TV program. The problem is that I also want to know which is the currently visible news, so when tapping on the MarqueeLabel I can redirect the user to the news detail he is currently reading on the screen. Is there any way to achieve this behaviour?

cbpowell commented 4 years ago

Hi! So I think it's doable, but I’m reluctant to incorporate it into the base functionality because the news ticker usage isn’t really what ML was designed to do. The big reason is because ML just takes a single String as the text to display. So for a new ticker you're going to combine a few individual headlines together into a super long string, and ML doesn't know how to differentiate between your headlines to know which was tapped.

But here's the general way I'd approach it - you would need to:

  1. Detect a tap and get the coordinates of the tap in the view frame (easy).
  2. Get the position/offset of the in-flight MarqueeLabel animation by looking at the presentation layer (easy).
  3. Translate the tap point in the view frame into the offset presentation layer frame (also pretty easy).
  4. Use that corrected tap coordinate point to determine which news headline was tapped.

Item 4 is where it gets tricky with the way ML is designed because it doesn't know where each headline starts and stops, at least without setting more more interface details

So let's say your total combination-of-headlines string ends up being 500 pt wide / 20 pt tall, and (after correcting for the animation offset) the user tapped at (x: 324, y:10). You would need a mapping of headlines to ranges of X coordinates, which could actually be done using the NSString function boundingRect(with:options:attributes:context:) on each headline individually. You'd then need to compare the tapped point (x:324) to your set of ranges, and then act on whichever range includes 324.

I'll see if there's a good way to have a helper function that does the coordinate conversion, but that's about as far as I'd want to go for base functionality in ML. Basically ML could provide a helper that does Item 3 above. But keeping track of ranges seems beyond the scope in my opinion!

I’d also suggest exploring doing it with a UICollectionView, or even just a UIScrollView - you can still animate and “loop” the scroll, and then get all the benefits of individual UIViews for each headline in your ticker. Plus you'd be able to change or append new headlines on the fly, instead of triggering the reset that occurs with ML when you change a string.

LucaGaspa commented 4 years ago

@cbpowell for me your answer is enough. Thank you very much, really appreciated! There is no need to add out of scope complexity to your component. I will try your strategy to solve the problem, otherwise I'll go for a ScrollView.

cbpowell commented 3 years ago

@LucaGaspa I added a function to convert a tap point into the coordinate system of the label, which in turn also lets you calculate what word or character a user tapped on - see release 4.3.0. There's also an example usage in the demo project now, linked on that release page.

I still think a UIScrollView or UICollectionView would work better in your case in the long run, but if you haven't already moved on in the ~1.5 years since you requested this maybe it'll help!

LucaGaspa commented 3 years ago

Hi @cbpowell, I'm glad to read about this issue! Thank you very much, I hope this helper function will be useful to many people.