How to set UIScrollView scrolled to bottom? - swift

I want my scroll view (which is in top half of view) to be scrolled to the bottom of the scroll view when my app is booted up. I want it so that when the app boots up I cannot scroll down any further.
I have tried:
scrollView.contentSize = CGSize(width: self.view.frame.size.width, height: 667)
scrollView.contentOffset = CGPoint(x:0, y:self.view.frame.size.height )
However, this only brings my scroll view almost to the bottom. I can still scroll down a little, which I don't want.
I have also tried:
let bottomOffset = CGPoint(x: 0, y: scrollView.contentSize.height - scrollView.bounds.size.height)
scrollView.setContentOffset(bottomOffset, animated: true)
but that doesn't seem to work either.

You can try below code for moving scroll view to the bottom of the screen:
extension UIScrollView {
func scrollToBottom() {
let bottomOffset = CGPoint(x: 0, y: contentSize.height - bounds.size.height + contentInset.bottom)
setContentOffset(bottomOffset, animated: true)
}
}
Add this extension and try below code to access this extension for scroll to the bottom:
scrlView.scrollToBottom()
If it is not working properly then you can also try to execute in mail queue:
DispatchQueue.main.async {
scrlView.scrollToBottom()
}
Let me know if you face any problem.

You can use this extension if you like :
extension UIScrollView {
func scrollToBottom(animated: Bool) {
if self.contentSize.height < self.bounds.size.height { return }
let bottomOffset = CGPoint(x: 0, y: self.contentSize.height - self.bounds.size.height)
self.setContentOffset(bottomOffset, animated: animated)
}
}
But i would suggest you to not use this as the code is little harder to maintain . What i would suggest is that use a TableView or Collection View inside your View Controller and do the following :
Invert your TableView or CollectionView using the below code . This will make the top the TableView or CollectionView go to bottom but the cell will be inversed:
yourTableView.transform = CGAffineTransform(scaleX: 1, y: -1)
Then in invert your cells using the same code.
cell.transform = CGAffineTransform(scaleX: 1, y: -1)
So by default properties of ios , the tableview should begin from the top . Since we transformed it to show up in bottom of view , the content will be there.

Here is the NSScrollView equivalent to the UIScrollView Extension above.
extension NSScrollView {
func scrollToBottom() {
let variableHeight = self.contentView.documentRect.height
let fixedHeight = self.contentSize.height
if (variableHeight > fixedHeight) {
self.contentView.scroll(to: CGPoint(x: 0, y: variableHeight))
}
}
}

Related

Customising UItababbar swift

Can anyone point me how could I achieve such design in the UITabbar. I have tried adding the back-ground Image, but that does not look like the design. Here the curve is extended beyond the frame of UITabbar, not sure how to add this views on top of active tabbar.
Creating a custom TabBar from UITabBarController can be solved the problem. Instead of adding a direct image to the Tabbar, use an on the fly image using UIGraphicsBeginImageContext for selectedTabBackgroundImage.
Create the image.
Clip the top part round in the image
Here is the example of the code.
import UIKit
class CustomTabBarViewController: UITabBarController {
var topClipSize: CGFloat = 24.5 //Adjust based on the number of tabbar
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let singleTabWidth: CGFloat = self.tabBar.frame.size.width / CGFloat((self.tabBar.items?.count)!)
let singleTabSize = CGSize(width:singleTabWidth , height: self.tabBar.frame.size.height)
// Create the backgound image
let selectedTabBackgroundImage: UIImage = self.imageWithColor(color: .blue, size: singleTabSize)
// Clip the top
self.tabBar.selectionIndicatorImage = selectedTabBackgroundImage.roundTopImage(topClipSize: topClipSize)
}
func imageWithColor(color: UIColor, size: CGSize) -> UIImage {
if size.height > 55 {
topClipSize = 30.0 // iPhone 8 tabbar height is 53 and iPnone X is 83 - We need more space on top.
}
let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height + topClipSize)
UIGraphicsBeginImageContext(rect.size)
let context = UIGraphicsGetCurrentContext()
context!.setFillColor(color.cgColor)
context!.fill(rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image!
}
}
extension UIImage {
func roundTopImage(topClipSize: CGFloat) -> UIImage {
let rect = CGRect(origin:CGPoint(x: 0, y: 0), size: self.size)
let rectBounds: CGRect = CGRect(x: rect.origin.x, y: rect.origin.y + (topClipSize * 2), width: rect.size.width, height: rect.size.height - (topClipSize * 2))
let ovalBounds: CGRect = CGRect(x: rect.origin.x - topClipSize, y: rect.origin.y, width: rect.size.width + (topClipSize * 2), height: rect.size.height)
UIGraphicsBeginImageContextWithOptions(self.size, false, 1)
let rectPath = UIBezierPath(rect: rectBounds)
let ovalPath = UIBezierPath(ovalIn: ovalBounds)
rectPath.append(ovalPath)
rectPath.addClip()
self.draw(in: rect)
return UIGraphicsGetImageFromCurrentImageContext()!
}
}
Here is the output:
It's not really possible to elegantly change the native UITabBar's appearance to that extent. Your options are to create a custom container view controller that acts like a UITabBarController, or just hide the default tab bar and implement your own view in that space.
Even though it's less elegant because you'd be just throwing a view on top of the default tab bar, I actually like that method because you retain the benefits of the native UITabBarController (calling self.tabBarController? from its view controllers, it already adjusts layout margins, etc).
To do this, in your subclass of UITabBarController hide the tabBar:
self.tabBar.isHidden = true
self.tabBar.alpha = 0
Then after implementing your custom view however you want, just set the frame of your custom view to self.tabBar.frame in viewDidLayoutSubviews.
For changing viewControllers, call this when the user taps one of your custom tabs:
self.selectedIndex = newIndex

