SwiftUI TabView:scaledToFit与scaledToFill为何导致指示器显示差异?
问题原因分析
这两个缩放修饰符的布局行为差异,直接导致了TabView页面指示器的显示状态不同:
.scaledToFill()的行为:
这个修饰符会让Image在保持自身宽高比的前提下,尽可能填满父容器的可用空间,超出容器的部分会被自动裁剪。在你的代码中,TabView被设置了固定高度320,使用.scaledToFill()后,Image会完全撑满这个高度(宽度也会按比例拉伸或收缩),此时TabView的内容视图高度和自身frame高度完全匹配,页面指示器作为TabView的附属元素,能被正确布局在内容视图的底部可视区域内,所以正常显示。.scaledToFit()的行为:
这个修饰符是让Image在保持宽高比的前提下,缩小到刚好能完整放入父容器中,也就是说Image的实际尺寸会小于等于父容器的尺寸(当图片宽高比和容器宽高比不一致时,会出现留白)。此时TabView的内容视图高度是Image的实际高度,而非TabView自身的320高度,页面指示器会被放置在Image的底部位置,但因为TabView的frame被固定为320,指示器可能被推到了Image下方的留白区域之外,或者被clipShape裁剪掉,所以看起来就消失了。
如果不添加任何缩放修饰符,Image会因为.resizable()默认拉伸填满容器(和.scaledToFill()效果类似),所以指示器也能正常显示。
解决方法
如果想使用.scaledToFit()同时保留页面指示器,可以给Image额外添加一个占满容器的frame修饰符,强制让Image先填充父容器空间,再进行缩放适配:
TabView { ForEach(images, id: \.self) { image in Image(image) .resizable() .frame(maxWidth: .infinity, maxHeight: .infinity) // 强制占满容器 .scaledToFit() .clipped() // 可选,裁剪超出部分 } } .frame(height: 320) .clipShape(RoundedRectangle(cornerRadius: 10)) .tabViewStyle(.page)
这样TabView能正确识别内容视图的高度等于自身frame高度,页面指示器就会正常显示在底部了。
内容的提问来源于stack exchange,提问作者Seungsoo Han

