I'm using Carbonkit's CarbonTabSwipeNavigation by ermalkaleci , when the view loads, the viewcontrollers are aligned properly, then when switching to the second viewcontroller the alignment of the second viewcontroller is correct but when scrolling back to the first viewcontroller, it is aligned wrong and a small portion of the second viewcontroller is visible. Tried everything I could find but no luck so far.
My current setup I'm using a toolbar but same behavior when inserting with insertIntoRootViewController :(
class func setupCarbonPages(carbonSwipeTabsItem:CarbonTabSwipeNavigation,tabTitles:[String],totalWidth: CGFloat) -> CarbonTabSwipeNavigation{
// Setup the default style
carbonSwipeTabsItem.toolbar.translucent = false
carbonSwipeTabsItem.setIndicatorColor(AppConfig.BLUECOLOR)
carbonSwipeTabsItem.setNormalColor(AppConfig.BLUECOLOR)
carbonSwipeTabsItem.setSelectedColor(AppConfig.BLUECOLOR, font: UIFont.boldSystemFontOfSize(14))
carbonSwipeTabsItem.setTabExtraWidth(CGFloat(0))
carbonSwipeTabsItem.setNormalColor(AppConfig.BLUECOLOR, font: AppConfig.REGULAR_FONT_16)
carbonSwipeTabsItem.setSelectedColor(AppConfig.BLUECOLOR, font: AppConfig.REGULAR_FONT_16)
// Assign tab width & preload all data
let menuTabItems:UInt = UInt(tabTitles.count)
let tabWidth = (totalWidth / CGFloat(menuTabItems))
for index:UInt in 0 ..< menuTabItems {
carbonSwipeTabsItem.carbonSegmentedControl!.setWidth(tabWidth, forSegmentAtIndex: Int(index))
}
return carbonSwipeTabsItem
}
Then in my viewcontroller :
private func setupSwipableTabs(){
let width = self.view.frame.width
carbonTabSwipeNavigation = CarbonTabSwipeNavigation(items: menuTabTitles as [AnyObject], toolBar: self.toolBar,delegate: self)
Helper.setupCarbonPages(carbonTabSwipeNavigation, tabTitles: menuTabTitles, totalWidth: self.view.frame.width).insertIntoRootViewController(self)
Helper.preloadCarbonPages(carbonTabSwipeNavigation, tabs: 2)
}
Library: https://github.com/ermalkaleci/CarbonKit
(Red line is part of the second viewcontroller)
try to setup the carbon kit code on main thread
in
dispatch_async(dispatch_get_main_queue(), {
let width = self.view.frame.width
carbonTabSwipeNavigation = CarbonTabSwipeNavigation(items: menuTabTitles as [AnyObject], toolBar: self.toolBar,delegate: self)
Helper.setupCarbonPages(carbonTabSwipeNavigation, tabTitles: menuTabTitles, totalWidth: self.view.frame.width).insertIntoRootViewController(self)
Helper.preloadCarbonPages(carbonTabSwipeNavigation, tabs: 2)
})
I Found the problem, for some reason the "Clip Subviews" checkbox was unchecked in the misaligning viewcontroller's storyboard. It appeared to have a negative constraint to the left side what was causing the views to misallign and making the viewcontroller clip all subviews forced all subviews to stay in the main viewcontroller's bounds.
If you are setting up viewcontroller through storyboard.Set the constraint for the container view as
- Align center x to superview
- Equal width to superview(if your container view is equal to superview).
Avoid setting leading and trailing constraint.Removing these two constraint resolved the problem.
you can set width of its segment:
carbonTabSwipeNavigation.carbonSegmentedControl?.setWidth(widthItem, forSegmentAt: 0)
Related
I have been trying this for quite sometime now and went through a lot of posts on SO and some video tutorials. What I want to create is a horizontal scroll view with some buttons in it, like this:
My view hierarchy is as follows:
View Controller
View
Scroll View
View
Buttons
I have set top, leading, trailing, bottom constraints to the scroll view. I have set it's width equal to it's superview, and have set it's height to 200. So far so good, for the view inside the scroll view, I have set it's constraints leading, trailing, top and bottom to zero with respect to it's superview i.e. scroll view. I have made it's width equals to the View controllers view, since that was the solution here on SO to the ambiguous width issue. It solved the issue. Now I added all the buttons and set up their constraints to their parent view. Now when I run the app, a screen like the above added screenshot appears, however I cant scroll to the last element.Any help is greatly appretiated.
It's so simple.
Take a scrollView(Draw top left bottom right and hight constraint)
Take a UIView which will act like as a container View.(Draw top left bottom right constraint with scrollView).
Make your View Controller 1000px so that you can make your scrollView bigger and easy to watch.later on you can minimize it.
Now keep your button inside of it. keep in mind that, Every button will have top leading width , height and trailing if necessary. Width & height is important for scrollView because how big it will be depends on it.
Pictures worth a thousand word.
here is my hierarchy
And here is the Storyboard layout design.White background is basically containerView.
And here is the output.I have given some color for better understanding.
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var btnBack: UIButton!
#IBOutlet weak var btnForward: UIButton!
let headerView = UIView()
headerView.backgroundColor = UIColor.white
for i in 0..<selectedRestaurant.multipleImages.count {
let url = selectedRestaurant.multipleImages[i]
let imgView = UIImageView()
imgView.frame = CGRect(x: CGFloat(i) * self.view.frame.size.width, y: 0, width: self.view.frame.size.width, height: scrollView.frame.size.height)
let task = URLSession.shared.dataTask(with: URL(string: url as! String)!, completionHandler: { (data, response, error) in
if error == nil {
if data != nil {
imgView.contentMode = UIViewContentMode.scaleAspectFill
imgView.image = UIImage(data: data!)
}
}
})
scrollView.addSubview(imgView)
scrollView.contentSize = CGSize(width: self.view.frame.size.width * CGFloat(selectedRestaurant.multipleImages.count), height: scrollView.frame.size.height)
write code in btn back clicked <<<<
let index = Int(scrollView.contentOffset.x/self.view.frame.size.width) - 1
print("\(index)")
if index >= 0 {
scrollView.setContentOffset(CGPoint(x: CGFloat(index) * self.view.frame.size.width, y: 0), animated: true)
}
write code in btn next clicked >>>>>
let index = Int(scrollView.contentOffset.x/self.view.frame.size.width) + 1
print("\(index)")
if index < (selectedRestaurant.multipleImages.count) {
scrollView.setContentOffset(CGPoint(x: CGFloat(index) * self.view.frame.size.width, y: 0), animated: true)
}
For swift 5.1
firstly put scrollView give constraint to superView. Make them all what you desire.
Then put view on scroll view like I showed in pic. Give constraint shown below then to avoid error follow 3. step.
https://i.stack.imgur.com/MwzVN.png
Push control button and drag to the view then select Equal Height.
https://i.stack.imgur.com/91yzl.png
I give name to view over scrollview which name is ContentView . Be sure view size form is freeform and make width 1200 same as ContentView.
https://i.stack.imgur.com/jqo0v.png
https://i.stack.imgur.com/ntW4p.png
5)Then put TableView over the ContentView and give all the constraint zero.
https://i.stack.imgur.com/JPbXH.png
This is my TableviewCell
https://i.stack.imgur.com/4Fxwk.png
And this one is the result.
https://i.stack.imgur.com/zxBAT.gif
I have as simple setup where I have a view controller embedded in a UINavigationController. On this view controller, I want to display multiple tableViews next to each other (horizontally). I am creating and adding the tableViews in viewDidLoad. It works well except that there is an issue with the y-position of every tableView except for the first one.
When creating the tableViews programmatically, the first tableView is always properly displayed right below the UINavigationBar. However all of the others are displayed "behind" the UINavigationBar even though the y-coordinate is equal to 0 in all of the tableViews.
This is what it looks like when you run it on the simulator (note that the tableView itself has a green backgroundColor):
And this is the code for it:
class ViewController: UIViewController {
var tableViews: [UITableView] = [] // maintained only for debugging purposes
override func viewDidLoad() {
super.viewDidLoad()
let tableViewCount: Int = 5
createTableViews(tableViewCount)
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
print(#function, tableViews)
}
func createTableView(count: Int) {
let width = Int(self.view.bounds.width)/count
for i in 0..<count {
let x = width * i
let y = 0
let height = Int(view.bounds.height)
let frame = CGRect(x: x, y: y , width: width, height: height)
let tableView = UITableView(frame: frame, style: UITableViewStyle.Plain)
tableView.backgroundColor = .greenColor()
tableView.dataSource = self
tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "Cell")
view.addSubview(tableView)
tableView.tag = i
tableViews.append(tableView)
}
}
}
I am only maintaining the tableViews property so that I can print them in viewDidAppear. The console output in viewDidAppear shows that all my tableViews have the same y position on their frames (= 0).
I did find an easy (yet hacky) fix for this. All I have to do is set the first y to 0 and the rest to 64 (which is the height of the status bar plus the height of the navigation bar). Like so:
let y = i == 0 ? 0 : 64
Does anyone have an idea why the tableViews are being misplaced? According to my understanding, all tableViews should be displayed right below the navigation bar, as this is the vertical origin of the view controller's view?
According to my understanding, all tableViews should be displayed right below the navigation bar, as this is the vertical origin of the view controller's view?
This is only the case in my experience if you set myNavigationController.navigationBar.translucent = false. If the navigation bar is translucent, the top of your view is still the top of the screen.
On a side note, you should definitely look into laying these views out via auto layout, rather than this hacky frame math. Would simplify your code a lot.
I've noticed some strange behavior of UITablesViews inside a ViewController when said ViewController is embedded within a UINavigationController. The following is the code of a simple prototype for selecting different UITableViews to be shown in a scene excluding the methods for the TableView and selecting which view to show.
class ChooseTableViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var tableViewA: UITableView = UITableView()
var tableViewB: UITableView = UITableView()
var colors: [String] = ["red", "blue", "green"]
var shapes: [String] = ["triangle", "circle", "square"]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
tableViewA.frame = CGRectMake(0, 50, 320, 200)
tableViewA.delegate = self
tableViewA.dataSource = self
tableViewA.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cellTableViewA")
tableViewB.frame = CGRectMake(0, 50, 320, 200)
tableViewB.delegate = self
tableViewB.dataSource = self
tableViewB.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cellTableViewB")
self.view.addSubview(tableViewB)
self.view.addSubview(tableViewA)
tableViewB.hidden = true
let segmentSelectorLabels = ["one", "two"]
let segmentSelector = UISegmentedControl(items: segmentSelectorLabels)
segmentSelector.frame = CGRectMake(self.view.frame.width/2 - 50, self.view.frame.height - 100, 100, 40)
self.view.addSubview(segmentSelector)
segmentSelector.selectedSegmentIndex = 0
segmentSelector.addTarget(self, action: "chooseTable:", forControlEvents: .ValueChanged)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
...
}
and here are the results of when ChooseTableViewController is not embedded in a UINavigationController and when it is
Simulators results
In another project the behavior can be fixed by simply adding
let emptyView:UIView = UIView()
and
self.view.addSubiew(emptyView)
with emptyView being the first added subview. This is a hackney solution to having the UITableViews placed in their proper position. Any insight to this behavior is appreciated.
uncheck "Adjust Scroll View Insets" for the viewcontroller in the Attributes inspector or do it in code:
self.automaticallyAdjustsScrollViewInsets = NO; (Obj-C)
self.automaticallyAdjustsScrollViewInsets = false (Swift)
some more explanation:
if this property is set to YES / true the viewcontroller - as the name says - automatically adjusts the insets for the first scrollview in its view hierarchy. this can be helpful if your scrollview / tableview / textview / webview takes up the whole screen and parts of it are normally hidden by the statusbar / navigationbar / tabbar or toolbar. then those insets make your content appear below the top bars / above the bottom bars. this though only happens when the scrollview / ... is the TOP MOST subview in the view hierarchy (= the subview at index 0). to make things clearer i uploaded four examples:
property set to YES / true and two textviews in the view hierarchy (at index 0 and 1): as you can see the insets are only set for the first scrollview
property set to NO / false and two textviews in the view hierarchy (at index 0 and 1): as you can see no insets are set at all
property set to YES / true, a button (subview at index 0) and two textviews (at index 1 and 2): as you can see no insets are set at all although the property is YES / true. that is because no scrollview is the TOP MOST subview in the view hierarchy.
property set to YES / true an a textview in the view hierarchy at index 0 taking up the whole screen: as you can see although the textview takes up the whole screen (starts at 0,0) the text is not hidden by the status- / navigationbar because the viewcontroller automatically adjusted the textview's (scrollview's) inset.
i hope i could help making things a bit clearer. :)
Set table view's frame origin y-positions to 0. Find "Extended Edges" settings for this view controller in storyboard and turn off "Under Top Bars".
I am trying to show 2 UITableViewController's inside an horizontal UIScrollView. I am using AUtoLayout and Size Classes. My storyboards are of size Inferred. This is what I did for my test:
Add an UIScrollView and choose Update Constraints for AutoLayout from Resolve AutoLayout Issues.
Setup one UITableViewController and one UIViewController which inherits from UITableViewDelegate and UITableViewDataSource (I wanted to try both ways to use table views)
For both of them I added an UIButton on the right hand side and for AutoLayout I choose top and trailing edge to be fixed on the right.
After I run the code for UITableViewController I can only the "n" from Button if I click on the cell, I can see the button.
For the UIViewController table view I don't see the button at all.
Here is a print screen:
And here is my code for the scroll view controller:
override func viewDidLoad() {
super.viewDidLoad()
self.automaticallyAdjustsScrollViewInsets = false
let testTableVC = self.storyboard!.instantiateViewControllerWithIdentifier("TestTableViewController") as! TestTableViewController;
let testVC = self.storyboard!.instantiateViewControllerWithIdentifier("TestViewController") as! TestViewController;
var bounds = UIScreen.mainScreen().bounds
var width = bounds.size.width
var height = bounds.size.height;
scrollView!.contentSize = CGSizeMake(2*width, height);
let viewControllers = [testTableVC, testVC]
var idx:Int = 0;
for viewController in viewControllers {
// index is the index within the array
// participant is the real object contained in the array
addChildViewController(viewController);
let originX:CGFloat = CGFloat(idx) * width;
viewController.view.frame = CGRectMake(originX, 0, width, height);
scrollView!.addSubview(viewController.view)
viewController.didMoveToParentViewController(self)
idx++;
}
}
edit:
https://github.com/adrianstanciu24/scrollviewtest
I've created pull request that solves this. What was wrong is that you've used default UITableViewCell and added some custom content to it in storyboard. Table view then dequeued the default cell and drawn the default cell atop of the one from storyboard (with margins on sides - so only right part of your button was visible). You need to create custom UITableViewCell, connect outlets and dequeue the custom cell, the default one doesn't know anything about your button.
I am using project from github as a reference.
project URL:
https://github.com/lephuocdai/iOSsample/tree/master/PageViewDemo
In this project i want to show the UIPageControl at top left position .
I tried setting the rect property of pageControl using CGRectMake() to some value ;But it shows always at bottom center
here s a very neat and 100% effective way to get to change the position of the pageControl
extension UIPageViewController {
override open func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
for subV in self.view.subviews {
if type(of: subV).description() == "UIPageControl" {
let pos = CGPoint(x: newX, y: newY)
subV.frame = CGRect(origin: pos, size: subV.frame.size)
}
}
}
}
The project uses a UIPageViewController to handle the display and movement through the content.
You can supply data to that object so it displays a UIPageControl as you say.
BUT you have no control over the display of that item, other than maybe some colour styling.
If you want to position it, you'll need to implement you're own instance of UIPageControl and handle it's content, position and changes manually.
Override the viewDidLayoutSubviews() of the pageviewcontroller and use this
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// get pageControl and scroll view from view's subviews
let pageControl = view.subviews.filter{ $0 is UIPageControl }.first! as! UIPageControl
let scrollView = view.subviews.filter{ $0 is UIScrollView }.first! as! UIScrollView
// remove all constraint from view that are tied to pagecontrol
let const = view.constraints.filter { $0.firstItem as? NSObject == pageControl || $0.secondItem as? NSObject == pageControl }
view.removeConstraints(const)
// customize pagecontroll
pageControl.translatesAutoresizingMaskIntoConstraints = false
pageControl.addConstraint(pageControl.heightAnchor.constraintEqualToConstant(35))
pageControl.backgroundColor = view.backgroundColor
// create constraints for pagecontrol
let leading = pageControl.leadingAnchor.constraintEqualToAnchor(view.leadingAnchor)
let trailing = pageControl.trailingAnchor.constraintEqualToAnchor(view.trailingAnchor)
let bottom = pageControl.bottomAnchor.constraintEqualToAnchor(scrollView.topAnchor, constant:8) // add to scrollview not view
// pagecontrol constraint to view
view.addConstraints([leading, trailing, bottom])
view.bounds.origin.y -= pageControl.bounds.maxY
}
is your page control encompassed within some other View, if so then you may be setting the co-ordinates wrong, try to put log of your page control.frame so as to know where it lies
If using storyboards, place a UIPageControl object using the menu in the bottom right and set constraints.
If using frames, just programmatically add it:
var pageControl = UIPageControl()
pageControl.frame = CGRectMake(0,0,0,0) <- These are the coordinates.
self.view.addSubView(pageControl)
if you set the frame for UIPageControl , it doesnot work.
Other than this you can set the transform.
[_pageControl setTransform:CGAffineTransformMakeTranslation(100, 0.0)];
enjoy coding