0

I want to add a new subview every time I swipe right, but then it will stack many times if I do not destroy the previous subview. I know how to remove it, but I am struggling with the logic of how to remove it only after I add a new one.

I have tried this:

    var view1: UIView?
    var view2: UIView?
    var ctrl = false

    override func viewDidLoad() {
        super.viewDidLoad()

        view1?.addSubview(CustomView(frame: self.view.bounds))
    }

    func addView(){
        if let test1 = view1, let test2 = view2 {
            if ctrl == true {
                test1.addSubview(CustomView(frame: self.view.bounds))
                test2.removeFromSuperview()
                ctrl = false
            }else{
                test2.addSubview(CustomView(frame: self.view.bounds))
                test1.removeFromSuperview()
                ctrl = true
            }   
        }
    }

    @IBAction func swipeRight(_ sender: Any) {
        print("Right")
        UIView.animate(withDuration: 1, animations: {
            self.view.layer.backgroundColor = .black
        }){(isFinished) in
            self.addView()
            //I am hoping that after this the view stack before will be removed
        }
    }

the Class CustomView is this:

var primaryColors: [UIColor] = [
    .red,
    .yellow,
    .blue]

class CustomView: UIView {

    override func draw(_ rect: CGRect) {
        super.draw(rect)
        primaryColors.shuffle()

        let leftRect = CGRect(x: 0, y: 0, width: rect.size.width/2, height: rect.size.height)
        primaryColors[0].set()
        guard let leftContext = UIGraphicsGetCurrentContext() else { return }
        leftContext.fill(leftRect)

        let rightRect = CGRect(x: rect.size.width/2, y: 0, width: rect.size.width/2, height: rect.size.height)
        primaryColors[1].set()
        guard let rightContext = UIGraphicsGetCurrentContext() else { return }
        rightContext.fill(rightRect)
    }
}
Mardydu
  • 45
  • 1
  • 7
  • I guess you should add the view in the second completion handler but remove the previous ones in the first completion handler. Give it a try. – Mihir Luthra May 26 '19 at 05:53
  • What did you mean by second completion handler and first completion handler? @Mihir – Mardydu May 26 '19 at 06:05
  • In `UIView.animate()`, you are using 2 closures or simply the first block and the second block. Also give [this](https://medium.com/@nimjea/completion-handler-in-swift-4-2-671f12d33178) article a read. Its about completion handlers. – Mihir Luthra May 26 '19 at 06:10
  • @Mihir I see what you are suggesting, but I don't know why it won't add then remove, it's just keep removing the view, I was hoping it will add a view first and then remove previous, but since the view does not change I take that it's just keep removing instead of adding it first. – Mardydu May 26 '19 at 06:33
  • In your code `view1?.addSubview(CustomView(frame: self.view.bounds))` this line adds a view to view1 and you have no reference to the view you are adding. You should first store the view you add like `view3 = CustomView(frame: self.view.bounds)` so that later you can remove view3. Also your `view1` actually never gets added If I am right because to add `view1` you need to add like `view.addSubview(view1)` first. – Mihir Luthra May 26 '19 at 06:36
  • Maybe if you provide some more detail about your issue, a better solution can be given. – Mihir Luthra May 26 '19 at 06:41
  • @Mihir I am so confuse right now.... and there i've provided some more details about the CustomView i've been using – Mardydu May 26 '19 at 06:50
  • I will make some changes to my answer, maybe then it may help. – Mihir Luthra May 26 '19 at 07:01
  • Check the answer now, it may help you understand. – Mihir Luthra May 26 '19 at 07:10

2 Answers2

1

Well I am not that good at swift, but till what I understand your problem, this should solve it. And if it doesn’t, I am pretty sure someone better in swift will answer it in a more better way.

Every ViewController has a base view which you can access like view.method().

You add a subview to an existing view.

Supposing you want to add a new UIView named view1.

First you initialise it like,

view1 = UIView(frame: view.bounds)

Now to make it appear on screen, do like,

view.addSubview(view1)

This line would add view1 as subview of view.

Also now view1 is on top of view. So now when you touch your screen it is the view1 you are accessing. So the next time you swipe right, the gesture recogniser should be on view1.

let swipeRight = UISwipeGestureRecognizer(target: self, action: #selector(swipeRightAction))
        swipeRight.direction = UISwipeGestureRecognizer.Direction.right

view1.addGestureRecognizer(swipeRight)

This is how you will add gesture of right swipe to view1.

Now when you right swipe on screen, swipeRightAction() will get called.

You can remove this view1 from super view like,

view1.removeFromSuperView()

Now view1 is gone and now you can add view2 by similar process as we added view1.

See the following code, it may be relatable.

//The old view that has to be replaced
    var viewToBeRemoved : UIView!

    //The new view that will replace the previous one
    var viewToBeAdded   : UIView!

    override func viewDidLoad() {
        super.viewDidLoad()

        replaceWithNewSwipableSubview()


    }


    func replaceWithNewSwipableSubview()
    {
        viewToBeAdded = UIView(frame: view.bounds)

        //New view being added should have a gesture so that next time
        //it need to be replaced we can swipe on it
        let swipeRight = UISwipeGestureRecognizer(target: self, action: #selector(swipeRightAction))
        swipeRight.direction = UISwipeGestureRecognizer.Direction.right

        viewToBeAdded.addGestureRecognizer(swipeRight)

        UIView.animate(withDuration: 1, animations: {

            //This will be animated

            if self.viewToBeRemoved != nil{

                //As there is no subview the very first time this func is called

                self.viewToBeRemoved.removeFromSuperview()

            }

            //Because next time it will be the view to be removed
            self.viewToBeRemoved = self.viewToBeAdded


        }) { (isFinished) in

            //This block gets executed only after
            //the block above has completed
            self.view.addSubview(self.viewToBeAdded)


            //view is the default base view of a ViewController

        }
    }



    @objc func swipeRightAction() {

        replaceWithNewSwipableSubview()

    }

In your code view1?.addSubview(CustomView(frame: self.view.bounds)) this line adds a view to view1 and you have no reference to the view you are adding. You should first store the view you add like view3 = CustomView(frame: self.view.bounds) so that later you can remove view3. Also your view1 actually never gets added If I am right because to add view1 you need to add like view.addSubview(view1) first.

Although possibly a better solution can be suggested if you tell us what you actually want to achieve?

Mihir Luthra
  • 6,059
  • 3
  • 14
  • 39
  • with a view adjustment of the code above, it actually works. but now another problem appear, I cannot animate the background color – Mardydu May 26 '19 at 08:20
  • [Animate UIView background color swift](https://stackoverflow.com/questions/34383678/animate-uiview-background-color-swift) and [UIView.animate syntax for swift 4](https://stackoverflow.com/questions/30991822/whats-the-swift-3-animatewithduration-syntax). These shall help you with coloraturas animations. I can only help you if you exactly tell what you want to make by doing this? Do you want to swap UIViews getting a different colour each time? – Mihir Luthra May 26 '19 at 09:52
0

It might not working. In your if condition of addSubview method you are using: if let test1 = view1, let test2 = view2 which check that both view1 and view2 are not nil. This must be failing because you only initialized view1 in viewDidLoad(): view1?.addSubview(CustomView(frame: self.view.bounds)). So in above if condition view2 must be nil and hence it will evaluate to false.

Try placing your if condition like this: if let test1 = view1 || let test2 = view2 (check for syntax if it gives error, ll will give error in if condition with let). And let me know if it doesn't work. Try below code:

if((self.view1 != nil) || (self.view2 != nil){
    if ctrl == true {
        self.view1?.addSubview(CustomView(frame: self.view.bounds))
        self.view2?.removeFromSuperview()
        ctrl = false
    }else{
        self.view2?.addSubview(CustomView(frame: self.view.bounds))
        self.view1?.removeFromSuperview()
        ctrl = true
    }   
 }