Facebook Picture zoom animation in swift, picture not showing the initial position

I've been trying to make a zoom animation like Facebook when you click a picture into a cell to move into the middle of the screen. The animation works, but for a reason that I can not figure out, it is not starting from the initial position it is giving me another frame. Please help, I've been struggling with this for a few days now.
I am using a collectionView with CustomCell and everything it's done programmatically:
The function in CenterVC:
//MARK: Function to animate Image View (it will animate to the middle of the View)
func animateImageView(statusImageView : UIImageView) {
//Get access to a starting frame
statusImageView.frame.origin.x = 0
if let startingFrame = statusImageView.superview?.convert(statusImageView.frame, to: nil) {
//Add the view from cell to the main view
let zoomImageView = UIView()
zoomImageView.backgroundColor = .red
zoomImageView.frame = statusImageView.frame
view.addSubview(zoomImageView)
print("Starting frame is: \(startingFrame)")
print("Image view frame is: \(statusImageView.frame)")
UIView.animate(withDuration: 0.75, animations: {
let height = (self.view.frame.width / startingFrame.width) * startingFrame.height
let y = self.view.frame.height / 2 - (height / 2)
zoomImageView.frame = CGRect(x: 0, y: y, width: self.view.frame.width, height: height)
})
}
}
This is the pictureView inside the cell and the constraints (this is where I am setting up the picture for the view, and I am using in cellForRowAtIndexPath cell.centerVC = self):
var centerVC : CenterVC?
func animate() {
centerVC?.animateImageView(statusImageView: pictureView)
}
let pictureView : UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFill
imageView.image = UIImage(named: "cat")
imageView.backgroundColor = UIColor.clear
imageView.layer.masksToBounds = true
imageView.isUserInteractionEnabled = true
imageView.translatesAutoresizingMaskIntoConstraints = false
return imageView
}()
addConstraintsWithFormat(format: "V:|-5-[v0(40)]-5-[v1]-5-[v2(200)]", views: profileImage, postTextView, pictureView)
addConstraintsWithFormat(format: "H:|[v0]|", views: pictureView)
This is what it prints out in the debugger:
Starting frame is: (5.0, 547.5, 365.0, 200.0)
Image view frame is: (0.0, 195.5, 365.0, 200.0)
As you can see the starting frame it's different from the initial frame and position of the picture. The animation it's not leaving the initial position it just appears somewhere on top and animates to the middle. I don't know what to do, please advice.
For some reason, it was not reading the starting frame rect so I've made a new CGRect that gets the origin and set the size of the picture: (it animates perfectly now)
zoomImageView.frame = CGRect(origin: startingFrame.origin, size: CGSize(width: view.frame.width, height: statusImageView.frame.height))

How to add custom View according to UIButton location in swift?

