Setting UITextView exclusion paths within UITableViewCell fails to layout properly - ios

I am trying to specify a word wrap around an image within a UITableViewCell. I have a UITextView and a UIImageView (on top). When there is an image present for a given cell, the image will be unhidden and the exclusion paths will be set on the textContainer within the UITextView. When there is no image, the textContainer.exclusionPaths is set to nil. The problem is that when there is no image, the exclusion path remains set and sometimes vice versa (the exclusion paths are set, but the text doesn't wrap). I am using dequeueCellWithIdentifier within the UITableView and calling my function setFields (within cellForRowAtIndexPath). When the cell gets reinitialized, the exclusion path works most of the time. I'm thinking it has to do with timing. Any help is appreciated.
func setFields(avatar:UIImage?, author:String?, text:String?, images:[AnyObject], timestamp:String?){
....
self.thumbImageView.image = nil
self.thumbImageView.imageURL = nil
self.thumbImageView.hidden = true
self.titleText.textContainer.exclusionPaths = nil
if images.count > 0 {
self.images = images
if images is [UIImage] {
self.thumbImageView.image = images[0] as? UIImage
} else {
self.thumbImageView.imageURL = NSURL(string: images[0] as String)
}
self.cycleToImage(0)
self.thumbImageView.hidden = false
let convertedFrame:CGRect = self.titleText.convertRect(self.thumbImageView.frame, fromView: self)
let exclusionPath:UIBezierPath = UIBezierPath(roundedRect: convertedFrame, cornerRadius: 0.0);
self.titleText.textContainer.exclusionPaths = [exclusionPath]
}
}

Related

Multiple UIImage view

I currently have an image set into my UIImageView the following way:
art_image.image = UIImage(named:(artworkPin.title!))
where art_image is the image View and artworkPin.title refers to the name of the image. However, I want to add a second image to the UIImageView if it exists I thought of programming it as
art_image.image = UIImage(named:(artworkPin.title + "1")?)
would this work? In this case I would name a second image the name of the first image but with a '1' on the end. Example: 'Photo.jpeg' and 'Photo1.jpeg' would both be in the image view if Photo1.jpeg existed.
Thanks for your help.
I came across a similar task myself once. What I did was, I created a UIScrollView with the frame of the UIImageView and its contentSize would be imageView.frame.size.width * numberOfImages.
let scrollView = UIScrollView(frame: view.bounds)
view.addSubview(scrollView)
scrollView.contentSize = CGSize(width: scrollView.bounds.size.width * CGFloat(numberOfImages), height: scrollView.bounds.size.height))
for i in 0...<numberOfImages.count-1 {
let imageView = UIImageView(frame: CGRect(x: scrollView.bounds.size.width * CGFloat(i), y: scrollView.bounds.origin.y, width: scrollView.bounds.size.width, height: scrollView.bounds.size.height))
imageView.image = UIImage(named: "artwork" + "\(i)")
scrollView.addSubview(imageView)
}
You can animate it to scroll with a Timer if you want.
scrollView.setContentOffset(scrollPoint, animated: true)
you can only show 1 image inside the imageivew at a time, so if you have the lines as follows:
art_image.image = UIImage(named:(artworkPin.title!))
art_image.image = UIImage(named:(artworkPin.title! + "1")?)
art_image would consist only of Photo1 provided such an image exists and that the artworkPin.title unwrapped is also not nil otherwise you could see some different results.
if you do want to add multiple images to an image view for the purpose of animation, you need to use the animationImages property of UIImageView which takes an array of UIImages for example
art_image.animationImages = [UIImage.init(named:"Photo")!,
UIImage.init(named:"Photo1")!]
Hope this helps
EDIT
var imagesListArray = [UIImage]()
for position in 1...5
{
if let image = UIImage.init(named: ("Photo\(position)"))
{
imagesListArray.append(image)
}
}
art_image.animationImages = imagesListArray
art_image.animationDuration = 3.0
art_image.startAnimating()
This would be a safer a way to add the images so it will ONLY add an image if it is not nil and adds Photo1, Photo2 ..... Photo5
With regards to your other questions:
If you want the user to be able to
What do you mean by animation?
I have added two more lines of code, and it gives this result:
art_image.animationDuration = 1.0
art_image.startAnimating()
It will give you something like this:
If you want the user to swipe, then you need to make some changes such as:
using a scrollview, collectionview for example is the easiest or using a gesture recognizer on swipe you need to change the image
EDIT
Have a look at this example. Imagine each button is your annotation so when I tap it, the image changes.
I have 3 images named Photo11.png, Photo21.png and Photo31.png and this is my code inside the button handler
#IBAction func buttonTapped(_ sender: UIButton)
{
if let image = UIImage.init(named: sender.currentTitle!+"1")
{
art_image.image = image
}
}
As you can see I am setting the image with the title of my button + "1" as so it displays either Photo11.png or Photo21.png etc

