73、iOS界面栏控件的使用与定制

iOS界面栏控件的使用与定制

在iOS开发中,界面栏控件如导航栏、工具栏和标签栏等起着至关重要的作用。它们不仅为用户提供了操作入口,还能增强应用的整体美观性和易用性。下面将详细介绍这些栏控件的相关特性和定制方法。

1. 栏控件的尺寸调整

在紧凑水平尺寸类环境中,父视图控制器会自动调整栏控件的尺寸。具体如下:
- UINavigationController :会将导航栏和工具栏的高度调整为44(常规垂直尺寸类)或32(紧凑垂直尺寸类)。
- UITabBarController :会将标签栏的高度调整为49(常规垂直尺寸类)或32(紧凑垂直尺寸类)。

可能的栏指标值(UIBarMetrics)有:
- .default
- .compact
- .defaultPrompt
- .compactPrompt

紧凑指标适用于紧凑垂直尺寸类环境,提示指标适用于高度向下扩展以容纳提示文本的栏(以及显示范围按钮的搜索栏)。

2. 栏的外观定制

栏的样式可以在三个级别进行定制:
- barStyle, isTranslucent
- barStyle选项(UIBarStyle)有:
- .default (扁平白色)
- .black (扁平黑色)
- isTranslucent属性用于切换模糊半透明特性。
- barTintColor :该属性用纯色为栏着色。
- backgroundImage :通过 setBackgroundImage(_:for:barMetrics:) 方法设置背景图像。如果图像太大,会缩小以适应;如果太小,默认会平铺,但可以通过提供可调整大小的图像来改变这种行为。如果栏的isTranslucent为false,则barTintColor可能会显示在背景图像后面;如果为true,则栏在图像后面是透明的。

以下是一个为导航栏设置背景图像和阴影图像的示例代码:

do { // must set the background image if you want a shadow image
    let sz = CGSize(20,20)
    let r = UIGraphicsImageRenderer(size:sz)
    self.navbar.setBackgroundImage( r.image { ctx in
        UIColor(white:0.95, alpha:0.85).setFill()
        ctx.fill(CGRect(0,0,20,20))
    }, for:.any, barMetrics: .default)
}
do { // now we can set the shadow image
    let sz = CGSize(4,4)
    let r = UIGraphicsImageRenderer(size:sz)
    self.navbar.shadowImage = r.image { ctx in
        UIColor.gray.withAlphaComponent(0.3).setFill()
        ctx.fill(CGRect(0,0,4,2))
        UIColor.gray.withAlphaComponent(0.15).setFill()
        ctx.fill(CGRect(0,2,4,2))
    }
}
3. 栏按钮项(UIBarButtonItem)

栏中不添加子视图,而是用栏项填充。对于工具栏或导航栏,这些栏项通常是栏按钮项(UIBarButtonItem,UIBarItem的子类)。栏按钮项不是UIView,但可以将任意视图放入栏中,因为栏按钮项可以包含自定义视图。

栏按钮项可以通过以下五种方法实例化:
- init(barButtonSystemItem:target:action:)
- init(title:style:target:action:)
- init(image:style:target:action:)
- init(image:landscapeImagePhone:style:target:action:)
- init(customView:)

样式选项(UIBarButtonItem.Style)有 .plain .done ,区别仅在于 .done 的标题文本为粗体。如果同时提供了图像和landscapeImagePhone,则当栏指标名称中包含compact时使用后者。栏按钮项的图像默认被视为模板图像,除非明确提供 .alwaysOriginal 图像。

栏按钮项继承了UIBarItem的一些属性和方法,如通过 imageInsets (和 landscapeImagePhoneInsets )调整图像位置,以及 isEnabled tag 属性。可以设置栏按钮项的width属性,但如果栏按钮项有自定义视图,应该使用约束从内部调整视图大小。

栏按钮项的tintColor属性为按钮的标题文本或模板图像着色,它继承自栏的tintColor,也可以为单个栏按钮项覆盖该值。还可以应用属性字典到栏按钮项的标题,并为栏按钮项提供背景图像:
- setTitleTextAttributes(_:for:) (继承自UIBarItem)
- setTitlePositionAdjustment(_:for:)
- setBackgroundImage(_:for:barMetrics:)
- setBackgroundImage(_:for:style:barMetrics:)
- setBackgroundVerticalPositionAdjustment(_:for:)

如果栏按钮项用作导航栏中的返回按钮项,还可以使用以下方法:
- setBackButtonTitlePositionAdjustment(_:for:)
- setBackButtonBackgroundImage(_:for:barMetrics:)
- setBackButtonBackgroundVerticalPositionAdjustment(_:for:)

4. 导航栏(UINavigationBar)

