Как и когда вызывать метод setNeedsLayout (), чтобы расположить подпредставления в суперпредставлении? - PullRequest
0 голосов
/ 24 июня 2019

Я учусь в Стэнфорде под названием CS193p.В этом назначении они предоставляют объект Grid для разделения прямоугольника на сетку ячеек.Я создал два пользовательских вида, называемых GridView и SetView, где первый - это суперпредставление, а второй - это подпредставление.Я пытаюсь добавить 12 SetViews как подпредставления в GridView, но мой код, приведенный ниже, не дает никаких результатов.

Для вызова метода layoutSubviews () разве метод setNeedsLayout () не нужно вызывать?LayoutSubviews () помогает в позиционировании подпредставлений, насколько я понимаю, поэтому после извлечения прямоугольника или ячейки из объекта сетки я устанавливаю frame.origin каждого подпредставления в начало этого прямоугольника.Я не получаю никакого результата.

import UIKit
class GridView: UIView {

lazy var grid = Grid (макет: Grid.Layout.fixedCellSize (CGSize (ширина: 100.0, высота: 120.0)), фрейм: CGRect (источник: CGPoint (x: bounds.minX, y: bounds.minY), размер: CGSize (ширина: bounds.width, высота: bounds.height)))

private var cardsFlag = false

lazy var listOfSetCard = createSetCards()
lazy var cardsOnScreen = countTheNumberOfCards()

var trackOfCards: Int {
    get {
        return cardsOnScreen
    }
    set(newValue) {
        self.trackOfCards = newValue
    }
}

private func createSetCards() -> [SetView] {
    var cards = [SetView]()
    for _ in 0..<12 {
        let card = SetView()
        addSubview(card)
        cards.append(card)
    }
    print("I am here")
    return cards
}

private func countTheNumberOfCards() -> Int {

    if !cardsFlag && !listOfSetCard.isEmpty {
        cardsOnScreen = 12
        setNeedsLayout()
        cardsFlag = true
    }
    print("But I come here first")
    return cardsOnScreen

}

override func layoutSubviews() {
    for index in listOfSetCard.indices {
        let card = listOfSetCard[index]
        if let rect = grid[index] {
            card.frame.origin = rect.origin
            print(card.frame.origin)
        }
    }
}

} / * Объект сетки * / import UIKit

