ios - UINavigationBar 约束问题中的奇怪 UITextField 仅在 iOS 10 或更高版本上

标签 ios iphone swift constraints ios10

在我将 iPhone 更新到 iOS 10 后,我注意到 UINavigationBar 中的 UITextField 这个非常奇怪的约束问题出现在我的应用程序中iOS 提示。我认为将我的 Xcode 项目更新为 swift 3 会修复它,但问题仍然存在。出于某种原因,这不是 iOS 9 及更低版本的问题。

Please download my app Tipped... it's free and you will be able to see the issue described below.

1: 当我启动我的应用程序并点击标签栏到 viewController 时,导航栏中出现文本字段约束问题,我得到 this

2: 当我按另一个选项卡栏选项(如主页)点击搜索选项卡栏选项(导航回有问题的页面)时,我得到 this

3: 此外,当我在出现约束问题的搜索页面上点击其中一个分割按钮时。约束问题将再次出现,并且文本字段将过于靠右。

我已尝试使用以下代码来尝试解决问题,但似乎没有任何效果。真的卡在这里任何帮助都会很棒。

override func viewWillAppear(_ animated: Bool) {
// searchTextField.becomeFirstResponder()
   print("viewWillAppear: searchPageViewController")
   navigationView.setNeedsLayout()
   navigationView.updateConstraintsIfNeeded()
   searchTextField.updateConstraints()
   searchTextField.setNeedsLayout()
}

下面是我遇到问题的 View Controller 的代码。

class SearchPageViewController: UIPageViewController, UISearchBarDelegate, UISearchDisplayDelegate, UIPageViewControllerDataSource, UIPageViewControllerDelegate, UIScrollViewDelegate, UITextFieldDelegate {

        //%%% customizeable button attributes
        let X_BUFFER:CGFloat = 0.0; //%%% the number of pixels on either side of the segment
        let Y_BUFFER:CGFloat = 0.0; //%%% number of pixels on top of the segment
        let HEIGHT:CGFloat = 44.0; //%%% height of the segment

        //%%% customizeable selector bar attributes (the black bar under the buttons)
        let BOUNCE_BUFFER:CGFloat = 10.0; //%%% adds bounce to the selection bar when you scroll
        let ANIMATION_SPEED:CGFloat = 0.2; //%%% the number of seconds it takes to complete the animation
        let SELECTOR_Y_BUFFER:CGFloat = 40.0; //%%% the y-value of the bar that shows what page you are on (0 is the top)
        let SELECTOR_HEIGHT:CGFloat = 4.0; //%%% thickness of the selector bar

        let X_OFFSET:CGFloat = 0.0; //%%% for some reason there's a little bit of a glitchy offset.  I'm going to look for a better workaround in the future

        var navigationView = UIView()
        var containerView = UIView()

        var buttonText:NSArray = []
        var pageScrollView:UIScrollView!
        var currentPageIndex:Int!
        var selectionBar = UIView()

        var buttonOneTap:Bool = false
        var buttonTwoTap:Bool = false

        fileprivate var _controllerEnum: ControllerEnum = ControllerEnum()
        fileprivate var _dict: [UIViewController: ControllerEnum] = [:]

        var editView = UIView()
        //    var delegate: ViewControllerDelegate? = nil
        //    var userList = NSMutableArray()
        //    var searchString = String()
        //    var viewControllerArray:NSMutableArray = NSMutableArray()
        var blogSearchCollectionViewController:BlogSearchCollectionViewController!
    //    var pageViewController: UIPageViewController!
        var pageTitles: NSArray!
        var pageImages: NSArray!

        let searchController = UISearchController(searchResultsController: nil)

        @IBOutlet weak var searchBarView: UIView!
        @IBOutlet weak var searchTextField: UITextField!
        //Stretching the searchTextField full width of navbar
        override func viewWillLayoutSubviews() {
            super.viewWillLayoutSubviews()
            widenTextField()
        }

