3
\$\begingroup\$

I have create a UISegmentedControl and a view under it. The view under contains all a view for each segment. I am doing an animation when the segment is changing.

  1. Is a center view that contains all subviews the right approach?
  2. Is the animation is correctly done (this is my first animation in swift) or is there a better way to achieve that effect?

import UIKit;

class ViewLikes:UIViewController {

    var innerViews:[UIViewController] = [UIViewController]()
    var currentIndex:Int = 0;
var centerView:UIViewController = UIViewController()

override func viewDidLoad()  {
    super.viewDidLoad()
    title = "some title";

    var view1:UIViewController = UIViewController()
    var lab1:UILabel = UILabel(frame: CGRectMake(0, 10, 200, 21))
    lab1.center = CGPointMake(160, 284)
    lab1.textAlignment = NSTextAlignment.Center
    lab1.text = "I'am a label1"
    view1.view.addSubview(lab1)

    var view2:UIViewController = UIViewController()
    var lab2:UILabel = UILabel(frame: CGRectMake(0, 10, 200, 21))
    lab2.center = CGPointMake(160, 284)
    lab2.textAlignment = NSTextAlignment.Center
    lab2.text = "I'am a label2"
    view2.view.addSubview(lab2)

    var view3:UIViewController = UIViewController()
    var lab3:UILabel = UILabel(frame: CGRectMake(0, 10, 200, 21))
    lab3.center = CGPointMake(160, 284)
    lab3.textAlignment = NSTextAlignment.Center
    lab3.text = "I'am a label3"
    view3.view.addSubview(lab3)

    innerViews.append(view1)
    innerViews.append(view2)
    innerViews.append(view3)

    var segmentControl:UISegmentedControl = UISegmentedControl(items:["blah", "blah1", "blah2"]);
    segmentControl.selectedSegmentIndex = currentIndex;
    segmentControl.addTarget(self, action:"segmentSwitch:" , forControlEvents:UIControlEvents.ValueChanged  )

    segmentControl.setTranslatesAutoresizingMaskIntoConstraints(false)
    self.centerView.view.setTranslatesAutoresizingMaskIntoConstraints(false)

    self.view.addSubview(segmentControl)
    self.view.addSubview(self.centerView.view)

    var currentView = self.innerViews[0]
    currentView.viewWillAppear(false)
    self.centerView.view.addSubview(currentView.view)
    currentView.viewDidAppear(false)


    //Set layout
    var viewsDict = Dictionary <String, UIView>()
    viewsDict["segment"] = segmentControl;
    viewsDict["center"] = self.centerView.view;

    self.view.addConstraint(NSLayoutConstraint(item: segmentControl,
                                attribute: NSLayoutAttribute.Top,
                                relatedBy: NSLayoutRelation.Equal,
                                toItem: self.topLayoutGuide,
                                attribute: NSLayoutAttribute.Bottom,
                                multiplier: 1, constant: 10))


    self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[segment]-|",
                                options: NSLayoutFormatOptions(0),
                                metrics: nil,
                                views: viewsDict))


    self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-0-[center]-0-|",
        options: NSLayoutFormatOptions(0),
        metrics: nil,
        views: viewsDict))

    self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:[segment]-[center]-0-|",
        options: NSLayoutFormatOptions(0),
        metrics: nil,
        views: viewsDict))

}

func segmentSwitch(control:UISegmentedControl) {
    selectView(control.selectedSegmentIndex)
}

