【问题标题】:Recursively loop through array of arrays using functional使用函数递归遍历数组数组
【发布时间】:2016-06-04 02:39:54
【问题描述】:

我想遍历我的视图子视图,并为每个子视图循环其子视图等。

假设我有以下代码:

let view = myVC.view
view.backgroundColor = UIColor.clearColor()

然后对每个子视图重复此操作。我想在功能上做到这一点。

非常感谢任何见解。

编辑:

说清楚

我正在寻找这样的东西:

view.subviews.chanageColor() { (aView, aColor) in
aView.backgroundColor = aColor
}

但它应该是递归的,它会转到每个视图子视图。

【问题讨论】:

  • 这样做没有意义。将冷却器传递给递归函数会更有意义。
  • 从“功能上”执行此操作有什么好处?
  • @Fogmeister 也许什么都没有。我只是想“功能性地”做,因为我可以递归地做,而且我在功能上很弱。
  • 好的,看看我的回答。它可能会做你想做的事?

标签: ios swift


【解决方案1】:

这是一个可能的通用解决方案,它允许 遍历任何“嵌套序列”。不限于UIViewsubviews,但采用一个参数,将每个序列元素映射到其直接子元素。

它还将遍历序列的任务与 action 对元素(例如设置背景颜色),通过返回 Generator 可以与forEach()等现有方法一起使用。

(这受到Implementing recursive generator for simple tree structure in Swift 的一些回答的启发。)

extension SequenceType {

    public func generate<S : SequenceType where S.Generator.Element == Generator.Element>
        (children children: Generator.Element -> S) -> AnyGenerator<Generator.Element> {

            var selfGenerator = self.generate()
            var childGenerator : AnyGenerator<Generator.Element>?

            return anyGenerator {
                // Enumerate all children of the current element:
                if childGenerator != nil {
                    if let next = childGenerator!.next() {
                        return next
                    }
                }

                // Get next element of self, and prepare subGenerator to enumerate its children:
                if let next = selfGenerator.next() {
                    childGenerator = children(next).generate(children: children)
                    return next
                }

                return nil
            }
    }
}

例如,如果viewUIView 那么

view.subviews.generate(children: { $0.subviews })

返回所有(嵌套)子视图的(惰性)生成器。通过在这个生成器上调用forEach(),我们可以修改每个子视图:

view.subviews.generate(children: { $0.subviews }).forEach {
    $0.backgroundColor = UIColor.clearColor()
}

但它也可以用于获取包含所有嵌套子视图的数组:

let allViews = Array(view.subviews.generate(children: { $0.subviews }))

【讨论】:

    【解决方案2】:

    使用队列而不是递归。

    我认为你可以做这样的事情......

    extension UIView {    
        func changeColor(color: UIColor) {
            var queue = [self]
    
            while let view = queue.first() {
                queue += view.subviews
    
                view.backgroundColor = color
    
                queue.remove(view)
            }
        }
    }
    

    我会用队列来做,而不是递归地做。但是那个方法不起作用。

    做你想做的事

    您可以这样做,这就是您在问题中提出的您想做的事情。

    extension UIView {
        // this name isn't very good but you get the idea
        func applyChangeToAllSubviews(block: (UIView) -> ()) {
            var queue = [self]
    
            // I don't have my dev computer dos not sure of the syntax
            while let view = queue.first() {
                queue += view.subviews
    
                block(view)
    
                queue.remove(view)
            }
        }
    }
    

    然后像...一样运行它

    someView.applyChangeToAllSubviews {
        view in
        view.backgroundColor = UIColor.whiteColor()
    }
    

    我想这有点功能性...... ish。

    【讨论】:

    • 我已将您的解决方案用作递归函数式快速回答的灵感:) 虽然它更像是一个编辑,但是我使用了数组扩展。
    【解决方案3】:

    接下来怎么样:

    let _ = myVC.view.subviews.map{ $0.backgroundColor = UIColor.clearColor() }
    

    虽然地图应该以不同的方式使用恕我直言。

    编辑: 我刚刚看到 OP 想要递归循环,这是不同的事情。您应该将此代码移动到函数中,并为每个 $0 递归调用它。

    可能是这样的:

    extension Array where Element: UIView {
        func changeColor(color: UIColor) {
            for view in self {
                view.subviews.changeColor(color)
                view.backgroundColor = color
            }
        }
    }
    

    看起来很实用,可以这样调用:

    myVC.view.subviews.changeColor(color)
    

    受@fogmaister 启发的另一种解决方案:

    extension Array where Element: UIView {
        func applyChange(closure: (UIView)->()) {
            for view in self {
                view.subviews.applyChange(closure)
                closure(view)
            }
        }
    }
    

    【讨论】:

    • 酷,我也在考虑这样做。 :D
    【解决方案4】:

    这样的?

    func makeAllSubviewsClear(view: UIView!)
    {
        view.backgroundColor = UIColor.clearColor()
        for eachSubview in view.subviews
        {
            makeAllSubviewsClear(eachSubview)
        }
    }
    

    调用方式:

    let view = myVC.view
    makeAllSubviewsClear(view)
    

    【讨论】:

    • 感谢您的回答,但这是递归的。它不起作用
    • 单行调用递归方法有什么不好的地方?也许这个问题有点令人困惑。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-08
    • 1970-01-01
    • 2020-06-02
    • 2012-07-17
    相关资源
    最近更新 更多