New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add categories on UILabel, UITextView, and UITextField to accept a BONTextable object #108
Conversation
* Assign a @p BONChainable object to apply to the label text. When a new value is assigned to @p text the chain attributes will be applied. | ||
* If a new value is assigned directly to @p attributedText the @p bonChainable property will be set to @p nil. | ||
*/ | ||
@property (nonatomic, copy) id<BONChainable> bonChainable; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#req for properties, please use the order copy, nonatomic
to match the rest of the project. (If some place in the rest of the project doesn't match that, we should fix it. It should match the Raizlabs Cocoa Style Gudie)
df4f04d
to
2f4e96a
Compare
@jvisenti @ateliercw Here's a PR-in-progress for setting |
@jvisenti @ateliercw also, do you have opinions on how you would want to handle |
@KingOfBrian @jkaufman you may also have opinions over whether this use case merits swizzling, or whether a subclass would be sufficient. We use swizzling in RZDataBinding to great effect, but it's limited to just |
This is a very interesting use case. Adding more state to UILabel makes me a bit nervous. I'm never clear what happens when I set a font, a text color, text, and an attributed string. Adding a bonChain expands this uncertainty a bit. It would be good to think through the expected behavior here. It may be that the expected behavior is extracting the font and the color from the bonChain. This functionality could also just assign the color and font to the UILabel when a bonChain is assigned. What happens when there is a bonChain, and a user changes the textColor? It's hard to define a clear contract here (Thanks NSAttributedString!) The interesting end goal here to be able to assign a bonChain to a label and continue to change just the text property of the label and maintain the style, Correct? I wonder if this functionality really needs any knowledge of bonChain. It may be that you could just take the attributes out of the current attributedText and create a new attributed string with those same attributes.Honestly I'm not sure, but UILabel may already do this. I always do text + font + color, or attributedText. |
So, as I was implementing this I researched this a bit and it has slightly different behavior depending on which class you're using. For all three, setting For For
Correct, that's the desired goal.
This is somewhat tricky because some of what
I think this is the key question. I implemented in the way that I'd expect it to work, but I have no idea if what I expect matches the general expectations, so opinions very welcome :) What I expect is that when the |
I'd advocate putting this into a subspec of bonmot and making it a separate framework for carthage, or going a step further and making it a separate but related library. It's a useful feature, but it's expanding the scope of bonmot from a attributed string generation library into an attributed string generation library and the UI implementation details. |
I agree with @ateliercw. You could actually pull the |
@ateliercw +1 on making any UIKit extensions a subspec or separate component thingy, and @jvisenti +1 on moving @Imperiopolis it looks like you are expecting two code paths, one to perform initial styling/setup on UI elements, and a second when updates come in and the text needs to be updated. Without the categories in this PR, you'd have to do a form of dependency injection:
I'm wondering whether the potential code cleanliness is worth the tradeoff of readability/maintainability by having the labels do magic things. Why not structure your code in such a way that label text always passes through a // This is a one-way method that sets the attributed based on
// the object passed, but does not store the passed object
- (void)setBONChainable:(id<BONChainable>)bonChainable; |
Moving it to a subspec totally makes sense. I'll work on that change. @ZevEisenberg: The way I am treating this in my head is basically an evolution of |
I was figuring that the model would vend data, view model would concatenate that data into a formatted string, and the view would mix that string with a |
What you described is spot on with what I meant, I was just oversimplifying. The general path for me is model -> view model -> view, so theoretically I could rely on the view to hang onto the chain and reapply it when it has updated text data. The major problem I see with that approach is I now have to add storage for the chain to every view that behaves this way (basically, every view) so that it can be reapplied later.
Yeah, separation of content from markup is my major desire here. There are certainly more complex cases where you have to deal with Based on this discussion, I'm wondering if my usage of BonMot is different from most. For me, the fact that BonMot generates an |
@Imperiopolis you're, like, the third person I know of who has actually used it in more than a trivial way in a real app. Trailblazer 😄
This is pretty much what I had in mind when I started BonMot. I feel like I've drifted away from that, since I haven't had the chance to use it extensively in an app myself (hoping to change that soon), but that's spot-on for how it was intended to be used. I still think I originally had kind of a "composition" model in mind, where the way you would always set the text would be to grab the appropriate style, mix your string into it, and pass the resulting attributed string on to the view layer. That's effectively what you're doing, just shifting the responsibilities a little bit. I wonder whether there's a simple compromise, like making methods like |
One more thing, just to stir the pot: I know I suggested the |
The
Sure, I'm good with that approach as it accomplishes what I want without potentially confusing what |
It occurs to me that Anyone else, please weigh in if you have opinions. |
Trying to coalesce people's opinions, both from this thread and via private backchannel communications, so we have them all in one place. Let me know if I've misrepresented anything here. Here are the choices, and I think everyone is agreed that if we do these, they should go in a special UIKit Extensions subspec:
Votes:
By my count, with "leaning" or "on the fence" counted as 0.5, that makes it:
CC'ing @mgorbach, our software architect, who may have Opinions. Based on what I've seen here, I'd be fine with Bare Minimum or Associated Objects. |
Thinking about this, I think I'm fan of implementing an optional label subclass, in a separate framework, that stores bonChain state, and applies it to text. It could disable external screwing with the attributed string, and take full control of it itself instead. |
For me, a subclass prevents me from using this easily in some of the areas I would like, such as a cell's Could you elaborate on the benefit using a subclass brings here? Right now all I'm really seeing is greater usage complexity versus the Associated Objects strategy. With that strategy you need to explicitly set a chain and use the custom |
@Imperiopolis makes a good case that with a subclass, you couldn't use this feature with any class that exposes a label directly. I'm liking Associated Objects more and more. If it ends up being popular, we could consider adding swizzling later so we can get @jvisenti's "full magic". |
I've actually leaned more towards the Swizzle side of the fence as I've been using it in my current app. That "full magic" is really valuable in providing a simple and straightforward API. That said, it definitely seems like the Associated Objects route is a good middle ground and likely the best route to proceed with for now. |
Cool, @Imperiopolis why don't, you go ahead with the Associated Objects approach with your PR. Feel free to open an issue for associated objects, but I think we should give it a little time before adding it to see if it would be helpful to more people. Luckily, you can continue to swizzle in your own app if you want. Enjoy your dog food 😉 |
To confirm, we also do want to move this into a subspec along with |
7ef6328
to
6dfaa97
Compare
👍 on the subspec. I'm OK with that breaking change, since the fix is just to add the subspec. I'll still bump the version to 3.0 because of SemVer. |
^ apparently there's an easy way to accidentally edit the name of a PR 😳 |
There appear to be merge conflicts. Are they just related to the CocoaPods-generated Xcode project? |
@@ -1,4 +0,0 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#? any idea why this was deleted?
ab7d080
to
79ca4ab
Compare
For anyone following along at home, @Imperiopolis and I have been discussing naming things, The Hardest Problem In Computer Science™. We've settled on renaming |
{ | ||
UITextView *textView = UITextView.new; | ||
textView.textAndApplyChainable = @"Hello, world!"; | ||
textView.bonChainable = BONChain.new.font([UIFont systemFontOfSize:16]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#? Since a BONTextable
(as it's soon going to be called) contains its own string
, it's weird to me that it doesn't overwrite the text in the label. What do you think, @Imperiopolis?
86068f7
to
480dbe6
Compare
// | ||
// | ||
|
||
#import "BonMot.h" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#pp #import <BonMot/BonMot.h>
- I'm not sure why; I think it's because at one point, the <>
import got you autocomplete and the ""
one didn't. Now I think I prefer it because it explicitly makes it look like it's a library/framework, and it's scoped, not global.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is part of the BonMot library I think it should remain #import "BonMot.h"
. The <...>
syntax is intended for imports of files not provided by the project. See this SO thread for more info.
480dbe6
to
dfa6214
Compare
dfa6214
to
3b028e7
Compare
3b028e7
to
6730f8d
Compare
6730f8d
to
882911a
Compare
@@ -112,6 +112,33 @@ NSAttributedString *redBirdString = redBirds.attributedString; | |||
NSAttributedString *blueBirdString = blueBirds.attributedString; | |||
``` | |||
|
|||
## Label, TextView, and TextField Utilities |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#nth Change to one of the following:
UILabel
,UITextView
, andUITextField
Utilities- UIKit Utilities
Add categories on UILabel, UITextView, and UITextField to accept a BONTextable object
This allows assigning an
id <BONChainable>
directly to aUILabel
,UITextView
, orUITextField
in the same way you would assign a font. The BonMot styling assigned to the label/textView/textField will be applied to any future updates to the label/textView/textField'stext
property.This works via swizzling the
setText:
andsetAttributedText:
methods of these classes to allow applying the BonMot styling when appropriate.Fixes #68