how to copy one view to another view in Swift

I want to copy one UIView to another view without making it archive or unarchive.
Please help me if you have any solution.
I tried with by making an extension of UIView as already available an answer on Stack over flow. But its crashing when I pass the view with pattern Image Background color.
The code related to my comment below:
extension UIView
{
func copyView() -> UIView?
{
return NSKeyedUnarchiver.unarchiveObjectWithData(NSKeyedArchiver.archivedDataWithRootObject(self)) as? UIView
}
}
I've just tried this simple code in a Playground to check that the copy view works and it's not pointing the same view:
let originalView = UIView(frame: CGRectMake(0, 0, 100, 50));
originalView.backgroundColor = UIColor.redColor();
let originalLabel = UILabel(frame: originalView.frame);
originalLabel.text = "Hi";
originalLabel.backgroundColor = UIColor.whiteColor();
originalView.addSubview(originalLabel);
let copyView = originalView.copyView();
let copyLabel = copyView?.subviews[0] as! UILabel;
originalView.backgroundColor = UIColor.blackColor();
originalLabel.text = "Hola";
originalView.backgroundColor; // Returns black
originalLabel.text; // Returns "Hola"
copyView!.backgroundColor; // Returns red
copyLabel.text; // Returns "Hi"
If the extension wouldn't work, both copyView and originalView would have same backgroundColor and the same would happen to the text of the labels. So maybe there is the possibility that the problem is in other part.
Original Post
func copyView(viewforCopy: UIView) -> UIView {
viewforCopy.hidden = false //The copy not works if is hidden, just prevention
let viewCopy = viewforCopy.snapshotViewAfterScreenUpdates(true)
viewforCopy.hidden = true
return viewCopy
}
Updated for Swift 4
func copyView(viewforCopy: UIView) -> UIView {
viewforCopy.isHidden = false //The copy not works if is hidden, just prevention
let viewCopy = viewforCopy.snapshotView(afterScreenUpdates: true)
viewforCopy.isHidden = true
return viewCopy!
}

Animate background UIImage

I have this asset as the background of a view and its assigned to the background using the code described below.
The animation is to get the diagonal rows animate, so they move from left to right when the loading is happening.
Any pointers of how to get this done?
var view = UIImageView()
view.translatesAutoresizingMaskIntoConstraints = false
view.image = UIImage(assetIdentifier: "background-view")
view.layer.cornerRadius = 8.0
view.layer.masksToBounds = true
view.contentMode = .ScaleAspectFill
view.clipsToBounds = true
view.backgroundColor = UIColor.whiteColor()
return view
"background-view" is here
I guess the best would be to have all the images needed ( all the frames ) to create the animated image you want and then put these images in UIImageView's animationImages property.
For instance, if you get a loading bar gif loading_bar.gif, you can get all the different images in that gif ( c.f. this tutorial among others : http://www.macobserver.com/tmo/article/preview-extracting-frames-from-animated-gifs ).
Load all the images in your code ( in the assets folder for instance ) and then do something like :
func getAnimatedImages -> Array<UIImage>
{
var animatedImages = Array<UIImage>()
var allImagesLoaded = false
var i = 0
while !allImagesLoaded
{
if let image = UIImage(named: "background_" + String(i))
{
animatedImages.append(image)
i++
}
else
{
allImagesLoaded = true
}
}
return animatedImages
}
( if you called your images background_0, background_1, etc... )
and then
self.yourBackgroundImageView.animationImages = self.getAnimatedImages
self.yourBackgroundImageView.startAnimating()
I would use the method class func animatedImageNamed(_ name: String, duration duration: NSTimeInterval) -> UIImage?
From the Apple doc:
this method would attempt to load images from files with the names ‘image0’, ‘image1’ and so on all the way up to ‘image1024’. All images included in the animated image should share the same size and scale.
If you create an animated image you can assign it to your UIImageView and it will animate automatically.
As for the image creation #Randy had a pretty good idea :)

Load an image in certain cells using autolayout

So Ive got a Feed displaying posts made by users (Using Parse). At present the cell contains a title, profile image of user who posted, the date, content(text), an imageView (for an image they post) and a like & comment button.
My question is, Using autolayout what is the best approach to displaying the image they posted. Currently I have the imageView height set to 0 at all times, I then reference the constant height of the imageView in the code.
The app pulls down all the posts and starts loading the tableView of posts.
The code for loading an image in cell is
if let imageToFetch = post.objectForKey("postImage") as? PFObject {
self.postImageHeight.constant = 500
if let fetchImage = imageToFetch.objectForKey("image") as? PFFile{
fetchImage.getDataInBackgroundWithBlock {
(imageData: NSData?, error: NSError?) -> Void in
if error == nil {
if let imageData = imageData {
let image = UIImage(data:imageData)
self.postImage.image = image
self.imageGesture(self.postImage)
}
}
}
} else {
//Make the size constarints of the image 0
self.postImageHeight.constant = 0
}
But it seems like a unstable approach as I'm hardcoding the image height. Any help on the issue would be appriciated.
You can change them using the image's size property. But for this, you need to use a fixed width referenced here as kImageWidth
self.postImageHeight.constant = kImageWidth * image.size.height / image.size.width
self.layoutIfNeeded()
Simple answer: calculate postImageHeight manually after setting postImage.image:
/// .....
self.postImage.image = image
self.postImageHeight.constant = self.postImage.bounds.size.width * image.size.height / image.size.width
/// .....
A better option is to add a special method to UITableViewCell subclass:
func setPostImageWithImage(image: UIImage?) {
postImageView.image = image
// Manually adjust image height.
if let image = image {
postImageViewHeightConstraint.constant = postImageView.bounds.size.width * image.size.height / image.size.width
}
else {
// Collapse if there is no image.
postImageViewHeightConstraint.constant = 0
}
}

swift indexPath.row==0 works weirdly for UITableViewCell

I am trying to make a table wherein first cell has a differernt layout than the rest. I want to put the image as background for first cell i.e it shd look something like this:
and here is the code for my implementation
func imageCellAtIndexPath(indexPath:NSIndexPath) -> MainTableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier(imageCellIdentifier) as MainTableViewCell
let object = self.fetchedResultsController.objectAtIndexPath(indexPath) as NSManagedObject
let eTitle:NSString = object.valueForKey("title")!.description
let deTitle = eTitle.stringByDecodingHTMLEntities()
cell.artTitle.text = deTitle
var full_url = object.valueForKey("thumbnailURL")!.description
var url = NSURL(string: full_url)
var image: UIImage?
var request: NSURLRequest = NSURLRequest(URL: url!)
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue(), completionHandler: {(response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in
image = UIImage(data: data)
if((indexPath.row)==0) {
var imageView = UIImageView(frame: CGRectMake(10, 10, cell.frame.width - 10, cell.frame.height - 10))
imageView.image = image
cell.backgroundView = UIView()
cell.backgroundView?.addSubview(imageView)
}
else{
cell.thumb.image = image
}
})
return cell
}
bt the problem is.. when i scroll down and scroll up back again, the background image starts repeating and the thumbnails also get overlapped as shown:
If i scroll up and down again.. this is wht happens:
i might have done some silly mistake bt m not able to figure out what it is. pls help
In table views cells are reused and you should reset parts of the cell that are not relevant the specific version of the cell style you are after. Something like:
if((indexPath.row)==0) {
let frame = CGRectMake(10, 10,
cell.frame.width - 10, cell.frame.height - 10)
var imageView = UIImageView(frame: frame)
imageView.image = image
cell.backgroundView = UIView()
cell.backgroundView?.addSubview(imageView)
// Reset
cell.thumb.image = nil
} else{
cell.thumb.image = image
// Reset
cell.backgroundView = nil
}
Even better and more idiomatic idea is to use separate UITableViewCell designs for these two cell types, each with different reuse identifiers. This way you don't need to care about resetting.
P.S. You should use dequeueReusableCellWithIdentifier:forIndexPath: instead of older dequeueReusableCellWithIdentifier: as it guarantees that a cell is returned.
The problem is that the cell is being reused by the tableView for efficiency purposes but it is never being reset.
You need to clear / remove the imageView from the background view if the cell is not at indexPath 0.

Resources