Merging two UIImageViews of different sizes - ios

I am making an iOS app where the user is supposed to be able to overlay one image over another. The user can choose a background image, which would be stored in a UIImageView called imageView. The user can then decided on a image to be overlaid on top of that image, which will be stored in a UIImageView called imageTwo. Then, the user is allowed to move and resize imageTwo. However, I want to then be able to merge those image views into one image and download it.
I am currently using the function below to try to download the image:
#IBAction func downloadImage(_ sender: Any) {
let bottomImage:UIImage = imageView.image!
let topImage:UIImage = imageTwo.image!
let mySize = CGSize(width: (imageView.frame.size.width), height:(imageView.frame.size.height))
UIGraphicsBeginImageContextWithOptions(mySize, false, 1)
bottomImage.draw(in: CGRect(x: 0,y:0,width: mySize.width,height: mySize.height))
topImage.draw(in: CGRect(x: imageTwo.frame.origin.x ,y: imageTwo.frame.origin.y , width: (imageTwo.frame.size.width),height:(imageTwo.frame.size.height)), blendMode:CGBlendMode.normal, alpha:1.0)
let newImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIImageWriteToSavedPhotosAlbum(newImage, nil,nil,nil)
UIGraphicsEndImageContext()
}
However, it doesn't work very well.
This is how the image looks in my app:
Image viewed from app
However, when I download the image using my function from above, the background image looks stretched and the topImage/bird isn't in the right position: Downloaded image
Anyone have any suggestions for easier ways to accomplish this task or how I should change my code from above? I read someone on here suggested putting the UIImageViews into their own View, but that proved to be quite tricky as well...

The issue is that you are compositing the images that back your image views which are different from what is actually displayed by your imageviews. You can either apply the necessary transform so that the image matches what's show in the imageview or you can be lazy and just capture the image that's on the screen snapshotView(afterScreenUpdates:)

Related

Setting Image To Nil Causes ImageView to Disappear in Stack View

