Custom views in Horizontal scroll view. Is it possible? - swift

I am designing an app which has a screen in which I have a horizontal scroll view which I fill with UIViews dynamically depending upon the number of data I have in my array . I did the same via programmatically. I have mentioned my approach below.
1) I put a Scroll view for scrolling horizontally and created a reference for that in my class.
2) I programatically added views as per my code -
var imagevieww = UIImageView()
#IBOutlet weak var hrzntlscrl: UIView!
#IBOutlet weak var scrollview: UIScrollView!
override func viewDidLoad()
{
super.viewDidLoad()
let viewcount = 15
for var i = 0; i < viewcount; i++
{
let viewnew = UIView(frame: CGRectMake( hrzntlscrl.frame.origin.x+110*CGFloat(i), 0, 100.0, hrzntlscrl.frame.height))
viewnew.backgroundColor = UIColor.orangeColor()
imagevieww = UIImageView(frame: CGRectMake(0, 10, 100.0, 50))
imagevieww.backgroundColor = UIColor.blackColor()
viewnew.addSubview(imagevieww)
scrollview.addSubview(viewnew)
}
}
So I just wanted to know that instead of creating a view and the corresponding subviews eg. here imageview and setting their location and frame size programatically , Can I have a standard custom view designed in my IB and use any reference of that in my for loop instead of creating one programmatically? If we can do that,can you please give me some steps.

Yes. This is possible. You can instantiate a class from a nib with
let customView: CustomView = NSBundle.mainBundle().loadNibNamed("CustomViewNibName", owner: self, options: nil)[safe: 0] as? CustomView
You would also need to set the content size of the horizontal scroll view to the combined width of all the views.
But I think that your use case would be better served by using a UICollectionView instad of a scroll view, UICollectionView does all this and more in a much simpler implementation.

Related

ScrollView Constraint problem threw storyboard

So I maybe successfully created a scroll view following a tutorial but something is terribly wrong. So I'm trying to create a scroll view with a table view inside and some buttons and labels and everything works fine but as soon as I add a single constraint it all goes just white with no explanation. I would assume content view is messing things up but I'm not sure, thx in advance!
So following some other people problems I tried filling constraints programmatically and doing views and subviews programmatically aswell, keep in mind tho that rest of constraints I did on storyboard. Btw I have tried equal width and height on newView --> scroll view and nothing seems to change.
my view hierarchy looks like this
myViewController -> View -> scrollView -> newView
class ThirdViewController: UIViewController {
#IBOutlet weak var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
scrollView.addSubview(newView)
newView.leftAnchor.constraint(equalTo: scrollView.leftAnchor).isActive = true
newView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
newView.rightAnchor.constraint(equalTo: scrollView.rightAnchor).isActive = true
newView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
//tableView1.dataSource = self
//tableView1.delegate = self
//tableView1.register(UITableViewCell.self, forCellReuseIdentifier: "cell1")
}
#IBOutlet weak var newView: UIView!
override func viewWillLayoutSubviews() {
scrollView.contentSize = CGSize(width: 375, height: 1950)
}
}
Sorry, if question is layout pretty badly - I'm not that pro!

UITextView Height Adjustment

