shopify / functionaltabledata Goto Github PK
View Code? Open in Web Editor NEWDeclarative UITableViewDataSource implementation
License: MIT License
Declarative UITableViewDataSource implementation
License: MIT License
We are missing the travis badge in the README file
Port this code over ios/pull/1311/commits/5e3b30fd6721459c5593dc1b190d35ef23eca9af
UITableViewCell
s have a function built in: setHighlighted(_ highlighted: Bool, animated: Bool)`, which allows you to manipulate any subviews as needed when the user touches down on a cell.
With FunctionalTableData, this callback does not occur for free. There's currently no way in a HostCell
implementation to override this callback.
The most common use case is when you have a UIView
or UIImageView
with an opaque background color. On highlight, UITableViewCell
will automatically remove the background color for any subviews to ensure the selection color is visible. In the cases where you want to retain the background color of any subviews during selection and highlighting with a UITableViewCell
you'd simply override setHighlighted
and ensure the background is set for either state.
CellActions
currently provides some state overriding, such as SelectionState
, however none exist for a highlighted state.
I reckon something like the following would be good:
public typealias HighlightingAction = (_ sender: UIView, highlighted: Bool) -> HighlightingState
with
public enum HighlightingState {
case highlighted
case unhighlighted
}
At the same time, I do see two callbacks for selection/deselection, so highlighting could have the same two, or perhaps selection could be reconfigured to one callback to have the two be the same:
public typealias SelectionAction = (_ sender: UIView, selected: Bool) -> SelectionState
At the moment you need to specify this somewhere in your app to make the separators appear. We should consider providing these as default
Separator.appearance().backgroundColor = UITableView().separatorColor
Separator.inset = 16
Port the changes from ios/pull/1102 into this repo
CellConfigType, TableSection, and ItemPath all use String's for their key's, we should instead use AnyHashable which makes things a bit nicer and easier to use (no need to convert Hashable key types to a String before they can be used as a key)
I found this crash in our POS app store build. Couldn't get more info than this but this at least has filenames and line numbers that might help locate the problem.
Thread 11 Crashed:
0 libswiftCore.dylib 0x0000000102b0e410 0x102884000 + 2663440
1 FunctionalTableData 0x0000000101c836ec function signature specialization <Arg[4] = Dead> of FunctionalTableData.TableSectionChangeSet.(isRow in _9D04CC2439151915054CA13314D61D66)(new: (section: FunctionalTableData.TableSection, row: Swift.Int), equalTo: (section: FunctionalTableData.TableSection, row: Swift.Int)) -> Swift.Bool (TableSectionChangeSet.swift:164)
2 FunctionalTableData 0x0000000101c7ea84 FunctionalTableData.TableSectionChangeSet.(compareRows in _9D04CC2439151915054CA13314D61D66)(newRows: inout Swift.Set<Swift.String>, oldRows: inout [Swift.String : Swift.Int], oldSectionIndex: Swift.Int, newSectionIndex: Swift.Int) -> () (TableSectionChangeSet.swift:0)
3 FunctionalTableData 0x0000000101c7ce5c FunctionalTableData.TableSectionChangeSet.(calculateChanges in _9D04CC2439151915054CA13314D61D66)() -> () (TableSectionChangeSet.swift:137)
4 FunctionalTableData 0x0000000101c8406c function signature specialization <Arg[0] = Owned To Guaranteed, Arg[1] = Owned To Guaranteed, Arg[2] = Owned To Guaranteed> of FunctionalTableData.TableSectionChangeSet.init(old: [FunctionalTableData.TableSection], new: [FunctionalTableData.TableSection], visibleIndexPaths: [Foundation.IndexPath]) -> FunctionalTableData.TableSectionChangeSet (TableSectionChangeSet.swift:76)
5 FunctionalTableData 0x0000000101c7249c function signature specialization <Arg[0] = Owned To Guaranteed, Arg[3] = Owned To Guaranteed> of FunctionalTableData.FunctionalTableData.(doRenderAndDiff in _1CE3C8B4455DD2DC43A2D2D02278E711)(_: [FunctionalTableData.TableSection], animated: Swift.Bool, animations: FunctionalTableData.FunctionalTableData.TableAnimations, completion: () -> ()?) -> () (FunctionalTableData.swift:0)
6 FunctionalTableData 0x0000000101c6b3d8 closure #1 () -> () in FunctionalTableData.FunctionalTableData.renderAndDiff(_: [FunctionalTableData.TableSection], animated: Swift.Bool, animations: FunctionalTableData.FunctionalTableData.TableAnimations, completion: () -> ()?) -> () (FunctionalTableData.swift:0)
7 FunctionalTableData 0x0000000101c786e8 partial apply forwarder for closure #1 () -> () in FunctionalTableData.FunctionalTableData.renderAndDiff(_: [FunctionalTableData.TableSection], animated: Swift.Bool, animations: FunctionalTableData.FunctionalTableData.TableAnimations, completion: () -> ()?) -> () (FunctionalTableData.swift:0)
8 FunctionalTableData 0x0000000101c525a4 reabstraction thunk helper from @escaping @callee_guaranteed () -> () to @escaping @callee_unowned @convention(block) () -> () (FunctionalCollectionData.swift:0)
9 Foundation 0x00000001bf0dab6c __NSBLOCKOPERATION_IS_CALLING_OUT_TO_A_BLOCK__ + 12
10 Foundation 0x00000001befe2cc8 -[NSBlockOperation main] + 68
11 Foundation 0x00000001befe219c -[__NSOperationInternal _start:] + 736
12 Foundation 0x00000001bf0dca40 __NSOQSchedule_f + 268
13 libdispatch.dylib 0x00000001be0866c8 _dispatch_call_block_and_release + 20
14 libdispatch.dylib 0x00000001be087484 _dispatch_client_callout + 12
15 libdispatch.dylib 0x00000001be02a874 _dispatch_continuation_pop$VARIANT$mp + 408
16 libdispatch.dylib 0x00000001be029f3c _dispatch_async_redirect_invoke + 596
17 libdispatch.dylib 0x00000001be036a60 _dispatch_root_queue_drain + 372
18 libdispatch.dylib 0x00000001be037308 _dispatch_worker_thread2 + 124
19 libsystem_pthread.dylib 0x00000001be269190 _pthread_wqthread + 468
20 libsystem_pthread.dylib 0x00000001be26bd00 start_wqthread + 0
Hi, I would like to understand how to correctly handle table view row deletions with FunctionalTableData. I understand that we should not work with indexes, however right now this is what I came up with:
class MyViewController: UITableViewController {
internal let functionalData = FunctionalTableData()
internal var items: [Translation] = [] {
didSet {
render()
}
}
private func render() {
let cellStyle = CellStyle(bottomSeparator: .inset, separatorColor: .gray, backgroundColor: .black)
let rows: [CellConfigType] = items.enumerated().map { index, item in
return HistoryCell(
key: "id-\(index)",
style: cellStyle,
actions: CellActions(rowActions: [UITableViewRowAction(style: .destructive, title: "Delete", handler: ({ (rowAction, indexPath) in
self.items.remove(at: indexPath.row)
}))]),
state: HistoryState(translationItem: item),
cellUpdater: HistoryState.updateView)
}
functionalData.renderAndDiff([
TableSection(key: "section", rows: rows)
])
}
...
}
My intuition was that I needed to create a new CellAction
, however, I'm not entirely sure how to handle the data deletion from the array (it's not working well, more specifically when I try to delete any row the last row gets deleted). I would appreciate any help with this, thanks!
Issue:
When there are multiple rows in a section and when I try to delete a row, it reuses the deleted row for the immediate row which shouldn't. This makes the row below the deleted row to be in editing mode.
FunctionalTableData/FunctionalTableData/TableView/FunctionalTableData.swift
Lines 385 to 387 in d347ccc
Adding tableView.setEditing(false)
when deleting the rows in the snippet fixes the issue. But that wouldn't be an appropriate solution. So we might need to look into a better solution.
Now that we can just use an URL in the Playgrounds app (or I just TIL today about it), we can make this a playground book as well
Check out Create Your Own Swift Playgrounds Subscription from #WWDC18 https://developer.apple.com/wwdc18/413
Please tell me how to solve it
How would I go about deselecting a table view cell? I'm currently using
let cellStyle = CellStyle(bottomSeparator: .inset,
separatorColor: .gray,
highlight: true,
selectionColor: UIColor(named: "selectedCell"),
backgroundColor: .black)
This code highlights the cell on tap but then the cell never gets deselected afterwards. I took a look at the CellStyle
docs but couldn't find a propriety that handles deselection. I'm aware there are CellActions
I can use (specifically the deselectionAction
), although the action returns an item/index so not sure if I can use that.
How would I go about having functional table data deselect my cell automatically? Thanks :)
Hello!
Thank you for contributing this to the community! I am trying it out and am having a hard time understanding how to create HostCell
cells who's view comes from a nib. I don't see any way that tableview's register(_ nib: UINib?, forCellReuseIdentifier identifier: String)
method can work and I've tried to have the views instantiated by HostCell load nibs in their initializers and ran into some issues there too.
Do you have a best practice for doing this with your framework?
Thanks!
Dan
We are basing the editing style on the presence of rowActions/leading/trailing config. This isn't right but is needed for the moment for backwards compatibility.
7950466
We should instead provide a canDelete
or deleteAction
type mechanism that this delegate method would utilize. Should also look into that 3rd insert
state that I can't remember the purpose of anymore
Implant a way to clip the contents of a cell. Specially useful for UICollectionViewCells. Maybe adding a mask property to CellConfig?
Travis is kind of noisy, update the yml with
notifications:
email:
on_success: never
on_failure: always
Right now, updateView
is not clear enough about reusability.
@robinsenior mentioned http://khanlou.com/2017/03/that-one-optional-property/ :
enum UseCase {
case prepareForReuse
case update(LabelState)
}
Remove our custom difference computation and replace with a more maintainable one.
Current options on the table are DifferenceKit and/or Swift 5.1's built in Collection Difference. The Swift 5.1 one however would require a "polyfill" type approach to support OS's that do not have that built in
Hey there, I'm trying to get my hands on this library but I'm having issues with using it. I installed FunctionalTableData through Carthage and then copied the sample classes (table view controller and label cell) to the newly project I've created. However, after running the project and tapping the plus button to add a new table view entry a crash occurs.
Specifically, it looks like there are some issues in the TableSectionChangeSet
file. I couldn't specifically track down what is going on, but here is the sample project I am using (https://github.com/cesaredecal/ShopifyFunctionalTableDataIssue). This issue is occurring on every single Xcode project I test this library on. Maybe there's something wrong with my Carthage configuration or something else in general. Happy to provide more info if necessary.
I've also tried to install this library the other way, by simply dragging and dropping the FunctionalTableData folder into the project. However this leads to conflicts on my end (duplicate info.plist files and so on...). So I'm not really sure what to do at this point.
Thanks for your help!
The CellStyle.backgroundView
property is too expensive. Views should only be created when they're needed. This background view property means that the view is created and destroyed on every render pass even though it's rarely used.
Loading the framework inside the playground works very sporadically. You have to delete and write again the same code over and over until the compiler finally picks it up
It's tough to use the vanilla addTarget(selector:) when you've got a table that includes a list of UI elements that are backed by some list of objects, and want to know which item in the list was interacted with. The closure-based addTarget makes it easy because you can capture the data item currently being interacted with (and the view controller itself to trigger a re-render).
So in my naive and humble opinion the closure-based addTarget should come with FTD.
Also, hi :)
The initializer for CellAction
now takes a PreviewingViewControllerAction
closure for the previewingViewControllerAction
argument. Implementors of this closure are expected to call previewingContext.sourceRect = previewingContext.sourceView.convert(cell.bounds, from: cell)
.
Since I imagine the majority of use cases for PreviewingViewControllerAction
will behave the same way, it would be nice if we could have the nominal path for this do the sourceRect
assignment automatically.
This would allow us to persist our current state easily and write a snapshot style test assert that a section matches the recorded encoded section.
I've been thinking about some potential changes to this library that could mean breaking small API changes. I don't have working implementations of these things so it's a list of dreams right now.
Hi, I previously used to delete rows by specifying a UITableViewRowAction
in rowActions
property like so
let deleteRowAction = UITableViewRowAction(style: .destructive, title: "Delete", handler: ({ (rowAction, indexPath) in
self.didTapDeleteOnRow(indexPath: indexPath)
}))
let rows: [CellConfigType] = getSourceArray().enumerated().map { index, item in
return HistoryCell(
key: "index-\(index)-\(item.translatedText)",
style: cellStyle,
actions: CellActions(
selectionAction: { _ in
self.didSelectCell(translation: item)
return .selected
},
deselectionAction: { _ in
return .deselected
}, rowActions: [deleteRowAction]), // <---- here
state: HistoryState(translationItem: item),
cellUpdater: HistoryState.updateView)
}
Now I see rowActions
isn't part of the initializer anymore, so I'm wondering how to enable cell deletion. Any insights?
Implement these changes ios/pull/1173
The link to the DemoApp in the Usage Section takes you to a page not found.
When long pressing on a UICollectionViewCell using FunctionalCollectionData, the touch will be cancelled. Unlike its UITableViewCell counterpart.
To reproduce, create a FCD cell that has a selectionColor and selectionAction. Long press on it and then release, the selection closure won't fire and the color will revert back before releasing
There is an issue with the UITableView
setting an incorrect contentOffset
during FunctionalTableData.applyTableChanges(...).applyTableSectionChanges(...)
. This results in the following "white flash" / choppy animation when pulling to refresh.
Table Sections Demo
to view TableSectionsViewController, where there is a list of ProgramicDetailCells, each within its own TableSection.The tableView jumps because estimatedHeightForHeaderInSection
and estimatedHeightForFooterInSection
have not been implemented in FunctionalTableData
.
This implementation fixes the issue.
public func tableView(_ tableView: UITableView, estimatedHeightForHeaderInSection section: Int) -> CGFloat {
return heightForHeaderInSection(tableViewStyle: tableView.style, section: section)
}
public func tableView(_ tableView: UITableView, estimatedHeightForFooterInSection section: Int) -> CGFloat {
return heightForFooterInSection(tableViewStyle: tableView.style, section: section)
}
public func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return heightForHeaderInSection(tableViewStyle: tableView.style, section: section)
}
public func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return heightForFooterInSection(tableViewStyle: tableView.style, section: section)
}
private func heightForHeaderInSection(tableViewStyle: UITableViewStyle, section: Int) -> CGFloat {
guard let header = sections[section].header else {
// When given a height of zero grouped style UITableView's use their default value instead of zero. By returning CGFloat.min we get around this behavior and force UITableView to end up using a height of zero after all.
return tableViewStyle == .grouped ? minimumHeaderHeight : 0
}
return header.height
}
private func heightForFooterInSection(tableViewStyle: UITableViewStyle, section: Int) -> CGFloat {
guard let footer = sections[section].footer else {
// When given a height of zero grouped style UITableView's use their default value instead of zero. By returning CGFloat.min we get around this behavior and force UITableView to end up using a height of zero after all.
return tableViewStyle == .grouped ? minimumHeaderHeight : 0
}
return footer.height
}
private var minimumHeaderHeight: CGFloat {
if #available(iOS 11.0, *) {
return CGFloat.leastNormalMagnitude
} else {
return 2.0
}
}
http://www.openradar.me/33107135
In FunctionalTableData, when the contextual actions are being created, we can determine if the cell is this height and instead return a pre-rendered (and likely cached) image that includes both the text and the image. This might be a configurable property to allow it to be opted in or out.
This could have some side effects in scenarios where the cell changes size while the action is visible (device rotation, tableview gets wider).
Port this changes to this PR ios/pull/1189
Here are a few rough edges in the API we could clean up at some point:
FunctionalTableData.tableView
should unsubscribe the FTD instance as the delegate
and dataSource
of the old tableView
(if any).keyPathForIndexPath
should return nil
for index paths that are out of range, instead of dying with an out-of-bounds error, to make the public API friendlier and to be consistent with indexPathFromKeyPath
. It could also be renamed to keyPath(for: IndexPath)
to be more Swift 4y.indexPathFromKeyPath
should be renamed to indexPathForKeyPath
or indexPath(for: KeyPath)
.rectForKeyPath
returns nil
if the KeyPath couldn't be found, but may also return CGRect.zero
if the corresponding IndexPath couldn't be found. This shouldn't happen in practice, but we should still catch and map that value to nil
also, to handle the no-value case consistently.sectionForKey
could be trivially reimplemented with sections.first { $0.key == key }
ios#1693/commits/
introduces scroll-to-keypath functions for FTD and FCD, and deprecates the old renderAndDiff
-based scroll API. These commits should be ported to the public repository.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.