I'm capturing a full page screenshot of a UIWebView and passing the image through a segue:
// WebViewController.swift
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "captureSegue" {
var captureViewcontroller:CaptureViewController = segue.destinationViewController as! CaptureViewController
captureViewcontroller.tempCaptureImage = image
}
}
The UIImage is then being saved to Documents/Images in the application directory just fine. If I open it from the file system it shows just fine in Preview. However, if the screenshot is really large, the images won't show in a UIImageView. The UIImageView renders blank. Adding a breakpoint shows that the image data is there, just not rendering.
// CaptureViewController.swift
var tempCaptureImage: UIImage?
#IBOutlet weak var imageScrollView: UIScrollView!
#IBOutlet weak var captureImageView: UIImageView!
#IBOutlet weak var titleField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
self.automaticallyAdjustsScrollViewInsets = false
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
if let image = tempCaptureImage {
captureImageView.frame = CGRect(origin: CGPoint(x: 0, y: 0), size: image.size)
captureImageView.image = image
imageScrollView.contentSize = image.size
let scrollViewFrame = imageScrollView.bounds
let scaleWidth = scrollViewFrame.size.width / imageScrollView.contentSize.width
let scaleHeight = scrollViewFrame.size.height / imageScrollView.contentSize.height
let minScale = min(scaleWidth, scaleHeight);
imageScrollView.minimumZoomScale = minScale;
imageScrollView.maximumZoomScale = 1.0
imageScrollView.zoomScale = minScale;
}
}
Could this be because the image is large? If the pages that I capture are smaller, then the images show just fine. Are there any recommended techniques for viewing large images on iDevices?
EDIT:
Here is the output of the Variables View with a breakpoint. Maybe you see something I'm missing.
For people have similar problem, maybe tried to test in real device. For me, image with size 1000 x 7000 isn't displayed in Simulator nor Interface Builder, but can be displayed in an iPhone 6s plus.
Also, Interface Builder will complain if image size is larger than 10000 x 10000.
You do not give any info on how large the image actually is, so it's impossible to say what limit you might be hitting. Remember, though, that a triple-scale image multiplies the underlying bitmap memory size by 9 in comparison to a single-scale image. That's an order of magnitude! I can well believe that you would quickly exceed the memory capacity of the app and even crash if you tried to access an image that's too large. So step one would surely be to load the image as a single-scale image; see the ImageIO framework to learn how to do that. See also the docs on CATiledLayer if you want to know how to display a very large image.
Related
I'm coding along with a tutorial on making a puzzle game in swift, and I keep getting this error message on line 16
Value of type 'Int' has no member 'frame'.
I tried checking other stack overflow threads and it would seem that this perhaps has something to do with the fact that I'm using Swift 4? If that is the case, how do I correct it?
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var gameView: UIView!
#IBOutlet weak var timerLabel: UILabel!
var gameViewWidth: Int!
var blockWidth: Int!
override func viewDidLoad() {
super.viewDidLoad()
gameViewWidth = Int( gameViewWidth.frame.size.width )
blockWidth = gameViewWidth / 4
let blockFrame: CGRect = CGRect(x: 0, y: 0, width: 200, height: 200)
let block: UILabel = UILabel(frame: blockFrame)
block.backgroundColor = UIColor.darkGray
gameView.addSubview(block)
}
#IBAction func resetAction(_ sender: Any) {
}
}
This line:
gameViewWidth = Int( gameViewWidth.frame.size.width )
needs to be:
gameViewWidth = Int( gameView.frame.size.width )
It is the width of gameView that you want. Or maybe just view. It's not 100% clear which view you want the width of.
Either way, viewDidLoad is not a good place for that code since the size of the view isn't finalized yet. It's too soon.
Code that depends on the size of a view should be in viewDidLayoutSubviews since a view's size can change over time (such as when a device is rotated or a user takes a phone call and switched back to your app while on the call, etc.).
Hi there,
I’ve been trying to solve a problem of how to create two progressViews in my viewController for the past week with little to no success. Please see the diagram I have attached as an idea as to the question I am asking.
I am creating a game with a timer which elapses with each question. As the time elapses I would like a UIProgressView to cascade down the entire screen transitioning from blue to white as it goes (Please see number 1 in the diagram, this indicates the white trackTintColor).
Number 2 in the diagram (the cyan part) represents the progressTintColor. The diagram should hopefully be clear in that I am hoping to customise the progressView so that it tracks downwards. Which is one of the main issues at the moment. (I can only seem to fins walkthroughs with small customisable Progressviews which move sideways not up and down)
The hardest part of what I am trying to achieve is (number 3 in the diagram), customisizing a UIImageView, so that it slowly drains downward with the inverse colours to the background (so the dog will be white with cyan flooding downwards to coincide with the progressView elapsing behind it).
These are the options I have tried to solve this issue, thus far, to no avail.
I have tried using a progressView for the backgroundColor, but I cannot find any examples anywhere of anyone else doing this (downwards)so I’m not sure if it’s even possible?
For the image I have tried drawing a CAShape layer but it appears the dog is too difficult a shape for a novice like myself to draw effectively. And upon realising that I would not be able to set a layer of a different colour behind the dog to move downwards as the screen will also be changing color I abandoned all hope of using this option.
I have tried a UIView transition for the dog image, however, the only option I could find that was anywhere close was the transitionCrossDissolve which did not give the downward effect I was hoping for, but instead just faded from a white dog to a cyan dog which was not appropriate. Should I somehow be using progressImage? If so, is there anywhere I can find help with the syntax for that? I can’t seem to find any anywhere.
I currently have 55 images in my assets folder, each with slightly more cyan in than the last, progressively moving downwards (as it animates through an array of the images). Although this works, it is not exactly seamless and does look a little like the user is waiting for an image to load on dial up.
If anyone has any ideas or could spare the time to walk me through how I would go about doing this I would very much appreciate it. I am very much still a beginner so the more detail the better! Oh yes to make matters more difficult, so far I have managed to do the app programatically, so an answers in this form would be great.
Thanks in advance!
I hope you have done number 1 and number 2, perfectly. I have tried for number 3.
I have tried with two UIView. Its working fine. I thought, it will give some idea to achieve yours.
I have two images.
With the help of TIMER , I tried sample for this ProgressView .
Intially, cyanDogView height should be Zero. Once Timer Starts, height should increased by 2px. Once cyanDogView's height should be greater than BlackDogView, then Timer Stops.
Coding
#IBOutlet weak var blackDogView: UIView!
#IBOutlet weak var blackDogImgVw: UIImageView!
#IBOutlet weak var cyanDogView: UIView!
#IBOutlet weak var cyanDogImgVw: UIImageView!
var getHeight : CGFloat = 0.0
var progressTime = Timer()
override func viewDidAppear(_ animated: Bool) {
cyanDogView.frame.size.height = 0
getHeight = blackDogView.frame.height
}
#IBAction func startAnimateButAcn(_ sender: UIButton) {
progressTime = Timer.scheduledTimer(timeInterval: 0.2, target: self, selector: #selector(self.update), userInfo: nil, repeats: true)
}
#objc func update() {
cyanDogView.frame.size.height = cyanDogView.frame.size.height + 2
if cyanDogView.frame.size.height >= getHeight
{
progressTime.invalidate()
cyanDogView.frame.size.height = 0
}
}
Story Board
Output
I'm going to give you some Frankenstein answers here, Part Obj-C Part Swift. I hope it helps.
First, You could create a bezier path of the image mask you're using as a template:
- (UIImage *)cerateImageFromImage:(UIImage *)image
withMaskImage:(UIImage *)mask {
CGImageRef imageRef = image.CGImage;
CGImageRef maskRef = mask.CGImage;
CGImageRef imageMask = CGImageMaskCreate(CGImageGetWidth(maskRef),
CGImageGetHeight(maskRef),
CGImageGetBitsPerComponent(maskRef),
CGImageGetBitsPerPixel(maskRef),
CGImageGetBytesPerRow(maskRef),
CGImageGetDataProvider(maskRef),
NULL,
YES);
CGImageRef maskedReference = CGImageCreateWithMask(imageRef, imageMask);
CGImageRelease(imageMask);
UIImage *maskedImage = [UIImage imageWithCGImage:maskedReference];
CGImageRelease(maskedReference);
return maskedImage;
}
UIImage *image = [UIImage imageNamed:#"Photo.png"];
UIImage *mask = [UIImage imageNamed:#"Mask.png"];
self.imageView.image = [self cerateImageFromImage:image
withMaskImage:mask];
Credit to Keenle
Next you can create a custom progress view based on a path :
func drawProgressLayer(){
let bezierPath = UIBezierPath(roundedRect: viewProg.bounds, cornerRadius: viewCornerRadius)
bezierPath.closePath()
borderLayer.path = bezierPath.CGPath
borderLayer.fillColor = UIColor.blackColor().CGColor
borderLayer.strokeEnd = 0
viewProg.layer.addSublayer(borderLayer)
}
//Make sure the value that you want in the function `rectProgress` that is going to define
//the width of your progress bar must be in the range of
// 0 <--> viewProg.bounds.width - 10 , reason why to keep the layer inside the view with some border left spare.
//if you are receiving your progress values in 0.00 -- 1.00 range , just multiply your progress values to viewProg.bounds.width - 10 and send them as *incremented:* parameter in this func
func rectProgress(incremented : CGFloat){
print(incremented)
if incremented <= viewProg.bounds.width - 10{
progressLayer.removeFromSuperlayer()
let bezierPathProg = UIBezierPath(roundedRect: CGRectMake(5, 5, incremented , viewProg.bounds.height - 10) , cornerRadius: viewCornerRadius)
bezierPathProg.closePath()
progressLayer.path = bezierPathProg.CGPath
progressLayer.fillColor = UIColor.whiteColor().CGColor
borderLayer.addSublayer(progressLayer)
}
}
Credit to Dravidian
Please click the blue links and explore their answers in full to get a grasp of what is possible.
Ok so using McDonal_11's answer I have managed to get the progressView working however, I am still experiencing some problems. I cannot add anything on top of the progressView, it just blanket covers everything else underneath, and before the dog begins its animation into a cyan dog there is a brief flash of the entire cyan dog image.
Code below
private let contentView = UIView(frame: .zero)
private let backgroundImageView = UIImageView(frame: .zero)
private let progressView = ProgressView(frame: .zero)
private let clearViewOverProgress = UIView(frame: .zero)
private let blackDogView = UIView(frame: .zero)
private let blackDogViewImage = UIImageView(frame: .zero)
private let cyanDogView = UIView(frame: .zero)
private let cyanDogViewImage = UIImageView()
var timer = Timer()
var startHeight : CGFloat = 0.0
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.progressView.setProgress(10.0, animated: true)
startHeight = cyanDogViewImage.frame.height
self.cyanDogViewImage.frame.size.height = 0
self.timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(self.updateImage), userInfo: nil, repeats: true)
}
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
}
func setupViews() {
self.view.addSubview(backgroundImageView)
self.backgroundImageView.addSubview(progressView)
self.progressView.addSubview(clearViewOverProgress)
self.clearViewOverProgress.addSubview(blackDogView)
self.blackDogView.addSubview(blackDogViewImage)
self.blackDogViewImage.addSubview(cyanDogView)
self.cyanDogView.addSubview(cyanDogViewImage)
// Setting up constraints of both labels (has been omitted for brevity)
self.blackDogViewImage.image = UIImage(named: “BlackDogImage”)
self.cyanDogViewImage.contentMode = UIViewContentMode.top
self.cyanDogViewImage.clipsToBounds = true
self.cyanDogViewImage.image = UIImage(named: “CyanDogImage”)
}
func updateImage() {
cyanDogViewImage.frame.size.height =
cyanDogViewImage.frame.size.height + 0.07
if cyanDogViewImage.frame.size.height >= blackDogViewImage.frame.size.height
{
timer.invalidate()
cyanDogViewImage.frame.size.height = blackDogViewImage.frame.size.height
}
}
func outOfTime() {
timer.invalidate()
}
I have a strange problem. I am trying to use Kingfisher in order to load and an cache an Image from Firebase in my app. The problem is that kKingfisher does not download the image. I am using a Placeholder Image that is locally stored and the scroll view displays that Image. If I remove the placeholder part from the command, the app crashes. So I know the Kingfisher function works by at least placing the placeholder Image into the UIImageView but not the image from the URL. Here is the code:
Can you point me to the right direction?
import UIKit
import Kingfisher
class Backprogramme: UIViewController {
override var prefersStatusBarHidden: Bool {
return true
}
#IBOutlet var BackPScroll: UIScrollView!
var imageArray = [UIImage]()
var folie1Image = UIImageView()
override func viewDidLoad() {
super.viewDidLoad()
let folie1URL = URL(string: "https://firebasestorage.googleapis.com/v0/b/backmaster-cdb60.appspot.com/o/Folie1.PNG?alt=media&token=efcb8e93-b817-41f3-a96f-946fd47cf468")!
folie1Image.kf.setImage(with: folie1URL, placeholder: #imageLiteral(resourceName: "first"))
imageArray = [folie1Image.image!]
for i in 0..<imageArray.count {
let imageView = UIImageView()
imageView.image = imageArray[i]
let xPosition = self.view.frame.width * CGFloat(i)
imageView.frame = CGRect(x: xPosition, y: 0, width: self.BackPScroll.frame.width, height: self.BackPScroll.frame.height)
BackPScroll.contentSize.width = BackPScroll.frame.width * CGFloat(i+1)
BackPScroll.addSubview(imageView)
}
// Do any additional setup after loading the view.
}
}
I had the same issue , discovered I had this in the log "The resource could not be loaded because the App Transport Security policy requires the use of a secure connection" .
I fixed it by adding NSAppTransportSecurity as a Dictionary in the info.plist and then I added Allow Arbitrary Loads boolean with YES as a value . It worked instantly.
I have a UIImageView, where the image is set with a given url. Then, I set the content mode to Scale Aspect Fit. This works fine, but there is a ton of blank space before and after the image, when the image is supposed to be directly at the top of the screen.
What I would like to do is rescale the UIImage size (maybe frame?) to match the new size created when Aspect Fit is applied (seems to be the suggestion most people received).
The problem is, whenever I test previous solutions, I'm getting a nul error. Particularly:
import UIKit
import AVFoundation
class OneItemViewController: UIViewController {
#IBOutlet weak var itemImage: UIImageView!
#IBOutlet weak var menuButton: UIBarButtonItem!
#IBOutlet weak var titleText: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let imageURL:NSURL? = NSURL(string: "https://upload.wikimedia.org/wikipedia/commons/d/d5/Pic_de_neige_cordier_Face_E.jpg")
if imageURL != nil {
itemImage.sd_setImageWithURL(imageURL)
itemImage.contentMode = UIViewContentMode.ScaleAspectFit
AVMakeRectWithAspectRatioInsideRect(itemImage.image!.size, itemImage.bounds)
/**
let imageSize:CGSize = onScreenPointSizeOfImageInImageView(itemImage)
var imageViewRect:CGRect = itemImage.frame
imageViewRect.size = imageSize
itemImage.frame = imageViewRect
**/
}
if self.revealViewController() != nil {
menuButton.target = self.revealViewController()
menuButton.action = "revealToggle:"
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
}
self.titleText.text = "Title: " + "Earl and Countess of Derby with Edward, their Infant Son, and Chaplain"
// Do any additional setup after loading the view.
}
/**
func onScreenPointSizeOfImageInImageView(imageV: UIImageView) -> CGSize {
var scale: CGFloat
if (imageV.frame.size.width > imageV.frame.size.height) {
if (imageV.image!.size.width > imageV.image!.size.height) {
scale = imageV.image!.size.height / imageV.frame.size.height
} else {
scale = imageV.image!.size.width / imageV.frame.size.width
}
} else {
if (imageV.image!.size.width > imageV.image!.size.height) {
scale = imageV.image!.size.width / imageV.frame.size.width
} else {
scale = imageV.image!.size.height / imageV.frame.size.height
}
}
return CGSizeMake(imageV.image!.size.width / scale, imageV.image!.size.height / scale)
}
**/
}
Tried two things here to get rid of blank space.
First attempt is the call to AVMakeRectWithAspectRatioInsideRect.
Second attempt is the two chunks of code in the /** **/ comments. (onScreenPointSizeOfImageInImageView function and calls to it in viewDidLoad.)
But I can't tell if either work because itemImage.image!.size is causing an error.
So two questions:
1) Why is itemImage.image!.size giving me a nil while unwrapping?
2) Has anyone found a faster solution to removing blank spaces caused by AspectFit?
imageView.widthAnchor.constraint(equalTo: imageView.heightAnchor, multiplier: image.size.width / image.size.height).isActive = true
This answer is answered programmatically with UIKit with Swift 5
As mentioned by #Ignelio, using NSLayoutConstraint would do the work for UIImageView.
The reasoning is that you want to keep maintain the aspect ratio - by using
// let UIImage be whatever you decide to name it
UIImage.contentMode = .scaleAspectFit
would make the UIImage inside the UIImageView fit back to its ratio size given the width. However, as mentioned in Apple's documentation, that will leave remaining area with transparent spacing. Hence, what you want to tackle is UIImageView's size/frame.
--
With this method, you're giving your UIImageView its width constraint equal to the UIImage's ratio - scaling back perfectly in regards to its parent's width constraint (whatever that may be).
// let UIImageView be whatever you name
UIImageView.widthAnchor.constraint(equalTo: UIImageView.heightAnchor, multiplier: UIImage.size.width / UIImage.size.height).isActive = true
I am currently trying some iOS programming. I decided to go with Swift.
Everything is going fine, untill I try to use a UIWebView it won't change it's size to the screen size. I started app development on Android, but afaik there is nothing like relative sizes in iOS right?
Currently this is what my code looks like:
import UIKit
class FirstViewController: UIViewController {
#IBOutlet weak var mainWebView: UIWebView!
var urlPath = "http://www.google.com"
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
loadWebView()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func loadWebView()
{
var newRect = mainWebView.bounds
newRect.size.height = self.view.frame.size.height
newRect.size.width = self.view.frame.size.width
mainWebView.frame.size = newRect.size
let requestURL = NSURL(string:urlPath)
let request = NSURLRequest(URL: requestURL!)
mainWebView.loadRequest(request)
}
}
I tried different approaches with the above (giving de frame size to the mainWebView, but none work. Maybe it has something to do with the moment I give the commands to change size? Should I change the size earlier in the loading proces?
I also tried this:
mainWebView.sizeThatFits(self.view.frame.size)
But that didn't do anything either.
I would definitely appreciate all help!
Thanks in advance
Friso
PS.
In the example I am using the values from self.frame.size, but also when using for instance 100 x 100, it just won't change the size...
You need to set the whole frame at once. Setting the size of a UIView's frame directly does not work.
var newRect = mainWebView.frame
newRect.size.width = self.view.frame.size.width
newRect.size.height = self.view.frame.size.height
mainWebView.frame = newRect