// 首页发现 viewModel
fileprivate var homeViewModel = HomeViewModel()
接下来,我们来配置 tableViewDataSource:
// Mark UITableViewDataSource
override func numberOfSections(in tableView: UITableView) -> Int {
if homeViewModel.sections.isEmpty {
return 0
}
return homeViewModel.sections.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return homeViewModel.sections[section].rowCount
}
现在我们就可以开始构建 UI 了。根据网易云音乐的样式,我们需要创建 12 种不同类型的 Cell, 每种 Cell 对应一种 ViewModelItems。
为了进一步的提高代码的质量,我们可以为这些 Cell 定义一个基类 BaseViewCell,这样通过该基类,我们就可以设置一些默认的属性,减少一些不必要的编码工作;另外,通过观察你会发现,大部分的 Section 都会包含一个 headView。关于 headView 的实现方式,想必使用过 UITableView 的同学都不会陌生,可以通过下面的方法来实现:
- (nullable UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section; // custom view for header. will be adjusted to default or specified header height
但是,在这个项目中,我并不打算使用上面的方法来实现 headView,主要原因是因为网易云音乐的每个 Section 都是有圆角效果的,如果我们定义了 viewForHeaderInSection,那么我们在实现圆角的时候就需要做如下的逻辑:
我们知道,要为一个视图添加圆角是非常有讲究的,如果直接调用 cornerRadius 和 masksToBounds 这俩个方法设置圆角就会出现离屏渲染,况且我们的首页有很多圆角视图,到时候首页加载显示就会感受到明显的卡顿,这样的体验可不好!而且使用这俩个方法也无法为视图指定设置圆角的方位,是要左上角呢还是右下角?
首先作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS开发交流群:130 595 548,不管你是小白还是大牛都欢迎入驻 ,让我们一起进步,共同发展!(群内会免费提供一些群主收藏的免费学习书籍资料以及整理好的几百道面试题和答案文档!)上面讲到为视图设置圆角一不小心就会造成离屏渲染,那么这个问题该如何解决呢!在这里,我们可以通过利用 UIBezierPath 来为视图绘制圆角,以及还可以指定画圆角的方位:
func roundCorners(_ rect: CGRect, corners: UIRectCorner, radius: CGFloat) {
let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
let mask = CAShapeLayer()
mask.path = path.cgPath
self.layer.mask = mask
}
考虑到如果通过 viewForHeaderInSection 方法来创建 HeadView,那么我们就要为俩个视图来绘制圆角,分别是 TableViewCell 和 viewForHeaderInSection 创建的 headView。这里我想了一个比较好的办法,只需要调用一次绘制方法即可,那就是将我们的 headView 实现在我们的 tableViewCell 中,如下所示:
另外,因为每个 Section 都有 headView ,所以我们可以在 BaseViewCell 这个基类中去实现这个头视图:
/// UITableViewCell 的基类
class BaseViewCell: UITableViewCell {
var headerView: JJTableViewHeader?
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.backgroundColor = UIColor.homeCellColor
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
接下来,我们来构建具体的 Cell ,由于代码过多,这里仅展示部分代码:
/// 首页 Bannerl
class ScrollBannerCell: BaseViewCell {
class var identifier: String {
return String(describing: self)
}
var scrollBanner: JJNewsBanner!
var item: HomeViewModelSection? {
didSet {
guard let item = item as? BannerModel else {
return
}
self.setupUI(model: item)
}
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
/// 初始化
scrollBanner = JJNewsBanner(frame: CGRect.zero)
self.contentView.addSubview(scrollBanner!)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
}
func setupUI(model: BannerModel) {
self.scrollBanner.frame = model.frame
self.scrollBanner.updateUI(model: model, placeholderImage: UIImage(named: "ad_placeholder"))
}
}
/// 首页-发现 圆形按钮
class CircleMenusCell: BaseViewCell {
class var identifier: String {
return String(describing: self)
}
var homeMenu: HomeMenu!
var item: HomeViewModelSection? {
didSet {
guard let item = item as? MenusModel else {
return
}
self.setupUI(model: item)
}
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
/// 初始化
homeMenu = HomeMenu(frame: CGRect.zero)
self.contentView.addSubview(homeMenu!)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
}
func setupUI(model: MenusModel) {
self.homeMenu.frame = model.frame
self.homeMenu.updateUI(data: model.data)
}
}
....
在现实中,每个 Cell 所展示的视图样式都是非常丰富的,于是我们必须为 Cell 创建不同的 UI 样式,每种样式对应自己的数据 Model。
不过实现起来也简单,中间放一个 Label 即可。如该侧面图所示(图借用自作者 Leo):
整体实现用的控件还是 UICollectionView。部分代码如下:
import UIKit
import Foundation
import SnapKit
import Kingfisher
class HomeMenuCell: UICollectionViewCell {
lazy var menuLayer: UIView = {
let view = UIView()
view.backgroundColor = UIColor.darkModeMenuColor
return view
}()
lazy var menuIcon: UIImageView = {
let mIcon = UIImageView()
mIcon.tintColor = UIColor.dragonBallColor
return mIcon
}()
lazy var menuText: UILabel = {
let mText = UILabel()
mText.textColor = UIColor.darkModeTextColor
mText.textAlignment = .center
mText.font = UIFont.systemFont(ofSize: 12)
return mText
}()
override init(frame: CGRect) {
super.init(frame: frame)
}
override func layoutSubviews() {
super.layoutSubviews()
self.contentView.addSubview(self.menuLayer)
self.menuLayer.addSubview(self.menuIcon)
self.contentView.addSubview(self.menuText)
self.menuLayer.snp.makeConstraints { (make) in
make.centerX.equalToSuperview()
make.width.equalTo(self.frame.size.width * 0.6)
make.height.equalTo(self.frame.size.width * 0.6)
}
self.menuIcon.snp.makeConstraints { (make) in
make.centerX.equalToSuperview()
make.centerY.equalToSuperview()
make.width.equalTo(self.frame.size.width * 0.6)
make.height.equalTo(self.frame.size.width * 0.6)
}
self.menuText.snp.makeConstraints { (make) in
make.centerX.equalToSuperview()
make.bottom.equalToSuperview()
make.height.equalTo(self.frame.size.width * 0.4)
make.width.equalTo(self.frame.size.width)
}
// 设置菜单圆角
self.menuLayer.layer.cornerRadius = self.frame.size.width * 0.6 * 0.5
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupUI(imageUrl: String, title: String) -> Void {
let cache = KingfisherManager.shared.cache
let imgModify = RenderingModeImageModifier(renderingMode: .alwaysTemplate)
let optionsInfo = [KingfisherOptionsInfoItem.imageModifier(imgModify),
KingfisherOptionsInfoItem.targetCache(cache)]
self.menuIcon.kf.setImage(with: URL(string: imageUrl), placeholder: nil, options: optionsInfo, completionHandler: { ( result ) in
})
self.menuText.text = title
}
}
因为这些 UI 的效果是差不多的,第一个冒出来想法就是在 Cell 中放置 UICollectionView,它的布局也很简单,直接用系统提供的即可,不需要我们去自定义布局。
像这种上图下文的 CollectionViewCell 也很好定义,这里就不多做阐述,部分代码如下:
import UIKit
import SnapKit
import Kingfisher
class CardViewCell: UICollectionViewCell {
/// 封面
lazy var albumCover: UIImageView! = {
let cover = UIImageView()
cover.backgroundColor = UIColor.clear
cover.contentMode = .scaleAspectFill
return cover
}()
/// 描述
lazy var albumDesc: UILabel! = {
let descLabel = UILabel()
descLabel.backgroundColor = UIColor.clear
descLabel.font = UIFont.systemFont(ofSize: 12)
descLabel.numberOfLines = 0
return descLabel
}()
/// 阅读量
var views: String?
/// 内边距
let padding: CGFloat = 5
/// 阅读量按钮
lazy var viewsButton: UIButton! = {
let button = UIButton(type: .custom)
button.titleLabel?.font = UIFont.systemFont(ofSize: 10)
button.backgroundColor = UIColor(red: 182/255, green: 182/255, blue: 182/255, alpha: 0.6)
button.setImage(UIImage(named: "Views"), for: .normal)
button.setTitleColor(.white, for: .normal)
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = .clear
self.addSubview(self.albumCover)
self.albumCover.addSubview(self.viewsButton)
self.addSubview(self.albumDesc)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
let height: CGFloat = self.bounds.height
let width: CGFloat = self.bounds.width
let descHeight: CGFloat = height * (1/4)
// 封面样式设置
self.albumCover.snp.makeConstraints { (make) in
make.width.equalTo(width)
make.height.equalTo(width)
make.centerX.equalToSuperview()
make.top.equalToSuperview()
}
self.albumCover.roundCorners(self.albumCover.bounds, corners: [.allCorners], radius: 10)
// 设置按钮样式
let viewsRect = self.getStrBoundRect(str: self.views!, font: self.viewsButton.titleLabel!.font, constrainedSize: CGSize.zero)
let viewsW = viewsRect.width
let viewsH = viewsRect.height * 1.2
self.viewsButton.frame = CGRect(x: self.albumCover.frame.width - viewsW - padding, y: padding, width: viewsW, height: viewsH)
self.viewsButton.moveImageLeftTextCenterWithTinySpace(imagePadding: 5)
self.viewsButton.roundCorners(self.viewsButton.bounds, corners: [.allCorners], radius: viewsW * 0.2)
self.albumDesc.snp.makeConstraints { (make) in
make.width.equalTo(width - 10)
make.height.equalTo(descHeight)
make.centerX.equalToSuperview()
make.top.equalTo(self.albumCover.snp.bottom).offset(5)
}
}
....
}
/// 通用的卡片滚动视图,该控件适用于横向滚动并且上图下文形式
class CardCollectionView: UIView {
.....
/// 布局
lazy var cardFlowLayout: UICollectionViewFlowLayout = {
let layout = UICollectionViewFlowLayout()
layout.minimumLineSpacing = margin
layout.minimumInteritemSpacing = 0
layout.sectionInset = UIEdgeInsets.init(top: -20, left: margin, bottom: 0, right: 0)
layout.scrollDirection = .horizontal
return layout
}()
/// 歌单的视图
lazy var hotAlbumContainer: UICollectionView = {
let collectionView = UICollectionView(frame: CGRect.zero, collectionViewLayout: self.cardFlowLayout)
collectionView.register(CardViewCell.self, forCellWithReuseIdentifier: RecomendAlbumId)
collectionView.isPagingEnabled = true
collectionView.showsVerticalScrollIndicator = false
collectionView.showsHorizontalScrollIndicator = false
collectionView.delegate = self
collectionView.dataSource = self
collectionView.backgroundColor = UIColor.clear
collectionView.bounces = false
return collectionView
}()
override init(frame: CGRect) {
super.init(frame: frame)
self.addSubview(self.hotAlbumContainer)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
self.hotAlbumContainer.frame = CGRect(x: 0, y: 0, width: self.bounds.width, height: self.bounds.height)
// 设置 item size 大小
self.cardFlowLayout.itemSize = CGSize(width: itemA_width * scaleW, height: self.frame.size.height - 3 * margin)
}
deinit {
self.hotAlbumContainer.delegate = nil
self.hotAlbumContainer.dataSource = nil
}
}
// MARK: - UICollectionViewDelegate
extension CardCollectionView: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
}
}
// MARK: - UICollectionViewDataSource
extension CardCollectionView: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if self.songList == nil {
return 0
}
return self.songList!.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: RecomendAlbumId, for: indexPath) as! CardViewCell
let result:Creative = self.songList![indexPath.row]
if result.creativeType == "voiceList" {
cell.updateUI(coverUrl: (result.uiElement?.image!.imageURL)!, desc: (result.uiElement?.mainTitle!.title)!, views: String((result.creativeEXTInfoVO?.playCount)!))
} else {
let element = result.resources?[0]
cell.updateUI(coverUrl: (element?.uiElement.image.imageURL)!, desc: (element?.uiElement.mainTitle.title)!, views: String((element?.resourceEXTInfo?.playCount)!))
}
return cell
}
}
由于“个性推荐”,“新歌新碟数字专辑”这俩个功能的样式是差不多的,所以也将这俩并在一起说。在这我还是选择在 Cell 中放置 UICollectionView。但是,通过观察你会发现它的 UI 样式其实是有讲究的,就是在同一个页面中,它的第二个 item 也需要露出一部分,这该如何去实现呢!
为了能在一个页面中出现俩个 item,那我们必须要减少 itemSize 的宽度,这样设置 UICollectionViewFlowLayout 后就能在一个页面中出现俩个 item 了。
我们知道在 UICollectionView 的属性中,有一个分页的属性:isPagingEnabled,当设置成 true 时,每次滚动的位移量等于它自身 frame 的宽度;当不设置这个分页属性,它的默认值是 false, 所以它的滚动就不会有分页的效果。
OK,那这个想法是不是正确呢!其实当你动手实践后,你会发现这样实现后会有一个非常头疼的 bug,那就当 item 滚动的时候会出现遮挡,这用户体贴也太差了。
有人要问那是不是 UICollectionView 这个控件就只能按照屏幕的大小来分页呢!答案当然是否定的。我们还可以用自定义的方式来实现分页滚动。根据文档,Apple 在 UICollectionViewFlowLayout 的定义中提供了一个可重写的函数:
func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint // return a point at which to rest after scrolling - for layouts that want snap-to-point scrolling behavior
这个函数的返回值,决定了 UICollectionView 停止滚动时的偏移量,可以通过重写这个函数来实现自定义的分页滚动,重写这个函数的逻辑思路如下:
class RowStyleLayout: UICollectionViewFlowLayout {
private var lastOffset: CGPoint!
override init() {
super.init()
lastOffset = CGPoint.zero
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// 初始化
override func prepare() {
super.prepare()
self.collectionView?.decelerationRate = .fast
}
// 这个方法的返回值,决定了 CollectionView 停止滚动时的偏移量
override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
// 分页的 width
let pageSpace = self.stepSpace()
let offsetMax: CGFloat = self.collectionView!.contentSize.width - (pageSpace + self.sectionInset.right + self.minimumLineSpacing)
let offsetMin: CGFloat = 0
// 修改之前记录的位置,如果小于最小的contentsize或者最大的contentsize则重置值
if lastOffset.x < offsetMin {
lastOffset.x = offsetMin
} else if lastOffset.x > offsetMax{
lastOffset.x = offsetMax
}
// 目标位移点距离当前点距离的绝对值
let offsetForCurrentPointX: CGFloat = abs(proposedContentOffset.x - lastOffset.x)
let velocityX = velocity.x
// 判断当前滑动方向,向左 true, 向右 fasle
let direction: Bool = (proposedContentOffset.x - lastOffset.x) > 0
var newProposedContentOffset: CGPoint = CGPoint.zero
if (offsetForCurrentPointX > pageSpace/8.0) && (lastOffset.x >= offsetMin) && (lastOffset.x <= offsetMax) {
// 分页因子,用于计算滑过的cell数量
var pageFactor: NSInteger = 0
if velocityX != 0 {
// 滑动
// 速率越快,cell 滑过的数量越多
pageFactor = abs(NSInteger(velocityX))
} else {
// 拖动
pageFactor = abs(NSInteger(offsetForCurrentPointX / pageSpace))
}
//设置 pageFactor 的上限为2,防止滑动速率过大,导致翻页过多
pageFactor = pageFactor < 1 ? 1: (pageFactor < 3 ? 1: 2)
let pageOffsetX: CGFloat = pageSpace * CGFloat(pageFactor)
newProposedContentOffset = CGPoint(x: lastOffset.x + (direction ? pageOffsetX : -pageOffsetX), y: proposedContentOffset.y)
} else {
// 滚动距离小于翻页步距,则不进行翻页
newProposedContentOffset = CGPoint(x: lastOffset.x, y: lastOffset.y)
}
lastOffset.x = newProposedContentOffset.x
return newProposedContentOffset
}
// 每滑动一页的间距
public func stepSpace() -> CGFloat {
return self.itemSize.width + self.minimumLineSpacing
}
}
在我之前的文章中,我已经将实现这个效果的教程写出来了,查看即可
音乐日历的效果,不需要支持横向滚动,所以这里可以选择在 Cell 中放置一个 UIView,对有一点 iOS 开发基础的同学来说,实现这样的 UI 应该不难,大家可以通过 Xib 或者代码的方式来实现,Xib 实现起来应该更快,这里我就不在多做说明了。
经历过构建上面这么多 UI 后,想必看到这个效果,大家都心知肚明了,还有比用 UICollectionView 更简单的方式了吗? 同样是构建一个上图下文的 Cell, 只不过播客需要将图片加上圆角,代码实现起来也很简单,这里也不做多阐述了。
// 设置搜索视图
func setupSearchController () {
let leftItem = UIBarButtonItem(image: UIImage(named: "menu")?.withRenderingMode(.alwaysOriginal), style: UIBarButtonItem.Style.plain, target: self, action: #selector(menuBtnClicked))
let rightItem = UIBarButtonItem(image: UIImage(named: "microphone")?.withRenderingMode(.alwaysOriginal), style: UIBarButtonItem.Style.plain, target: self, action: #selector(microphoneBtnClicked))
self.navigationItem.leftBarButtonItem = leftItem
self.navigationItem.rightBarButtonItem = rightItem
self.cusSearchBar = JJCustomSearchbar(frame: CGRect(x: 0, y: 0, width: 200, height: 50))
self.cusSearchBar.delegate = self
self.navigationItem.titleView = self.cusSearchBar
}
自定义 UISearchBar,代码如下:
class JJCustomSearchbar: UISearchBar {
override init(frame: CGRect) {
super.init(frame: frame)
self.searchTextField.placeholder = "has not been"
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func adjustPosition() {
var frame :CGRect
frame = self.searchTextField.frame
// 获取 placeholder 大小
let r = self.searchTextField.placeholderRect(forBounds: self.searchTextField.bounds)
let offset = UIOffset(horizontal: (frame.size.width - r.width - 40)/2, vertical: 0)
self.setPositionAdjustment(offset, for: .search)
}
}
当我们点击顶部的搜索框时,页面需要跳转到真正的搜索页面,所以我们需要实现 UISearchBarDelegate 代理函数:
extension DiscoveryViewController: UISearchBarDelegate {
// 点击跳转
func searchBarShouldBeginEditing(_ searchBar: UISearchBar) -> Bool {
self.musicSearchController = MusicSearchViewController()
self.navigationController?.pushViewController(self.musicSearchController, animated: false)
return true
}
}
self.searchController = UISearchController(searchResultsController: nil)
self.searchController.delegate = self
self.searchController.searchResultsUpdater = self
self.searchController.searchBar.delegate = self
self.searchController.searchBar.placeholder = "Search"
self.searchController.searchBar.autocapitalizationType = .none
self.searchController.dimsBackgroundDuringPresentation = false
self.navigationItem.hidesBackButton = true
self.navigationItem.searchController = self.searchController
self.navigationItem.searchController?.isActive = true
self.navigationItem.hidesSearchBarWhenScrolling = false
definesPresentationContext = true
在本工程,我们仅实现一个简单的搜索演示功能,因为要真的做好搜索这个需求,需要服务器的”大力“配合,在本工程中,我们仅用一些静态数据来做演示:
musics = [
Results(name: "如果爱"),
Results(name: "情书"),
Results(name: "龙卷风"),
Results(name: "半岛铁盒"),
Results(name: "世界末日"),
Results(name: "爱在西元前"),
Results(name: "等你下课"),
Results(name: "黑色幽默"),
Results(name: "我不配")
]
首先作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS开发交流群:130 595 548,不管你是小白还是大牛都欢迎入驻 ,让我们一起进步,共同发展!(群内会免费提供一些群主收藏的免费学习书籍资料以及整理好的几百道面试题和答案文档!) 数据源有了,接下来就是来实现数据查找功能了,在搜索栏中输入要搜索的歌名,并在页面上列出我们搜索到的结果。这里就需要来实现 UISearchResultsUpdating 和 UISearchBarDelegate 这俩个代理了,通过 UISearchBar 获取到输入值,然后在提供的数据源中查找,并 reload 我们的表视图:
extension MusicSearchViewController: UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController) {
let searchBar = searchController.searchBar
filterContentForSearchText(searchBar.text!)
}
}
extension MusicSearchViewController: UISearchBarDelegate{
func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
filterContentForSearchText(searchBar.text!)
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
self.navigationController?.popViewController(animated: true)
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
self.searchController.searchBar.resignFirstResponder()
}
}
func filterContentForSearchText(_ searchText: String){
filteredMusic = musics.filter{ music in
return music.name.lowercased().contains(searchText.lowercased()) || searchText == ""
}
tableView.reloadData()
}
使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta
我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何
我想要做的是有2个不同的Controller,client和test_client。客户端Controller已经构建,我想创建一个test_clientController,我可以使用它来玩弄客户端的UI并根据需要进行调整。我主要是想绕过我在客户端中内置的验证及其对加载数据的管理Controller的依赖。所以我希望test_clientController加载示例数据集,然后呈现客户端Controller的索引View,以便我可以调整客户端UI。就是这样。我在test_clients索引方法中试过这个:classTestClientdefindexrender:template=>
如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象
关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion为什么SecureRandom.uuid创建一个唯一的字符串?SecureRandom.uuid#=>"35cb4e30-54e1-49f9-b5ce-4134799eb2c0"SecureRandom.uuid方法创建的字符串从不重复?
我有一个正在构建的应用程序,我需要一个模型来创建另一个模型的实例。我希望每辆车都有4个轮胎。汽车模型classCar轮胎模型classTire但是,在make_tires内部有一个错误,如果我为Tire尝试它,则没有用于创建或新建的activerecord方法。当我检查轮胎时,它没有这些方法。我该如何补救?错误是这样的:未定义的方法'create'forActiveRecord::AttributeMethods::Serialization::Tire::Module我测试了两个环境:测试和开发,它们都因相同的错误而失败。 最佳答案
我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b
我想让一个yaml对象引用另一个,如下所示:intro:"Hello,dearuser."registration:$introThanksforregistering!new_message:$introYouhaveanewmessage!上面的语法只是它如何工作的一个例子(这也是它在thiscpanmodule中的工作方式。)我正在使用标准的rubyyaml解析器。这可能吗? 最佳答案 一些yaml对象确实引用了其他对象:irb>require'yaml'#=>trueirb>str="hello"#=>"hello"ir
我的问题的一个例子是体育游戏。一场体育比赛有两支球队,一支主队和一支客队。我的事件记录模型如下:classTeam"Team"has_one:away_team,:class_name=>"Team"end我希望能够通过游戏访问一个团队,例如:Game.find(1).home_team但我收到一个单元化常量错误:Game::team。谁能告诉我我做错了什么?谢谢, 最佳答案 如果Gamehas_one:team那么Rails假设您的teams表有一个game_id列。不过,您想要的是games表有一个team_id列,在这种情况下
Unity自动旋转动画1.开门需要门把手先动,门再动2.关门需要门先动,门把手再动3.中途播放过程中不可以再次进行操作觉得太复杂?查看我的文章开关门简易进阶版效果:如果这个门可以直接打开的话,就不需要放置"门把手"如果门把手还有钥匙需要旋转,那就可以把钥匙放在门把手的"门把手",理论上是可以无限套娃的可调整参数有:角度,反向,轴向,速度运行时点击Test进行测试自己写的代码比较垃圾,命名与结构比较拉,高手轻点喷,新手有类似的需求可以拿去做参考上代码usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;u