1

I wrote this sample program to try and figure out what is going on. I have a textfield that upon clicking, shows a subview instead of showing a keyboard. In the subview, there is a dismiss button which shifts the subview so the top aligns with the bottom of the screen.

Just so I can build the subview in storyboard, the subview is aligned to the bottom edge of the screen and hidden at startup. viewDidLoad then calls the hideSubView to shift it offscreen before it starts.

I have println before and after each shift to tell me the position of the subview.

The problem I have is that the numbers don't add up. I specifically tell swift to offset the Y by 210, but somehow it only shifts it by 68.5 which is NOT the bottom of the screen (note this is not done by setting the subview as an inputview of the textfield). Yet when I click on the textfield, the subView animates from the bottom up, not some midway point. Can anyone explain why either of these two things is happening? One other thing is - where does the 457.0 for the initial minY come from? I checked the initial height of the subview which strangely is 143 (not 141.5).

Console Output (// - lines put in by me to clarify)

//hideSubview called from viewDidLoad
Before. minY:457.0 screenheight:667.0 offset:210.0
After. minY:525.5 screenheight:667.0 offset:210.0  //NOTE:457+210 is 667, not 525.5
//1st showSubView called by clicking on myTextField
Before. minY:525.5 screenheight:667.0 subview Height:141.5
After. minY:525.5 subView height:141.5
//hideSubView called by clicking on dismiss button
Before. minY:525.5 screenheight:667.0 offset:141.5
After. minY:667.0 screenheight:667.0 offset:141.5 
//2nd showSubView called by clicking on myTextField
Before. minY:667.0 screenheight:667.0 subview Height:141.5
After. minY:525.5 subView height:141.5
//hideSubView called by clicking on dismiss button
Before. minY:525.5 screenheight:667.0 offset:141.5
After. minY:667.0 screenheight:667.0 offset:141.5

Main.storyboard screen capture of Storyboard ViewController.swift

import UIKit

class ViewController: UIViewController, UITextFieldDelegate {

  @IBOutlet weak var myTextField: UITextField!
  @IBOutlet weak var mySubView: UIView!

  var screenSize = UIScreen.mainScreen().bounds
  var subViewIsVisible = false


  override func viewDidLoad() {
    super.viewDidLoad()
    myTextField.delegate = self
    hideSubView()
  }

  override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
  }

  @IBAction func dismissButtonClicked(sender: AnyObject) {
    if subViewIsVisible {
      hideSubView()
    }
  }

  func textFieldDidBeginEditing(textField: UITextField) {
    myTextField.endEditing(true)
    if !subViewIsVisible {
      showSubView()
    }
  }

  func hideSubView () {
    var offsetAmount = screenSize.height - mySubView.frame.minY
    println("Before. minY:\(mySubView.frame.minY) screenheight:\(screenSize.height) offset:\(offsetAmount)")
    UIView.animateWithDuration(0.25, delay: 0.0, options: UIViewAnimationOptions.CurveEaseOut , animations:
      {
        self.mySubView.frame.offset(dx: 0, dy: offsetAmount)
      }, completion: {_ in
        self.subViewIsVisible = false
        self.mySubView.hidden = true
        println("After. minY:\(self.mySubView.frame.minY) screenheight:\(self.screenSize.height) offset:\(offsetAmount)")
      }
    )
  }

  func showSubView () {
    mySubView.hidden = false
    println("Before. minY:\(mySubView.frame.minY) screenheight:\(screenSize.height) subview Height:\(mySubView.frame.height)")
    UIView.animateWithDuration(0.25, delay: 0.0, options: UIViewAnimationOptions.CurveEaseOut , animations:
      {
        self.mySubView.frame.offset(dx: 0, dy: -self.mySubView.frame.height)
      }, completion: {_ in
        self.subViewIsVisible = true
        println("After. minY:\(self.mySubView.frame.minY) subView height:\(self.mySubView.frame.height)")
      }
    )
  }
}

