NSCollectionView type select

I have implemented type selection for my NSCollectionView implementation.

In my collection view subclass, I have added additional delegate methods:

protocol CollectionViewDelegate : NSCollectionViewDelegate {
    func collectionViewDelegateTypeSelect(_ text: String)
    func collectionViewDelegateResetTypeSelect()
}

class CollectionView : NSCollectionView {
    
    override func keyDown(with event: NSEvent) {
        if let del = delegate as? CollectionViewDelegate {
            if (event.characters?.rangeOfCharacter(from: CharacterSet.alphanumerics.inverted) == nil) || (event.characters == " ") {
                del.collectionViewDelegateTypeSelect(event.characters!)
                return
            }
            else {
                del.collectionViewDelegateResetTypeSelect()
            }
        }
        if (event.specialKey == NSEvent.SpecialKey.pageUp) {
            scroll(NSMakePoint(0, max(0, enclosingScrollView!.documentVisibleRect.minY - enclosingScrollView!.documentVisibleRect.height)))
        } else if (event.specialKey == NSEvent.SpecialKey.pageDown) {
            scroll(NSMakePoint(0, min(enclosingScrollView!.documentVisibleRect.maxY, enclosingScrollView!.documentVisibleRect.origin.y + enclosingScrollView!.documentVisibleRect.height)))
        } else if (event.specialKey == NSEvent.SpecialKey.home) {
            scroll(NSMakePoint(0, 0))
        } else if (event.specialKey == NSEvent.SpecialKey.end) {
            scroll(NSMakePoint(0, frame.height))
        } else {
            super.keyDown(with: event)
        }
    }
    
}

Then in my delegate, which is a subclass of NSArrayController, I process the type selection:

    var timer: Timer?
    var typeString: String = ""
    var typeCount: Int = 0
    func collectionViewDelegateTypeSelect(_ text: String) {
        typeCount = 0
        typeString.append(text)
        findItemClosestToString(typeString)
        guard let _ = timer, timer!.isValid else {
            timer = Timer(timeInterval: 0.1, repeats: true, block: {_ in 
                self.typeCount = self.typeCount + 1
                if self.typeCount > 10 {
                    self.typeCount = 0
                    self.typeString = ""
                    self.timer?.invalidate()
                }
            })
            RunLoop.main.add(timer!, forMode: RunLoop.Mode.default)
            return
        }
    }
    
    func collectionViewDelegateResetTypeSelect() {
        if collectionViewDelegateIsTypeSelecting() {
            timer?.invalidate()
            timer = nil
        }
    }
    
    func sortedItems() -> ([NSManagedObject], [String]) {
        guard let items = (arrangedObjects as? [MediaItem]) else { return ([], []) }
        let sorted = (items as NSArray).sortedArray(using: [alphabeticalSortDescriptor]) as! [MediaSeries]
        return (sorted, items.map { $0.title })
    }
    
    func findItemClosestToString(_ text: String) {
        let (items, strings) = sortedItems()
        guard items.count > 0 else { return }
        var lo = 0
        var hi = items.count - 1
        var mid = lo
        while lo <= hi {
            mid = (lo + hi)/2
            let c = text.caseInsensitiveCompare(strings[mid])
            if c == .orderedDescending {
                lo = mid + 1
            } else if c == .orderedAscending {
                hi = mid - 1
            } else {
                break
            }
        }
        if strings[mid].localizedLowercase.hasPrefix(text.localizedLowercase) == false, mid + 1 < strings.count, strings[mid + 1].localizedLowercase.hasPrefix(text.localizedLowercase) {
            mid = mid + 1
        }
        selectItem(items, mid: mid)
    }
    
    func selectItem(_ items: [NSManagedObject], mid: Int) {
        let index = (arrangedObjects as! [NSManagedObject]).firstIndex(of: items[mid])!
        let path = IndexPath(item: index, section: 0)
        mainCollection.deselectAll(nil)
        mainCollection.reloadData()
        mainCollection.selectItems(at: [path], scrollPosition: .top)
        collectionView(mainCollection, didSelectItemsAt: [path])
    }

This works pretty good, however, as the number of items the collection view displays grows (> 1000), it is possible to type faster than the type selection works, leaving the user to watch a few seconds of the UI catching up.

The selection functions are broken apart to make overriding individual parts easier in subclasses.

The question I have is how to best collate multiple NSEvents so that in the event a few Events are queued, only the character is added, so that the unnecessary mid-selections occur. About the only way I can think to do this is to add an additional timer that collects one or more events before running the main selection algorithm. Hence all that happens between user input, is the typeString grows in length without proceeding further to doing the selection until I’m reasonably sure no additional input will come in.

When I started working through that, I figured there had to be a better approach. I know that NSTableViews and NSOutlineViews support type selection out of the box, but NSCollectionViews do not appear to.

What I’m curious about is if there is a better approach that I’ve just completely missed. I haven’t found anyone else attempting to add type selection to a NSCollectionView.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật