Swift generate single UIImage out of two UIImages side by side - ios

I found an answer for this same question for Objective-C #
Saving two UIImages side-by-side as one combined image? but not for Swift.
I want to combine two UIImages side-by-side to create one UIImage. Both images would be merged at the border and maintain original resolution(no screen-shots).
How can I save the two images as one image?
ImageA + ImageB = ImageC

You can use the idea from the linked question of using UIGraphicsContext and UIImage.draw to draw the 2 images side-by-side and then create a new UIImage from the context.
extension UIImage {
func mergedSideBySide(with otherImage: UIImage) -> UIImage? {
let mergedWidth = self.size.width + otherImage.size.width
let mergedHeight = max(self.size.height, otherImage.size.height)
let mergedSize = CGSize(width: mergedWidth, height: mergedHeight)
UIGraphicsBeginImageContext(mergedSize)
self.draw(in: CGRect(x: 0, y: 0, width: mergedWidth, height: mergedHeight))
otherImage.draw(in: CGRect(x: self.size.width, y: 0, width: mergedWidth, height: mergedHeight))
let mergedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return mergedImage
}
}
Usage (assuming leftImage and rightImage are both UIImages):
let mergedImage = leftImage.mergedSideBySide(with: rightImage)
Demo of the function (using SwiftUI for quicker display using previews):
struct Playground_Previews: PreviewProvider {
static let leftImage = UIColor.blue.image(CGSize(width: 128, height: 128))
static let rightImage = UIColor.red.image(CGSize(width: 128, height: 128))
static let mergedImage = leftImage.mergedSideBySide(with: rightImage) ?? UIImage()
static var previews: some View {
VStack(spacing: 10) {
Image(uiImage: leftImage)
Image(uiImage: rightImage)
Image(uiImage: mergedImage)
}
}
}
The UIColor.image function is from Create UIImage with solid color in Swift.

Related

In SwiftUI, how do I continuously update a view while a gesture is being preformed?