导航栏(UINavigationBar)由导航项(UINavigationItem)填充。导航栏维护一个栈,导航项可以被推入和弹出该栈。栈中当前最顶层的导航项(UINavigationBar的topItem)与紧挨着它下面的导航项(UINavigationBar的backItem)共同决定导航栏中显示的内容:
- title, titleView :topItem的标题(字符串)或titleView(UIView)显示在导航栏的中心。应该使用约束从内部调整titleView的大小。
- prefersLargeTitles :允许标题单独显示在导航栏底部,导航栏会向下扩展以容纳它。在这种情况下,标题和titleView可以同时显示。标题是否实际以这种方式显示取决于导航项的largeTitleDisplayMode — .always .never .automatic (从栈中更下方继承)。
- prompt :提示(字符串)显示在导航栏顶部,导航栏高度会增加以容纳它。
- rightBarButtonItem, rightBarButtonItems, leftBarButtonItem, leftBarButtonItems :rightBarButtonItem和leftBarButtonItem分别显示在导航栏的右端和左端。UINavigationItem可以有多个右侧和左侧栏按钮项,其rightBarButtonItems和leftBarButtonItems属性是数组(栏按钮项数组)。栏按钮项从外向内显示,即leftBarButtonItems中的第一个项最靠左,rightBarButtonItems中的第一个项最靠右。如果一侧有多个按钮,rightBarButtonItem是rightBarButtonItems数组的第一个项,leftBarButtonItem是leftBarButtonItems数组的第一个项。
- backBarButtonItem :backItem的backBarButtonItem显示在导航栏的左端。点击时,topItem会从栈中弹出。如果backItem没有backBarButtonItem,导航栏左端仍会有一个返回按钮,其标题取自backItem的标题。但是,如果topItem的hidesBackButton设置为true,则返回按钮会被隐藏。此外,如果topItem有leftBarButtonItem,除非topItem的leftItemsSupplementBackButton也设置为true,否则返回按钮也会被隐藏。

导航栏的返回按钮指示由导航栏的backIndicatorImage提供,默认是一个向左的箭头,显示在返回按钮的左侧。可以自定义此图像,提供的图像默认被视为模板图像。如果设置了backIndicatorImage,还必须提供backIndicatorTransitionMaskImage。以下是一个将箭头替换为垂直条的示例:

let sz = CGSize(10,20)
self.navbar.backIndicatorImage =
    UIGraphicsImageRenderer(size:sz).image { ctx in
        ctx.fill(CGRect(6,0,4,20))
    }
self.navbar.backIndicatorTransitionMaskImage =
    UIGraphicsImageRenderer(size:sz).image {_ in}

导航栏按钮的更改可以通过向其topItem发送以下消息进行动画处理:
- setRightBarButton(_:animated:)
- setLeftBarButton(_:animated:)
- setRightBarButtonItems(_:animated:)
- setLeftBarButtonItems(_:animated:)
- setHidesBackButton(_:animated:)

导航项可以通过 pushItem(_:animated:) popItemAnimated(_:) 方法进行推入和弹出操作,也可以调用 setItems(_:animated:) 方法一次性设置栈中的所有项。

可以通过设置导航栏的 titleTextAttributes 来确定标题的属性字典,通过调用 setTitleVerticalPositionAdjustment(for:) 方法来移动标题的垂直位置。可以通过设置导航栏的 largeTitleTextAttributes 来确定大标题的属性字典。

当UINavigationBar是UINavigationController界面的一部分时,导航控制器是导航栏的委托。如果单独使用UINavigationBar,可能需要提供自己的委托。委托方法有:
- navigationBar(_:shouldPush:)
- navigationBar(_:didPush:)
- navigationBar(_:shouldPop:)
- navigationBar(_:didPop:)

以下是一个独立UINavigationBar的简单示例:

override func viewDidLoad() {
    super.viewDidLoad()
    let ni = UINavigationItem(title: "Tinker")
    let b = UIBarButtonItem(title: "Evers", style: .plain,
        target: self, action: #selector(pushNext))
    ni.rightBarButtonItem = b
    self.navbar.items = [ni]
}
@objc func pushNext(_ sender: Any) {
    let oldb = sender as! UIBarButtonItem
    let s = oldb.title!
    let ni = UINavigationItem(title:s)
    if s == "Evers" {
        let b = UIBarButtonItem(title:"Chance", style: .plain,
            target:self, action:#selector(pushNext))
        ni.rightBarButtonItem = b
    }
    self.navbar.pushItem(ni, animated:true)
}
5. 工具栏(UIToolbar)

工具栏(UIToolbar)显示一行UIBarButtonItems,即其items属性。这些项按照在items数组中出现的顺序从左到右显示。可以通过调用 setItems(_:animated:) 方法以动画方式设置项。工具栏内的项会自动定位,可以使用系统栏按钮项 .flexibleSpace .fixedSpace 以及UIBarButtonItem的width属性来干预定位。

6. 标签栏(UITabBar)

标签栏(UITabBar)显示标签栏项(UITabBarItem),即其items属性,每个项由图像和名称组成。可以通过调用 setItems(_:animated:) 方法以动画方式更改项。

标签栏维护其项中的当前选择,即其selectedItem属性,它是一个UITabBarItem,而不是索引编号。可以在代码中设置它,用户也可以通过点击标签栏项来设置。当用户更改选择时, tabBar(_:didSelect:) 消息会发送给委托(UITabBarDelegate)。

对标签栏项的布局控制非常有限:
- itemPositioning :有三种可能的值(UITabBar.ItemPositioning):
- .centered :项在中心聚集在一起。
- .fill :项均匀分布。
- .automatic :在iPad上与 .centered 相同,在iPhone上与 .fill 相同。
- itemSpacing :如果定位为 .centered ,则为项之间的间距。默认间距指定为0。
- itemWidth :如果定位为 .centered ,则为项的宽度。默认宽度指定为0。

可以设置一个图像,显示在所选标签栏项后面以指示其被选中,即标签栏的selectionIndicatorImage。

UITabBarItem可以通过以下方法创建:
- init(tabBarSystemItem:tag:)
- init(title:image:tag:)
- init(title:image:selectedImage:)

UITabBarItem是UIBarItem的子类,除了标题和图像外,还继承了通过 imageInsets 调整图像位置的能力,以及 isEnabled tag 属性。UITabBarItem本身添加了 selectedImage 属性,当该项被选中时,该图像会替换原来的图像。

可以为标签栏项分配一个备用的 landscapeImagePhone (继承自UIBarItem),用于iPhone的横向方向。但这样做会禁用 selectedImage ,可以将图像作为PDF矢量图像提供来解决这个问题。

标签栏项的图像默认被视为模板图像。其标题文本和模板图像在选中时用标签栏的tintColor着色,否则用其unselectedItemTintColor着色。要完全控制标题颜色(和其他文本属性),可以调用 setTitleTextAttributes(_:for:) (继承自UIBarItem)。要完全控制图像颜色,需要为图像和selectedImage提供 .alwaysOriginal 图像。

用户可以被允许更改标签栏的内容,通过调用 beginCustomizingItems(_:) 方法,传入一个可能或可能不在标签栏中出现的UITabBarItem数组。要监听自定义视图的出现和消失,需要实现委托方法:
- tabBar(_:willBeginCustomizing:)
- tabBar(_:didBeginCustomizing:)
- tabBar(_:willEndCustomizing:changed:)
- tabBar(_:didEndCustomizing:changed:)

当与UITabBarController结合使用时,会自动提供一个复杂的自定义界面。如果有很多项,标签栏的最后一个项会是“More”项,用户可以点击它通过表格视图访问其余项。可以通过UITabBarController的 moreNavigationController 访问这个导航控制器,进而访问根视图控制器和表格视图,从而自定义用户点击“More”按钮时的显示内容。

以下是一个自定义“More”列表导航栏的示例:

let more = self.tabBarController.moreNavigationController
let list = more.viewControllers[0]
list.title = ""
let b = UIBarButtonItem()
b.title = "Back"
list.navigationItem.backBarButtonItem = b
more.navigationBar.barTintColor = .red
more.navigationBar.tintColor = .white

还可以通过补充表格视图的数据源来自定义表格本身。以下是一个替换表格视图数据源的示例:

let tv = list.view as! UITableView
let mds = MyDataSource(originalDataSource: tv.dataSource!)
self.myDataSource = mds
tv.dataSource = mds

MyDataSource 中,可以使用消息转发来让它作为原始数据源的前端:

unowned let orig : UITableViewDataSource
init(originalDataSource:UITableViewDataSource) {
    self.orig = originalDataSource
}
override func forwardingTarget(for aSelector: Selector) -> Any? {
    if self.orig.responds(to:aSelector) {
        return self.orig
    }
    return nil
}

综上所述,iOS中的栏控件提供了丰富的定制选项,可以根据应用的需求进行个性化设置,以提升用户体验。通过合理运用这些特性和方法,开发者可以创建出美观、易用的界面。

iOS界面栏控件的使用与定制

7. 总结与对比

为了更清晰地了解各种栏控件的特点和使用方法,下面通过表格进行总结对比:
| 栏控件 | 填充元素 | 主要功能 | 定制点 |
| ---- | ---- | ---- | ---- |
| UINavigationBar | UINavigationItem | 管理导航栈,显示标题、按钮等导航信息 | 背景图像、阴影图像、返回按钮、标题属性等 |
| UIToolbar | UIBarButtonItem | 显示一行操作按钮 | 项的定位和宽度 |
| UITabBar | UITabBarItem | 提供多个标签页的切换 | 项的布局、选择指示器、文本和图像颜色等 |

8. 开发流程建议

在实际开发中,使用和定制这些栏控件可以遵循以下流程:

graph TD;
    A[确定需求] --> B[选择合适的栏控件];
    B --> C[创建栏控件实例];
    C --> D[定制外观];
    D --> E[添加栏项];
    E --> F[设置委托和响应方法];
    F --> G[测试和优化];
  • 确定需求 :明确应用中需要哪些栏控件以及它们的功能。
  • 选择合适的栏控件 :根据需求选择导航栏、工具栏或标签栏。
  • 创建栏控件实例 :使用相应的类创建栏控件对象。
  • 定制外观 :根据设计要求定制栏的样式,如背景颜色、图像等。
  • 添加栏项 :为栏控件添加按钮、标签等项。
  • 设置委托和响应方法 :处理用户的交互事件。
  • 测试和优化 :在不同设备和场景下测试,确保栏控件的功能和外观符合预期。
9. 注意事项

在使用和定制栏控件时,还需要注意以下几点:
- 图像处理 :栏按钮项和栏的图像默认是模板图像,需要根据需求设置为 .alwaysOriginal 以显示原始图像。
- 尺寸适配 :不同的尺寸类环境下,栏控件的尺寸会自动调整,需要确保定制的内容在各种尺寸下都能正常显示。
- 委托方法 :正确实现委托方法,以处理栏控件的各种事件,如导航项的推入和弹出、标签的选择等。
- 性能优化 :避免在栏控件的定制过程中使用过多的资源,影响应用的性能。

10. 常见问题及解决方案

以下是一些在使用栏控件时可能遇到的常见问题及解决方案:
| 问题 | 解决方案 |
| ---- | ---- |
| 栏按钮项的图像显示异常 | 检查图像是否为模板图像,是否需要设置为 .alwaysOriginal |
| 导航栏返回按钮不显示 | 检查topItem的hidesBackButton和leftItemsSupplementBackButton属性 |
| 标签栏项的布局不符合预期 | 检查itemPositioning、itemSpacing和itemWidth属性的设置 |
| 栏的背景颜色显示不正常 | 检查isTranslucent属性和barTintColor的设置 |

11. 示例应用场景
  • 导航栏在电商应用中的应用 :在电商应用中,导航栏可以显示商品分类、搜索框和购物车按钮等。通过定制导航栏的外观和按钮,可以提升用户的购物体验。
// 创建导航栏
let navigationBar = UINavigationBar(frame: CGRect(x: 0, y: 0, width: view.bounds.width, height: 44))
// 设置背景颜色
navigationBar.barTintColor = .white
// 创建导航项
let navigationItem = UINavigationItem(title: "商品分类")
// 创建搜索按钮
let searchButton = UIBarButtonItem(barButtonSystemItem: .search, target: self, action: #selector(searchAction))
// 创建购物车按钮
let cartButton = UIBarButtonItem(image: UIImage(named: "cart"), style: .plain, target: self, action: #selector(cartAction))
// 设置导航项的右侧按钮
navigationItem.rightBarButtonItems = [searchButton, cartButton]
// 将导航项添加到导航栏
navigationBar.items = [navigationItem]
// 将导航栏添加到视图中
view.addSubview(navigationBar)
  • 标签栏在社交应用中的应用 :在社交应用中,标签栏可以提供主页、消息、我的等多个标签页的切换。通过定制标签栏的外观和选择指示器,可以让用户更方便地访问不同的功能。
// 创建标签栏
let tabBar = UITabBar(frame: CGRect(x: 0, y: view.bounds.height - 49, width: view.bounds.width, height: 49))
// 设置背景颜色
tabBar.barTintColor = .white
// 创建标签栏项
let homeItem = UITabBarItem(tabBarSystemItem: .featured, tag: 0)
let messageItem = UITabBarItem(tabBarSystemItem: .contacts, tag: 1)
let myItem = UITabBarItem(tabBarSystemItem: .more, tag: 2)
// 设置标签栏项的标题和图像
homeItem.title = "主页"
homeItem.image = UIImage(named: "home")
messageItem.title = "消息"
messageItem.image = UIImage(named: "message")
myItem.title = "我的"
myItem.image = UIImage(named: "my")
// 设置标签栏的项
tabBar.items = [homeItem, messageItem, myItem]
// 设置委托
tabBar.delegate = self
// 将标签栏添加到视图中
view.addSubview(tabBar)

通过以上的介绍和示例,我们可以看到iOS中的栏控件具有丰富的功能和定制选项。开发者可以根据应用的需求,灵活运用这些控件,打造出美观、易用的用户界面。在实际开发中,还需要不断地实践和探索,以充分发挥栏控件的优势。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值