Draft for generic support
Hi, I wanted to ask if there are any plans to add generic support to FlexLayout. This would allow for more flexible usage, such as the following code example:
extension Flex where Base: UILabel {
var text: String? {
get { base?.text }
set { base?.text = newValue; markDirty() }
}
}
I’ve made some changes to the code, and I’m submitting this as a draft for review. Let me know if this is something that could be considered.
Thanks!
You could use this to enable auto markDirty. :)
extension UIView {
static func enableFlexAutoMarkDirty() {
swizzle(
classType: UIView.self,
origin: #selector(UIView.invalidateIntrinsicContentSize),
swizzled: #selector(UIView.flex_invalidateIntrinsicContentSize)
)
// UILabel().invalidateIntrinsicContentSize doesn't call super in some iOS versions
if class_getInstanceMethod(UILabel.self, #selector(UILabel.invalidateIntrinsicContentSize))
!= class_getInstanceMethod(UIView.self, #selector(UIView.invalidateIntrinsicContentSize)) {
swizzle(
classType: UILabel.self,
origin: #selector(UILabel.invalidateIntrinsicContentSize),
swizzled: #selector(UILabel.flex_uilabel_invalidateIntrinsicContentSize)
)
}
// UITextView().invalidateIntrinsicContentSize doesn't call super in some iOS versions
if class_getInstanceMethod(UITextView.self, #selector(UITextView.invalidateIntrinsicContentSize))
!= class_getInstanceMethod(UIView.self, #selector(UIView.invalidateIntrinsicContentSize)) {
swizzle(
classType: UITextView.self,
origin: #selector(UITextView.invalidateIntrinsicContentSize),
swizzled: #selector(UITextView.flex_uitextview_invalidateIntrinsicContentSize)
)
}
}
}
private extension UIView {
@objc
func flex_invalidateIntrinsicContentSize() {
flex_invalidateIntrinsicContentSize()
if isFlexEnabled {
flex.markDirty()
}
}
}
private extension UILabel {
@objc
func flex_uilabel_invalidateIntrinsicContentSize() {
flex_uilabel_invalidateIntrinsicContentSize()
if isFlexEnabled {
flex.markDirty()
}
}
}
private extension UITextView {
@objc
func flex_uitextview_invalidateIntrinsicContentSize() {
flex_uitextview_invalidateIntrinsicContentSize()
if isFlexEnabled {
flex.markDirty()
}
}
}
You could use this to enable auto
markDirty. :)
Hi @nuomi1 👋 Thanks a lot for the response and code snippet. I believe this approach is effective, but could it potentially result in unnecessary markDirty calls, triggering a view relayout? For instance, when updating a grow-width UILabel that already has enough space to accommodate the updated text?
However, I still believe that adding generic support could offer better extensibility. For example, consider the following code - and yes, it might seem unusual, but I have indeed written similar code before :)
extension Flex where Base: UIVisualEffectView {
func addItem<Item>(_ view: Item) -> Flex<Item> {
if let host = base {
return host.contentView.flex.addItem(view)
} else {
preconditionFailure("Trying to modify deallocated host view")
}
}
}
Great work! Thank you.
While reviewing the implementation, I was wondering whether using generics here brings significant benefits, given that FlexLayoutCompatible enforces inheritance from UIView. Since every Base is essentially a UIView, and the modified Flex object uses weak var base: Base? but ultimately consumes UIView, it seems like we could just pass UIView directly without generic.
As it stands, it feels like the current implementation mainly exists to enable generic extensions. If we need subclass-specific behavior, it looks like we can simply access the view directly and handle it there.
Your proposed structure:
extension Flex where Base: UILabel {
var text: String? {
get { base?.text }
set { base?.text = newValue; markDirty() }
}
}
label.flex.text = "hello"
What’s currently possible in our project:
class FlexLabel: UILabel {
override var text: String? {
get { super.text }
set { super.text = newValue; flex.markDirty() }
}
}
label.text = "hello"
extension UILabel {
func setTextAndMarkDirty(_ text: String?) {
self.text = text
self.flex.markDirty()
}
}
label.setTextAndMarkDirty("hello")
How about introducing the generic approach when FlexLayout supports platforms beyond UIKit, or when there’s more feedback?