问题描述
Swift 5 / Xcode 12.4
我有一个 png 图像,该图像已下载到 Documents
文件夹中,然后在运行时加载(当前为 UIImage
)。此图像必须充当某种类型的地图:
- 双指缩放
- 盘
- 我想在特定地点放置某种类型的地图标记(例如一个点):用户可以点击它们(打开一个包含更多信息的弹出窗口),它们根据缩放/平移移动但始终保持不变大小。
- 不是全屏,而是在我的
ViewController
的特定区域内。
@H_404_19@
- 使用“标准”滚动视图缩放/平移功能
- 在滚动视图中使用图像视图作为
viewForZooming
- 将“标记视图”添加到滚动视图作为图像视图的兄弟(而不是图像视图的子视图)
我已经在 Android 中做了同样的事情,但是我发现的所有 Java 地图库都需要图块(我只有一个大图像),所以我最终使用了一个“缩放/平移”库(也可以让你设置最大缩放)并为标记创建了我自己的不可见图像子层。
对于 iOS,到目前为止我已经找到了 Goggle Maps SDK 和 Apple MapKit,但它们看起来都像是加载了 rl 地图数据并且您无法设置实际图像 - 这是否可以通过以下任一方式实现?他们?
我还没有找到适用于 iOS 的缩放/平移库(至少不是 5 年以上的),那么我该如何最好地完成此操作?编写我自己的缩放/平移侦听器并使用某种类型的子图层(与父图层一起移动)作为地图标记 - 这是要走的路吗/我必须使用哪些 UI 对象?
解决方法
这将有助于缩放 - https://stackoverflow.com/a/58558272/2481602
这将帮助您实现平底锅 - How do I pan the image inside a UIImageView?
就强加标记而言,您必须手动处理转换并应用标记图像的锚点。此处的文档 - https://developer.apple.com/documentation/coregraphics/cgaffinetransform 这提供了一个松散的解释 - https://medium.com/weeronline/swift-transforms-5981398b437d
它不是全屏只需要一个视图来保存滚动视图,该视图将在屏幕上您想要的位置保存地图。
不是一个完整的答案,但希望这都能为您指明正确的方向
,使用 UIScrollView 进行捏/缩放/平移。要添加标记,请将它们添加到滚动视图顶部的容器视图,并通过更新容器中注释视图的位置来响应滚动视图更改(scrollViewDidEndZooming、scrollViewDidZoom、scrollViewDidEndDragging...) - 您需要使用 UIView转换以在坐标系之间进行转换,将注释视图的中心设置为从滚动视图转换为容器视图的适当点。容器视图应该与滚动视图的大小相同,而不是滚动视图的内容。 或者,您可以将注释添加到滚动视图的内容中,但随后您必须更新这些视图的变换以在放大时反放大它们。
,一种方法...
对于标记位置,计算百分比位置。因此,例如,如果您的图像是 1000x1000,而您想要一个位于 100,200
的标记,那么该标记的“点百分比”将为 0.1,0.2
。当图像视图被缩放时,通过其位置百分比更改标记的框架原点。
这是一个完整的例子(完成得很快,所以只是为了让你开始)......
我使用了这张带有标记位置的 1600 x 1600
图像:
一个简单的“标记视图”类:
class MarkerView: UILabel {
var yPCT: CGFloat = 0
var xPCT: CGFloat = 0
}
和控制器类:
class ZoomWithMarkersViewController: UIViewController,UIScrollViewDelegate {
let imgView: UIImageView = {
let v = UIImageView()
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
let scrollView: UIScrollView = {
let v = UIScrollView()
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
var points: [CGPoint] = [
CGPoint(x: 200,y: 200),CGPoint(x: 800,y: 300),CGPoint(x: 500,y: 700),CGPoint(x: 1100,y: 900),CGPoint(x: 300,y: 1200),CGPoint(x: 1300,y: 1400),]
var markers: [MarkerView] = []
override func viewDidLoad() {
super.viewDidLoad()
// make sure we have an image
guard let img = UIImage(named: "points1600x1600") else {
fatalError("Could not load image!!!!")
}
// set the image
imgView.image = img
// add the image view to the scroll view
scrollView.addSubview(imgView)
// add scroll view to view
view.addSubview(scrollView)
// respect safe area
let safeG = view.safeAreaLayoutGuide
// to save on typing
let contentG = scrollView.contentLayoutGuide
NSLayoutConstraint.activate([
// scroll view inset 20-pts on each side
scrollView.leadingAnchor.constraint(equalTo: safeG.leadingAnchor,constant: 20.0),scrollView.trailingAnchor.constraint(equalTo: safeG.trailingAnchor,constant: -20.0),// square (1:1 ratio)
scrollView.heightAnchor.constraint(equalTo: scrollView.widthAnchor),// center vertically
scrollView.centerYAnchor.constraint(equalTo: safeG.centerYAnchor),// constrain all 4 sides of image view to scroll view's Content Layout Guide
imgView.topAnchor.constraint(equalTo: contentG.topAnchor),imgView.leadingAnchor.constraint(equalTo: contentG.leadingAnchor),imgView.trailingAnchor.constraint(equalTo: contentG.trailingAnchor),imgView.bottomAnchor.constraint(equalTo: contentG.bottomAnchor),// we will want zoom scale of 1 to show the "native size" of the image
imgView.widthAnchor.constraint(equalToConstant: img.size.width),imgView.heightAnchor.constraint(equalToConstant: img.size.height),])
// create marker views and
// add them as subviews of the scroll view
// add them to our array of marker views
var i: Int = 0
points.forEach { pt in
i += 1
let v = MarkerView()
v.textAlignment = .center
v.font = .systemFont(ofSize: 12.0)
v.text = "\(i)"
v.backgroundColor = UIColor.green.withAlphaComponent(0.5)
scrollView.addSubview(v)
markers.append(v)
v.yPCT = pt.y / img.size.height
v.xPCT = pt.x / img.size.width
v.frame = CGRect(origin: .zero,size: CGSize(width: 30.0,height: 30.0))
}
// assign scroll view's delegate
scrollView.delegate = self
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
print(#function)
guard let img = imgView.image else { return }
// max scale is 1.0 (original image size)
scrollView.maximumZoomScale = 1.0
// min scale fits the image in the scroll view frame
scrollView.minimumZoomScale = scrollView.frame.width / img.size.width
// start at min scale (so full image is visible)
scrollView.zoomScale = scrollView.minimumZoomScale
// just to make the markers "appear" nicely
markers.forEach { v in
v.center = CGPoint(x: scrollView.bounds.midX,y: scrollView.bounds.midY)
v.alpha = 0.0
}
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// animate the markers into position
UIView.animate(withDuration: 1.0,animations: {
self.markers.forEach { v in
v.alpha = 1.0
}
self.updateMarkers()
})
}
func updateMarkers() -> Void {
markers.forEach { v in
let x = imgView.frame.origin.x + v.xPCT * imgView.frame.width
let y = imgView.frame.origin.y + v.yPCT * imgView.frame.height
// for example:
// put bottom-left corner of marker at coordinates
v.frame.origin = CGPoint(x: x,y: y - v.frame.height)
// or
// put center of marker at coordinates
//v.center = CGPoint(x: x,y: y)
}
}
func scrollViewDidZoom(_ scrollView: UIScrollView) {
updateMarkers()
}
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return imgView
}
}
我正在放置标记,因此它们的左下角位于标记点。
它是这样开始的:
放大标记 #3 后看起来像这样: