โœจMyGrid

โญ๏ธ ๆณจๆ„๏ผš๐Ÿ‘” MyGrid<Item, ItemView> ๆ˜ฏไธ€ๅ€‹ Generic Typesโ—๏ธ

ไฝฟ็”จ generic type ็š„ nested type ่ฆๅฐๅฟƒโ—๏ธ

  • ๐Ÿ‘” MyGrid (line: 44)๏ผšๅœจ MyGrid ๅ…ง้ƒจไฝฟ็”จ Layout ๅŸบๆœฌไธŠๆฒ’ไป€้บผๅ•้กŒ

  • ๐Ÿ‘๏ธ TestMyGrid (line: 52)๏ผšๅœจ MyGrid ๅค–้ขไฝฟ็”จ Layout ๅฐฑ่ฆๅฐๅฟƒไบ†๏ผŒๅฆ‚ๆžœ่ฉฒ่กŒๅฏซๆˆไธ‹้ข้€™ๆจฃ๏ผš

let layout = MyGrid.Layout( ... )

้‚ฃๅฐฑๆœƒๅ‡บ็พไธ‹ๅˆ—ๅ•้กŒ๏ผš

Files

โฌ†๏ธ ้œ€่ฆ๏ผš arr.index(of:)

// 2022.02.15

import SwiftUI

/// ๐Ÿ‘” MyGrid<Item, ItemView>
/// ```
/// MyGrid(items: items) { item in 
///   /* build item view */ 
/// }
/// ```
struct MyGrid<Item: Identifiable, ItemView: View>: View {
    
    // 1. ไฝฟ็”จ่€…่ฆๆไพ›๏ผš
    // โ€ข items          : ๆŽ’็‰ˆ็š„้ …็›ฎ
    // โ€ข cellAspectRatio: ๅธŒๆœ›็š„ๆ ผๅญๆฏ”ไพ‹    (default = 1)
    // โ€ข viewForItem    : ็นช่ฃฝ้ …็›ฎ่ฆ–ๅœ–็š„ๆ–นๆณ• (@ViewBuilder closure)
    let items: [Item]
    var cellAspectRatio: CGFloat = 1
    @ViewBuilder let viewForItem: (Item) -> ItemView
    
    // 2. ็•ถ MyGrid ้–‹ๅง‹ๆŽ’็‰ˆๆ™‚๏ผš
    public var body: some View {
        // โญ๏ธ 2.1 MyGrid ๆœƒๅˆฉ็”จ GeometryReader ๅพ—ๅˆฐ proposed size๏ผŒ
        GeometryReader { geo in 
            // โญ๏ธ 2.2 ็„ถๅพŒ็”จ `self.itemViews(in: size)` ็นช่ฃฝๆ‰€ๆœ‰้ …็›ฎ็š„่ฆ–ๅœ–ใ€‚
            self.itemViews(in: geo.size)
        }
    }
}

extension MyGrid {
    
    // 2.2
    @ViewBuilder func itemViews(in size: CGSize) -> some View {
        
        // โญ๏ธ 2.2.1: ๅง”่จ— MyGrid.Layout ่จˆ็ฎ—ใ€Œๆœ€ไฝณ็š„ๆŽ’็‰ˆๆ–นๅผใ€(layout)ใ€‚
        //
        //    layout ๆ“ๆœ‰ไปฅไธ‹่ณ‡่จŠ๏ผš
        //      โ€ข .rows, .cols:ใ€Œ่ฉฒๅˆ‡ๆˆๅนพ่กŒๅนพๅˆ—ใ€
        //      โ€ข .cellSize   :ใ€Œๆ ผๅญๆฏ”ไพ‹ใ€
        //      โ€ข .centerOfCell(at: i):ใ€Œ็ฌฌๅนพๅ€‹้ …็›ฎๆ‡‰่ฉฒๆŽ’ๅœจๅ“ชๅ€‹ไฝ็ฝฎใ€
        //
        // ๐Ÿ‘” MyGrid<Item, ItemView>.Layout
        let layout = Layout(      
            cellAspectRatio, count: items.count, in: size
        )
        
        // โญ๏ธ ForEach
        ForEach(items) { item in 
            // โญ๏ธ 2.2.2: ๅง”่จ— `self.view(for: item, in: layout)` ็นช่ฃฝๆฏๅ€‹้ …็›ฎ็š„่ฆ–ๅœ–ใ€‚
            self.view(for: item, in: layout)
        }
    }
    
    // 2.2.2
    @ViewBuilder func view(for  item: Item, in layout: Layout) -> some View 
    {
        // โญ๏ธ 2.2.2.1: ่จˆ็ฎ—้ …็›ฎ็ทจ่™Ÿ๏ผŒๅพ—็Ÿฅๅฎƒๆ˜ฏ็ฌฌๅนพๅ€‹้ …็›ฎใ€‚
        let i = index(of: item)
        
        // โญ๏ธ 2.2.2.2: ่จˆ็ฎ—้ …็›ฎ็š„ไธญๅฟƒไฝ็ฝฎ
        let center = layout.centerOfCell(at: i)  // ๐Ÿ‘” MyGrid<Item, ItemView>.Layout
        
        // โญ๏ธ 2.2.2.3: ็นช่ฃฝ้ …็›ฎ่ฆ–ๅœ–๏ผŒไธฆๆ”พๅœจ่ฉฒๆ”พ็š„ไฝ็ฝฎใ€‚
        viewForItem(item)
            .frame(layout.cellSize)   // โญ๏ธ cell size
            // -------------------------------------------
            // โš ๏ธ ๆณจๆ„๏ผš
            //    ไธ€ๆ—ฆๅŠ ไธŠ .position ไน‹ๅพŒ๏ผŒ.frame ๆœƒ่ฎŠๆˆๆ•ดๅ€‹
            //    GeometryReader ็š„ๅคงๅฐ๏ผŒไธๅ†ๆ˜ฏๅ–ฎไธ€ๅก็‰‡็š„ๅคงๅฐใ€‚
            // -------------------------------------------
            .position(center)         // โญ๏ธ put item view in position
    }
    
    /// 2.2.2.1: index of item
    func index(of item: Item) -> Int {
        items.index(of: item)!        // ๐ŸŒ€Array + .index(of:)
    }                                 //     where Element: Identifiable
}

Last updated