        func widenTextField() {



            let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: 8, height: 0))
            self.searchTextField.leftView = paddingView
            self.searchTextField.leftViewMode = .always

            let paddingViewRight = UIView(frame: CGRect(x: 0, y: 0, width: 8, height: 0))
            self.searchTextField.rightView = paddingViewRight
            self.searchTextField.rightViewMode = .always

            searchBarView.frame = CGRect(x: 16, y: 5, width: self.view.frame.width, height: 34)
    //        var frame:CGRect = self.searchTextField.frame
    //        frame.size.width = self.view.frame.width
    //        self.searchTextField.frame = CGRectMake(16, 5, self.view.frame.width, 33)
        }

        // TextFieldDelegates
        func textFieldShouldReturn(_ textField: UITextField) -> Bool {
    //        textField.resignFirstResponder()
            handleTap(editView)

            if currentPageIndex == 0 {

                let vc = self.viewControllerAtIndex(currentPageIndex) as BlogSearchCollectionViewController
    //            vc.loadUsers(textField.text.lowercaseString)
                vc.searchText = textField.text!.lowercased()
                vc.loadObjects()
                vc.showActivityIndicator()
                let viewControllers = NSArray(object: vc)

                setViewControllers(viewControllers as? [UIViewController], direction: .forward, animated: false, completion: nil)
            } else if currentPageIndex == 1 {

                let vc = self.viewControllerAtIndexTwo(currentPageIndex) as PhotoSearchController
                vc.search(textField.text!.lowercased())

                vc.showActivityIndicator()
                let viewControllers = NSArray(object: vc)

                setViewControllers(viewControllers as? [UIViewController], direction: .forward, animated: false, completion: nil)
            }
            return true
        }

    //    var lastTextFieldEdit:String!

        func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
            showEditView()
            if let text = textField.text {
                if text.characters.count > 0 {
                    DispatchQueue.main.async{
                        textField.selectAll(nil)
                    }
                }
            }
            searchTextField.textAlignment = NSTextAlignment.left

            return true
        }

        func textFieldDidBeginEditing(_ textField: UITextField) {
            print(textField)
        }

        override func viewWillAppear(_ animated: Bool) {
    //      searchTextField.becomeFirstResponder()
            print("viewWillAppear: searchPageViewController")

        }

        override func viewDidAppear(_ animated: Bool) {
        }

        override func viewDidLoad() {
            super.viewDidLoad()
            self.setupSegmentButtons()
            self.setupPage()

            currentPageIndex = 0

    //        self.navigationItem.titleView = searchTextField
        }

        //Mark: SearchBar stuff
        func searchBarShouldBeginEditing(_ searchBar: UISearchBar) -> Bool {
    //        searchController.searchBar.showsCancelButton = true
            return true
        }


        func searchBarShouldEndEditing(_ searchBar: UISearchBar) -> Bool {
            searchController.searchBar.showsCancelButton = true
    //        for subView in searchController.view.subviews  {
    //            if let dimView = subView as? UIView {
    //                dimView.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.6)
    //            }
    //        }

            return true
        }

        func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
            for subView in searchController.view.subviews  {
            }
        }

        func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {

            print("hithere")
            searchController.searchBar.showsCancelButton = true

    //        searchController.dimsBackgroundDuringPresentation = false

            print("searchController.view.subviews1: - \(searchController.searchBar.subviews)")
            for subView in searchController.view.subviews  {
                print("searchController.view.subviews2: - \(searchController.view.subviews)")

                if let dimView = subView as? UIView {
                    dimView.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.0)
                }
            }
    //        searchController.dismissViewControllerAnimated(true, completion: nil)

            if currentPageIndex == 0 {

                let vc = self.viewControllerAtIndex(currentPageIndex) as BlogSearchCollectionViewController
    //            vc.loadUsers(searchBarString.lowercaseString)
                let viewControllers = NSArray(object: vc)

                setViewControllers(viewControllers as? [UIViewController], direction: .forward, animated: false, completion: nil)
            } else if currentPageIndex == 1 {

                let vc = self.viewControllerAtIndexTwo(currentPageIndex) as PhotoSearchController
                vc.search(searchBarString.lowercased())
                let viewControllers = NSArray(object: vc)

                setViewControllers(viewControllers as? [UIViewController], direction: .forward, animated: false, completion: nil)
            }
        }
        var searchBarString:String!

        func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {

            searchController.searchBar.showsCancelButton = true
            let uiButton = searchController.searchBar.value(forKey: "cancelButton") as! UIButton
            uiButton.setTitle("Cancel", for: UIControlState())
            uiButton.setTitleColor(UIColor.black, for: UIControlState())
            uiButton.titleLabel!.font =  UIFont(name: "Helvetica Neue", size: 17)

        }

        func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
            searchBarString = searchText
        }

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

        func setupSegmentButtons() {
            navigationView = UIView(frame: CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: HEIGHT))
            navigationView.backgroundColor = UIColor.brown

            let numControllers = 2

            //        if buttonText == NSNotFound {
            buttonText = NSArray(objects: "BLOGS", "TIPS")
            //        }

            let buttonOne:UIButton = UIButton(frame: CGRect(x: 0, y: 0, width: self.view.frame.size.width/2, height: HEIGHT))
            let buttonTwo:UIButton = UIButton(frame: CGRect(x: self.view.frame.size.width/2, y: 0, width: self.view.frame.size.width/2, height: HEIGHT))

            navigationView.addSubview(buttonOne)
            navigationView.addSubview(buttonTwo)

            buttonOne.tag = 0
            buttonOne.backgroundColor = UIColor.white
            buttonOne.addTarget(self, action: #selector(SearchPageViewController.tapSegmentButtonAction(_:)), for: UIControlEvents.touchUpInside)
            buttonOne.setTitle(buttonText[0] as? String, for: UIControlState())
            buttonOne.setTitleColor(UIColor.black, for: UIControlState())
            buttonOne.titleLabel!.font = UIFont(name: "Interstate-Bold", size: 17)!

            buttonTwo.tag = 1
            buttonTwo.backgroundColor = UIColor.white
            buttonTwo.addTarget(self, action: #selector(SearchPageViewController.tapSegmentButtonAction(_:)), for: UIControlEvents.touchUpInside)
            buttonTwo.setTitle(buttonText[1] as? String, for: UIControlState())
            buttonTwo.setTitleColor(UIColor.black, for: UIControlState())
            buttonTwo.titleLabel!.font =  UIFont(name: "Interstate-Bold", size: 17)

            self.view.addSubview(navigationView)
            self.setupSelector()
        }

        func setupSelector() {
            selectionBar = UIView(frame: CGRect(x: X_BUFFER-X_OFFSET, y: SELECTOR_Y_BUFFER,width: (self.view.frame.size.width-2*X_BUFFER)/2, height: SELECTOR_HEIGHT))
            selectionBar.backgroundColor = UIColor(red: 251/255, green: 73/255, blue: 90/255, alpha: 1.0)
    //        selectionBar.alpha = 1
            navigationView.addSubview(selectionBar)
        }

        func setupPage() {

    //        self.navigationItem.titleView = searchTextField

            view.backgroundColor = UIColor.white
            //TextFieldStuff
            searchTextField.delegate = self
            searchTextField.placeholder = "Search for blogs"
            searchTextField.textAlignment = NSTextAlignment.center
            searchTextField.autocorrectionType = .no

            //TextField is editing translucent view
            editView.backgroundColor = UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.6)
            editView.frame = CGRect(x: 0, /*self.navigationController!.navigationBar.frame.size.height + 20 +*/ y: HEIGHT, width: self.view.frame.size.width, height: self.view.frame.size.height - (self.navigationController!.navigationBar.frame.size.height + 20 + HEIGHT))
            self.view.addSubview(editView)
            editView.isHidden = true

            //tapGesture
            editView.isUserInteractionEnabled = true
            let tapGesture = UITapGestureRecognizer(target: self, action: #selector(SearchPageViewController.handleTap(_:)))
            editView.addGestureRecognizer(tapGesture)

            let pageControl = UIPageControl()
            pageControl.backgroundColor = UIColor.white

            self.pageTitles = NSArray(objects: "Explore", "Today Widget")
            self.pageImages = NSArray(objects: "page1", "page2")

            //PageViewControllers
            delegate = self
            dataSource = self

                let vc = self.viewControllerAtIndex(0) as BlogSearchCollectionViewController
    //            vc.loadUsers("")
                vc.searchText = ""
                vc.loadObjects()

                vc.showActivityIndicator()
                let viewControllers = NSArray(object: vc)

                setViewControllers(viewControllers as? [UIViewController], direction: UIPageViewControllerNavigationDirection.forward, animated: true, completion: nil)
            print("pageSetUp:- \(currentPageIndex)")
            self.syncScrollView()
        }

        func handleTap(_ sender : UIView) {
    //        searchController.resignFirstResponder()
            searchTextField.resignFirstResponder()
            searchTextField.textAlignment = NSTextAlignment.center

            UIView.animate(withDuration: 0.3, animations: { () -> Void in
                self.editView.alpha = 0

                }, completion: { (success:Bool) -> Void in
                    if success {
                        self.editView.isHidden = true
                    }
            }) 
    //        UIView.animateWithDuration(0.5, animations: { () -> Void in
    //            self.editView.alpha = 0
    //        })
            print("Tap Gesture recognized")
        }

        func showEditView() {

            editView.alpha = 0
            editView.isHidden = false

            UIView.animate(withDuration: 0.3, animations: { () -> Void in
                self.editView.alpha = 0.6
                })
        }

        func viewControllerAtIndex(_ index: Int) -> BlogSearchCollectionViewController {

            let vc: BlogSearchCollectionViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "BlogSearchCollectionViewController") as! BlogSearchCollectionViewController

            return vc
        }

        func viewControllerAtIndexTwo(_ index: Int) -> PhotoSearchController {
            let vc: PhotoSearchController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "PhotoSearchController") as! PhotoSearchController
    //        currentPageIndex = vc.pageIndex

            return vc
        }

        func syncScrollView() {

            let x = self.view.subviews
            var view:UIView!

            for view in x {

                if view.isKind(of: UIScrollView.self) {
                    pageScrollView = view as! UIScrollView
                    pageScrollView.delegate = self
                }
            }
        }
        //%%% when you tap one of the buttons, it shows that page,
        //but it also has to animate the other pages to make it feel like you're crossing a 2d expansion,
        //so there's a loop that shows every view controller in the array up to the one you selected
        //eg: if you're on page 1 and you click tab 3, then it shows you page 2 and then page 3

        func tapSegmentButtonAction(_ button:UIButton) {

            if buttonOneTap == false && buttonTwoTap == false {

                let tempIndex:Int = self.currentPageIndex
                //%%% check to see if you're going left -> right or right -> left
                //        println("buttonTag + tempIndex \(button.tag) \(tempIndex)")
                if button.tag > tempIndex {
                    //%%% scroll through all the objects between the two points
                    buttonOneTap = true
    //                var i = tempIndex+1
    //                for i in stride(from:i, through:(button.tag), by: 1) {
                    for tempIndex in 0..<button.tag {
    //                for var i = tempIndex+1; i<=button.tag; i += 1 {
                        //                println(i)

                        let vc: PhotoSearchController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "PhotoSearchController") as! PhotoSearchController
                        let viewControllers = NSArray(object: vc)

                        setViewControllers(viewControllers as? [UIViewController] /*[getController(.Debts)!]viewControllerArray.objectAtIndex(i) as! [AnyObject]*/, direction: UIPageViewControllerNavigationDirection.forward, animated: true, completion: { (success:Bool) -> Void in
                            //%%% if the action finishes scrolling (i.e. the user doesn't stop it in the middle),
                            //then it updates the page that it's currently on

                            if success == true {
                                self.buttonOneTap = false
                                self.updateCurrentPageIndex(1)
    //                            self.searchController.searchBar.placeholder = "Search for tips"

    //                            let txtField = self.navigationItem.titleView as! UITextField
    //                            txtField.placeholder = "Search for tips"
                                self.searchTextField.placeholder = "Search for tips"
                            }
                        })
                    }
                    //%%% this is the same thing but for going right -> left

                } else if button.tag < tempIndex {

                    buttonTwoTap = true
    //                var i = tempIndex+1
    //                for i in stride(from:i, through:(button.tag), by: -1) {
    //                for var i = tempIndex-1; i >= button.tag; i -= 1 {
                    for tempIndex in (0...button.tag).reversed() {

                        //                println(i)

                        let vc: BlogSearchCollectionViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "BlogSearchCollectionViewController") as! BlogSearchCollectionViewController
    //                    vc.loadUsers("")
                        vc.searchText = ""
                        vc.loadObjects()

                        vc.showActivityIndicator()
                        let viewControllers = NSArray(object: vc)

                        setViewControllers(viewControllers as? [UIViewController] /*[getController(_controllerEnum)!] viewControllerArray.objectAtIndex(i) as! [AnyObject]*/, direction: UIPageViewControllerNavigationDirection.reverse, animated: true, completion: { (success:Bool) -> Void in
                            if success == true {

                                self.buttonTwoTap = false
                                self.updateCurrentPageIndex(0)
    //                            self.searchController.searchBar.placeholder = "Search for blogs"
    //                            let txtField = self.navigationItem.titleView as! UITextField
    //                            txtField.placeholder = "Search for blogs"
                                self.searchTextField.placeholder = "Search for blogs"
                            }
                        })
                    }
                }
            }
        }

        func updateCurrentPageIndex(_ newIndex:Int) {
            self.currentPageIndex = newIndex
        }
        //%%% makes sure the nav bar is always aware of what page you're on
        //in reference to the array of view controllers you gave
        //%%% method is called when any of the pages moves.
        var xFromCenter:CGFloat!
        func scrollViewDidScroll(_ scrollView: UIScrollView) {
    //        println(self.currentPageIndex)

            //It extracts the xcoordinate from the center point and instructs the selection bar to move accordingly
            xFromCenter = self.view.frame.size.width-scrollView.contentOffset.x
            print(xFromCenter)

    //        if xFromCenter > 0 && currentPageIndex == 0 {
    //            updateCurrentPageIndex(0)
    //        }
    //        println(self.currentPageIndex)


            var a = X_BUFFER + selectionBar.frame.size.width
            var b = CGFloat(currentPageIndex) - X_OFFSET

            let xCoor = selectionBar.frame.size.width * CGFloat(self.currentPageIndex)
    //        println(xCoor)
            //%%% checks to see what page you are on and adjusts the xCoor accordingly.
            //i.e. if you're on the second page, it makes sure that the bar starts from the frame.origin.x of the
            //second tab instead of the beginning

            selectionBar.frame = CGRect(x: xCoor - xFromCenter/2, y: selectionBar.frame.origin.y, width: selectionBar.frame.size.width, height: selectionBar.frame.size.height)
        }

        // MARK: - Page View Controller Data Source
        var vcOne:BlogSearchCollectionViewController!

        func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
            var index:Int = currentPageIndex

            if index == NSNotFound || index == 0 {
                return nil
            }
    //        println(currentPageIndex)

            index -= 1

            let vcOne = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "BlogSearchCollectionViewController") as! BlogSearchCollectionViewController
    //        currentPageIndex = vc.pageIndex
    //        println("actaulIndexBlogSearchCollectionViewController: - \(actaulIndex)")

            return vcOne
    //        return getController(_dict[viewController]!.prevIndex())
        }

        var vcTwo:PhotoSearchController!

        func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {

            var index:Int = currentPageIndex

            if index == NSNotFound {
                return nil
            }
            index += 1

            if index == buttonText.count {
                return nil
            }


            vcTwo = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "PhotoSearchController") as! PhotoSearchController
    //        currentPageIndex = vc.pageIndex
    //        if vc.pageIndex == 1 && index == 0 {
    //            return vc
    //        }
    //        println("actaulIndexPhotoSearchController: - \(actaulIndex)")

            return vcTwo
    //        return getController(_dict[viewController]!.nextIndex())
        }

        var actaulIndex:Int!

        func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {

            print("finished: - \(finished)")
            print("completed: - \(completed)")

            if finished == true && completed == true {
                print("currentPageIndex: - \(currentPageIndex)")
                if finished == true && completed == true && currentPageIndex == 0 && xFromCenter < 0 {
                    print("hi")
    //                searchController.searchBar.placeholder = "Search for tips"
    //                let txtField = self.navigationItem.titleView as! UITextField
    //                txtField.placeholder = "Search for tips"
                    searchTextField.placeholder = "Search for tips"

                    updateCurrentPageIndex(1)
                } else if finished == true && completed == true && currentPageIndex == 1 && xFromCenter > 0 {
                    print("hi 2")
    //                searchController.searchBar.placeholder = "Search for blogs"
    //                let txtField = self.navigationItem.titleView as! UITextField
    //                txtField.placeholder = "Search for blogs"
                    searchTextField.placeholder = "Search for blogs"

                    updateCurrentPageIndex(0)
                }
            }
        }

    }

最佳答案

我想通了。在 widen textfield 函数中。我需要更改以下行:

searchBarView.frame = CGRect(x: 16, y: 5, width: self.view.frame.width, height: 34)

searchBarView.frame = CGRect(x: 8, y: 5, width: self.view.frame.width - 16, height: 34)

关于ios - UINavigationBar 约束问题中的奇怪 UITextField 仅在 iOS 10 或更高版本上,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41329166/

相关文章:

iphone - 恢复 NSUserDefault 中的值

iphone - 我的 iPhone 应用程序应该使用哪种类型的 "No Network Connectivity"警告

ios - PerformSegueWithIdentifier 未呈现所需的 View

ios - 使用适用于 iOS 的 Google Places API 声明类型时出错

ios - 在 iOS 中实现 Thrift 服务器

ios - 我如何在 Storyboard 片段之间传递信息?

ios - UILocalNotifcation 加载时出现“NSInvalidArgumentException”

ios - 在 hh :mm:ss format to NSDate for Timer Countdown 中转换 NSString

swift - 无法选择我添加到屏幕的暂停按钮

arrays - 数组引用/值