Edit:

Constraints of mySubView: (easier to see than my comment for Bannings) Constraints on mySubView

janson
  • 683
  • 1
  • 5
  • 14
  • Why're you not assigning the `mySubView` to the `myTextField`'s `inputAccessoryView`? – Bannings Jun 04 '15 at 05:11
  • @Bannings This is a simplified version of a program that I'm having issues with. In that one, I have a `TextField` and a `Searchbar` that can both pull up `mySubView`. My limited knowledge is that a view can have no more than 1 parent view. In this example, if I set it as you say, then `mySubView` will have 2 parents - the main view where I drew it on the story board and the `TextField`. Is this correct? – janson Jun 04 '15 at 06:37
  • Did you add any constraints at your `mySubView`? – Bannings Jun 04 '15 at 06:47
  • @Bannings ***External Constraints*** for `mySubView`: 1. Align Center X to superview. 2. Leading Space to superview = 50. 3. Bottom Space = Bottom Layout Guide. ***Internal Constraints*** (within the subview): All 4 labels (Align Center X to Superview). Top and Bottom of all 4 labels = 10. So there is no explicit constraint on the subview to define it's precise height. The height is defined by constrainst set on the internal components i.e. height of Dismiss button + L1+L2+L3 and 5x10 spaces. – janson Jun 04 '15 at 07:16
  • I guess the 457 and the 143 comes from the inferred size as shown from `mySubView`'s Constraint screen capture. However, I have set iOS simulator to iPhone6 so by the time viewDidLoad is running it should have all the right numbers. Assuming the starting size is 143, I would expect autolayout to have placed `mySubView` at 667-143 = 524... – janson Jun 04 '15 at 07:39

1 Answers1

0

The 457 and the 143 comes from your storyboard like this:

enter image description here

This behavior happens because the view's origin is incorrect in the viewDidLoad method. Although you had the bottom constraint with the Bottom Layout Guide, however, the frame hasn't been adjusted yet.

So, if you set the view's y to 500 then you getting a log as below:

enter image description here

Before. minY:500.0 screenheight:667.0 offset:167.0
After. minY:524.5 screenheight:667.0 offset:167.0

Your mySubView's frame will be correct after viewDidLoad has been called by AutoLayout.

Bannings
  • 10,376
  • 7
  • 44
  • 54
  • Thanks for trying it out. But you do see the problem right? There are multiple issues, but the most glaring, unresolved issue I have is: In between those two `println`'s, there is a `self.mySubView.frame.offset(dx: 0, dy: offsetAmount)` (from hideSubView). In your case, offset is 167. 500 + 167 is **NOT** 524.5. Also note - 524.5 is NOT the bottom of the screen, but when you click on the textfield, the view will come up from the bottom where based on console output, it isn't at! – janson Jun 04 '15 at 08:21
  • This is because you had a `bottom constraint` that will cause its always on the screen. You can trying to comments the line `self.mySubView.hidden = true` – Bannings Jun 04 '15 at 08:33
  • No matter what the value of this `offset` will not affect its position on the screen – Bannings Jun 04 '15 at 08:36
  • I unchecked the "hidden" option for `mySubView` (in Storyboard) and commented out both `self.mySubView.hidden = true`. When run, as you said, it is sitting on the bottom margin (essentially ignoring the hideSubView function I placed in viewDidLoad). However, when you click on the textfield - which should animate the subview from it's current location to current-location minus subview's height, it comes from the bottom up as opposed to standing still since it is already in that location. – janson Jun 04 '15 at 08:50
  • You can update its `frame` indeed, however that is unsafed, the Auto Layout will update its `frame` again in the next drawing update(e.g. rotate screen) so the result is not guaranteed. If you want to really to change it, you should make a IBOutlet, like this:http://stackoverflow.com/questions/23655096/change-frame-programmatically-with-auto-layout – Bannings Jun 04 '15 at 09:25