I have recently moved a bunch of UIImageViews into nested Stack Views (Horizontal and Vertical). Whenever the user presses the play button it animates, then starts a timer. Once the timer reaches 0 it is supposed to flip each card consecutively and hide the image itself. This was working until I added them to a Stack View. I've read that the stack views are buggy when it comes to this sort of thing, but any attempt at fixing it hasn't worked thus far. I'm at an impasse, can anyone point me in the right direction?
Issue Occurs In Here I Think
//INFO: Disable cards while viewing.
for card in cardsPhone
{
card.isUserInteractionEnabled = false
flipCard(sender: card)
}
DispatchQueue.global(qos: .default).asyncAfter(deadline: .now() + 5) {
for card in self.cardsPhone
{
DispatchQueue.main.async {
//THIS IS THE ANIMATION CALL THAT MAKES VIEWS DISAPPEAR.
self.flipCard(sender: card)
}
usleep(20000)
}
DispatchQueue.main.async{
//INFO: Enable cards after hidden.
for card in self.cardsPhone
{
card.isUserInteractionEnabled = true
}
//INFO: Enable play button after cards are hidden to prevent crashing layout.
self.playButtonView.isUserInteractionEnabled = true
}
}
Flip Animation Controller
func flipCard(sender: UIImageView)
{
playButtonView.isUserInteractionEnabled = false
//INFO: If there is no UIImage, assign the appropriate image from the array.
if sender.image == nil
{
sender.image = cardsImages[sender.tag]
UIView.transition(with: sender, duration: 0.3, options: .transitionFlipFromLeft, animations: nil, completion: nil)
}
//INFO: If there is an image, remove it and replace with no Image.
else
{
sender.image = nil
UIView.transition(with: sender, duration: 0.3, options: .transitionFlipFromRight, animations: nil, completion: nil)
}
}
Update
I've discovered that if I replace the image on the UIImageView then that is actually what is causing it to disappear. So the issue is related to the code above in the else statement where sender.image = UIImage(). It doesn't matter if I replace with a different image or not, the moment the image is changed it disappears.
I've read that the stack views are buggy when it comes to this sort of thing
Well, that's not so. They are in fact quite sophisticated and consistent, and their behavior is well defined.
What is a stack view? It's a builder of autolayout constraints. And that is all it is. Putting views inside a stack view means "Please configure these views with equal spacing" (or whatever your setting is for the stack view) "using autolayout constraints that you impose."
Okay, but how does the stack view do that? Autolayout requires four pieces of information, x and y position, and width and height. The stack view will supply the position, but what about the size (width and height) of its views? Well, one way to tell it is to give your views internal constraints (if they don't conflict with what else the stack view will do). But in the absence of that, it has to use the intrinsic size of its views. And the intrinsic size of an image view under autolayout is the size of the image it contains.
So everything was fine when you had image views with images that were the same size. They all had the same intrinsic size and now the stack view could space them out. But then you took away an image view's image. Now it has no image. So it has no intrinsic size. So the stack view just removes it from the row (or column or whatever it is).
Your workaround is actually quite a good one, namely, to make sure that the image view always has an image; if it is to look blank, you just give it a blank image. The image looks like the background, but it is still an image with an actual size, and so it gives the image view a size and the image view continues to hold its place.
However another solution would have been simply to give the image view a width constraint the same size as the card image's width.
Solution
I've discovered that changing an image on a UIImageView while it's inside of a stack causes it to completely disappear. This is a weird Xcode but but the solution I found was to create a custom background on the fly to get rid of the image and only have the background color. This was done with the following code snippet. It's a bit messy, but it gets the job done.
func setColorImage(viewToModify: UIImageView, colorToSet: UIColor)
{
let rect = CGRect(origin: .zero, size: viewToModify.layer.bounds.size)
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)
colorToSet.setFill()
UIRectFill(rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
guard (image?.cgImage) != nil else { return }
viewToModify.image = image
viewToModify.backgroundColor = colorToSet
viewToModify.isUserInteractionEnabled = false
}
Essentially this code creates a new cgRect, sets the color, creates a new image from that color, then applies it back to the UIImageView.

How to handle Asynchronous images and video when adding image as SubView?

I have a collection view where I add either an image or a video depending on the kind of media in a Media() object.
What I am unsure about is how I could use something like SDWebbImage when adding the video or image as a SubView instead of adding it to a UIImage view.
if postArray[indexPath.item].media[0].imageURLString != nil { //its an image
print("display image")
var imageView = UIImageView(image: postArray[indexPath.item].media[0].imageURLString!)
imageView.frame = CGRect(x: 0, y: 0, width: 126, height: 203)//cell.frame
cell.imageOrVideoView//Here I need to add image
//.addSubview(imageView)//Here I would usualy add a UIImage to subview
I have also asked this question regarding the same general topic.
First: you shouldn't add subviews inside cellForItemAt as the cell is dequeud and this will overflow the content and mix them unless you clear it before reuse , but you need to create an outlet or imageView once as an instance var then use it
Second: it's better to have a separete cell for image and another for video
Finally: use SDWebImage for images and VGPlayer for videos

Keep aspect ratio for UIbuttons background image

I've been looking for hours and all I find is old answers saying that it cannot be done.
I have a button where I'd like the background image to be as large as it can be, while keeping its aspect ratio. The background image keeps getting streched no matter what I do. I've tried.
var bluecircle = #imageLiteral(resourceName: "BLUE.png")
monBreak.contentMode = UIViewContentMode.scaleAspectFit
monBreak.setBackgroundImage(bluecircle, for: UIControlState.normal)
Do I really need to make an imageview and put an invisible button over it? That seems like welcoming A LOT of new things that could go wrong.
I tried
#IBOutlet weak var monBreak: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
var bluecircle = #imageLiteral(resourceName: "BLUE.png")
let theimagebehindmonBreak = UIImageView(image: bluecircle)
theimagebehindmonBreak.frame = CGRectFromString( "{{0,0},{40,40}}")
monBreak.contentMode = UIViewContentMode.scaleAspectFit
monBreak.addSubview(theimagebehindmonBreak)
theimagebehindmonBreak.image=bluecircle
theimagebehindmonBreak.contentMode = UIViewContentMode.scaleAspectFit
This gets the aspect ratio right, but since I want the layout to be dynamic setting its size to fixed values won't work, plus it's not centered.
At first I tired simply applying the constraints of the button like so
var dacontraints = NSLayoutConstraint(monBreak.constraints)
But at this point I get the error "Cannot invoke initializer for type 'NSLayoutConstraint' with an argument list of type '{[NSLayoutconstraints]}'
This is killing me
Sounds like a solution where subclassing fits.
Subclass UIButton, add an ImageView in the init, size it to the frame, and set the contentMode properly.
Old question but since I was looking for the same I think this is still valid.
I was trying to do the same (as everyone says) for backgroundImage it is impossible. However, if your button will consist of only an icon, then first you should be using the setImage method AND if you are using it you'll have access to the imageView property of the button. Since this is an UIImageView you can set the contentMode to whatever you like.

How to programmatically create a UISlider like that of Apple Music in Swift

I'm looking to create a UISlider like the one below:
I've seen code like the following to change the image, but ideally I'd like to forgo using an image and create a basic downward line programmatically.
var thumbImage : UIImage = UIImage(named: "yourImage")!
var size = CGSizeMake( thumbImage.size.width * ratio, thumbImage.size.height * ratio )
self.setThumbImage( self.imageWithImage(thumbImage, scaledToSize: size), forState: UIControlState.Normal )
A UISlider is designed to use an image for the "thumb" control. If you want to draw the thumb control programmatically you will need to create your own control. A slider isn't that complicated. It would be fairly straightforward. (But it would be a heck of a lot easier to just use the standard slider control with a red line as the thumb image.)

How add background image to UIControl

I create 3rd party keyboard and i try add background to my keys. I use UIControl for my keys, without storyboard.
I have several types of buttons and try to add background UIView for my characters.
let img = UIImage(named:"KeyBackground")
var bgImage: UIImageView?
bgImage = UIImageView(image: img)
bgImage!.frame = keyboardKey.frame
keyboardKey.addSubview(bgImage!)
keyboardKey.sendSubviewToBack(bgImage!)
keyboardKey.backgroundColor = UIColor.clearColor()
But is don't work.
I try to add background image like this:
keyboardKey.backgroundColor = UIColor(patternImage: UIImage(named:"KeyBackground")!)
But is looks bad...
What am i doing wrong?
The background looks like that because the size of your image is a little smaller than the size of each key, and the image is being tiled (repeated to fill the space - so each key contains the background image and pieces of the surrounding tiles to the right, bottom, and bottom-right).
The UIColor(patternImage:) docs say that
During drawing, the image in the pattern color is tiled as necessary to cover the given area.
Your first solution probably didn't work because you inserted your UIImageView as the bottom-most subview - I assume another (opaque) view was on top of it. What type of UIControl are you using? UIButton, for example, already provides some "background image" support out of the box.
Oh, it's so easy. My keyboardKey.frame, at the time of the call this method, have frame = 0, 0, 0, 0.
Night a bad time for work:)

Resources