Swift 2 collection view extension for horizontal scroll to vertical - swift2

I've got this extension for my collection view that makes horizontal scroll but I want to change it on vertical scroll??
extension viewRe : UIScrollViewDelegate
{
func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>)
{
let layout = self.recipesCollView?.collectionViewLayout as! UICollectionViewFlowLayout
let cellWidthIncludingSpacing = layout.itemSize.width + layout.minimumLineSpacing
var offset = targetContentOffset.memory
let index = (offset.x + scrollView.contentInset.left) / cellWidthIncludingSpacing
let roundedIndex = round(index)
offset = CGPoint(x: roundedIndex * cellWidthIncludingSpacing - scrollView.contentInset.left, y: -scrollView.contentInset.top)
targetContentOffset.memory = offset
}
}
With this extension I'm trying to make the cells to stick to the TOP of the view even when he scrolls because the paging is enabled. So whenever the user scrolls i would like the cell to stick to the top and so on when the user scrolls.

#donnyWals is right. If you are using a UICollectionView just change its UICollectionViewFlowLayout
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = . Horizontal
let collectionView = UICollectionView(frame: frame, collectionViewLayout: layout)
or if you have an existent UICollectionView
if let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
layout.scrollDirection = .Horizontal
}
Follow the official API:
UICollectionView
UICollectionViewFlowLayout

Related

NSCollectionView Scroll to bottom

I have an NSCollectionView which consists of dozens of rows of single-columned items (chat messages) in a messaging application.
Each item contains a text area of which the heights vary. Therefore the view should be defaulted to the bottom when the view is created and scrolled to the bottom when new messages are received.
I am struggling to either default the scroll to the bottom or work out how to get the height of the CollectionView's contents to scroll to the bottom.
Any help would be greatly appreciated, thanks!
If you support only macOS 10.11 and up, and if you use a NSCollectionViewLayout, you could ask the layouter for the size of the entire content. That's also how the scrollbars are getting sized.
So, in ObjC, you'd simply ask your collectionView for its layouter's content size and scroll to the bottom:
NSSize contentSize = theCollectionView.collectionViewLayout.collectionViewContentSize.height;
[theCollectionView.enclosingScrollView.contentView scrollPoint:NSMakePoint(0, contentSize.height)];
hi i suggest you to work on Offset for horizontal UICollectionView and Apple provided a method for bottom for vertical UICollectionView
UICollectionView Horizontal Next and Previous Item
extension UICollectionView {
func scrollToNextItem() {
let contentOffset = CGFloat(floor(self.contentOffset.x + self.bounds.size.width))
self.moveToFrame(contentOffset: contentOffset)
}
func scrollToPreviousItem() {
let contentOffset = CGFloat(floor(self.contentOffset.x - self.bounds.size.width))
self.moveToFrame(contentOffset: contentOffset)
}
func moveToFrame(contentOffset : CGFloat) {
let frame: CGRect = CGRect(x: contentOffset, y: self.contentOffset.y , width: self.frame.width, height: self.frame.height)
self.scrollRectToVisible(frame, animated: true)
}
}
UICollectionView Vertical Scroll to Bottom
extension UICollectionView {
func scrollToBottom(animated: Bool) {
let sections = self.numberOfSections
if sections > 0 {
let rows = self.numberOfItems(inSection: sections - 1)
let last = IndexPath(row: rows - 1, section: sections - 1)
DispatchQueue.main.async {
self.scrollToItem(at: last, at: .bottom, animated: animated)
}
}
}
}
UITableView Scroll to Bottom
extension UITableView {
func scrollToBottom(animated: Bool) {
let sections = self.numberOfSections
if sections > 0 {
let rows = self.numberOfRows(inSection: sections - 1)
let last = IndexPath(row: rows - 1, section: sections - 1)
DispatchQueue.main.async {
self.scrollToRow(at: last, at: .bottom, animated: animated)
}
}
}
}
Usage
self.collectionView.scrollToBottom(animated: true)
self.tableView.scrollToBottom(animated: true)

How to make a scroll view programmatically in Xcode and add view in it?

I want to make a scroll view programmatically in xcode and want to add anchor constraints using safe area layout guide Auto Layout. And want to add some text views button and map init but could not find any proper way to do this. I have tried many codes. What is the proper code for this?
Please try below code for programmatically create Scroll view and add UIView inside XCode
Swift 4.0
import UIKit
class ViewController: UIViewController {
let scrollView: UIScrollView = {
let view = UIScrollView()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
let myView: UIView = {
let view = UIView()
view.backgroundColor = .yellow
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
// add the scroll view to self.view
self.view.addSubview(scrollView)
// constrain the scroll view to 8-pts on each side
scrollView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 8.0).isActive = true
scrollView.topAnchor.constraint(equalTo: view.topAnchor, constant: 8.0).isActive = true
scrollView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -8.0).isActive = true
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -8.0).isActive = true
//Frame for UIView here
myView.frame = CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: self.view.frame.size.height)
scrollView.addSubview(myView)
}
}