I am trying to add custom View on button action. custom View is add successfully, but it's not show properly
#IBAction func sideButtonAction(_ sender: UIButton) {
sideSubView = SideView(frame: CGRect(x: sender.frame.origin.x ,y: sender.frame.origin.y + 40,width: 200,height: 200))
self.view.addSubview(sideSubView)
}
In UIView class
func xibSetup() {
view = loadViewFromNib()
view.frame = bounds
view.autoresizingMask = [UIViewAutoresizing.flexibleWidth, UIViewAutoresizing.flexibleHeight]
addSubview(view)
}
override init(frame: CGRect) {
super.init(frame: frame)
xibSetup()
}
In screenshot, blue View is custom view, it's not show properly, I have set height and width is 200.
Thanks
Please use this,
sideSubView = SideView(frame: CGRect(x: sender.frame.origin.x - 200 ,y: sender.frame.origin.y + 40,width: 200,height: 200))
The screenshot shows the code working as it should. Your view starts underneath the button and moves right. I'm assuming that you wanted the whole 200 width and height visible under the button. If that is the case, then you need to change this line
sideSubView = SideView(frame: CGRect(x: sender.frame.origin.x ,y: sender.frame.origin.y + 40,width: 200,height: 200))
to
sideSubView = SideView(frame: CGRect(x: sender.frame.origin.x + sender.frame.size.width - 200 , y: sender.frame.origin.y + sender.frame.size.height, width: 200, height: 200))
Its look like your button on Navigationbar and you are adding blue view on controller's view. Both view are different so point coordinate will be different for both.
You need to pass relative coordinate to your blue view's frame.

How to scroll one view's height (not page) at a time with a PDF in WKWebView (Swift)

I have a collection of PDFs, some are one page and some are two. I want to see the next visible screen height's worth of the PDF, or scroll to the bottom of the PDF if the remaining height of the PDF is less than the height of the webView. So far, I can only make the webView scroll to the bottom. Here's the function I have...what am I missing?
func scrollDown() {
let scrollView = webview.scrollView
let contentSize = scrollView.contentSize
let contentOffset = scrollView.contentOffset
let frameSize = webview.frame.size
if scrollView.contentOffset.y <= 0 {
// Scroll one view's height at a time
// If the contentSize - offset.y is greater than the frame's height
if (contentSize.height - contentOffset.y) > frameSize.height {
scrollView.setContentOffset(CGPoint(x: 0, y: contentOffset.y + (contentSize.height - frameSize.height)), animated: true)
} else {
scrollView.setContentOffset(CGPoint(x: 0, y: 0), animated: true)
}
}
}
In case anybody else needs this. The lesson learned is draw it on paper to understand what's going on...
func scrollDown() {
let scrollView = webview.scrollView
let contentSize = scrollView.contentSize
let contentOffset = scrollView.contentOffset
let frameSize = webview.frame.size
let frameHeight = frameSize.height
// Next view's height
let heightOffset = frameSize.height + contentOffset.y
let offsetToBottom = contentSize.height - frameSize.height
if contentOffset.y + frameHeight > contentSize.height - frameHeight {
scrollView.setContentOffset(CGPoint(x: 0, y: offsetToBottom), animated: true)
print("Should be scrolling to bottom")
} else {
scrollView.setContentOffset(CGPoint(x: 0, y: heightOffset), animated: true)
print("Should be scrolling by one page")
}
}

NSView with NSBezierPath not updating after window resize

I probably miss something obvious, but...
I have a simple NSView:
class TestView : NSView {
override func drawRect(dirtyRect: NSRect) {
let path = NSBezierPath()
path.moveToPoint(NSPoint(x: 0 ,y: 0))
path.lineToPoint(NSPoint(x: frame.size.width, y: 0))
path.lineToPoint(NSPoint(x: frame.size.width, y: frame.size.height))
path.lineToPoint(NSPoint(x: 0 ,y: frame.size.height))
path.lineToPoint(NSPoint(x: 0 ,y: 0))
path.lineWidth = 1
path.stroke()
}
}
This is placed sole in a window. The window controller has
func windowDidEndLiveResize(notification: NSNotification) {
var frame = textFrame
frame.size.height = window!.frame.size.height - 60
frame.size.width = window!.frame.size.width - 40
field.frame = frame
field.needsDisplay = true
}
where frame is the size of the frame on window load and field is my TestView. So when I start my app it will draw the frame nicely as expected:
But when scaled down
it appears that the view is not erased and it leaves artifacts on the window.
It's probably something trivial, but I'm currently blind.
Try getting the superview to redraw and clear that visual artifact using field.superview?.needsDisplay = true.
Depending on your goals, you might want to use windowDidResize(notification: NSNotification) instead which will continually update the view while the window is being resized.