带有 Swift 滑动菜单的空白菜单按钮

Blank Menu Button with Swift Slide Menu

我在 swift、

中创建滑出菜单时遇到一个奇怪的问题

我正在使用 AKSwiftSlideMenu 作为参考构建菜单,

这只发生在具有 UITableViewDataSource、UITableViewDelegate 的 ViewController 上。

如果我在没有一个的情况下转到其他视图控制器,菜单显示正常。

下面是我的 BaseViewController 代码

class BaseViewController: UIViewController, SlideMenuDelegate {

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

func slideMenuItemSelectedAtIndex(_ index: Int32) {
    let topViewController : UIViewController = self.navigationController!.topViewController!
    print("View Controller is : \(topViewController) \n", terminator: "")
    switch(index){
    case 0:
        print("Locations\n", terminator: "")

        self.openViewControllerBasedOnIdentifier("Locations")

        break
    case 1:
        print("Offers\n", terminator: "")

        self.openViewControllerBasedOnIdentifier("Offers")

        break
    case 2:
        print("Feedback\n", terminator: "")

        self.openViewControllerBasedOnIdentifier("Feedback")

        break
    case 3:
        print("About\n", terminator: "")

        self.openViewControllerBasedOnIdentifier("About")

        break
    case 4:
        for key in UserDefaults.standard.dictionaryRepresentation().keys {
            UserDefaults.standard.removeObject(forKey: key)
        }
        //fb logout
        if(FBSDKAccessToken.current() != nil) {
            FBSDKAccessToken.setCurrent(nil)
            FBSDKProfile.setCurrent(nil)
        }

        self.openViewControllerBasedOnIdentifier("SocialLogin")
    default:
        print("default\n", terminator: "")
    }
}

func openViewControllerBasedOnIdentifier(_ strIdentifier:String){
    let destViewController : UIViewController = self.storyboard!.instantiateViewController(withIdentifier: strIdentifier)

    let topViewController : UIViewController = self.navigationController!.topViewController!

    if (topViewController.restorationIdentifier! == destViewController.restorationIdentifier!){
        print("Same VC")
    } else {
        self.navigationController!.pushViewController(destViewController, animated: true)
    }
}

func addSlideMenuButton(){
    let btnShowMenu = UIButton(type: UIButtonType.system)
    btnShowMenu.setImage(self.defaultMenuImage(), for: UIControlState())
    btnShowMenu.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
    btnShowMenu.addTarget(self, action: #selector(BaseViewController.onSlideMenuButtonPressed(_:)), for: UIControlEvents.touchUpInside)
    let customBarItem = UIBarButtonItem(customView: btnShowMenu)
    self.navigationItem.leftBarButtonItem = customBarItem;
}

func defaultMenuImage() -> UIImage {
    var defaultMenuImage = UIImage()

    UIGraphicsBeginImageContextWithOptions(CGSize(width: 30, height: 22), false, 0.0)

    UIColor.black.setFill()
    UIBezierPath(rect: CGRect(x: 0, y: 3, width: 30, height: 1)).fill()
    UIBezierPath(rect: CGRect(x: 0, y: 10, width: 30, height: 1)).fill()
    UIBezierPath(rect: CGRect(x: 0, y: 17, width: 30, height: 1)).fill()

    UIColor.white.setFill()
    UIBezierPath(rect: CGRect(x: 0, y: 4, width: 30, height: 1)).fill()
    UIBezierPath(rect: CGRect(x: 0, y: 11,  width: 30, height: 1)).fill()
    UIBezierPath(rect: CGRect(x: 0, y: 18, width: 30, height: 1)).fill()

    defaultMenuImage = UIGraphicsGetImageFromCurrentImageContext()!

    UIGraphicsEndImageContext()

    return defaultMenuImage;
}

func onSlideMenuButtonPressed(_ sender : UIButton){
    if (sender.tag == 10)
    {
        // To Hide Menu If it already there
        self.slideMenuItemSelectedAtIndex(-1);

        sender.tag = 0;

        let viewMenuBack : UIView = view.subviews.last!

        UIView.animate(withDuration: 0.3, animations: { () -> Void in
            var frameMenu : CGRect = viewMenuBack.frame
            frameMenu.origin.x = -1 * UIScreen.main.bounds.size.width
            viewMenuBack.frame = frameMenu
            viewMenuBack.layoutIfNeeded()
            viewMenuBack.backgroundColor = UIColor.clear
            }, completion: { (finished) -> Void in
                viewMenuBack.removeFromSuperview()
        })

        return
    }

    sender.isEnabled = false
    sender.tag = 10

    let menuVC : MenuViewController = self.storyboard!.instantiateViewController(withIdentifier: "MenuViewController") as! MenuViewController
    menuVC.btnMenu = sender
    menuVC.delegate = self
    self.view.addSubview(menuVC.view)
    self.addChildViewController(menuVC)
    menuVC.view.layoutIfNeeded()


    menuVC.view.frame=CGRect(x: 0 - UIScreen.main.bounds.size.width, y: 0, width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.height);

    UIView.animate(withDuration: 0.3, animations: { () -> Void in
        menuVC.view.frame=CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.height);
        sender.isEnabled = true
        }, completion:nil)
}
}

