Skip to content

Instantly share code, notes, and snippets.

@SlappyAUS
Created January 28, 2021 04:29
Show Gist options
  • Save SlappyAUS/eb5017f963c94a795c8762026636e334 to your computer and use it in GitHub Desktop.
Save SlappyAUS/eb5017f963c94a795c8762026636e334 to your computer and use it in GitHub Desktop.
SearchableGrid
import SwiftUI
import CoreData
struct SearchableGrid<T: NSManagedObject, Content: View, EmptyContent: View>: View where T: Identifiable {
@Environment(\.managedObjectContext) var context
@StateObject private var viewModel = SearchableGridGridViewModel<T>()
let dropEntered : (DropInfo, SearchableGridGridViewModel<T>, T) -> ()
let dropUpdated : ((DropInfo, T) -> DropProposal?)?
let content : (T) -> Content
let contentWhenEmpty : () -> EmptyContent
var fetchRequest : FetchRequest<T>
var results : FetchedResults<T> {
fetchRequest.wrappedValue
}
let gridConfig : [GridItem]
let spacing : CGFloat
let horizontal : Bool
init(basePredicate: NSPredicate? = nil,
keyName : String,
filter : String,
orderBy : [(String, Bool)],
gridConfig : [GridItem]? = nil,
spacing : CGFloat = 10,
horizontal : Bool = true,
dropEntered : @escaping (DropInfo, SearchableGridGridViewModel<T>, T) -> (),
dropUpdated : ((DropInfo, T) -> DropProposal?)? = nil,
@ViewBuilder content : @escaping (T) -> Content,
@ViewBuilder contentWhenEmpty : @escaping () -> EmptyContent) {
let sortDescriptors = orderBy.compactMap( { NSSortDescriptor(key: $0.0, ascending: $0.1) })
let predicate: NSPredicate
if filter.count > 0 {
let filterPredicate = filter.count > 0 ?
NSPredicate(format: "%K CONTAINS[cd] %@", keyName, filter) : NSPredicate(format: "YES = YES")
predicate = NSCompoundPredicate(
andPredicateWithSubpredicates: [basePredicate ?? NSPredicate(format: "YES = YES"), filterPredicate])
} else {
predicate = basePredicate ?? NSPredicate(format: "YES = YES")
}
fetchRequest = FetchRequest<T>(entity: T.entity(),
sortDescriptors: sortDescriptors,
predicate: predicate,
animation: .default)
self.content = content
self.contentWhenEmpty = contentWhenEmpty
self.dropEntered = dropEntered
self.dropUpdated = dropUpdated
self.gridConfig = gridConfig ?? [GridItem(.adaptive(minimum: 100))]
self.spacing = spacing
self.horizontal = horizontal
}
var body: some View {
if results.isEmpty { //TODO: Bug here when result become empty from full
self.contentWhenEmpty()
} else {
ScrollView(horizontal ? .horizontal : .vertical) {
if horizontal {
LazyHGrid(rows: gridConfig, alignment: .top, content: {
gridData()
})
} else {
LazyVGrid(columns: gridConfig, alignment: .center, content: {
gridData()
})
}
}
}
}
private func gridData() -> some View {
ForEach(results) { result in
self.content(result)
.onDrag({
viewModel.currentDragging = result
return NSItemProvider(contentsOf: URL(string: "\(result.id)"))!
})
.onDrop(of: [.url],
delegate: SearchableDropDelegate(item: result,
viewModel: viewModel,
dropEntered: dropEntered,
dropUpdated: dropUpdated))
}
}
}
// MARK: - StickerBookGridViewModel
class SearchableGridGridViewModel<T: NSManagedObject>: ObservableObject {
@Published var currentDragging: T? = nil
}
// MARK: - StickerBookDropDelegate
fileprivate struct SearchableDropDelegate<T: NSManagedObject>: DropDelegate {
@ObservedObject var item: T
@ObservedObject var viewModel: SearchableGridGridViewModel<T>
let dropEntered: (DropInfo, SearchableGridGridViewModel<T>, T) -> ()
let dropUpdated: ((DropInfo, T) -> DropProposal?)?
func performDrop(info: DropInfo) -> Bool {
guard viewModel.currentDragging != nil else {
return true
}
viewModel.currentDragging = nil
return true
}
func dropEntered(info: DropInfo) {
dropEntered(info, viewModel, item)
}
func dropUpdated(info: DropInfo) -> DropProposal? {
if let du = dropUpdated {
return du(info, item)
} else {
return DropProposal(operation: .move)
}
}
}
// MARK: - Preview
struct SearchableGrid_Previews: PreviewProvider {
static var previews: some View {
let filterText = "tomatoe"
SearchableGrid(basePredicate: NSPredicate(format: "isFavorite = YES"),
keyName: "name_",
filter: filterText,
orderBy: [("sortOrderFavorite", true)],
dropEntered: { (info, viewModel, item) in
if let currentDragging = viewModel.currentDragging {
withAnimation {
let insertPosition = item.sortOrderFavorites
currentDragging.insertIntoFavorite(at: insertPosition)
}
}
}, content: { (sticker: Sticker) in
StickerTile(sticker: sticker)
}, contentWhenEmpty: { () in
Text("No Items in the list")
})
.environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment