Skip to content

Commit caea2b2

Browse files
restauranttdyongxu
andauthored
fix: 🐛 [IOSSDKBUG-480]Popover view layout on iPad (SAP#905)
Co-authored-by: dyongxu <[email protected]>
1 parent d562369 commit caea2b2

File tree

4 files changed

+108
-20
lines changed

4 files changed

+108
-20
lines changed

Sources/FioriSwiftUICore/Models/ModelDefinitions.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,7 @@ public protocol OptionListPickerItemModel: OptionListPickerComponent {
554554
// sourcery: virtualPropKeyboardHeight = "@State var _keyboardHeight: CGFloat = 0.0"
555555
// sourcery: virtualPropDisableListEntriesSection = "var disableListEntriesSection: Bool = false"
556556
// sourcery: virtualPropAllowsDisplaySelectionCount = "var allowsDisplaySelectionCount: Bool = true"
557+
// sourcery: virtualPropBarItemFrame = "var barItemFrame: CGRect = .zero"
557558
public protocol SearchListPickerItemModel: OptionListPickerComponent {
558559
// sourcery: default.value = nil
559560
// sourcery: no_view

Sources/FioriSwiftUICore/Views/SearchListPickerItem+View.swift

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ public extension SearchListPickerItem {
1414
/// - updateSearchListPickerHeight: The closure to update the parent view.
1515
/// - disableListEntriesSection: A boolean value to indicate to disable entries section or not.
1616
/// - allowsDisplaySelectionCount: A boolean value to indicate to display selection count or not.
17-
init(value: Binding<[Int]>, valueOptions: [String] = [], hint: String? = nil, allowsMultipleSelection: Bool, allowsEmptySelection: Bool, isSearchBarHidden: Bool = false, disableListEntriesSection: Bool, allowsDisplaySelectionCount: Bool, onTap: ((_ index: Int) -> Void)? = nil, selectAll: ((_ isAll: Bool) -> Void)? = nil, updateSearchListPickerHeight: ((CGFloat) -> Void)? = nil) {
17+
/// - barItemFrame: The frame of the bar item, which toggle to show this view.
18+
init(value: Binding<[Int]>, valueOptions: [String] = [], hint: String? = nil, allowsMultipleSelection: Bool, allowsEmptySelection: Bool, isSearchBarHidden: Bool = false, disableListEntriesSection: Bool, allowsDisplaySelectionCount: Bool, barItemFrame: CGRect = .zero, onTap: ((_ index: Int) -> Void)? = nil, selectAll: ((_ isAll: Bool) -> Void)? = nil, updateSearchListPickerHeight: ((CGFloat) -> Void)? = nil) {
1819
self.init(value: value, valueOptions: valueOptions, hint: hint, onTap: onTap)
1920

2021
self.allowsMultipleSelection = allowsMultipleSelection
@@ -24,6 +25,7 @@ public extension SearchListPickerItem {
2425
self.updateSearchListPickerHeight = updateSearchListPickerHeight
2526
self.disableListEntriesSection = disableListEntriesSection
2627
self.allowsDisplaySelectionCount = allowsDisplaySelectionCount
28+
self.barItemFrame = barItemFrame
2729
}
2830
}
2931

@@ -83,12 +85,25 @@ extension SearchListPickerItem: View {
8385
return
8486
}
8587
DispatchQueue.main.async {
86-
let popverHeight = Screen.bounds.size.height
88+
let screenHeight = Screen.bounds.size.height
8789
let safeAreaInset = self.getSafeAreaInsets()
88-
var maxScrollViewHeight = popverHeight - self.additionalHeight() - safeAreaInset.top - (UIDevice.current.userInterfaceIdiom != .phone ? 250 : 30)
89-
maxScrollViewHeight -= self._keyboardHeight
90-
if self._keyboardHeight > 0 {
91-
maxScrollViewHeight -= 52
90+
var maxScrollViewHeight = screenHeight - self.additionalHeight()
91+
if UIDevice.current.userInterfaceIdiom != .phone {
92+
if self.barItemFrame.arrowDirection() == .top {
93+
maxScrollViewHeight -= (self.barItemFrame.maxY + 80)
94+
maxScrollViewHeight -= self._keyboardHeight
95+
} else if self.barItemFrame.arrowDirection() == .bottom {
96+
maxScrollViewHeight -= (screenHeight - self.barItemFrame.minY + 80) + safeAreaInset.bottom + 13
97+
if self._keyboardHeight > 0 {
98+
let keyboardItemHeight = (self._keyboardHeight - (screenHeight - self.barItemFrame.minY))
99+
if keyboardItemHeight > 0 {
100+
maxScrollViewHeight -= keyboardItemHeight
101+
}
102+
}
103+
}
104+
} else {
105+
maxScrollViewHeight -= (safeAreaInset.top + 30)
106+
maxScrollViewHeight -= self._keyboardHeight
92107
}
93108
self._height = min(scrollView.contentSize.height, maxScrollViewHeight)
94109
updateSearchListPickerHeight?(self._height)

Sources/FioriSwiftUICore/Views/SortFilter/FilterFeedbackBarItem+View.swift

Lines changed: 77 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ struct SliderMenuItem: View {
7272
@State var isSheetVisible = false
7373

7474
@State var detentHeight: CGFloat = 0
75+
@State var barItemFrame: CGRect = .zero
7576

7677
var onUpdate: () -> Void
7778

@@ -85,7 +86,7 @@ struct SliderMenuItem: View {
8586
.onTapGesture {
8687
self.isSheetVisible.toggle()
8788
}
88-
.popover(isPresented: self.$isSheetVisible) {
89+
.popover(isPresented: self.$isSheetVisible, arrowEdge: self.barItemFrame.arrowDirection()) {
8990
CancellableResettableDialogForm {
9091
SortFilterItemTitle(title: self.item.name)
9192
} cancelAction: {
@@ -120,6 +121,17 @@ struct SliderMenuItem: View {
120121
}
121122
.presentationDetents([.height(self.detentHeight)])
122123
}
124+
.ifApply(UIDevice.current.userInterfaceIdiom != .phone, content: { v in
125+
v.background(GeometryReader { geometry in
126+
Color.clear
127+
.onAppear {
128+
self.barItemFrame = geometry.frame(in: .global)
129+
}
130+
.onChange(of: geometry.frame(in: .global)) { newValue in
131+
self.barItemFrame = newValue
132+
}
133+
})
134+
})
123135
}
124136
}
125137

@@ -132,6 +144,7 @@ struct PickerMenuItem: View {
132144
@State var detentHeight: CGFloat = ((UIDevice.current.userInterfaceIdiom == .phone || UIDevice.current.userInterfaceIdiom != .phone) ? 88 : 0)
133145
let popoverWidth = 393.0
134146
@State var _keyboardHeight = 0.0
147+
@State var barItemFrame: CGRect = .zero
135148

136149
public init(item: Binding<SortFilterItem.PickerItem>, onUpdate: @escaping () -> Void) {
137150
self._item = item
@@ -163,7 +176,7 @@ struct PickerMenuItem: View {
163176
.onTapGesture {
164177
self.isSheetVisible.toggle()
165178
}
166-
.popover(isPresented: self.$isSheetVisible) {
179+
.popover(isPresented: self.$isSheetVisible, arrowEdge: self.barItemFrame.arrowDirection()) {
167180
CancellableResettableDialogForm {
168181
SortFilterItemTitle(title: self.item.name)
169182
} cancelAction: {
@@ -210,6 +223,17 @@ struct PickerMenuItem: View {
210223
}
211224
.presentationDetents([.height(self.detentHeight)])
212225
}
226+
.ifApply(UIDevice.current.userInterfaceIdiom != .phone, content: { v in
227+
v.background(GeometryReader { geometry in
228+
Color.clear
229+
.onAppear {
230+
self.barItemFrame = geometry.frame(in: .global)
231+
}
232+
.onChange(of: geometry.frame(in: .global)) { newValue in
233+
self.barItemFrame = newValue
234+
}
235+
})
236+
})
213237
}
214238

215239
@ViewBuilder
@@ -245,7 +269,7 @@ struct PickerMenuItem: View {
245269
.onTapGesture {
246270
self.isSheetVisible.toggle()
247271
}
248-
.popover(isPresented: self.$isSheetVisible) {
272+
.popover(isPresented: self.$isSheetVisible, arrowEdge: self.barItemFrame.arrowDirection()) {
249273
CancellableResettableDialogNavigationForm {
250274
SortFilterItemTitle(title: self.item.name)
251275
} cancelAction: {
@@ -276,7 +300,7 @@ struct PickerMenuItem: View {
276300
})
277301
.buttonStyle(ApplyButtonStyle())
278302
} components: {
279-
SearchListPickerItem(value: self.$item.workingValue, valueOptions: self.item.valueOptions, hint: nil, allowsMultipleSelection: self.item.allowsMultipleSelection, allowsEmptySelection: self.item.allowsEmptySelection, isSearchBarHidden: self.item.isSearchBarHidden, disableListEntriesSection: self.item.disableListEntriesSection, allowsDisplaySelectionCount: self.item.allowsDisplaySelectionCount) { index in
303+
SearchListPickerItem(value: self.$item.workingValue, valueOptions: self.item.valueOptions, hint: nil, allowsMultipleSelection: self.item.allowsMultipleSelection, allowsEmptySelection: self.item.allowsEmptySelection, isSearchBarHidden: self.item.isSearchBarHidden, disableListEntriesSection: self.item.disableListEntriesSection, allowsDisplaySelectionCount: self.item.allowsDisplaySelectionCount, barItemFrame: self.barItemFrame) { index in
280304
self.item.onTap(option: self.item.valueOptions[index])
281305
} selectAll: { isAll in
282306
self.item.selectAll(isAll)
@@ -297,6 +321,17 @@ struct PickerMenuItem: View {
297321
.frame(height: UIDevice.current.userInterfaceIdiom != .phone ? self.calculateDetentHeight() : nil)
298322
.presentationDetents([.height(self.calculateDetentHeight()), .medium, .large])
299323
}
324+
.ifApply(UIDevice.current.userInterfaceIdiom != .phone, content: { v in
325+
v.background(GeometryReader { geometry in
326+
Color.clear
327+
.onAppear {
328+
self.barItemFrame = geometry.frame(in: .global)
329+
}
330+
.onChange(of: geometry.frame(in: .global)) { newValue in
331+
self.barItemFrame = newValue
332+
}
333+
})
334+
})
300335
}
301336

302337
private func calculateDetentHeight() -> CGFloat {
@@ -369,6 +404,7 @@ struct DateTimeMenuItem: View {
369404
@State private var isSheetVisible: Bool = false
370405

371406
@State var detentHeight: CGFloat = 0
407+
@State var barItemFrame: CGRect = .zero
372408

373409
var onUpdate: () -> Void
374410

@@ -388,7 +424,7 @@ struct DateTimeMenuItem: View {
388424
.onTapGesture {
389425
self.isSheetVisible.toggle()
390426
}
391-
.popover(isPresented: self.$isSheetVisible) {
427+
.popover(isPresented: self.$isSheetVisible, arrowEdge: self.barItemFrame.arrowDirection()) {
392428
CancellableResettableDialogForm {
393429
SortFilterItemTitle(title: self.item.name)
394430
} cancelAction: {
@@ -448,6 +484,17 @@ struct DateTimeMenuItem: View {
448484
}
449485
.presentationDetents([.height(self.detentHeight)])
450486
}
487+
.ifApply(UIDevice.current.userInterfaceIdiom != .phone, content: { v in
488+
v.background(GeometryReader { geometry in
489+
Color.clear
490+
.onAppear {
491+
self.barItemFrame = geometry.frame(in: .global)
492+
}
493+
.onChange(of: geometry.frame(in: .global)) { newValue in
494+
self.barItemFrame = newValue
495+
}
496+
})
497+
})
451498
}
452499
}
453500

@@ -512,6 +559,7 @@ struct StepperMenuItem: View {
512559
@State var isSheetVisible = false
513560

514561
@State var detentHeight: CGFloat = 0
562+
@State var barItemFrame: CGRect = .zero
515563

516564
var onUpdate: () -> Void
517565

@@ -527,7 +575,7 @@ struct StepperMenuItem: View {
527575
.onTapGesture {
528576
self.isSheetVisible.toggle()
529577
}
530-
.popover(isPresented: self.$isSheetVisible) {
578+
.popover(isPresented: self.$isSheetVisible, arrowEdge: self.barItemFrame.arrowDirection()) {
531579
CancellableResettableDialogForm {
532580
SortFilterItemTitle(title: self.item.name)
533581
} cancelAction: {
@@ -598,6 +646,17 @@ struct StepperMenuItem: View {
598646
}
599647
.presentationDetents([.height(self.detentHeight)])
600648
}
649+
.ifApply(UIDevice.current.userInterfaceIdiom != .phone, content: { v in
650+
v.background(GeometryReader { geometry in
651+
Color.clear
652+
.onAppear {
653+
self.barItemFrame = geometry.frame(in: .global)
654+
}
655+
.onChange(of: geometry.frame(in: .global)) { newValue in
656+
self.barItemFrame = newValue
657+
}
658+
})
659+
})
601660
}
602661
}
603662

@@ -668,6 +727,18 @@ struct FullCFGMenuItem: View {
668727
}
669728
}
670729

730+
extension CGRect {
731+
/// Popover arrowEdge depends on bar item position,
732+
/// only return `.top` or `.bottom` in this case
733+
/// - Returns: Edge
734+
func arrowDirection() -> Edge {
735+
if self.minY > Screen.bounds.size.height / 2 {
736+
return .bottom
737+
}
738+
return .top
739+
}
740+
}
741+
671742
#Preview {
672743
VStack {
673744
Spacer()

Sources/FioriSwiftUICore/_generated/ViewModels/API/SearchListPickerItem+API.generated.swift

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,18 @@ public struct SearchListPickerItem {
99
var _valueOptions: [String]
1010
var _hint: String? = nil
1111
var _onTap: ((_ index: Int) -> Void)? = nil
12-
var updateSearchListPickerHeight: ((CGFloat) -> ())? = nil
13-
var selectAll: ((Bool) -> ())? = nil
12+
var barItemFrame: CGRect = .zero
1413
var disableListEntriesSection: Bool = false
15-
let popoverWidth = 393.0
16-
@State var _height: CGFloat = 44
17-
@State var _searchViewCornerRadius: CGFloat = 18
18-
@State var _searchText: String = ""
19-
var allowsEmptySelection: Bool = false
14+
var isSearchBarHidden: Bool = false
2015
var allowsMultipleSelection: Bool = false
16+
var allowsEmptySelection: Bool = false
17+
@State var _searchViewCornerRadius: CGFloat = 18
18+
var selectAll: ((Bool) -> ())? = nil
19+
let popoverWidth = 393.0
2120
var allowsDisplaySelectionCount: Bool = true
22-
var isSearchBarHidden: Bool = false
21+
@State var _searchText: String = ""
22+
var updateSearchListPickerHeight: ((CGFloat) -> ())? = nil
23+
@State var _height: CGFloat = 44
2324
@State var _keyboardHeight: CGFloat = 0.0
2425
public init(model: SearchListPickerItemModel) {
2526
self.init(value: Binding<[Int]>(get: { model.value }, set: { model.value = $0 }), valueOptions: model.valueOptions, hint: model.hint, onTap: model.onTap)

0 commit comments

Comments
 (0)