func selectView(index:Int) {
    var currentView = self.innerViews[self.currentIndex]

    var nextView = self.innerViews[index]
    nextView.viewWillAppear(false)
    self.centerView.view.addSubview(nextView.view)
    nextView.viewDidAppear(false)

    if(index > currentIndex) {
        nextView.view.frame.origin.x = nextView.view.frame.width;
    } else {
        nextView.view.frame.origin.x -= nextView.view.frame.width;
    }

    UIView.animateWithDuration(0.8,
        animations: {
            var currentFrame = currentView.view.frame;
            var nextFrame = nextView.view.frame;

            if(index > self.currentIndex) {
                currentFrame.origin.x -= currentFrame.size.width;
            } else {
                currentFrame.origin.x = currentFrame.size.width;
            }
            nextFrame.origin.x = 0.0;

            currentView.view.frame = currentFrame
            nextView.view.frame = nextFrame
        }, completion: {finished in
            currentView.viewWillDisappear(false)
            currentView.view.removeFromSuperview()
            currentView.viewDidDisappear(false)
        }
    );



    self.currentIndex = index

}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
    for innerView in self.innerViews {
        innerView.didReceiveMemoryWarning();
    }
}
}
\$\endgroup\$
5
  • \$\begingroup\$ "Is the animation is correctly done" -- does it animate correctly? \$\endgroup\$
    – nhgrif
    Aug 25 '14 at 11:22
  • \$\begingroup\$ yes, its working as expected, the views are moving from left to right, or right to left, but is it the way this animation should be implemented ? \$\endgroup\$
    – shay te
    Aug 25 '14 at 11:30
  • \$\begingroup\$ Okay, I just wanted clarification that it was actually working as you expected. \$\endgroup\$
    – nhgrif
    Aug 25 '14 at 11:31
  • \$\begingroup\$ again, i am asking this cause this is my first animation , want to know that i am programming correctly in swift, and not hacking things . \$\endgroup\$
    – shay te
    Aug 25 '14 at 11:35
  • 1
    \$\begingroup\$ I understand. We just always make sure the results are what you intend. I'm working on an answer at the moment. \$\endgroup\$
    – nhgrif
    Aug 25 '14 at 11:37
2
\$\begingroup\$
func segmentSwitch(control:UISegmentedControl) {
    selectView(control.selectedSegmentIndex)
}

Is it strictly necessary for this to be a separate method? Can we not just do this:

func selectView(control:UISegmentedControl) {
    let index = control.selectedSegmentIndex
    // do stuff
}

The only reason I can think not to do this would be if you've got other methods calling selectView or if you intend to more than just selectView within the segmentSwitch method, but as far as I can tell, neither of these are the case.


You have this:

nextView.viewWillAppear(false)
self.centerView.view.addSubview(nextView.view)
nextView.viewDidAppear(false)

Followed later by this in a completion block:

currentView.viewWillDisappear(false)
currentView.view.removeFromSuperview()
currentView.viewDidDisappear(false)

My first problem is that despite animating this change, we're sending false as the animate argument to all of the willAppear-esque methods of these view controllers.

My other problem is the order in which these methods are called.

The viewWillAppear and viewWillDisappear methods for both view controllers should be called just before the animation block. Meanwhile, the viewDidAppear and viewDidDisappear methods should be called in the completion block of the animations.

It's been a while since I've truly looked at the exact synching of these events between two view controllers, but I highly suggest you put an NSLog statement in all of the view life cycle events and use a regular storyboard segue transition between the two to see the order and timing of which these are called. And better yet, do it in a navigation controller and use the swipe-left-to-right gesture to navigate backward. Notice as you start that gesture, the current view controller has viewWillDisappear: called, but it waits until the transition is complete for viewDidDisappear:. And ultimately, this means that if you start your gesture then go back, viewWillDisappear: can be called without viewDidDisappear:.


You have a lot of code in viewDidLoad. I'd highly recommend jobbing this out to other smaller functions, each of which get called by viewDidLoad.


Finally, I'd highly recommend that you create a custom subclass of UIViewController (or maybe UITabBarController). Ultimately, this seems like a very custom looking UITabBarController, right? Rather than writing all this code each time you wanted to implement it, it seems it'd be better if you narrowed it down to the navigation code and made it work for any (reasonable) number of contained view controllers, just as UITabBarController does.

\$\endgroup\$
4
  • \$\begingroup\$ i am going to extract this view to a generic view that i will use latter on this is the beginning :) , as for selectView i moved the functionality to segmentSwitch, i showed the first view using selectView before the animation, when the animation added it was no longer needed, fixed. \$\endgroup\$
    – shay te
    Aug 25 '14 at 12:04
  • \$\begingroup\$ as for viewWillDisappear/willAppear i just copy the implementation from here redartisan.com/2010/5/26/uisegmented-control-view-switching so i don't fully understand what is to correct approach, i am only beginning with uikit \$\endgroup\$
    – shay te
    Aug 25 '14 at 12:07
  • \$\begingroup\$ can i give me a snippet of the correct way to viewWillDisappear/willAppear i am not fully understand \$\endgroup\$
    – shay te
    Aug 25 '14 at 12:18
  • \$\begingroup\$ As I said, put the willAppear/willDisappear before the animations, put the didAppear/didDisappear in the animation completion block. \$\endgroup\$
    – nhgrif
    Aug 25 '14 at 21:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.