how to save 2 images on top of each other (swift3) - ios

My code right now just saves 1 image. I would like to save a smaller image in the top left corner above the first image. The big thing is just to make it save together in the photo gallery. 2 images on top of each other.
import UIKit
class ViewController: UIViewController {
let imageView = UIImageView(frame: CGRect(x: 50, y: 50, width: 300, height: 300))
let imageView2 = UIImageView(frame: CGRect(x:200, y: 50, width: 100, height: 100))
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let image = UIImage(named: "wall")!
imageView.image = image
let image2 = UIImage(named: "pic")!
imageView2.image = image2
imageView.addSubview(imageView2)
imageView2.image = image2
imageView.addSubview(imageView2)
let topImage = UIImage(named: "wall")
let bottomImage = UIImage(named: "pic")
let size = CGSize(width: topImage!.size.width, height: topImage!.size.height + bottomImage!.size.height)
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
topImage!.draw(in: CGRect(x: 0, y: topImage!.size.height, width: size.width, height: bottomImage!.size.height))
bottomImage!.draw(in: CGRect(x: 0, y: topImage!.size.height, width: size.width, height: bottomImage!.size.height))
//your new Image
let newImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
}
#IBAction func press(_ sender: Any) {
UIImageWriteToSavedPhotosAlbum(imageView.image!, self, #selector(image(_:didFinishSavingWithError:contextInfo:)), nil)
}
func image(_ image: UIImage, didFinishSavingWithError error: Error?, contextInfo: UnsafeRawPointer) {
if let error = error {
// we got back an error!
let ac = UIAlertController(title: "Save error", message: error.localizedDescription, preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "OK", style: .default))
present(ac, animated: true)
} else {
let ac = UIAlertController(title: "Saved!", message: "Your altered image has been saved to your photos.", preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "OK", style: .default))
present(ac, animated: true)
}}}
pic

let imageView2 = UIImageView(frame: CGRect(x:200, y: 50, width: 100, height:100))
let image2 = UIImage(named: "wallsmallImage")!
imageView2.image = image2
imageView.addSub(imageView2)
#Now merge both images
let image = UIImage(named: "wall")!
imageView.image = image
let image2 = UIImage(named: "pic")!
imageView2.image = image2
imageView.addSubview(imageView2)
let topImage = UIImage(named: "wall")
let bottomImage = UIImage(named: "pic")
let size = CGSize(width: topImage!.size.width, height: topImage!.size.height + bottomImage!.size.height)
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
topImage!.draw(in: CGRect(x: 0, y: topImage!.size.height, width: size.width, height: bottomImage!.size.height))
bottomImage!.draw(in: CGRect(x: 0, y: topImage!.size.height, width: size.width, height: bottomImage!.size.height))
//your new Image
let newImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()

All of this code goes into a different function to add it to a image view.
let bottomImage:UIImage = UIImage(named: "backdrop")!
let topImage:UIImage = UIImage(named:"wall")!
let topImage2:UIImage = UIImage(named:"wall")!
let tpp:UIImage = letImageVIEW.image!
let right:UIImage = rightIMAGEVIEW.image!
// Change here the new image size if you want
let newSize = CGSize(width: bottomImage.size.width, height: bottomImage.size.height )
let newSize2 = CGSize(width: bottomImage.size.width, height: bottomImage.size.height)
UIGraphicsBeginImageContextWithOptions(newSize, false, bottomImage.scale)
// bottomImage.draw(in: CGRect(x: 0,y: 0,width: newSize.width,height: newSize.height))
tpp.draw(in: CGRect(x: 0,y: 0,width: newSize2.width,height: newSize2.height), blendMode:CGBlendMode.normal, alpha:1.0)
right.draw(in: CGRect(x: 0,y: 0,width: newSize2.width,height: newSize2.height), blendMode:CGBlendMode.normal, alpha:0.2)
topImage.draw(in: CGRect(x: 0,y: 0,width: newSize.width/2,height: newSize.height/2), blendMode:CGBlendMode.normal, alpha:1.0)
topImage2.draw(in: CGRect(x: 50,y: 0,width: newSize2.width/4,height: newSize2.height/4), blendMode:CGBlendMode.normal, alpha:1.0)
let newImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
finalImage.image = newImage