菜单视图控制器

protocol SlideMenuDelegate {
func slideMenuItemSelectedAtIndex(_ index : Int32)
}

class MenuViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

/**
*  Array to display menu options
*/
@IBOutlet var tblMenuOptions : UITableView!

/**
*  Transparent button to hide menu
*/
@IBOutlet var btnCloseMenuOverlay : UIButton!

/**
*  Array containing menu options
*/
var arrayMenuOptions = [Dictionary<String,String>]()

/**
*  Menu button which was tapped to display the menu
*/
var btnMenu : UIButton!

/**
*  Delegate of the MenuVC
*/
var delegate : SlideMenuDelegate?

override func viewDidLoad() {
    super.viewDidLoad()
    tblMenuOptions.tableFooterView = UIView()
    // Do any additional setup after loading the view.
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    updateArrayMenuOptions()
}

func updateArrayMenuOptions(){
    arrayMenuOptions.append(["title":"Locations", "icon":"LocationIcon"])
    arrayMenuOptions.append(["title":"Offers", "icon":"OffersIcon"])
    arrayMenuOptions.append(["title":"Feedback", "icon":"FeedbackIcon"])
    arrayMenuOptions.append(["title":"About", "icon":"AboutIcon"])
    arrayMenuOptions.append(["title":"Logout", "icon":"LogoutIcon"])

    tblMenuOptions.reloadData()
}

@IBAction func onCloseMenuClick(_ button:UIButton!){
    btnMenu.tag = 0

    if (self.delegate != nil) {
        var index = Int32(button.tag)
        if(button == self.btnCloseMenuOverlay){
            index = -1
        }
        delegate?.slideMenuItemSelectedAtIndex(index)
    }

    UIView.animate(withDuration: 0.3, animations: { () -> Void in
        self.view.frame = CGRect(x: -UIScreen.main.bounds.size.width, y: 0, width: UIScreen.main.bounds.size.width,height: UIScreen.main.bounds.size.height)
        self.view.layoutIfNeeded()
        self.view.backgroundColor = UIColor.clear
        }, completion: { (finished) -> Void in
            self.view.removeFromSuperview()
            self.removeFromParentViewController()
    })
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell : UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "cellMenu")!

    cell.selectionStyle = UITableViewCellSelectionStyle.none
    cell.layoutMargins = UIEdgeInsets.zero
    cell.preservesSuperviewLayoutMargins = false
    cell.backgroundColor = UIColor.clear

    let lblTitle : UILabel = cell.contentView.viewWithTag(101) as! UILabel
    let imgIcon : UIImageView = cell.contentView.viewWithTag(100) as! UIImageView

    imgIcon.image = UIImage(named: arrayMenuOptions[indexPath.row]["icon"]!)
    lblTitle.text = arrayMenuOptions[indexPath.row]["title"]!

    return cell
}

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    let btn = UIButton(type: UIButtonType.custom)
    btn.tag = indexPath.row
    self.onCloseMenuClick(btn)
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return arrayMenuOptions.count
}