I'm trying to create a ViewController which will hold some large text. I used UILabel but since the text can be long, it won't work. Trying to switch to UITextViewbut I'm having some problems.
I'm using ScrollView since there is a image on the top and button on the bottom. So I don't it scrolling inside the UITextView itself. I've disabled it.
I want to Text to height itself automatically according to the text length. So it can scroll with the images, buttons etc. Just like the apps Instapaper, Medium, Pocket etc.
I've tried all the code and solutions on StackOverflow, but they either didn't work or they were Objective-C.
Storyboard Structure:
View Controller -> View -> ScrollView -> Image, TextView, Button
class ViewController: UIViewController {
#IBOutlet weak var textView: UITextView!
var theContent = fromClass.text
override func viewDidLoad() {
super.viewDidLoad()
textView.text = theContent
}
}
There is few ways to change the size of the UITextView
extension String {
func bounds(approximated width: CGFloat, approximated height: CGFloat, font: UIFont) -> CGRect {
let size = CGSize(width: width, height: height)
let attribs = [NSAttributedStringKey.font: font]
return NSString(string: self).boundingRect(with: size, options: .usesLineFragmentOrigin, attributes: attribs, context: nil)
}
}
approximated width = width of your UITextView I assume it is going to be static value which you won't change.
approximated height = minimal height of your UITextView.
The method going to return frame of your text input. Take from it height property and depending of how big is that change the size of the UITextView.
If it won't satisfy you, you can use another way.
func heightOfString(_ attributes: [NSAttributedKeyString : Any]) -> CGFloat {
let size = self.size(withAttributes: attributes)
return size.height
}
The same usage as from above.
Adjust your view hierarchy to the following view hierarchy :
View Controller -> View -> ScrollView -> View1 -> Image, UILabel, Button
1- give view1 top-left-bottom-right constraints and and align it center X to it's parent view
2- add UIImageView in the top of View1 and give it top-left-right constraint and a fixed height constraint.
3- add a UILabel and give it top constraint to the image and right-bottom-left constraint to it's superview.
make UILabel number of lines = 0
The content not should be scrollable.
Note that: in your case; you do not need UITextView .
If you want to use UITextView:
1- add a height constraint to the UITextView and connect an outlet to it
called: constTextViewHeight
let textViewContentHeight = textView.contentSize.height//call this line after adding the text to the textView
self.constTextViewHeight.contant = textViewContentHeight
self.view.layoutIfNeeded()

How to add constraints to a Custom UITableViewCell

I have created a custom cell for my table view and it works fine except that my Image View will not align correctly.
Is there a way to add constraints to a custom cell view? Programmatically or otherwise?
This is what my cell looks like in the storyboard - which is what I was hoping to achieve at run time:
But when I demo the app this is what happens:
Here is another image from the storyboard:
View Controller /w TableView
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
println(questions)
println(finalResults)
let theWidth = view.frame.size.width
let theHeight = view.frame.size.height
tableView.frame = CGRectMake(0,0, theHeight, theWidth)
}
Cell View Controller
override func awakeFromNib() {
let theWidth = UIScreen.mainScreen().bounds.width
contentView.frame = CGRectMake(0,0, theWidth, 64)
answerImage.center = CGPointMake(115, 15)
}
You only have to set up your constraints correctly. Indeed, even if Add Missing Constraints and Reset to Suggested Constraints are handy sometimes they don't know what you want, thus the result cannot always be what you expect.
What you might want to do here is the following.
For you question label set it in center Y of it's container and set it's leading space to the superview. Like so :
Then for you image, you want to set it in center Y of it's container too and set a ratio 1:1 on it. Like so :
Note that you might also want to check the width and height if you don't want your image to upscale (if you set a bigger cell on some device).
You should now have something like that :
And the result :
Let me know if it helped.
PS : I don't have your image so I've just set a background color on the uiimageview
Assuming that you started with a View Controller, Dragged a table and a Cell into the table...
in ViewController: UIViewController...{
#IBOutlet weak var resultsTable: UITableView! // connect to tableView
override func viewDidLoad() {
super.viewDidLoad()
let theWidth = view.frame.size.width
let theHeight = view.frame.size.height
resultsTable.frame = CGRectMake(0,0, theWidth, theHeight)
}
}
in UITableViewCell file:
#IBOutlet weak var imageView: UIImageView!
override func awakeFromNib() {
let theWidth = UIScreen.mainScreen().bounds.width
contentView.frame = CGRectMake(0,0 theWidth, 64)
imageView.center = CGPointMake(115, 15) //mess around with these #
}

How to apply Swift constraints on imageView on rotation in code?