Related

How to make image to watermarkimage in swift [duplicate]

I know there are several other ways to do this; I don't want to import anything that I don't need to. If someone can help me with his code, that would be great.
Currently, it is only saving the original image without the watermark image.
extension UIImage {
class func imageWithWatermark(image1: UIImageView, image2: UIImageView) -> UIImage {
UIGraphicsBeginImageContextWithOptions(image1.bounds.size, false, 0.0)
image2.layer.renderInContext(UIGraphicsGetCurrentContext()!)
image1.layer.renderInContext(UIGraphicsGetCurrentContext()!)
let img = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return img
}
}
func addWatermark() {
let newImage = UIImage.imageWithWatermark(imageView, image2: watermarkImageView)
UIImageWriteToSavedPhotosAlbum(newImage, nil, nil, nil)
}
EDIT: I've got the watermark appearing on the saved images.
I had to switch the order of the layers:
image1.layer.renderInContext(UIGraphicsGetCurrentContext()!)
image2.layer.renderInContext(UIGraphicsGetCurrentContext()!)
HOWEVER, it is not appearing in the correct place.It seems to always appear in the center of the image.
If you grab the UIImageViews' images you could use the following concept:
if let img = UIImage(named: "image.png"), img2 = UIImage(named: "watermark.png") {
let rect = CGRect(x: 0, y: 0, width: img.size.width, height: img.size.height)
UIGraphicsBeginImageContextWithOptions(img.size, true, 0)
let context = UIGraphicsGetCurrentContext()
CGContextSetFillColorWithColor(context, UIColor.whiteColor().CGColor)
CGContextFillRect(context, rect)
img.drawInRect(rect, blendMode: .Normal, alpha: 1)
img2.drawInRect(CGRectMake(x,y,width,height), blendMode: .Normal, alpha: 1)
let result = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
UIImageWriteToSavedPhotosAlbum(result, nil, nil, nil)
}
SWIFT 4
Use this
let backgroundImage = imageData!
let watermarkImage = #imageLiteral(resourceName: "jodi_url_icon")
let size = backgroundImage.size
let scale = backgroundImage.scale
UIGraphicsBeginImageContextWithOptions(size, false, scale)
backgroundImage.draw(in: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height))
watermarkImage.draw(in: CGRect(x: 10, y: 10, width: size.width, height: size.height - 40))
let result = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
Use result to UIImageView, tested.

UISegmentedControl TintColor to Gradient Color

I am trying to set UIsegmentedControl tint color for the selected segment to gradient color and I am unable to do it
I am trying to follow this article https://www.bethedev.com/2019/02/set-gradient-tint-color-for-segmented.html
Trying to use this code:
segmentedControl.setTitleTextAttributes([NSAttributedString.Key.foregroundColor:UIColor.white],for: UIControl.State.normal)
segmentedControl.setTitleTextAttributes([NSAttributedString.Key.foregroundColor:UIColor.white],for: UIControl.State.selected)
fileprivate func updateGradientBackground() {
let sortedViews = segmentedControl.subviews.sorted( by: { $0.frame.origin.x < $1.frame.origin.x } )
for (_, view) in sortedViews.enumerated() {
// let gradientImage = gradient(size: segmentedControl.frame.size, color: [UIColor.cyan,UIColor.blue])!
view.backgroundColor = UIColor(patternImage: UIImage(named: "segmentedRectangle.png")!)
view.tintColor = UIColor.clear
}
}
I am expecting only one segment to be of the segmentedRectangle.png image color but it is displaying on the entire segmented control like this.
Try this code, I put comments on relevant parts. Let me know if you need more explanation.
let segmentedControl: UISegmentedControl = {
let view = UISegmentedControl(items: ["Pounds", "Kilograms"])
view.selectedSegmentIndex = 0
view.tintColor = .black
view.backgroundColor = .white
view.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width - 40, height: 20)
/// Gradient
let gradient = CAGradientLayer()
gradient.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width - 40, height: 20)
let leftColor = UIColor.red
let rightColor = UIColor.purple
gradient.colors = [leftColor.cgColor, rightColor.cgColor]
gradient.startPoint = CGPoint(x: 0, y: 0.5)
gradient.endPoint = CGPoint(x: 1.0, y: 0.5)
/// Create gradient image
UIGraphicsBeginImageContext(gradient.frame.size)
gradient.render(in: UIGraphicsGetCurrentContext()!)
let segmentedControlImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
// Normal Image
let rect: CGRect = CGRect(x: 0, y: 0, width: 1, height: 1)
UIGraphicsBeginImageContext(rect.size);
let context:CGContext = UIGraphicsGetCurrentContext()!;
context.setFillColor(UIColor.white.cgColor)
context.fill(rect)
let normalImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
/// Set segmentedControl image
view.setBackgroundImage(normalImage, for: .normal, barMetrics: .default)
view.setBackgroundImage(segmentedControlImage, for: .selected, barMetrics: .default)
return view
}()
Usage:
On your ViewDidLoad set navigationItem title view as your segmented control like so:-
self.navigationItem.titleView = segmentedControl
I think with few modifications/custominization you can get want you want, Cheers :)
StoryBoard/InterfaceBuilder
Just call this inside your ViewDidLoad and pass your outlet name on the function call: -
func configureSegementedControl(segmentedControl: UISegmentedControl) {
segmentedControl.selectedSegmentIndex = 0
segmentedControl.tintColor = .black
segmentedControl.backgroundColor = .white
segmentedControl.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width - 40, height: 20)
/// Gradient
let gradient = CAGradientLayer()
gradient.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width - 40, height: 20)
let leftColor = UIColor.red
let rightColor = UIColor.purple
gradient.colors = [leftColor.cgColor, rightColor.cgColor]
gradient.startPoint = CGPoint(x: 0, y: 0.5)
gradient.endPoint = CGPoint(x: 1.0, y: 0.5)
/// Create gradient image
UIGraphicsBeginImageContext(gradient.frame.size)
gradient.render(in: UIGraphicsGetCurrentContext()!)
let segmentedControlImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
// Normal Image
let rect: CGRect = CGRect(x: 0, y: 0, width: 1, height: 1)
UIGraphicsBeginImageContext(rect.size);
let context:CGContext = UIGraphicsGetCurrentContext()!;
context.setFillColor(UIColor.white.cgColor)
context.fill(rect)
let normalImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
/// Set segmentedControl image
segmentedControl.setBackgroundImage(normalImage, for: .normal, barMetrics: .default)
segmentedControl.setBackgroundImage(segmentedControlImage, for: .selected, barMetrics: .default)
}

how to layer an image over another

My code below kind of works but the frame is not symmetrical and I am using 2 images let bottomImage and let frame and only let frame should be used. To give you a example check out the image below I created. Frame should take the place of the black border.
#IBAction func Mask(_ sender: Any) {
let bottomImage:UIImage = UIImage(named: "backdropd")!
let frame:UIImage = UIImage(named: "ff")!
let newSize = CGSize(width: bottomImage.size.width, height: bottomImage.size.height )
let newSize2 = CGSize(width: bottomImage.size.width, height: bottomImage.size.height)
UIGraphicsBeginImageContextWithOptions(newSize, false, bottomImage.scale)
let left:UIImage = imageTake.image!
left.draw(in: CGRect(x: newSize2.width/7,y: newSize2.height/8.9,width: newSize2.width/1,height: newSize2.height/1.29), blendMode:CGBlendMode.normal, alpha:1.0)
frame.draw(in: CGRect(x: 0,y: 0,width: newSize2.width,height: newSize2.height), blendMode:CGBlendMode.normal, alpha:1.0)
let newImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
imageTake.image = newImage
}

Why is custom navigation bar button image stretched?

I tried to add a navigation bar button with custom image. However, whatever method I use, the image always appears stretched.
Method 1:
let barbuttonitem = UIBarButtonItem(image: UIImage(named: "apps_small"), style: .plain, target: self, action: nil)
navigationItem.leftBarButtonItem = barbuttonitem
It appears like this:
Method 2:
let button = UIButton(type: .custom)
button.setImage(UIImage(named: "apps_small"), for: .normal)
button.addTarget(self, action: #selector(TeachingRootViewController.appsTouched), for: .touchUpInside)
button.frame = CGRect(x: 0, y: 0, width: 10, height: 10)
button.bounds = CGRect(x: 0, y: 0, width: 10, height: 10)
navigationItem.leftBarButtonItem = UIBarButtonItem(customView: button)
It appears like this:
Method 3:
let button = UIButton(type: .custom)
button.setImage(UIImage(named: "apps_small"), for: .normal)
button.setTitle("Button", for: .normal)
button.frame = CGRect(x: 0, y: 0, width: 10, height: 10)
button.bounds = CGRect(x: 0, y: 0, width: 10, height: 10)
button.imageEdgeInsets = .init(top: 5, left: 5, bottom: 5, right: 300)
navigationItem.leftBarButtonItem = UIBarButtonItem(customView: button)
navigationItem.leftBarButtonItem?.imageInsets = .init(top: 5, left: 5, bottom: 5, right: 300)
It looks like this:
As you can see the title is gone.
In the UI Hierarchy, it looks like this:
It appears that the button has taken up all spaces in the navigation bar.
However, there is no problem for button with a system item:
Is there anybody who knows the reason for this problem? I think I have run out of ideas.
The above answer is a workaround and not a proper fix.
The correct answer is you should have generate your photos with the correct sizes
asset#1x: 22x22px
asset#2x: 44x44px
asset#3x: 66x66px
Try adding it as a subView instead of setting it as the leftBarButtonItem
var leftButton = UIButton(frame:CGRect(x: 0, y: 0, width: 10, height: 10))
var background = UIImageView(image: UIImage(named: "apps_small"))
background.frame = CGRect(x: 0, y: 0, width: 10, height: 10)
leftButton.addSubview(background)
self.navigationController.navigationBar.addSubview(leftButton)
As per my experience, You have faced this issue because you're added large size images for all resolution 1X, 2X and 3X. You need
to scale it down to a size more appropriate for the navigation bar.
Solution: 1
You need to scale it down to a size more appropriate for the navigation bar. Please have look image sizes for UINavigationBar
asset#1X Size: 24X24
asset#2X Size: 48X48
asset#3X Size: 72X72
Solution: 2
If you want to go with the same images then you need to do below changes in your code.
You just need to add UIImageView inside the UIView then everything
works as expected.
Code:
let containerView = UIControl(frame: CGRect.init(x: 0, y: 0, width: 30, height: 30))
containerView.addTarget(self, action: #selector(handleSearch), for: .touchUpInside)
let imageSearch = UIImageView(frame: CGRect.init(x: 0, y: 0, width: 30, height: 30))
imageSearch.image = UIImage(named: "search")
containerView.addSubview(imageSearch)
let searchBarButtonItem = UIBarButtonItem(customView: containerView)
navigationItem.rightBarButtonItem = searchBarButtonItem
It's all about the Image size that you put inside your UIButton.
If you are using UIButton as a custom view for
UIBarButtonItem(customView: button)
and inside that custom button, you are setting an image. It is better to set the image size to something you need.
let button = UIButton(type: .custom)
button.setImage(UIImage(named: "image")!.resizeImage(newWidth: 25), for: .normal)
And this is a good extension for resizing images in your app. it is using CoreGraphic to resize.
extension UIImage{
//Resize image
func resizeImage(newWidth: CGFloat) -> UIImage {
let originalImage = self
let scale = newWidth / self.size.width
let newHeight = self.size.height * scale
let newImage = self.resizeWithCoreGraphics(to: CGSize(width: newWidth, height: newHeight)) ?? originalImage
return newImage
}
private func resizeWithCoreGraphics(to newSize: CGSize) -> UIImage? {
guard let cgImage = cgImage, let colorSpace = cgImage.colorSpace else { return nil }
let width = Int(newSize.width)
let height = Int(newSize.height)
let bitsPerComponent = cgImage.bitsPerComponent
let bytesPerRow = cgImage.bytesPerRow
let bitmapInfo = cgImage.bitmapInfo
guard let context = CGContext(data: nil, width: width, height: height,
bitsPerComponent: bitsPerComponent,
bytesPerRow: bytesPerRow, space: colorSpace,
bitmapInfo: bitmapInfo.rawValue) else { return nil }
context.interpolationQuality = .high
let rect = CGRect(origin: CGPoint.zero, size: newSize)
context.draw(cgImage, in: rect)
return context.makeImage().flatMap { UIImage(cgImage: $0) }
}
}

Swift 3 draw uiimage programmatically

I don't have experience in Core Graphics but I need to draw a dynamic uiimage that look like these:
left
whole
(Actually I want the grey area to be clear. So the red color will look like floating)
This is the code I tried:
public extension UIImage {
public convenience init?(color: UIColor, size: CGSize = CGSize(width: 27, height: 5), isWhole: Bool = true) {
let totalHeight: CGFloat = 5.0
let topRectHeight: CGFloat = 1.0
//if (isWhole) {
let topRect = CGRect(origin: .zero, size: CGSize(width: size.width, height: topRectHeight))
UIGraphicsBeginImageContextWithOptions(topRect.size, false, 0.0)
color.setFill()
UIRectFill(topRect)
let bottomRect = CGRect(origin: CGPoint(x: 0, y: topRectHeight), size: CGSize(width: size.width, height: totalHeight - topRectHeight))
UIGraphicsBeginImageContextWithOptions(bottomRect.size, false, 0.0)
UIColor.blue.setFill()
UIRectFill(bottomRect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
guard let cgImage = image?.cgImage else { return nil }
self.init(cgImage: cgImage)
}
Here is example you can have first image if you set isWhole property to false and have second image if you set it to true. You can paste this code in viewDidLoad to test and play with it.
var isWhole = false
UIGraphicsBeginImageContextWithOptions(CGSize.init(width: 27, height: 5), false,0.0)
var context = UIGraphicsGetCurrentContext()
context?.setFillColor(UIColor.red.cgColor)
if(context != nil){
if(isWhole){
context?.fill(CGRect(x: 0, y: 0, width: 27, height: 2.5))
}
else{
context?.fill(CGRect(x: 0, y: 0, width: 13.5, height: 2.5))
}
context?.setFillColor(UIColor.gray.cgColor)
context?.fill(CGRect(x: 0, y: 2.5, width: 27, height: 2.5))
}
let newImage = UIGraphicsGetImageFromCurrentImageContext()
var imageView = UIImageView(frame: CGRect(x: 100, y: 200, width: 27, height: 5))
imageView.image = newImage
self.view.addSubview(imageView)
UIGraphicsEndImageContext()
If you need your red rectangle to be with rounder corners just change fill(rect:CGRect) with path like this:
if(isWhole){
context?.addPath(CGPath(roundedRect: CGRect(x: 0, y: 0, width: 27, height: 2.5), cornerWidth: 1, cornerHeight: 1, transform: nil))
context?.fillPath()
}
I'd recommend creating two paths in the first context that you create, i.e.
let topPath = UIBezierPath(rect: topRect)
color.setFill()
topPath.fill()
let bottomPath = UIBezierPath(rect: bottomRect)
UIColor.blue.setFill()
bottomPath.fill()
Then you can get the image from the current context.
I know you said in your comments that you can't use UIViews, but what you want "looks" easily done through views. Why not do that, then simply turn it into a UIImage?
override func viewDidLoad() {
super.viewDidLoad()
let imageContainer = UIView(frame: CGRect(x: 30, y: 40, width: 110, height: 60))
imageContainer.backgroundColor = view.backgroundColor
view.addSubview(imageContainer)
let redView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 30))
redView.backgroundColor = UIColor.red
let grayView = UIView(frame: CGRect(x: 10, y: 30, width: 100, height: 30))
grayView.backgroundColor = UIColor.gray
imageContainer.addSubview(redView)
imageContainer.addSubview(grayView)
let imageView = UIImageView(frame: CGRect(x: 100, y: 140, width: 200, height: 200))
imageView.contentMode = .scaleAspectFit
imageView.image = createImage(imageContainer)
view.addSubview(imageView)
}
func createImage(_ view: UIView) -> UIImage {
UIGraphicsBeginImageContextWithOptions(
CGSize(width: view.frame.width, height: view.frame.height), true, 1)
view.layer.render(in: UIGraphicsGetCurrentContext()!)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image!
}

Resources