func numberOfSections(in tableView: UITableView) -> Int {
    return 1;
}
}

发生这种情况的视图控制器示例

class LocationViewController: BaseViewController, CLLocationManagerDelegate, UITableViewDataSource, UITableViewDelegate {
var locItems:Array<LocItems>?
var locItemsWrapper:LocItemsWrapper?
var isLoadingLocItems = false


private var tb: UITableView?

override func viewDidLoad() {
    super.viewDidLoad()

    //set up tableview
    tb = UITableView()

    //Set button to open up menu
    self.addSlideMenuButton()

}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    if (self.locItems == nil) {
        return 0
    }
    return self.locItems!.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "locCell", for: indexPath) as! LocItemCell

    if(self.locItems != nil && self.locItems!.count >= indexPath.row) {
        let locItem = self.locItems![indexPath.row]
        let rowsLoaded = self.locItems!.count
        if (!self.isLoadingLocItems && (indexPath.row >= (rowsLoaded - rowsToLoadFromBottom))) {
            let totalRows = self.locItemsWrapper!.count!
            let remainingLocItemsToLoad = totalRows - rowsLoaded
            if(remainingLocItemsToLoad > 0) {
                self.loadMoreLocItems()
            }
        }
    }

    return cell
}

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

    if(self.locItems!.count >= indexPath.row) {
        selectedLocId = self.locItems![indexPath.row].id!
        selectedLocBg = self.locItems![indexPath.row].locationBackground!

        //TODO: Set up Switch on api call

        let parameters = [
            "locId": selectedLocId
        ]

    }

}

override func prepare(for segue: (UIStoryboardSegue!), sender: Any!) {
    if(segue.identifier == "showCongratOffer") {
        let svc = segue.destination as! CongratOfferViewController

        svc.offerId = selectedLocId
        svc.type = 1
    }
}

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    if((indexPath.row % 2) == 0) {
        cell.backgroundColor = UIColor(red: 0.9, green: 0.9, blue: 0.9, alpha: 1.0)
    } else {
        cell.backgroundColor = UIColor.white
    }
}

我知道它有很多代码,我尝试在示例中尽可能多地删除它,同时保留菜单代码完整,

如果有人能帮助我解决这个问题或指出正确的方向,我将不胜感激。

打开菜单前的tableview图片,

故事板的图片, 左上方的控制器可以忽略, 从登录(中上),我们转到菜单所在的位置(右下,中下), 关于(右上角)将正确显示菜单而没有空白 space, 菜单控制器在左下方。

虽然空 space 显示在带有 UITableViewDataSource、UITableViewDelegate 的控制器中,但菜单仍然有效。

更新:查看视图及其构建方式,我发现菜单高度限制不同。在 BaseViewController 中的 menuVC.view.layoutIfNeeded() 之后未设置。

print(self.childViewControllers[0].topLayoutGuide)

Empty Space Issue -> <_UILayoutGuide: 0x7fce6e7115e0; frame = (0 0; 0 0); hidden = YES; layer = <CALayer: 0x600000238700>>
Working -> <_UILayoutGuide: 0x7fce6e51ad30; frame = (0 0; 0 64); hidden = YES; layer = <CALayer: 0x60000023c9a0>>

您如何仅更改视图的高度限制? 我可以像这样获得只读视图约束, self.childViewControllers[0].view.constraints

我现在明白了。您需要做的是从 TableView 复制单元格,删除原始单元格,确保 TableView 完全为空,然后将单元格粘贴回去。您也可以通过完全创建一个新单元格来手动完成此操作。顺便说一句,问题是您的单元格上方有一个空的 space,您需要将其从 TableView 中删除。