struct Grid {enum Layout {case fixedCellSize (CGSize) case измерения (rowCount: Int, columnCount: Int) case aspectRatio (CGFloat)}

var layout: Layout { didSet { recalculate() } }

var frame: CGRect { didSet { recalculate() } }

init(layout: Layout, frame: CGRect = CGRect.zero) {
    self.frame = frame
    self.layout = layout
    recalculate()
}

subscript(row: Int, column: Int) -> CGRect? {
    return self[row * dimensions.columnCount + column]
}

subscript(index: Int) -> CGRect? {
    return index < cellFrames.count ? cellFrames[index] : nil
}

var cellCount: Int {
    get {
        switch layout {
        case .aspectRatio: return cellCountForAspectRatioLayout
        case .fixedCellSize, .dimensions: return calculatedDimensions.rowCount * calculatedDimensions.columnCount
        }
    }
    set { cellCountForAspectRatioLayout = newValue }
}

var cellSize: CGSize {
    get { return cellFrames.first?.size ?? CGSize.zero }
    set { layout = .fixedCellSize(newValue) }
}

var dimensions: (rowCount: Int, columnCount: Int) {
    get { return calculatedDimensions }
    set { layout = .dimensions(rowCount: newValue.rowCount, columnCount: newValue.columnCount) }
}

var aspectRatio: CGFloat {
    get {
        switch layout {
        case .aspectRatio(let aspectRatio):
            return aspectRatio
        case .fixedCellSize(let size):
            return size.height > 0 ? size.width / size.height : 0.0
        case .dimensions(let rowCount, let columnCount):
            if rowCount > 0 && columnCount > 0 && frame.size.width > 0 {
                return (frame.size.width / CGFloat(columnCount)) / (frame.size.width / CGFloat(rowCount))
            } else {
                return 0.0
            }
        }
    }
    set { layout = .aspectRatio(newValue) }
}

private var cellFrames = [CGRect]()
private var cellCountForAspectRatioLayout = 0 { didSet { recalculate() } }
private var calculatedDimensions: (rowCount: Int, columnCount: Int) = (0, 0)

private mutating func recalculate() {
    switch layout {
    case .fixedCellSize(let cellSize):
        if cellSize.width > 0 && cellSize.height > 0 {
            calculatedDimensions.rowCount = Int(frame.size.height / cellSize.height)
            calculatedDimensions.columnCount = Int(frame.size.width / cellSize.width)
            updateCellFrames(to: cellSize)
        } else {
            assert(false, "Grid: for fixedCellSize layout, cellSize height and width must be positive numbers")
            calculatedDimensions = (0, 0)
            cellFrames.removeAll()
        }
    case .dimensions(let rowCount, let columnCount):
        if columnCount > 0 && rowCount > 0 {
            calculatedDimensions.rowCount = rowCount
            calculatedDimensions.columnCount = columnCount
            let cellSize = CGSize(width: frame.size.width/CGFloat(columnCount), height: frame.size.height/CGFloat(rowCount))
            updateCellFrames(to: cellSize)
        } else {
            assert(false, "Grid: for dimensions layout, rowCount and columnCount must be positive numbers")
            calculatedDimensions = (0, 0)
            cellFrames.removeAll()
        }
    case .aspectRatio:
        assert(aspectRatio > 0, "Grid: for aspectRatio layout, aspectRatio must be a positive number")
        let cellSize = largestCellSizeThatFitsAspectRatio()
        if cellSize.area > 0 {
            calculatedDimensions.columnCount = Int(frame.size.width / cellSize.width)
            calculatedDimensions.rowCount = (cellCount + calculatedDimensions.columnCount - 1) / calculatedDimensions.columnCount
        } else {
            calculatedDimensions = (0, 0)
        }
        updateCellFrames(to: cellSize)
        break
    }
}

private mutating func updateCellFrames(to cellSize: CGSize) {
    cellFrames.removeAll()

    let boundingSize = CGSize(
        width: CGFloat(dimensions.columnCount) * cellSize.width,
        height: CGFloat(dimensions.rowCount) * cellSize.height
    )
    let offset = (
        dx: (frame.size.width - boundingSize.width) / 2,
        dy: (frame.size.height - boundingSize.height) / 2
    )
    var origin = frame.origin
    origin.x += offset.dx
    origin.y += offset.dy

    if cellCount > 0 {
        for _ in 0..<cellCount {
            cellFrames.append(CGRect(origin: origin, size: cellSize))
            origin.x += cellSize.width
            if round(origin.x) > round(frame.maxX - cellSize.width) {
                origin.x = frame.origin.x + offset.dx
                origin.y += cellSize.height
            }
        }
    }
}

private func largestCellSizeThatFitsAspectRatio() -> CGSize {
    var largestSoFar = CGSize.zero
    if cellCount > 0 && aspectRatio > 0 {
        for rowCount in 1...cellCount {
            largestSoFar = cellSizeAssuming(rowCount: rowCount, minimumAllowedSize: largestSoFar)
        }
        for columnCount in 1...cellCount {
            largestSoFar = cellSizeAssuming(columnCount: columnCount, minimumAllowedSize: largestSoFar)
        }
    }
    return largestSoFar
}

private func cellSizeAssuming(rowCount: Int? = nil, columnCount: Int? = nil, minimumAllowedSize: CGSize = CGSize.zero) -> CGSize {
    var size = CGSize.zero
    if let columnCount = columnCount {
        size.width = frame.size.width / CGFloat(columnCount)
        size.height = size.width / aspectRatio
    } else if let rowCount = rowCount {
        size.height = frame.size.height / CGFloat(rowCount)
        size.width = size.height * aspectRatio
    }
    if size.area > minimumAllowedSize.area {
        if Int(frame.size.height / size.height) * Int(frame.size.width / size.width) >= cellCount {
            return size
        }
    }
    return minimumAllowedSize
}

}

частное расширение CGSize {var area: CGFloat {возвращаемая ширина * высота}}

...