Add scenic selection and cooperation order flows aligned with Android.

Upgrade scenic picker with search, location sorting, permission apply/status, and blocking home dialog; add cooperation order module and order source picker improvements with unit tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-07 10:48:43 +08:00
parent 3c4ca7eefb
commit 2bea05b1d9
50 changed files with 5603 additions and 191 deletions

View File

@ -0,0 +1,102 @@
//
// ImagePreviewViewController.swift
// suixinkan
//
import SnapKit
import UIKit
/// Android `MediaPreviewDialog`
final class ImagePreviewViewController: UIViewController {
private let imageURLs: [String]
private let startIndex: Int
private let scrollView = UIScrollView()
private let pageControl = UIPageControl()
init(imageURLs: [String], startIndex: Int) {
self.imageURLs = imageURLs
self.startIndex = min(max(0, startIndex), max(0, imageURLs.count - 1))
super.init(nibName: nil, bundle: nil)
modalPresentationStyle = .fullScreen
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .black
scrollView.isPagingEnabled = true
scrollView.showsHorizontalScrollIndicator = false
scrollView.delegate = self
pageControl.numberOfPages = imageURLs.count
pageControl.currentPage = startIndex
pageControl.isHidden = imageURLs.count <= 1
let closeButton = UIButton(type: .system)
closeButton.setImage(UIImage(systemName: "xmark"), for: .normal)
closeButton.tintColor = .white
closeButton.addTarget(self, action: #selector(closeTapped), for: .touchUpInside)
view.addSubview(scrollView)
view.addSubview(pageControl)
view.addSubview(closeButton)
scrollView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
pageControl.snp.makeConstraints { make in
make.bottom.equalTo(view.safeAreaLayoutGuide).inset(AppSpacing.md)
make.centerX.equalToSuperview()
}
closeButton.snp.makeConstraints { make in
make.top.equalTo(view.safeAreaLayoutGuide).offset(AppSpacing.sm)
make.trailing.equalToSuperview().inset(AppSpacing.md)
make.size.equalTo(AppSpacing.minTouchTarget)
}
imageURLs.enumerated().forEach { index, url in
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFit
imageView.loadRemoteImage(urlString: url, placeholderColor: .darkGray)
scrollView.addSubview(imageView)
imageView.snp.makeConstraints { make in
make.top.bottom.equalToSuperview()
make.width.equalTo(view.snp.width)
make.height.equalTo(view.snp.height)
make.leading.equalToSuperview().offset(CGFloat(index) * view.bounds.width)
}
}
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
scrollView.contentSize = CGSize(width: view.bounds.width * CGFloat(imageURLs.count), height: view.bounds.height)
scrollView.setContentOffset(CGPoint(x: view.bounds.width * CGFloat(startIndex), y: 0), animated: false)
scrollView.subviews.enumerated().forEach { index, subview in
subview.frame = CGRect(
x: view.bounds.width * CGFloat(index),
y: 0,
width: view.bounds.width,
height: view.bounds.height
)
}
}
@objc private func closeTapped() {
dismiss(animated: true)
}
}
extension ImagePreviewViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
guard view.bounds.width > 0 else { return }
let page = Int(round(scrollView.contentOffset.x / view.bounds.width))
pageControl.currentPage = page
}
}