How to create AppleTV buttons?

At first glance they look like regular UIButtons however they got a label below it. Also the background of the button seems to be a blurred effect.
So my thoughts are that they are put in a CollectionView (Horizontal). With each cell containing a UIButton and a UILabel. Although that may work the UIButton doesn't seem to get the move effect for free.
Is that custom behavior? And if so, how are you able to create such an effect?
I bet it is not an UICollectionView but a horizontal UIStackView of custom views in which there is a UIButton and UILabel vertically aligned.
Here you have an example, using stackViews:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let stackView = UIStackView()
stackView.axis = .horizontal
stackView.distribution = .equalSpacing
stackView.alignment = .center
stackView.spacing = 30
view.addSubview(stackView)
["One", "Two", "Three", "Caramba"].forEach {
let buttonStackView = UIStackView()
buttonStackView.axis = .vertical
buttonStackView.distribution = .fillProportionally
buttonStackView.alignment = .center
buttonStackView.spacing = 15
let button = UIButton(type: .system)
button.setTitle($0, for: .normal)
buttonStackView.addArrangedSubview(button)
let label = UILabel()
label.text = $0
label.font = UIFont.systemFont(ofSize: 20)
buttonStackView.addArrangedSubview(label)
stackView.addArrangedSubview(buttonStackView)
}
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
}
}
Having a custom view instead of a vertical uistackview for each button would allow to customize its layout when focused, including Parallax effect.
For adding parallax effect to each button in the stack, take a look to How to get Parallax Effect on UIButton in tvOS?

NSPopOver & NSViewController - Drag to resize

I am making a MenuBar app for which I am using NSPopOver. The problem is that NSPopover uses NSViewController as a contentViewController, whose size gets fixed. My requirement is to make the size of NSViewController flexible i.e just like NSWindowController(set the minimum size and maximum depends upon the total screen size). In simple words how to change the size of NSViewController(NSPopOver) when user drags it. I am new to OS X programming.
I finally got it working by using Mouse Events. Just need to monitor the
override func mouseDown(theEvent: NSEvent) {}
override func mouseDragged(theEvent: NSEvent) {}
events, and reset the content size of the popOver.
Hope this would be helpful to someone one day.
EDIT
override func mouseDragged(theEvent: NSEvent) {
var currentLocation = NSEvent.mouseLocation()
println("Dragged at : \(currentLocation)")
var newOrigin = currentLocation
let screenFrame = NSScreen.mainScreen()?.frame
var windowFrame = self.view.window?.frame
newOrigin.x = screenFrame!.size.width - currentLocation.x
newOrigin.y = screenFrame!.size.height - currentLocation.y
println("the New Origin Points : \(newOrigin)")
// Don't let window get dragged up under the menu bar
if newOrigin.x < 450 {
newOrigin.x = 450
}
if newOrigin.y < 650 {
newOrigin.y = 650
}
println("the New Origin Points : \(newOrigin)")
let appDelegate : AppDelegate = NSApplication.sharedApplication().delegate as! AppDelegate
appDelegate.popover.contentSize = NSSize(width: newOrigin.x, height: newOrigin.y)
}
This is how i tracked the mouse event. On Mouse drag just calculated the current position and the new position(To the point where user has dragged), then checked if the point is smaller then my default size for the Popover i.e. (450, 650) in this case. Once the point has been calculated, just set the size of the popover.
This is just a proposed way. There must be something better then this, but for time being this is what I did.
I had the same need, and thanks to the helpful comments above, I created PopoverResize. This allows a user to resize a menubar NSPopover. It includes cursors for the edges as well.
https://github.com/dboydor/PopoverResize
Here is an answer that handles only vertical, but not horizontal resizing of an NSPopover.
override func mouseDragged(with theEvent: NSEvent) {
let currentLocation = NSEvent.mouseLocation()
let screenFrame = NSScreen.main()?.frame
var newY = screenFrame!.size.height - currentLocation.y
if newY < MIN_HEIGHT {
newY = MIN_HEIGHT
}
if newY > MAX_HEIGHT {
newY = MAX_HEIGHT
}
let appDelegate : AppDelegate = NSApplication.shared().delegate as! AppDelegate
appDelegate.popover.contentSize = NSSize(width: FIXED_WIDTH, height: newY)
}