The following elegant code derived from the new text by Vandad Nahavandipoor on Swift works fine in landscape however since the scrollView is constructed by code one cannot use usual constraints to properly display the chart named CrossTalk.png in landscape. In fact in landscape the chart initially displays in 50% of the screen and when dragged to the right in an attempt to fill the screen the initiating viewController-1 that segued into the current, displaying viewController-2 takes over the entire landscape view. Is there a way to fix this problem in code?
SWIFT latest version 6.1
import UIKit
class ChartViewController: UIViewController, UIScrollViewDelegate,UIGestureRecognizerDelegate {
var imageView: UIImageView?
var scrollView: UIScrollView?
let image = UIImage(named: "CrossTalk.png")
override func viewDidLoad() {
super.viewDidLoad()
imageView = UIImageView(image: image)
scrollView = UIScrollView(frame: view.bounds)
if let theScrollView = scrollView{
theScrollView.addSubview(imageView!)
theScrollView.contentSize = imageView!.bounds.size
view.addSubview(theScrollView)
}
}

Add view over tableview (UITableViewController)

Situation: I've got a UITableViewController loading some data asynchronously from a service. During this time I would like to place a full screen (except navigation bar) view over the table view showing my custom indicator and text.
Problem: The problem I'm facing is that when my custom view (it has a red background) is placed over the UITableView the lines of the table view are shown trough my custom view (see image below).
What I tried:
I tried to use insertBelow and above, didn't work. I also tried to do: tableview.Hidden = true, but this also hides the custom view for some reason as seen on image 2.
Image1: For some reason I can see the lines threw my view.
Image 2: Tableview + custom view gone when hidden = true used.
My code:
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
UIView view = new UIView (new RectangleF (0, 0, this.TableView.Frame.Width, this.TableView.Frame.Height));
view.BackgroundColor = UIColor.Red;
this.TableView.AddSubview (view);
TableView.Source = new SessionTableViewSource ();
}
You can use self.navigationController.view as view for adding subview.
The issue is that the View of a UITableViewController is a UITableView, so you cannot add subviews to the controller on top of the table.
I'd recommend switching from a UITableViewController to a simple UIViewController that contains a UITableView. This way the controller main view is a plain UIView that contains a table, and you can add subviews to the main UIView and they will be placed on top of the table view.
You can try to add the view to the window instead of nesting it in the table view like this:
UIWindow* mainWindow = [[UIApplication sharedApplication] keyWindow];
[mainWindow addSubview: overlayview];
UIWindow* window = [[UIApplication sharedApplication].delegate.window;
[window addSubview: your-overlayview];
Swift / Storyboard Solution
Note: The code below assumes one has a custom view (ratingView in my case) that is to be presented over a UITableView.
I've read many answers to this and similar questions on SO. The other answers from these sources worked to varying degrees for me (e.g.,view loaded but not shown or not accessible,...). I am using Swift 2.0+ and I am sharing the complete solution for doing this using a UITableViewController.
Create an outlet to the Navigation Bar and the view, which you want to bring over the tableview.
//MARK:Outlets
#IBOutlet weak var navBar:UINavigationBar!
#IBOutlet var ratingView: MNGStarRating!
In my case I also wanted to animate the view over the tableview so I used a class variable to hold a reference to the inflection point and a point above the scene (off-screen).
var centerYInflection:NSLayoutConstraint!
var aPointAboveScene = -(max(UIScreen.mainScreen().bounds.width,UIScreen.mainScreen().bounds.height) * 2.0)
Then in viewDidLoad I called a function (configureRatingViewAutoLayout) which configures and adds the constraints for the new view to be animated over the tableview.
func configureRatingViewAutoLayout() {
//REQUIRED
self.navBar.superview?.addSubview(self.ratingView)
var newConstraints:[NSLayoutConstraint] = []
newConstraints.append(self.ratingView.leadingAnchor.constraintEqualToAnchor(self.view.leadingAnchor,constant: 10))
newConstraints.append(self.ratingView.trailingAnchor.constraintEqualToAnchor(self.view.trailingAnchor,constant: 10))
newConstraints.append(self.ratingView.centerXAnchor.constraintEqualToAnchor(self.view.centerXAnchor))
//hides the rating view above the scene
self.centerYInflection = self.ratingView.centerYAnchor.constraintEqualToAnchor(self.view.centerYAnchor, constant: self.aPointAboveScene)
//the priority must be set below 1000 if you intend to change it after it has been added to a view
self.centerYInflection.priority = 750
newConstraints.append(self.centerYInflection)
//constraints must be added to the container view of the two items
self.ratingView.superview?.addConstraints(newConstraints)
}
Nota Bene - On a UITableViewController; the self.view is the
self.tableView. They point to the same thing so I guess one could also
use the self.tableView reference above.
Sometime later... In response to a UIControl event I call this method.
#IBAction func toggleRatingView (sender:AnyObject?){
//REQUIRED
self.ratingView.superview?.layoutIfNeeded()
UIView.animateWithDuration(1.0, delay: 0.0, usingSpringWithDamping: 0.37, initialSpringVelocity: 0.99, options: [.CurveEaseOut], animations: { () -> Void in
if CGRectContainsRect(self.view.frame, self.ratingView.frame) {
//in frame ~ animate away
//I play a sound to alert the user something is happening
self.centerYInflection.constant = self.aPointAboveScene
self.centerYInflection.priority = UILayoutPriority(950)
//I disable portions of the UI
self.disableUIElements(nil)
} else {
//out of frame ~ animate in
//I play a different sound here
self.centerYInflection.constant = 0
self.centerYInflection.priority = UILayoutPriority(950)
//I enable the UI fully
self.enableUIElements(nil)
}
//REQUIRED
self.ratingView.superview?.setNeedsLayout()
self.ratingView.superview?.layoutIfNeeded()
}) { (success) -> Void in
//do something else
}
}
These helper methods can be configured to control access to elements in your scene during the presentation of the view.
func disableUIElements(sender:AnyObject?) {
//UI
}
func enableUIElements(sender:AnyObject?) {
//UI
}
Caveats
My view is a custom view in the Storyboard (sitting outside of the
tableview but connected to the TableView Controller). The view has a
required user runtime attribute defined layer.zPosition with a Number value set to 2 (this ensures that it presents in front of the
UITableView).
One could also try playing around with bringSubviewToFront:
and sendSubviewToBack: methods if you don't want to set the zPosition
(I think zPosition is simpler to use)
Try this to hook a button at bottom of the UITableViewController
declare button as a variable:
var submitButton: UIButton!
and in viewDidLoad:
submitButton = UIButton(frame: CGRect(x: 5, y: UIScreen.main.bounds.size.height - 50, width: UIScreen.main.bounds.size.width - 10, height: 50))
submitButton.backgroundColor = UIColor.init(red: 180/255, green: 40/255, blue: 56/255, alpha: 1.0)
submitButton.setTitle("Submit", for: .normal)
submitButton.titleLabel?.font = UIFont(name: "Arial", size: 15)
submitButton.titleLabel?.textColor = .white
submitButton.addTarget(self, action: #selector(submit), for: .touchUpInside)
submitButton.layer.cornerRadius = 5
self.view.addSubview(submitButton)
and implement this method:
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
submitButton.frame = CGRect.init(x: submitButton.frame.origin.x, y: UIScreen.main.bounds.size.height + scrollView.contentOffset.y - 50, width: submitButton.frame.width, height: submitButton.frame.height)
}
This works for me:
if let myTopView = Bundle.main.loadNibNamed("MyTopView", owner: self, options: nil)?.first as? MyTopView {
if let view = UIApplication.shared.keyWindow{
view.addSubview(myView);
myTopView.translatesAutoresizingMaskIntoConstraints = false
myTopView.topAnchor.constraint(equalTo: view.topAnchor ).isActive = true
myTopView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
myTopView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
myTopView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
}
}