This is part of an ongoing attempt at teaching myself how to create a basic painting app in iOS, like MSPaint. I'm using SwiftUI and CoreImage to do this.
While I have my mind wrapped around the pixel manipulation in CoreImage (I've been looking at this), I'm not sure how to add a drag gesture to SwiftUI so that I can "paint".
With the drag gesture, I'd like to do this:
onBegin and onChanged:
send the current x,y position of my finger to the function handling the CoreImage manipulation;
receive and display the updated image;
repeat until gesture ends.
So in other words, continuously update the image as my finger moves.
UPDATE: I've taken a look at what Asperi below responded with, and added .gesture below .onAppear. However, this results in a warning "Modifying state during view update, this will cause undefined behavior."
struct ContentView: View {
#State private var image: Image?
#GestureState var location = CGPoint(x: 0, y: 0)
var body: some View {
VStack {
image?
.resizable()
.scaledToFit()
}
.onAppear(perform: newPainting)
.gesture(
DragGesture()
.updating($location) { (value, gestureState, transaction) in
gestureState = value.location
paint(location: location)
}
)
}
func newPainting() {
guard let newPainting = createBlankCanvas(size: CGSize(width: 128, height: 128)) else {
print("failed to create a blank canvas")
return
}
image = Image(uiImage: newPainting)
}
func createBlankCanvas(size: CGSize, filledWithColor color: UIColor = UIColor.clear, scale: CGFloat = 0.0, opaque: Bool = false) -> UIImage? {
let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
UIGraphicsBeginImageContextWithOptions(size, opaque, scale)
color.set()
UIRectFill(rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
func paint(location: CGPoint) {
// do the CoreImage manipulation here
// now, take the output of the CI manipulation and
// attempt to get a CGImage from our CIImage
if let cgimg = context.createCGImage(outputImage, from: outputImage.extent) {
// convert that to a UIImage
let uiImage = UIImage(cgImage: cgimg)
// and convert that to a SwiftUI image
image = Image(uiImage: uiImage) // <- Modifying state during view update, this will cause undefined behavior.
}
}
}
Where do I add the gesture and have it repeatedly call the paint() func?
How do I get view to update continuously as long as the gesture continues?
Thank you!
You shouldn't store SwiftUI views(like Image) inside #State variables. Instead you should store UIImage:
struct ContentView: View {
#State private var uiImage: UIImage?
#GestureState var location = CGPoint(x: 0, y: 0)
var body: some View {
VStack {
uiImage.map { uiImage in
Image(uiImage: uiImage)
.resizable()
.scaledToFit()
}
}
.onAppear(perform: newPainting)
.gesture(
DragGesture()
.updating($location) { (value, gestureState, transaction) in
gestureState = value.location
paint(location: location)
}
)
}
func newPainting() {
guard let newPainting = createBlankCanvas(size: CGSize(width: 128, height: 128)) else {
print("failed to create a blank canvas")
return
}
uiImage = newPainting
}
func createBlankCanvas(size: CGSize, filledWithColor color: UIColor = UIColor.clear, scale: CGFloat = 0.0, opaque: Bool = false) -> UIImage? {
let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
UIGraphicsBeginImageContextWithOptions(size, opaque, scale)
color.set()
UIRectFill(rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
func paint(location: CGPoint) {
// do the CoreImage manipulation here
// now, take the output of the CI manipulation and
// attempt to get a CGImage from our CIImage
if let cgimg = context.createCGImage(outputImage, from: outputImage.extent) {
// convert that to a UIImage
uiImage = UIImage(cgImage: cgimg)
}
}
}

Is it possible to use UIKit extensions in SwiftUI?

I have a nice UIImage extension that renders circular images in high quality using less memory. I want to either use this extension or re-create it in SwiftUI so I can use it. The problem is I am very new to SwiftUI and am not sure if it is even possible. Is there a way to use this?
Here's the extension:
extension UIImage {
class func circularImage(from image: UIImage, size: CGSize) -> UIImage? {
let scale = UIScreen.main.scale
let circleRect = CGRect(x: 0, y: 0, width: size.width * scale, height: size.height * scale)
UIGraphicsBeginImageContextWithOptions(circleRect.size, false, scale)
let circlePath = UIBezierPath(roundedRect: circleRect, cornerRadius: circleRect.size.width/2.0)
circlePath.addClip()
image.draw(in: circleRect)
if let roundImage = UIGraphicsGetImageFromCurrentImageContext() {
return roundImage
}
return nil
}
}
You can create your UIImage like normal.
Then, just convert this to a SwiftUI image with:
Image(uiImage: image)
Do not initialize your UIImage in the view body or initializer, as this can be quite expensive - instead do it on appear with onAppear(perform:).
Example:
struct ContentView: View {
#State private var circularImage: UIImage?
var body: some View {
VStack {
Text("Hello world!")
if let circularImage = circularImage {
Image(uiImage: circularImage)
}
}
.onAppear {
guard let image: UIImage = UIImage(named: "background") else { return }
circularImage = UIImage.circularImage(from: image, size: CGSize(width: 100, height: 100))
}
}
}

How can I animate a series of images in SwiftUI?

I am new to SwiftUI and iOS development (I started learning 2 days ago). Before this I was doing Android apps. I learned some of the basics but I can't figure it out how can I animate several images. I need to display a series of images with 1.2 seconds delay before changing them in a frame.
I found some code here, but they use UIImage which is the old framework and the animation can't be stopped.
import SwiftUI
struct SplashScreen: UIViewRepresentable {
let imageSize: CGSize
let imageNames: [String]
let duration: Double
func makeUIView(context: Self.Context) -> UIView {
let containerView = UIView(frame: CGRect(x: 0, y: 0
, width: imageSize.width, height: imageSize.height))
let animationImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: imageSize.width, height: imageSize.height))
animationImageView.clipsToBounds = true
animationImageView.layer.cornerRadius = 5
animationImageView.autoresizesSubviews = true
animationImageView.animationRepeatCount = 3
animationImageView.contentMode = UIView.ContentMode.scaleAspectFill
var images = [UIImage]()
imageNames.forEach { imageName in
if let img = UIImage(named: imageName) {
images.append(img)
}
}
animationImageView.image = UIImage.animatedImage(with: images, duration: duration)
animationImageView.image = animationImageView.animationImages?.last!
containerView.addSubview(animationImageView)
return containerView
}
func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<SplashScreen>) {
}
}
This code is from Julio Bailon.
I couldn't find any tutorial which shows how to do this in SwiftUI.
**1. Animate the images like 3 times.
On end, launch another ViewController without the possibility of going back. (I am thinking like activities on Android but I don't know how much that helps)**

What is the most optimized way of changing alpha of blended images in CoreImage in Swift

I want to blend two images with darkenBlendMode and using Slider to manipulate alpha of first and second image. Unfortunately my method is laggy on iPhone 6S. I know that problem is in calling "loadImage" function in every Slider touch but I have no idea how to make it less aggravating. Have you any idea how to repair it?
extension UIImage {
func alpha(_ value:CGFloat) -> UIImage {
UIGraphicsBeginImageContextWithOptions(size, false, scale)
draw(at: CGPoint.zero, blendMode: .normal, alpha: value)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
}
struct ContentView: View {
#State private var image: Image?
#State private var opacity: CGFloat = 0
let context = CIContext()
var body: some View {
return VStack(alignment: .center) {
image?
.resizable()
.padding(14.0)
.scaledToFit()
Slider(value: Binding(
get: {
self.opacity
},
set: {(newValue) in
self.opacity = newValue
self.loadImage()
}
), in: -0.5...0.5)
}
.onAppear(perform: loadImage)
}
func loadImage(){
let uiInputImage = UIImage(named: "photo1")!.alpha(0.5 + opacity)
let uiBackgroundInputImage = UIImage(named: "photo2")!.alpha(0.5 - opacity)
let ciInputImage = CIImage(image: uiInputImage)
let ciBackgroundInputImage = CIImage(image: uiBackgroundInputImage)
let currentFilter = CIFilter.darkenBlendMode()
currentFilter.inputImage = ciInputImage
currentFilter.backgroundImage = ciBackgroundInputImage
guard let blendedImage = currentFilter.outputImage else {return}
if let cgBlendedImage = context.createCGImage(blendedImage, from: blendedImage.extent){
let uiblendedImage = UIImage(cgImage: cgBlendedImage)
image = Image(uiImage: uiblendedImage)
}
}
}
You could use the CIColorMatrix filter to change the alpha of the images without loading the bitmap data again:
let colorMatrixFilter = CIFilter.colorMatrix()
colorMatrixFilter.inputImage = ciInputImage // no need to load that every time
colorMatrixFilter.inputAVector = CIVector(x: 0, y: 0, z: 0, w: 0.5 + opacity)
let semiTransparentImage = colorMatrixFilter.outputImage
// perform blending

In Swift, How to add Badges on UIImageView in TableViewCell in TableView?

I am developing a social networking site where I have a TableView with multiple cells(rows), in each cell I have a "comment" and "like" Imageview, so whenever update comes from the server I have to increment the number of "comment" and "like" through badges. So how to add badges on UIImageView in TableViewCell in Swift
You can add Badge on UIImageView this way
func drawImageView(mainImage: UIImage, withBadge badge: UIImage) -> UIImage
{
UIGraphicsBeginImageContextWithOptions(mainImage.size, false, 0.0)
mainImage.drawInRect(CGRectMake(0, 0, mainImage.size.width, mainImage.size.height))
badge.drawInRect(CGRectMake(mainImage.size.width - badge.size.width, 0, badge.size.width, badge.size.height))
let resultImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return resultImage
}
and then use this function
let mainImage = UIImage(named: "main_image_name_here")
let badgeImage = UIImage(named: "badge_icon_name_here")
let myBadgedImage: UIImage = drawImageView(mainImage!, withBadge: badgeImage!)
myImageView.image = myBadgedImage//set Image on ImageView
check out this MIBadgeButton-Swift
Good luck
RDC's answer in SWIFT 3
func drawImageView(mainImage: UIImage, withBadge badge: UIImage) -> UIImage {
UIGraphicsBeginImageContextWithOptions(mainImage.size, false, 0.0)
mainImage.draw(in: CGRect(x: 0, y: 0, width: mainImage.size.width, height: mainImage.size.height))
badge.draw(in: CGRect(x: mainImage.size.width - badge.size.width, y: 0, width: badge.size.width, height: badge.size.height))
let resultImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return resultImage
}

Resources