简体   繁体   中英

Recursively loop through array of arrays using functional

I want to loop through my view subviews, and for each subview loop through its subviews and etc.

so let say I have the following code:

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

then repeat this same for each subview. I want to do it functionally.

any insight is much appreciated.

EDIT:

to make it clear

I'm looking for something like this:

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

but it should be recursive it goes to each view subview.

Something like this?

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

called via:

let view = myVC.view
makeAllSubviewsClear(view)

Use a queue instead of recursing.

I think you could do something like this...

extension UIView {    
    func changeColor(color: UIColor) {
        var queue = [self]

        while let view = queue.first() {
            queue += view.subviews

            view.backgroundColor = color

            queue.remove(view)
        }
    }
}

I'd do it with a queue instead of doing it recursively. That method isn't functional though.

Doing what your question wanted

You could do this way, which is what you put in your question as what you wanted to do.

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)
        }
    }
}

Then run it like...

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

I guess that's a bit more functional... ish.

How about follwing:

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

Although map should be used differently imho.

EDIT: I've just looked that the OP wanted recursive loop which is different thing. You should move this code into function and call it recursively for each $0.

Something like this perhaps:

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

Looks functional and can be called like this:

myVC.view.subviews.changeColor(color)

Inspired by @fogmaister one more solution:

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

Here is a possible generic solution which allows to traverse any "nested sequence". It is not restricted to UIView and subviews , but takes a parameter which maps each sequence element to its immediate children.

It also separates the task of traversing the sequence from the action on the elements (like setting a background color), by returning a Generator which can be used with existing methods like forEach() .

(This is inspired by some answers to 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
            }
    }
}

As an example, if view is a UIView then

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

returns a (lazy) generator of all (nested) subviews. By calling forEach() on this generator, we can modify each subview:

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

But it could also be used to get an array with all nested subviews:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM