Rotate UICollectionView with expensive Cells - ios

So I have a UICollectionView with different number of UIImageViews and each has a custom size and position. On device rotate to landscape it seems kind of jerky, as it removes all the views and adds them again in CellForItem
What I am doing currently:
func updateUI() {
for item in allItems { item.removeFromSuperview() }
allItems = []
for (index, imageData) in imageDataArray.enumerated() {
let imageView = UIImageView(image: image)
contentView.addSubview(imageView)
imageView.frame = ...
imageView.center = ...
allItems.append(imageView)
}
}
What could I change to make rotation make less calculations or at least don't look glitchy if there's no other way. Thanks!

Related

How can I automatically round the edges of a popup view?

I am working on a popup view for an app I am making. If you take a second to look at the image attached below, you will see that the top edges are rounded, but the bottom edges are not. This is because I only rounded the edges of the view (it is lowest in the hierarchy). I cannot round the edges of the images (the colorful boxes) because they are tables in a scrolling view. The only solution I can think of is a very ugly one where I mask the bottom edges with a UIImageView that appears once the popup has faded in. Does anyone have a better solution? If so, I would greatly appreciate your help. Also, my scrolling view is not yet functional, so that is not referenced here and the solution (if functional) should work regardless.
My code:
allSeenPopover.layer.cornerRadius = 5
userProfile.layer.cornerRadius = 15
colorBackground.layer.cornerRadius = 15
colorBackground.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
#IBAction func loadUserProfile(_ sender: Any) {
if darken.alpha == 0 {
darken.alpha = 1
self.view.addSubview(userProfile)
userProfile.center = self.view.center
userProfile.transform = CGAffineTransform.init(scaleX: 1.3, y: 1.3)
userProfile.alpha = 0
UIView.animate(withDuration: 0.3) {
self.largeDropShadow.alpha = 0.3
self.userProfile.alpha = 1
self.userProfile.transform = CGAffineTransform.identity
}
}
else {
UIView.animate(withDuration: 0.2, animations: {
self.userProfile.transform = CGAffineTransform.init(scaleX: 1.3, y: 1.3)
self.userProfile.alpha = 0
self.darken.alpha = 0
self.largeDropShadow.alpha = 0
}) { (success:Bool) in
self.userProfile.removeFromSuperview()
}
}
}
The image I was referring to:
Since the view you want rounded is inside a table view cell, you must take care that the view is created added only once per reused cell.
Each time a reused cell scrolls into view, check to see if it has had an imageView subview (using a tag that is unique within the cell is a quick way to make that check). If you don't have one, create it and then configure it for the particular row, otherwise just configure it for the particular row...
(warning, I'm not swift fluent, but the idea should be clear)
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:UITableViewCell = tableView.dequeueReusableCellWithIdentifier(cellReuseIdentifier) as UITableViewCell!
let imageView:UIImageView = cell.viewWithTag(99) as? UIImageView
if (!imageView) {
// this code runs just once per reused cell, so setup
// imageView properties here that are row-independent
imageView = UIImageView()
imageView.tag = 99 // so we'll find this when the cell gets reused
imageView.layer.cornerRadius = 15.0
imageView.clipsToBounds = true
// any other props that are the same for all rows
imageView.frame = // your framing code here
cell.addSubview(imageView)
}
// this code runs each time a row scrolls into view
// so setup properties here that are row-dependent
imageView.image = // some function of indexPath.row
return cell
}

How to Overlap subViews in a stackView?

I have a stackView that I fill as such:
override func awakeFromNib() {
super.awakeFromNib()
for _ in 0 ..< 31 {
let tick = UIView()
self.dayTicks?.addArrangedSubview(tick)
let arrowImage = UIImage(named: "Up Arrow")
let arrowImageView = UIImageView(image: arrowImage)
arrowImageView.clipsToBounds = false
self.arrowTicks.addArrangedSubview(arrowImageView)
}
}
basically I am adding a normal view as well as an arrowImageView from a UIImage. I am doing addArrangedSubview in both cases. With the normal View, they all show up as rectangles (the default I suppose) which works fine. The arrow ticks stackView currently looks like:
As you can see the views are narrowing themselves as not to overlap. I want them to just overlap. How can I do this? ClipsToBounds does not seem to help in this case.
Well, the main idea of the stack view is that it handles subviews arrangement by itself so you don't have to deal with it by yourself. If you want your subviews to be overlapped why not just add them to a regular view?

Creating swipe gesture in swift to scroll through image stack

I'm a swift newbie. Any help is appreciated.
I have a stack of 10 images. I want the user to be able to swipe up and down to update the the image view to new images.
For example: the Image View starts of by displaying 1.jpg. Then the user gestures down on the Image View and it updates the image to 2.jpg then 3.jpg... etc. depending on how far the gesture is held (kind of like an animation). I am able to update the image by one increment using the following code:
#IBAction func down(sender: UISwipeGestureRecognizer) {
image.image = UIImage(named: "2.jpg")
}
How can I make this a continuous gesture?
The end result should look something like this: https://figure1.com/sections/blog/wp-content/uploads/2014/08/resized.gif
So let's start off first things first.
Based off the idea that this is distance dragged
1) We need a UIScrollView()
2) Set the Scrollview Frame to the frame of the screen
3) Allow use of scrollview delegate, so add UIScrollViewDelegate to the list of subclasses (At the top where it says ClassName: list, of, subclasses, here, separated, by, commas)
4) In your class (If you are in your View Controller class), add the delegate to the scrollview
5) Let's set the contentSize of the scrollView
6) Let's add the images to your view, using a loop
7) Before step 6, make sure that your images are named something with numbers, and the same name, such as Image1 Image2 Image3 so that we can loop through them.
8) Declare a UIImage array
9) I was gonna keep typing steps but here you go :P
class ViewController:UIViewController, UIScrollViewDelegate {
let scrollView = UIScrollView()
let images:[UIImage] = []
let imageView:UIImageView!
override func viewDidLoad() {
//Setting up stuff
for index in 0 ..< 10 {
//It's either %t, %i or %d (I don't recall which is which lol)
images.append(UIImage(named: String(format: "Image %i", index)))
}
imageView = UIImageView(frame: view.bounds)
imageView.contentMode = .ScaleAspectFit
imageView.image = images[0]
imageView.backgroundColor = UIColor.clearColor()
self.view.addSubview(imageView)
scrollView.frame = view.bounds
scrollView.delegate = self
scrollView.backGroundColor = UIColor.clearColor()
//Let's say every time the user drags to 1/10 of the screen, the picture will change
scrollView.contentSize = CGSize(width: self.view.frame.width, height: self.view.frame.height+(self.view.frame*(1/10)*CGFloat(images.count)))
self.view.addSubview(scrollView)
}
//Now let's add some UIScrollView delegates to be called whenever the user scrolls!
func scrollViewDidScroll(scrollView: UIScrollView) {
if(scrollView.contentOffSet.y > self.view.frame.height) {
//(1/10) because we are changing the scrollView every 1/10 of the screen
let pictureCount = scrollView.contentOffset.y/scrollView.frame.height*(1/10)
imageView.image = images[pictureCount]
}
}
}
Don't quote me off this, mainly because I pulled it out of the air, so if it has errors/bugs, I apologize, but feel free to comment and I will try to adjust what is needed.

Animate Scroll View to show same content with different values iOS Swift

I need to animate the scroll view to show the same views but with different values based on the selection on the segmented control.I need left to right & right to left slide animation to show different values on the same UI. I dont want to create duplicate views.
I had the exact same problem. Wanted to avoid creating multiple tableviews just for animation sake.
Here is how I solved it :
Basic idea : Take a screenshot of tableview and display it on the root view. Then, do a slide animation of tableview from Left to Right (or Right to left depending on the switching of segment control)
Code when swiped to right
func switchToLeft() {
let newIndex = currentIndex - 1
if newIndex > 0 {
let sS = showScreenShot()
self.tableView.animateFromLeft(0.5){
sS.removeFromSuperview()
}
//update data and RELOAD your tableview here
}
}
func showScreenShot() -> UIView{
let image = getScreenShot()
let imageView = UIImageView(image: image)
let blankView = UIView(frame: self.view.frame)
blankView.addSubview(imageView)
self.tableView.superview?.addSubview(blankView)
self.tableView.superview?.bringSubviewToFront(self.tableView)
return blankView
}
func getScreenShot() -> UIImage?{
let viewToRender = self.tableView
let contentOffset = self.tableView.contentOffset
UIGraphicsBeginImageContext(viewToRender.bounds.size)
let ctx = UIGraphicsGetCurrentContext()!
// need to translate the context down to the current visible portion of the tableview
CGContextTranslateCTM(ctx, 0, -contentOffset.y-tableHeaderHt)
viewToRender.layer.renderInContext(ctx)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
Leaving off code for animateFromLeft as an exercise!

How to make scrollView to scroll ImageView until the center?

Sorry for the header, I just did not know how to call it.
I want to make my scrollView able to scroll from top or bottom until the circle in the center, like LinkedIn does it:
So there you can scroll your image until the circle in the center. How can I improve my code, to achieve this?
My code is:
scrollView!.delegate = self
imageView.frame = CGRectMake(0, 0, (scrollView?.frame.width)!, (scrollView?.frame.height)!)
if let validImage = self.avatarImage {
self.imageView.image = validImage
imageView.contentMode = .ScaleAspectFill
imageView.frame = avatarImageFrame!
}
imageView.userInteractionEnabled = true
scrollView?.addSubview(imageView)
scrollView?.contentSize = (avatarImage?.size)!
let scrollViewFrame = scrollView?.frame
let scaleWidth = (scrollViewFrame?.size.width)! / (scrollView?.contentSize.width)!
let scaleHeight = (scrollViewFrame?.size.height)! / (scrollView?.contentSize.height)!
let minScale = min(scaleHeight, scaleWidth)
scrollView?.minimumZoomScale = minScale
scrollView?.maximumZoomScale = 1
scrollView?.zoomScale = minScale
centerScrollViewContents()
}
func centerScrollViewContents() {
let boundsSize = scrollView?.bounds.size
var contentsFrame = imageView.frame
if contentsFrame.size.width < boundsSize?.width {
contentsFrame.origin.x = ((boundsSize?.width)! - contentsFrame.size.width) / 2
} else {
contentsFrame.origin.x = 0
}
if contentsFrame.size.height < boundsSize?.height {
contentsFrame.origin.y = ((boundsSize?.height)! - contentsFrame.size.height) / 2
} else {
contentsFrame.origin.y = 0
}
contentsFrame.size.height = UIScreen.mainScreen().bounds.height
imageView.frame = contentsFrame
}
func scrollViewDidZoom(scrollView: UIScrollView) {
centerScrollViewContents()
}
There are many ways to achieve this. Probably what would work best with the scroll view is to set the frame of the scroll view the same as is the frame of the circle. The content size should still be the same as the background (or rather the image in your case) and when you first enter the screen the content offset should be set so that the image is in center.
So till now you can imagine a small scroll view just behind the circle but the rest of the background is missing. To fix this all you need to do is disable bound clipping on the scroll view which is set to true by default. So set "clip subviews" to false.
Now you can already see the whole image and bouncing is correct to always keep the image inside the circle but one more thing is missing. Since the scroll view is actually quite small you can see you may not interact with it outside its frame. This is a natural behavior which can be changed by overriding a method called hitTest. This method will return a view which should collect the interaction. So we need to subclass the superview of the scroll view:
Regardless of where you do this in the interface builder or in the code I expect you have some view (will call it background) which contains a scroll view (the small one) and an overlay (the one with the circle). The background must be subclassed and have a reference to the scroll view. Then simply override the hit test method like so:
override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
var myFrame = frame
myFrame.origin = CGPointZero // maybe this should be commented out
if CGRectContainsPoint(myFrame, point) {
return scrollView
} else {
return super.hitTest(point, withEvent: event)
}
}
By doing this the scroll view is interactable from anywhere within the background, whole image is visible on the background, the image will always be within the circle.
Good luck.

Resources