Resizing NSWindow to match view controller size in storyboard

I am working on Xcode 6.1.1 on OSX 10.10. I am trying out storyboards for Mac apps. I have a NSTabViewController using the new NSTabViewControllerTabStyleToolbar tabStyle and it is set as the default view controller for the window controller. How do I make my window resize according to the current selected view controller?
Is it possible to do entirely in Interface Builder?
Here is what my storyboard looks like:
The auto layout answer is half of it. You need to set the preferredContentSize in your ViewController for each tab to the fitting size (if you wanted the tab to size to the smallest size satisfying all constraints).
override func viewWillAppear() {
super.viewWillAppear()
preferredContentSize = view.fittingSize
}
If your constraints are causing an issue below try first with a fixed size, the example below sets this in the tab item's view controller's viewWillAppear function (Swift used here, but the Objective-C version works just as well).
override func viewWillAppear() {
super.viewWillAppear()
preferredContentSize = NSSize(width: 400, height: 280)
}
If that works, fiddle with your constraints to figure out what's going on
This solution for 'toolbar style' tab view controllers does animate and supports the nice crossfade effect. In the storyboard designer, add 'TabViewController' in the custom class name field of the NSTabViewController. Don't forget to assign a title to each viewController, this is used as a key value.
import Cocoa
class TabViewController: NSTabViewController {
private lazy var tabViewSizes: [String : NSSize] = [:]
override func viewDidLoad() {
// Add size of first tab to tabViewSizes
if let viewController = self.tabViewItems.first?.viewController, let title = viewController.title {
tabViewSizes[title] = viewController.view.frame.size
}
super.viewDidLoad()
}
override func transition(from fromViewController: NSViewController, to toViewController: NSViewController, options: NSViewController.TransitionOptions, completionHandler completion: (() -> Void)?) {
NSAnimationContext.runAnimationGroup({ context in
context.duration = 0.5
self.updateWindowFrameAnimated(viewController: toViewController)
super.transition(from: fromViewController, to: toViewController, options: [.crossfade, .allowUserInteraction], completionHandler: completion)
}, completionHandler: nil)
}
func updateWindowFrameAnimated(viewController: NSViewController) {
guard let title = viewController.title, let window = view.window else {
return
}
let contentSize: NSSize
if tabViewSizes.keys.contains(title) {
contentSize = tabViewSizes[title]!
}
else {
contentSize = viewController.view.frame.size
tabViewSizes[title] = contentSize
}
let newWindowSize = window.frameRect(forContentRect: NSRect(origin: NSPoint.zero, size: contentSize)).size
var frame = window.frame
frame.origin.y += frame.height
frame.origin.y -= newWindowSize.height
frame.size = newWindowSize
window.animator().setFrame(frame, display: false)
}
}
The window containing a toolbar style tab view controller does resize without any code if you have auto layout constraints in your storyboard tab views (macOS 11.1, Xcode 12.3). I haven't tried other style tab view controllers.
If you want to resize with animation as in Finder, it is sufficient to add one override in your tab view controller. It will resize the window with system-calculated resize animation time and will hide the tab view during resize animation:
class PreferencesTabViewController: NSTabViewController {
override func transition(from fromViewController: NSViewController, to toViewController: NSViewController, options: NSViewController.TransitionOptions = [], completionHandler completion: (() -> Void)? = nil) {
guard let window = view.window else {
super.transition(from: fromViewController, to: toViewController, options: options, completionHandler: completion)
return
}
let fromSize = window.frame.size
let toSize = window.frameRect(forContentRect: toViewController.view.frame).size
let widthDelta = toSize.width - fromSize.width
let heightDelta = toSize.height - fromSize.height
var toOrigin = window.frame.origin
toOrigin.x += widthDelta / 2
toOrigin.y -= heightDelta
let toFrame = NSRect(origin: toOrigin, size: toSize)
NSAnimationContext.runAnimationGroup { context in
context.duration = window.animationResizeTime(toFrame)
view.isHidden = true
window.animator().setFrame(toFrame, display: false)
super.transition(from: fromViewController, to: toViewController, options: options, completionHandler: completion)
} completionHandler: { [weak self] in
self?.view.isHidden = false
}
}
}
Please adjust closure syntax if you are using Swift versions older than 5.3.
Use autolayout. Set explicit size constraints on you views. Or once you have entered the UI into each tab view item's view set up the internal constraints such that they force view to be the size you want.

Resources