You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift开发:如何从底部弹出自定义XIB的UIView(指定高度)

嘿,作为从Android转Swift的开发者,太懂你想实现这种底部弹出UIView的需求了!之前用UIViewController的方案虽然可行,但直接用UIView会更轻量灵活。下面给你一步步拆解实现方法,满足自定义XIB和指定高度的要求:

第一步:创建带XIB的自定义弹窗View

首先你需要创建一个BottomPopupView类,同时搭配对应的XIB文件来布局弹窗内容:

import UIKit

class BottomPopupView: UIView {
    // 用来控制弹窗高度的属性,默认设300,外部可以直接修改
    var popupHeight: CGFloat = 300 {
        didSet {
            heightConstraint?.constant = popupHeight
        }
    }
    
    private var heightConstraint: NSLayoutConstraint?
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupContentView()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        setupContentView()
    }
    
    private func setupContentView() {
        // 加载XIB文件
        let nib = UINib(nibName: "BottomPopupView", bundle: nil)
        guard let contentView = nib.instantiate(withOwner: self, options: nil).first as? UIView else { return }
        
        // 将XIB视图添加到当前View
        contentView.frame = bounds
        contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        addSubview(contentView)
        
        // 给弹窗加圆角和阴影,模拟原生弹窗效果
        layer.cornerRadius = 16
        layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] // 只给顶部两个角加圆角
        layer.shadowColor = UIColor.black.cgColor
        layer.shadowOpacity = 0.2
        layer.shadowOffset = CGSize(width: 0, height: -2)
        layer.shadowRadius = 8
    }
}

在XIB里你可以自由布局弹窗内容,比如添加按钮、文本、列表等,记得把XIB的File's Owner设置为BottomPopupView,这样可以关联控件。

第二步:封装弹窗的弹出/收起逻辑

为了方便在任何ViewController里调用,我们给UIViewController写个扩展,封装弹出和隐藏的动画逻辑:

extension UIViewController {
    func showBottomPopup(_ popupView: BottomPopupView, height: CGFloat? = nil) {
        // 如果外部指定了高度,就更新弹窗的高度属性
        if let targetHeight = height {
            popupView.popupHeight = targetHeight
        }
        
        // 将弹窗添加到当前控制器的视图上
        view.addSubview(popupView)
        popupView.translatesAutoresizingMaskIntoConstraints = false
        
        // 初始时把弹窗放在屏幕底部外面,准备动画
        let bottomConstraint = popupView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: popupView.popupHeight)
        heightConstraint = popupView.heightAnchor.constraint(equalToConstant: popupView.popupHeight)
        
        NSLayoutConstraint.activate([
            popupView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            popupView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            bottomConstraint,
            heightConstraint!
        ])
        
        // 添加背景遮罩,增强弹窗层次感
        let dimBackground = UIView()
        dimBackground.backgroundColor = UIColor.black.withAlphaComponent(0.5)
        dimBackground.alpha = 0
        dimBackground.tag = 999 // 标记遮罩,方便后续移除
        view.addSubview(dimBackground)
        dimBackground.translatesAutoresizingMaskIntoConstraints = false
        
        NSLayoutConstraint.activate([
            dimBackground.topAnchor.constraint(equalTo: view.topAnchor),
            dimBackground.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            dimBackground.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            dimBackground.bottomAnchor.constraint(equalTo: view.bottomAnchor)
        ])
        
        // 点击遮罩关闭弹窗
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(hideBottomPopup))
        dimBackground.addGestureRecognizer(tapGesture)
        
        // 执行弹出动画,用弹簧动画让效果更自然
        UIView.animate(withDuration: 0.3, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0.5) {
            bottomConstraint.constant = 0 // 把弹窗移到屏幕内
            dimBackground.alpha = 1
            self.view.layoutIfNeeded()
        }
    }
    
    @objc private func hideBottomPopup() {
        // 找到弹窗和遮罩视图
        guard let popupView = view.subviews.first(where: { $0 is BottomPopupView }) as? BottomPopupView,
              let dimBackground = view.viewWithTag(999) else { return }
        
        // 执行收起动画,完成后移除视图
        UIView.animate(withDuration: 0.3, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0.5) {
            popupView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: popupView.popupHeight).isActive = true
            dimBackground.alpha = 0
            self.view.layoutIfNeeded()
        } completion: { _ in
            popupView.removeFromSuperview()
            dimBackground.removeFromSuperview()
        }
    }
}
第三步:调用弹窗

现在你可以在任意ViewController里轻松调用这个弹窗了:

// 创建弹窗实例
let myPopup = BottomPopupView()
// 弹出弹窗,指定高度为400(不指定的话用默认300)
showBottomPopup(myPopup, height: 400)
额外小提示
  • 如果需要在弹窗内部添加关闭按钮,可以在BottomPopupView里添加按钮,然后通过闭包或者代理通知父控制器调用hideBottomPopup()方法。
  • XIB里的布局要注意适配不同屏幕,比如用AutoLayout约束让内容自适应弹窗高度。
  • 动画参数(时长、弹簧阻尼等)可以根据你的需求调整,让弹窗动效更贴合APP风格。

内容的提问来源于stack exchange,提问作者LMaker

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 09:09:38