我需要帮助编写一个objective-c函数,该函数将接收一组UIImages/PNG,并返回/保存所有垂直拼接在一起的图像的一个高图像。我是新手,所以请放慢脚步,放轻松:)
到目前为止我的想法:绘制一个 UIView,然后将Subviews 添加到每个人的父级(图像),然后 ???
我需要帮助编写一个objective-c函数,该函数将接收一组UIImages/PNG,并返回/保存所有垂直拼接在一起的图像的一个高图像。我是新手,所以请放慢脚步,放轻松:)
到目前为止我的想法:绘制一个 UIView,然后将Subviews 添加到每个人的父级(图像),然后 ???
下面是 Swift 3 和 Swift 2 将图像垂直或水平拼接在一起的示例。它们使用调用者提供的数组中最大图像的尺寸来确定每个单独的帧使用的通用尺寸,每个单独的图像被缝合到其中。
注意:Swift 3 示例保留了每个图像的纵横比,而 Swift 2 示例没有。请参阅下面的内联注释。
更新:添加了 Swift 3 示例
斯威夫特 3:
import UIKit
import AVFoundation
func stitchImages(images: [UIImage], isVertical: Bool) -> UIImage {
var stitchedImages : UIImage!
if images.count > 0 {
var maxWidth = CGFloat(0), maxHeight = CGFloat(0)
for image in images {
if image.size.width > maxWidth {
maxWidth = image.size.width
}
if image.size.height > maxHeight {
maxHeight = image.size.height
}
}
var totalSize : CGSize
let maxSize = CGSize(width: maxWidth, height: maxHeight)
if isVertical {
totalSize = CGSize(width: maxSize.width, height: maxSize.height * (CGFloat)(images.count))
} else {
totalSize = CGSize(width: maxSize.width * (CGFloat)(images.count), height: maxSize.height)
}
UIGraphicsBeginImageContext(totalSize)
for image in images {
let offset = (CGFloat)(images.index(of: image)!)
let rect = AVMakeRect(aspectRatio: image.size, insideRect: isVertical ?
CGRect(x: 0, y: maxSize.height * offset, width: maxSize.width, height: maxSize.height) :
CGRect(x: maxSize.width * offset, y: 0, width: maxSize.width, height: maxSize.height))
image.draw(in: rect)
}
stitchedImages = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
}
return stitchedImages
}
注意:下面的原始 Swift 2 示例不保留纵横比(例如,在 Swift 2 示例中,所有图像都被扩展以适合表示图像宽度和高度极值的边界框,因此任何非方形图像可以在其中一个维度上不成比例地拉伸)。如果您使用的是 Swift 2 并希望保留纵横比,请使用
AVMakeRect()
Swift 3 示例中的修改。由于我不再可以访问 Swift 2 游乐场并且无法对其进行测试以确保没有错误,因此我不会在此处更新 Swift 2 示例。
Swift 2:(不保留纵横比。在上面的 Swift 3 示例中已修复)
import UIKit
import AVFoundation
func stitchImages(images: [UIImage], isVertical: Bool) -> UIImage {
var stitchedImages : UIImage!
if images.count > 0 {
var maxWidth = CGFloat(0), maxHeight = CGFloat(0)
for image in images {
if image.size.width > maxWidth {
maxWidth = image.size.width
}
if image.size.height > maxHeight {
maxHeight = image.size.height
}
}
var totalSize : CGSize, maxSize = CGSizeMake(maxWidth, maxHeight)
if isVertical {
totalSize = CGSizeMake(maxSize.width, maxSize.height * (CGFloat)(images.count))
} else {
totalSize = CGSizeMake(maxSize.width * (CGFloat)(images.count), maxSize.height)
}
UIGraphicsBeginImageContext(totalSize)
for image in images {
var rect : CGRect, offset = (CGFloat)((images as NSArray).indexOfObject(image))
if isVertical {
rect = CGRectMake(0, maxSize.height * offset, maxSize.width, maxSize.height)
} else {
rect = CGRectMake(maxSize.width * offset, 0 , maxSize.width, maxSize.height)
}
image.drawInRect(rect)
}
stitchedImages = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
}
return stitchedImages
}
正常的方法是创建一个位图图像上下文,在需要的位置绘制图像,然后从图像上下文中获取图像。
您可以使用 UIKit 来执行此操作,这有点容易,但不是线程安全的,因此需要在主线程中运行并且会阻塞 UI。
有很多示例代码,但如果你想正确理解它,你应该查看 UIGraphicsContextBeginImageContext、UIGraphicsGetCurrentContext、UIGraphicsGetImageFromCurrentImageContext 和 UIImageDrawInRect。不要忘记 UIGraphicsPopCurrentContext。
您也可以使用 AFAIK 的 Core Graphics 来执行此操作,可以安全地在后台线程上使用(我还没有因为它崩溃)。效率大致相同,因为 UIKit 只是在后台使用 CG。关键字是 CGBitmapContextCreate、CGContextDrawImage、CGBitmapContextCreateImage、CGContextTranslateCTM、CGContextScaleCTM 和 CGContextRelease(核心图形没有 ARC)。缩放和平移是因为 CG 的原点在右下角,Y 向上增加。
还有第三种方法,即使用 CG 作为上下文,但使用 CALayer 省去所有坐标的痛苦,将 CGImage ( UIImage.CGImage) 设置为内容,然后将图层渲染到上下文。这仍然是线程安全的,并让层负责所有的转换。关键字是-renderInContext:
我知道我在这里有点晚了,但希望这可以帮助别人。如果您尝试从数组中创建一个大图像,您可以使用此方法
- (UIImage *)mergeImagesFromArray: (NSArray *)imageArray {
if ([imageArray count] == 0) return nil;
UIImage *exampleImage = [imageArray firstObject];
CGSize imageSize = exampleImage.size;
CGSize finalSize = CGSizeMake(imageSize.width, imageSize.height * [imageArray count]);
UIGraphicsBeginImageContext(finalSize);
for (UIImage *image in imageArray) {
[image drawInRect: CGRectMake(0, imageSize.height * [imageArray indexOfObject: image],
imageSize.width, imageSize.height)];
}
UIImage *finalImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return finalImage;
}
斯威夫特 4
extension Array where Element: UIImage {
func stitchImages(isVertical: Bool) -> UIImage {
let maxWidth = self.compactMap { $0.size.width }.max()
let maxHeight = self.compactMap { $0.size.height }.max()
let maxSize = CGSize(width: maxWidth ?? 0, height: maxHeight ?? 0)
let totalSize = isVertical ?
CGSize(width: maxSize.width, height: maxSize.height * (CGFloat)(self.count))
: CGSize(width: maxSize.width * (CGFloat)(self.count), height: maxSize.height)
let renderer = UIGraphicsImageRenderer(size: totalSize)
return renderer.image { (context) in
for (index, image) in self.enumerated() {
let rect = AVMakeRect(aspectRatio: image.size, insideRect: isVertical ?
CGRect(x: 0, y: maxSize.height * CGFloat(index), width: maxSize.width, height: maxSize.height) :
CGRect(x: maxSize.width * CGFloat(index), y: 0, width: maxSize.width, height: maxSize.height))
image.draw(in: rect)
}
}
}
}
试试这段代码,我尝试将两个图像拼接在一起并在 ImageView 中显示它们
UIImage *bottomImage = [UIImage imageNamed:@"bottom.png"]; //first image
UIImage *image = [UIImage imageNamed:@"top.png"]; //foreground image
CGSize newSize = CGSizeMake(209, 260); //size of image view
UIGraphicsBeginImageContext( newSize );
// drawing 1st image
[bottomImage drawInRect:CGRectMake(0,0,newSize.width/2,newSize.height/2)];
// drawing the 2nd image after the 1st
[image drawInRect:CGRectMake(0,newSize.height/2,newSize.width/2,newSize.height/2)] ;
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
join.image = newImage;
join 是图像视图的名称,您可以将图像视为单个图像。
上面的解决方案很有帮助,但对我来说有一个严重的缺陷。问题是,如果图像的大小不同,则生成的拼接图像可能在部分之间有很大的空间。我提出的解决方案将所有图像组合在一起,使其看起来更像单个图像,无论单个图像大小如何。
对于 Swift 3.x
static func combineImages(images:[UIImage]) -> UIImage
{
var maxHeight:CGFloat = 0.0
var maxWidth:CGFloat = 0.0
for image in images
{
maxHeight += image.size.height
if image.size.width > maxWidth
{
maxWidth = image.size.width
}
}
let finalSize = CGSize(width: maxWidth, height: maxHeight)
UIGraphicsBeginImageContext(finalSize)
var runningHeight: CGFloat = 0.0
for image in images
{
image.draw(in: CGRect(x: 0.0, y: runningHeight, width: image.size.width, height: image.size.height))
runningHeight += image.size.height
}
let finalImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return finalImage!
}