【问题标题】:Swift - Getting the position of an Item in an array of arrays represented as an Int instead of item itselfSwift - 在表示为 Int 而不是项目本身的数组数组中获取项目的位置
【发布时间】:2021-06-21 18:33:45
【问题描述】:

我有一个按流派划分的电台列表,并将它们放置在一个数组中,如下所示:[[流派]、[流派]、[流派]]。我使用站[indexPath.section[indexPath.row] 进行索引,这将访问单个站。然后在 didSelectItemAt 中,我可以将此站传递给呈现的媒体视图控制器并为用户播放。但是,我想实现下一个按钮和上一个按钮,以便用户可以在媒体 VC 本身内切换站点,而无需返回到集合视图。我似乎无法找到一种方法将站点数组中站点的位置表示为 Int,然后我可以将其用作下一个/上一个按钮中的逻辑,正如我在下面尝试的那样。为简单起见,我尝试使用 flatMap 将数组展平,这样更好,因此我可以仅使用站 [indexPath.row] 进行索引,但这仍然不能解决我的问题。我尝试使用 .enumerated() 但我无法弄清楚如何成功使用它提供的索引,因为我需要将它返回的元组附加到数组中。有人对如何实现这一点有任何想法吗?我想这可能是不可能的..

import AVKit
import UIKit

private let reuseIdentifier = "Cell"

class MediaCollectionVC: UICollectionViewController {

var stations = [[Station]]()

let mediaVC = MediaPlayerVC()

override func viewDidLoad() {
    super.viewDidLoad()
    fetchRadioStation()

    collectionView.register(MediaCollectionViewCell.self, forCellWithReuseIdentifier: MediaCollectionViewCell.identifier)
    collectionView.register(MediaCollectionSectionReusableView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: MediaCollectionSectionReusableView.identifier)

    
}

override func numberOfSections(in collectionView: UICollectionView) -> Int { stations.count  }

override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return stations[section].count }

override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
    
    if kind == UICollectionView.elementKindSectionHeader {
        let sectionHeader = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: MediaCollectionSectionReusableView.identifier, for: indexPath) as! MediaCollectionSectionReusableView
        
        switch indexPath.section {
        case 0:
            sectionHeader.label.text = "TV"
            return sectionHeader
        case 1:
            sectionHeader.label.text = "News Radio"
            return sectionHeader
        case 2:
            sectionHeader.label.text = "Entertainment Radio"
            return sectionHeader
        case 3:
            sectionHeader.label.text = "Religious Radio"
            return sectionHeader
        default:
            sectionHeader.label.text = "Section Header Issue"
            return sectionHeader
        }
    } else {
        print("section header issue")
        return UICollectionReusableView()
    }
}

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MediaCollectionViewCell.identifier, for: indexPath) as! MediaCollectionViewCell
    
    let station = stations[indexPath.section][indexPath.row]
    cell.titleLabel.text = station.name
    cell.imageView.image = stationImages[station.name]
    
    return cell
}

override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {

    // Its here where I'd like to get the position of the station and pass it to the mediaVC

    let station = stations[indexPath.section][indexPath.row]
    mediaVC.station = station
    present(mediaVC, animated: true)
    
}

func parseJSON(data: Data) {
    let decoder = JSONDecoder()

    do {
        let decodedData = try decoder.decode(StationData.self, from: data)
        let newsData = decodedData.stations
        
        DispatchQueue.main.async {
            self.stations.append(newsData.filter { $0.category == "News" && $0.medium == "TV"})
            self.stations.append(newsData.filter { $0.medium == "Radio" && $0.category == "News"})
            self.stations.append(newsData.filter { $0.medium == "Radio" && $0.category == "Entertainment"})
            self.stations.append(newsData.filter { $0.medium == "Radio" && $0.category == "Religious"})
            self.collectionView.reloadData()
        }

        print("all stations loaded successfully")
    } catch {
        print("Error decoding: \(error)")
    }
}
}

// MediaVC 

import AVFoundation
import UIKit

class MediaPlayerVC: UIViewController {

public var position: Int = 0
public var stations = [[Station]]()

public var station: Station?
var player: AVPlayer?
var isPlaying: Bool = true

override func viewDidLoad() {
    super.viewDidLoad()
    updateUI()
    configure()

}


func configure() {
    
    guard let station = station else { return }
    
    let urlString = station.streamURL

    do {
        try AVAudioSession.sharedInstance().setCategory(.playback)
        try AVAudioSession.sharedInstance().setActive(true, options: .notifyOthersOnDeactivation)

        guard let url = URL(string: urlString) else {
            print("url issue")
            return
        }

        player = AVPlayer(url: url)

        guard let player = player else {
            print("player issue")
            return
        }
        player.volume = 0.5

        player.play()

    } catch {
        print("error: \(error)")
    }
  
    nextButton.addTarget(self, action: #selector(nextButtonTapped(_:)), for: .touchUpInside)
    backButton.addTarget(self, action: #selector(backButtonTapped(_:)), for: .touchUpInside)

}

// This is where I need to implement "position" somehow
@objc func nextButtonTapped(_ button: UIButton) {
    if position < stations.count - 1 {
        position = position + 1
        player?.pause()
        for subview in view.subviews {
            subview.removeFromSuperview()
        }
    }

}

@objc func backButtonTapped(_ button: UIButton) {
    if position > 0 {
        position = position - 1
        player?.pause()
        for subview in view.subviews {
            subview.removeFromSuperview()
        }
    }

}

}

【问题讨论】:

  • "这里是我想获取车站位置的地方" ???你似乎成功地得到了车站,对吧?而且您非常清楚它在数组中的位置;它位于[indexPath.section][indexPath.row]。那么究竟是什么问题呢?
  • @matt 如果我不想要下一个/上一个按钮,那么我可以关闭 mediaVC 并点击另一个电台,让 mediaVC 重新出现并播放该电台,这就是我认为的描述。但是,我想在 mediaVC 中包含一个下一个/上一个按钮,以避免不得不离开 mediaVC 来更换电台。为此,我必须将数组和当前(最初)播放站位置传递给 mediaVC,以便访问下一个/上一个按钮。当我这样做时,一旦我出于某种原因从不同的 indexPath.section 访问站点,索引就会中断。知道为什么吗?
  • 为什么不使用IndexPath
  • @Paulw11 再次感谢您解决它。我将 IndexPath 传递给 mediaVC,并且能够使用 position.section 和 position.row 进行索引。真的很感激!

标签: ios arrays swift uicollectionview uikit


【解决方案1】:

我认为您不一定需要Int。您只需要一个数组数组的单值索引,您可以或多或少地将其视为Int,这意味着您自己滚动。

我认为这将为您提供所需的:

extension Array where Element: RandomAccessCollection, Element.Index == Int
{
    struct DoubleIndex: Comparable, Strideable
    {
        let collection: [Element]
        let outerIndex: Int
        let innerIndex: Int
        
        public func advanced(by n: Int) -> DoubleIndex
        {
            precondition(outerIndex < collection.endIndex)
            precondition(innerIndex < collection[outerIndex].endIndex)
            
            if n == 0 { return self }
            if n > 0
            {
                if innerIndex + n >= collection[outerIndex].endIndex
                {
                    return DoubleIndex(
                        collection: collection,
                        outerIndex: outerIndex + 1,
                        innerIndex: collection[outerIndex + 1].startIndex
                    ).advanced(by: n - (collection[outerIndex].endIndex - innerIndex))
                }
                
                return DoubleIndex(
                    collection: collection,
                    outerIndex: outerIndex,
                    innerIndex: innerIndex + n
                )
            }
            
            if innerIndex + n < collection[outerIndex].startIndex
            {
                return DoubleIndex(
                    collection: collection,
                    outerIndex: outerIndex - 1,
                    innerIndex: collection[outerIndex - 1].endIndex - 1
                ).advanced(by: n + (innerIndex - collection[outerIndex].startIndex))
            }
            
            return DoubleIndex(
                collection: collection,
                outerIndex: outerIndex,
                innerIndex: innerIndex + n
            )
        }
        
        // Comparable conformance
        static func == (lhs: Self, rhs: Self) -> Bool
        {
            return lhs.outerIndex == rhs.outerIndex
                && lhs.innerIndex == lhs.innerIndex
        }
        
        static func < (lhs: Self, rhs: Self) -> Bool
        {
            return lhs.outerIndex < rhs.outerIndex
                || (lhs.outerIndex == rhs.outerIndex && lhs.innerIndex < rhs.innerIndex)
        }
        
        // Strideable Conformance
        func distance(to other: Array<Element>.DoubleIndex) -> Int
        {
            if other < self { return -other.distance(to: self) }
            
            if outerIndex == other.outerIndex {
                return other.innerIndex - innerIndex
            }
            
            return collection[outerIndex].endIndex - innerIndex
                + DoubleIndex(
                    collection: collection,
                    outerIndex: outerIndex + 1,
                    innerIndex: collection[outerIndex].startIndex
                ).distance(to: other)
        }
        
    }
    
    var doubleStartIndex: DoubleIndex
    {
        DoubleIndex(
            collection: self,
            outerIndex: startIndex,
            innerIndex: self[startIndex].startIndex
        )
    }
    
    var doubleEndIndex: DoubleIndex {
        DoubleIndex(collection: self, outerIndex: endIndex, innerIndex: 0)
    }
    
    var doubleIndices: Range<DoubleIndex> { doubleStartIndex..<doubleEndIndex }
    
    subscript(index: DoubleIndex) -> Element.Element {
        self[index.outerIndex][index.innerIndex]
    }
}

extension Array
    where Element: MutableCollection,
          Element: RandomAccessCollection,
          Element.Index == Int
{
    subscript(index: DoubleIndex) -> Element.Element
    {
        get { self[index.outerIndex][index.innerIndex] }
        set { self[index.outerIndex][index.innerIndex] = newValue }
    }
}

所以你可以像这样遍历整个数组:

for i in stations.doubleIndices {
   doSomethingWithAStation(stations[i]
} 

一般情况下,您可以将索引推进

doubleIndex = doubleIndex.advanced(by: 1)

请记住,如果您改变外部或内部数组,DoubleIndex 的此实现将继续引用创建时的数组。出于这个原因,您可能不应该包含符合MutableCollection 的第二个扩展名。只要您进行一对一替换,就可以使用 DoubleIndex 来改变集合,因此数组尺寸不会改变,但它会触发写时复制,因为索引有数组,以便它可以找出它在哪里。 appendremove 肯定会使 DoubleIndex 失效。

【讨论】:

  • 嗨芯片,感谢您考虑这个问题。另一位用户建议我使用 IndexPath,这似乎是一个更简单的实现,但我真的很感谢你花时间写下来。
【解决方案2】:

在我看来,您想要增加或减少描述数组数组的索引路径的东西。只要数组都不是空的,就可以了(抱歉,我没有费心去检查):

extension IndexPath {
    func inc<T>(against arr:[[T]]) -> IndexPath? {
        let ip = self
        let sub = arr[ip.section]
        if ip.section + 1 == arr.count && ip.item == arr[ip.section].count {
            return nil
        }
        var item = ip.item + 1
        if ip.item == arr[ip.section].count {
            return IndexPath(item: 0, section: ip.section + 1)
        }
        return IndexPath(item: ip.item + 1, section: ip.section)
    }
    func dec<T>(against arr:[[T]]) -> IndexPath? {
        let ip = self
        let sub = arr[ip.section]
        if ip.section == 0 && ip.item == 0 {
            return nil
        }
        var item = ip.item - 1
        if ip.item == 0 {
            return IndexPath(item: arr[ip.section-1].count, section: ip.section - 1)
        }
        return IndexPath(item: ip.item - 1, section: ip.section)
    }
}

这是一个测试;在操场上试一试:

let ip = IndexPath(item: 2, section: 1)
let arr = [[1,2],[3,4,5],[6,7,8,9,10]]

var result = ip.dec(against: arr)
result = result!.dec(against: arr)
result = result!.dec(against: arr)
result = result!.dec(against: arr)
result = result!.dec(against: arr)
result = result!.dec(against: arr)

result = ip.inc(against: arr)
result = result!.inc(against: arr)
result = result!.inc(against: arr)
result = result!.inc(against: arr)
result = result!.inc(against: arr)
result = result!.inc(against: arr)
result = result!.inc(against: arr)
result = result!.inc(against: arr)

【讨论】:

  • 嗨,马特,感谢您考虑这个问题。我需要练习泛型,所以我一定会试一试。另一位用户建议我使用 IndexPath,这似乎是一个更简单的实现。
  • am 向您展示如何使用 IndexPath。泛型无关紧要(甚至不确定我对那部分的想法)。
猜你喜欢
  • 1970-01-01
  • 2019-02-25
  • 2019-07-06
  • 1970-01-01
  • 2017-11-10
  • 2012-02-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多